From ede8113d3421d3d6063cac9532c2a510ef7a3b3e Mon Sep 17 00:00:00 2001 From: Jeffrey Morlan <jeffrey@everlaw.com> Date: Fri, 20 Nov 2015 18:18:24 -0800 Subject: [PATCH] Upgrade to TypeScript 1.7.3 --- README.md | 4 +- build.xml | 2 +- manifest.mf | 2 +- ts/compiler/binder.ts | 23 +- ts/compiler/checker.ts | 1352 ++++++++++------- ts/compiler/commandLineParser.ts | 35 +- ts/compiler/core.ts | 52 +- ts/compiler/declarationEmitter.ts | 22 +- .../diagnosticInformationMap.generated.ts | 40 +- ts/compiler/emitter.ts | 1215 ++++++++------- ts/compiler/parser.ts | 261 +++- ts/compiler/program.ts | 271 ++-- ts/compiler/scanner.ts | 49 +- ts/compiler/sys.ts | 156 +- ts/compiler/types.ts | 90 +- ts/compiler/utilities.ts | 81 +- ts/main.ts | 4 +- ts/services/formatting/formatting.ts | 62 +- ts/services/formatting/formattingScanner.ts | 21 +- ts/services/formatting/rules.ts | 60 +- ts/services/formatting/smartIndenter.ts | 41 +- ts/services/services.ts | 184 ++- ts/services/utilities.ts | 29 +- 23 files changed, 2574 insertions(+), 1482 deletions(-) diff --git a/README.md b/README.md index 00b51df..b1bb73a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Download the latest netbeanstypescript.nbm file from the [Releases](https://gith ### Versioning -The version number of this plugin reflects the version of TypeScript it incorporates (currently 1.6.2), with an extra digit for new versions that do not involve a TypeScript update. We intend to keep this plugin up to date with new versions of TypeScript when they come out. +The version number of this plugin reflects the version of TypeScript it incorporates (currently 1.7.3), with an extra digit for new versions that do not involve a TypeScript update. We intend to keep this plugin up to date with new versions of TypeScript when they come out. ### Contributing @@ -33,5 +33,5 @@ We are happy to receive Pull Requests. If you are planning a big change, it's pr To build the plugin yourself, there are a couple of things that must be done first: * Edit this line in `build.xml` to point to where you have TypeScript installed: - `<property name="typescript" value="${env.HOME}/TypeScript-1.6.2"/>` + `<property name="typescript" value="${env.HOME}/TypeScript-1.7.3"/>` * Open the project in NetBeans. (You can either build in NetBeans or run `ant` from the command line, but either way you must open the project first to create the `nbproject/private` files.) diff --git a/build.xml b/build.xml index 2ba99f6..2b04c51 100644 --- a/build.xml +++ b/build.xml @@ -7,7 +7,7 @@ <import file="nbproject/build-impl.xml"/> <property environment="env"/> - <property name="typescript" value="${env.HOME}/TypeScript-1.6.2"/> + <property name="typescript" value="${env.HOME}/TypeScript-1.7.3"/> <target name="compile" depends="projectized-common.compile"> <copy file="${typescript}/lib/lib.d.ts" todir="${build.classes.dir}/netbeanstypescript/resources/"/> diff --git a/manifest.mf b/manifest.mf index d4c7142..68fc6a9 100644 --- a/manifest.mf +++ b/manifest.mf @@ -3,4 +3,4 @@ AutoUpdate-Show-In-Client: true OpenIDE-Module: netbeanstypescript OpenIDE-Module-Layer: netbeanstypescript/resources/layer.xml OpenIDE-Module-Localizing-Bundle: netbeanstypescript/Bundle.properties -OpenIDE-Module-Specification-Version: 1.6.2.1 +OpenIDE-Module-Specification-Version: 1.7.3.0 diff --git a/ts/compiler/binder.ts b/ts/compiler/binder.ts index 7a0d165..b3f2219 100644 --- a/ts/compiler/binder.ts +++ b/ts/compiler/binder.ts @@ -91,6 +91,7 @@ namespace ts { let container: Node; let blockScopeContainer: Node; let lastContainer: Node; + let seenThisKeyword: boolean; // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or @@ -193,8 +194,9 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); + let isDefaultExport = node.flags & NodeFlags.Default; // The exported symbol for an export default function/class node is always named "default" - let name = node.flags & NodeFlags.Default && parent ? "default" : getDeclarationName(node); + let name = isDefaultExport && parent ? "default" : getDeclarationName(node); let symbol: Symbol; if (name !== undefined) { @@ -235,6 +237,13 @@ namespace ts { let message = symbol.flags & SymbolFlags.BlockScopedVariable ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; + + forEach(symbol.declarations, declaration => { + if (declaration.flags & NodeFlags.Default) { + message = Diagnostics.A_module_cannot_have_multiple_default_exports; + } + }); + forEach(symbol.declarations, declaration => { file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); }); @@ -338,7 +347,14 @@ namespace ts { blockScopeContainer.locals = undefined; } - forEachChild(node, bind); + if (node.kind === SyntaxKind.InterfaceDeclaration) { + seenThisKeyword = false; + forEachChild(node, bind); + node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis; + } + else { + forEachChild(node, bind); + } container = saveContainer; parent = saveParent; @@ -860,6 +876,9 @@ namespace ts { return checkStrictModePrefixUnaryExpression(<PrefixUnaryExpression>node); case SyntaxKind.WithStatement: return checkStrictModeWithStatement(<WithStatement>node); + case SyntaxKind.ThisKeyword: + seenThisKeyword = true; + return; case SyntaxKind.TypeParameter: return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); diff --git a/ts/compiler/checker.ts b/ts/compiler/checker.ts index bfe72d7..10e67a2 100644 --- a/ts/compiler/checker.ts +++ b/ts/compiler/checker.ts @@ -41,12 +41,14 @@ namespace ts { let Signature = objectAllocator.getSignatureConstructor(); let typeCount = 0; + let symbolCount = 0; let emptyArray: any[] = []; let emptySymbols: SymbolTable = {}; let compilerOptions = host.getCompilerOptions(); let languageVersion = compilerOptions.target || ScriptTarget.ES3; + let modulekind = compilerOptions.module ? compilerOptions.module : languageVersion === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.None; let emitResolver = createResolver(); @@ -56,7 +58,7 @@ namespace ts { let checker: TypeChecker = { getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), - getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount"), + getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount, getTypeCount: () => typeCount, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, @@ -245,6 +247,7 @@ namespace ts { } function createSymbol(flags: SymbolFlags, name: string): Symbol { + symbolCount++; return new Symbol(flags, name); } @@ -383,20 +386,72 @@ namespace ts { // return undefined if we can't find a symbol. } - /** Returns true if node1 is defined before node 2**/ - function isDefinedBefore(node1: Node, node2: Node): boolean { - let file1 = getSourceFileOfNode(node1); - let file2 = getSourceFileOfNode(node2); - if (file1 === file2) { - return node1.pos <= node2.pos; + function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean { + const declarationFile = getSourceFileOfNode(declaration); + const useFile = getSourceFileOfNode(usage); + if (declarationFile !== useFile) { + if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) { + // nodes are in different files and order cannot be determines + return true; + } + + const sourceFiles = host.getSourceFiles(); + return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile); } - if (!compilerOptions.outFile && !compilerOptions.out) { - return true; + if (declaration.pos <= usage.pos) { + // declaration is before usage + // still might be illegal if usage is in the initializer of the variable declaration + return declaration.kind !== SyntaxKind.VariableDeclaration || + !isImmediatelyUsedInInitializerOfBlockScopedVariable(<VariableDeclaration>declaration, usage); + } + + // declaration is after usage + // can be legal if usage is deferred (i.e. inside function or in initializer of instance property) + return isUsedInFunctionOrNonStaticProperty(declaration, usage); + + function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean { + const container = getEnclosingBlockScopeContainer(declaration); + + if (declaration.parent.parent.kind === SyntaxKind.VariableStatement || + declaration.parent.parent.kind === SyntaxKind.ForStatement) { + // variable statement/for statement case, + // use site should not be inside variable declaration (initializer of declaration or binding element) + return isSameScopeDescendentOf(usage, declaration, container); + } + else if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement || + declaration.parent.parent.kind === SyntaxKind.ForInStatement) { + // ForIn/ForOf case - use site should not be used in expression part + let expression = (<ForInStatement | ForOfStatement>declaration.parent.parent).expression; + return isSameScopeDescendentOf(usage, expression, container); + } } - let sourceFiles = host.getSourceFiles(); - return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2); + function isUsedInFunctionOrNonStaticProperty(declaration: Declaration, usage: Node): boolean { + const container = getEnclosingBlockScopeContainer(declaration); + let current = usage; + while (current) { + if (current === container) { + return false; + } + + if (isFunctionLike(current)) { + return true; + } + + const initializerOfNonStaticProperty = current.parent && + current.parent.kind === SyntaxKind.PropertyDeclaration && + (current.parent.flags & NodeFlags.Static) === 0 && + (<PropertyDeclaration>current.parent).initializer === current; + + if (initializerOfNonStaticProperty) { + return true; + } + + current = current.parent; + } + return false; + } } // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and @@ -610,8 +665,11 @@ namespace ts { // block - scope variable and namespace module. However, only when we // try to resolve name in /*1*/ which is used in variable position, // we want to check for block- scoped - if (meaning & SymbolFlags.BlockScopedVariable && result.flags & SymbolFlags.BlockScopedVariable) { - checkResolvedBlockScopedVariable(result, errorLocation); + if (meaning & SymbolFlags.BlockScopedVariable) { + const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result); + if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable) { + checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation); + } } } return result; @@ -624,34 +682,7 @@ namespace ts { Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined"); - // first check if usage is lexically located after the declaration - let isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation); - if (!isUsedBeforeDeclaration) { - // lexical check succeeded however code still can be illegal. - // - block scoped variables cannot be used in its initializers - // let x = x; // illegal but usage is lexically after definition - // - in ForIn/ForOf statements variable cannot be contained in expression part - // for (let x in x) - // for (let x of x) - - // climb up to the variable declaration skipping binding patterns - let variableDeclaration = <VariableDeclaration>getAncestor(declaration, SyntaxKind.VariableDeclaration); - let container = getEnclosingBlockScopeContainer(variableDeclaration); - - if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) { - // variable statement/for statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container); - } - else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement || - variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) { - // ForIn/ForOf case - use site should not be used in expression part - let expression = (<ForInStatement | ForOfStatement>variableDeclaration.parent.parent).expression; - isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container); - } - } - if (isUsedBeforeDeclaration) { + if (!isBlockScopedNameDeclaredBeforeUse(<Declaration>getAncestor(declaration, SyntaxKind.VariableDeclaration), errorLocation)) { error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name)); } } @@ -950,12 +981,6 @@ namespace ts { return symbol.flags & meaning ? symbol : resolveAlias(symbol); } - function isExternalModuleNameRelative(moduleName: string): boolean { - // TypeScript 1.0 spec (April 2014): 11.2.1 - // An external module name is "relative" if the first term is "." or "..". - return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\"; - } - function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol { if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral) { return; @@ -968,7 +993,7 @@ namespace ts { // Escape the name in the "require(...)" clause to ensure we find the right symbol. let moduleName = escapeIdentifier(moduleReferenceLiteral.text); - if (!moduleName) { + if (moduleName === undefined) { return; } let isRelative = isExternalModuleNameRelative(moduleName); @@ -1602,6 +1627,7 @@ namespace ts { function buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]) { let globalFlagsToPass = globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike; + let inObjectTypeLiteral = false; return writeType(type, globalFlags); function writeType(type: Type, flags: TypeFormatFlags) { @@ -1612,6 +1638,12 @@ namespace ts { ? "any" : (<IntrinsicType>type).intrinsicName); } + else if (type.flags & TypeFlags.ThisType) { + if (inObjectTypeLiteral) { + writer.reportInaccessibleThisError(); + } + writer.writeKeyword("this"); + } else if (type.flags & TypeFlags.Reference) { writeTypeReference(<TypeReference>type, flags); } @@ -1655,11 +1687,10 @@ namespace ts { } } - function writeSymbolTypeReference(symbol: Symbol, typeArguments: Type[], pos: number, end: number) { - // Unnamed function expressions, arrow functions, and unnamed class expressions have reserved names that - // we don't want to display - if (!isReservedMemberName(symbol.name)) { - buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type); + function writeSymbolTypeReference(symbol: Symbol, typeArguments: Type[], pos: number, end: number, flags: TypeFormatFlags) { + // Unnamed function expressions and arrow functions have reserved names that we don't want to display + if (symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.name)) { + buildSymbolDisplay(symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags); } if (pos < end) { writePunctuation(writer, SyntaxKind.LessThanToken); @@ -1674,7 +1705,7 @@ namespace ts { } function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) { - let typeArguments = type.typeArguments; + let typeArguments = type.typeArguments || emptyArray; if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) { writeType(typeArguments[0], TypeFormatFlags.InElementType); writePunctuation(writer, SyntaxKind.OpenBracketToken); @@ -1698,12 +1729,13 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - writeSymbolTypeReference(parent, typeArguments, start, i); + writeSymbolTypeReference(parent, typeArguments, start, i, flags); writePunctuation(writer, SyntaxKind.DotToken); } } } - writeSymbolTypeReference(type.symbol, typeArguments, i, typeArguments.length); + let typeParameterCount = (type.target.typeParameters || emptyArray).length; + writeSymbolTypeReference(type.symbol, typeArguments, i, typeParameterCount, flags); } } @@ -1826,6 +1858,8 @@ namespace ts { } } + let saveInObjectTypeLiteral = inObjectTypeLiteral; + inObjectTypeLiteral = true; writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); @@ -1898,6 +1932,7 @@ namespace ts { } writer.decreaseIndent(); writePunctuation(writer, SyntaxKind.CloseBraceToken); + inObjectTypeLiteral = saveInObjectTypeLiteral; } } @@ -2212,7 +2247,7 @@ namespace ts { /** * Push an entry on the type resolution stack. If an entry with the given target and the given property name - * is already on the stack, and no entries in between already have a type, then a circularity has occurred. + * is already on the stack, and no entries in between already have a type, then a circularity has occurred. * In this case, the result values of the existing entry and all entries pushed after it are changed to false, * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. * In order to see if the same query has already been done before, the target object and the propertyName both @@ -2303,10 +2338,17 @@ namespace ts { return type && (type.flags & TypeFlags.Any) !== 0; } + // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been + // assigned by contextual typing. + function getTypeForBindingElementParent(node: VariableLikeDeclaration) { + let symbol = getSymbolOfNode(node); + return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node); + } + // Return the inferred type for a binding element function getTypeForBindingElement(declaration: BindingElement): Type { let pattern = <BindingPattern>declaration.parent; - let parentType = getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>pattern.parent); + let parentType = getTypeForBindingElementParent(<VariableLikeDeclaration>pattern.parent); // If parent has the unknown (error) type, then so does this binding element if (parentType === unknownType) { return unknownType; @@ -2328,8 +2370,8 @@ namespace ts { // Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature, // or otherwise the type of the string index signature. type = getTypeOfPropertyOfType(parentType, name.text) || - isNumericLiteralName(name.text) && getIndexTypeOfType(parentType, IndexKind.Number) || - getIndexTypeOfType(parentType, IndexKind.String); + isNumericLiteralName(name.text) && getIndexTypeOfType(parentType, IndexKind.Number) || + getIndexTypeOfType(parentType, IndexKind.String); if (!type) { error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(parentType), declarationNameToString(name)); return unknownType; @@ -2382,7 +2424,7 @@ namespace ts { if (isBindingPattern(declaration.parent)) { return getTypeForBindingElement(<BindingElement>declaration); } - + // Use type from type annotation if one is present if (declaration.type) { return getTypeFromTypeNode(declaration.type); @@ -2403,12 +2445,12 @@ namespace ts { return type; } } - + // Use the type of the initializer expression if one is present if (declaration.initializer) { return checkExpressionCached(declaration.initializer); } - + // If it is a short-hand property assignment, use the type of the identifier if (declaration.kind === SyntaxKind.ShorthandPropertyAssignment) { return checkIdentifier(<Identifier>declaration.name); @@ -2416,7 +2458,7 @@ namespace ts { // If the declaration specifies a binding pattern, use the type implied by the binding pattern if (isBindingPattern(declaration.name)) { - return getTypeFromBindingPattern(<BindingPattern>declaration.name); + return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ false); } // No type specified and nothing can be inferred @@ -2426,48 +2468,47 @@ namespace ts { // Return the type implied by a binding pattern element. This is the type of the initializer of the element if // one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding // pattern. Otherwise, it is the type any. - function getTypeFromBindingElement(element: BindingElement): Type { + function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean): Type { if (element.initializer) { return getWidenedType(checkExpressionCached(element.initializer)); } if (isBindingPattern(element.name)) { - return getTypeFromBindingPattern(<BindingPattern>element.name); + return getTypeFromBindingPattern(<BindingPattern>element.name, includePatternInType); } return anyType; } // Return the type implied by an object binding pattern - function getTypeFromObjectBindingPattern(pattern: BindingPattern): Type { + function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type { let members: SymbolTable = {}; forEach(pattern.elements, e => { let flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0); let name = e.propertyName || <Identifier>e.name; let symbol = <TransientSymbol>createSymbol(flags, name.text); - symbol.type = getTypeFromBindingElement(e); + symbol.type = getTypeFromBindingElement(e, includePatternInType); + symbol.bindingElement = e; members[symbol.name] = symbol; }); - return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); + let result = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); + if (includePatternInType) { + result.pattern = pattern; + } + return result; } // Return the type implied by an array binding pattern - function getTypeFromArrayBindingPattern(pattern: BindingPattern): Type { - let hasSpreadElement: boolean = false; - let elementTypes: Type[] = []; - forEach(pattern.elements, e => { - elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e)); - if (e.dotDotDotToken) { - hasSpreadElement = true; - } - }); - if (!elementTypes.length) { + function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type { + let elements = pattern.elements; + if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) { return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType; } - else if (hasSpreadElement) { - let unionOfElements = getUnionType(elementTypes); - return languageVersion >= ScriptTarget.ES6 ? createIterableType(unionOfElements) : createArrayType(unionOfElements); - } - // If the pattern has at least one element, and no rest element, then it should imply a tuple type. + let elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType)); + if (includePatternInType) { + let result = createNewTupleType(elementTypes); + result.pattern = pattern; + return result; + } return createTupleType(elementTypes); } @@ -2478,10 +2519,10 @@ namespace ts { // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of // the parameter. - function getTypeFromBindingPattern(pattern: BindingPattern): Type { + function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean): Type { return pattern.kind === SyntaxKind.ObjectBindingPattern - ? getTypeFromObjectBindingPattern(pattern) - : getTypeFromArrayBindingPattern(pattern); + ? getTypeFromObjectBindingPattern(pattern, includePatternInType) + : getTypeFromArrayBindingPattern(pattern, includePatternInType); } // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type @@ -2504,10 +2545,10 @@ namespace ts { // tools see the actual type. return declaration.kind !== SyntaxKind.PropertyAssignment ? getWidenedType(type) : type; } - + // Rest parameters default to type any[], other parameters default to type any type = declaration.dotDotDotToken ? anyArrayType : anyType; - + // Report implicit any errors unless this is a private property within an ambient declaration if (reportErrors && compilerOptions.noImplicitAny) { let root = getRootDeclaration(declaration); @@ -2814,23 +2855,29 @@ namespace ts { return type.resolvedBaseConstructorType; } + function hasClassBaseType(type: InterfaceType): boolean { + return !!forEach(getBaseTypes(type), t => !!(t.symbol.flags & SymbolFlags.Class)); + } + function getBaseTypes(type: InterfaceType): ObjectType[] { + let isClass = type.symbol.flags & SymbolFlags.Class; + let isInterface = type.symbol.flags & SymbolFlags.Interface; if (!type.resolvedBaseTypes) { - if (type.symbol.flags & SymbolFlags.Class) { + if (!isClass && !isInterface) { + Debug.fail("type must be class or interface"); + } + if (isClass) { resolveBaseTypesOfClass(type); } - else if (type.symbol.flags & SymbolFlags.Interface) { + if (isInterface) { resolveBaseTypesOfInterface(type); } - else { - Debug.fail("type must be class or interface"); - } } return type.resolvedBaseTypes; } function resolveBaseTypesOfClass(type: InterfaceType): void { - type.resolvedBaseTypes = emptyArray; + type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; let baseContructorType = getBaseConstructorTypeOfClass(type); if (!(baseContructorType.flags & TypeFlags.ObjectType)) { return; @@ -2866,11 +2913,16 @@ namespace ts { typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); return; } - type.resolvedBaseTypes = [baseType]; + if (type.resolvedBaseTypes === emptyArray) { + type.resolvedBaseTypes = [baseType]; + } + else { + type.resolvedBaseTypes.push(baseType); + } } function resolveBaseTypesOfInterface(type: InterfaceType): void { - type.resolvedBaseTypes = []; + type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; for (let declaration of type.symbol.declarations) { if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) { for (let node of getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration)) { @@ -2878,7 +2930,12 @@ namespace ts { if (baseType !== unknownType) { if (getTargetType(baseType).flags & (TypeFlags.Class | TypeFlags.Interface)) { if (type !== baseType && !hasBaseType(<InterfaceType>baseType, type)) { - type.resolvedBaseTypes.push(baseType); + if (type.resolvedBaseTypes === emptyArray) { + type.resolvedBaseTypes = [baseType]; + } + else { + type.resolvedBaseTypes.push(baseType); + } } else { error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); @@ -2893,6 +2950,31 @@ namespace ts { } } + // Returns true if the interface given by the symbol is free of "this" references. Specifically, the result is + // true if the interface itself contains no references to "this" in its body, if all base types are interfaces, + // and if none of the base interfaces have a "this" type. + function isIndependentInterface(symbol: Symbol): boolean { + for (let declaration of symbol.declarations) { + if (declaration.kind === SyntaxKind.InterfaceDeclaration) { + if (declaration.flags & NodeFlags.ContainsThis) { + return false; + } + let baseTypeNodes = getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration); + if (baseTypeNodes) { + for (let node of baseTypeNodes) { + if (isSupportedExpressionWithTypeArguments(node)) { + let baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); + if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { + return false; + } + } + } + } + } + } + return true; + } + function getDeclaredTypeOfClassOrInterface(symbol: Symbol): InterfaceType { let links = getSymbolLinks(symbol); if (!links.declaredType) { @@ -2900,7 +2982,12 @@ namespace ts { let type = links.declaredType = <InterfaceType>createObjectType(kind, symbol); let outerTypeParameters = getOuterTypeParametersOfClassOrInterface(symbol); let localTypeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); - if (outerTypeParameters || localTypeParameters) { + // A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type + // because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular, + // property types inferred from initializers and method return types inferred from return statements are very hard + // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of + // "this" references. + if (outerTypeParameters || localTypeParameters || kind === TypeFlags.Class || !isIndependentInterface(symbol)) { type.flags |= TypeFlags.Reference; type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; @@ -2909,6 +2996,9 @@ namespace ts { (<GenericType>type).instantiations[getTypeListId(type.typeParameters)] = <GenericType>type; (<GenericType>type).target = <GenericType>type; (<GenericType>type).typeArguments = type.typeParameters; + type.thisType = <TypeParameter>createType(TypeFlags.TypeParameter | TypeFlags.ThisType); + type.thisType.symbol = symbol; + type.thisType.constraint = getTypeWithThisArgument(type); } } return <InterfaceType>links.declaredType; @@ -2993,6 +3083,82 @@ namespace ts { return unknownType; } + // A type reference is considered independent if each type argument is considered independent. + function isIndependentTypeReference(node: TypeReferenceNode): boolean { + if (node.typeArguments) { + for (let typeNode of node.typeArguments) { + if (!isIndependentType(typeNode)) { + return false; + } + } + } + return true; + } + + // A type is considered independent if it the any, string, number, boolean, symbol, or void keyword, a string + // literal type, an array with an element type that is considered independent, or a type reference that is + // considered independent. + function isIndependentType(node: TypeNode): boolean { + switch (node.kind) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.SymbolKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.StringLiteral: + return true; + case SyntaxKind.ArrayType: + return isIndependentType((<ArrayTypeNode>node).elementType); + case SyntaxKind.TypeReference: + return isIndependentTypeReference(<TypeReferenceNode>node); + } + return false; + } + + // A variable-like declaration is considered independent (free of this references) if it has a type annotation + // that specifies an independent type, or if it has no type annotation and no initializer (and thus of type any). + function isIndependentVariableLikeDeclaration(node: VariableLikeDeclaration): boolean { + return node.type && isIndependentType(node.type) || !node.type && !node.initializer; + } + + // A function-like declaration is considered independent (free of this references) if it has a return type + // annotation that is considered independent and if each parameter is considered independent. + function isIndependentFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean { + if (node.kind !== SyntaxKind.Constructor && (!node.type || !isIndependentType(node.type))) { + return false; + } + for (let parameter of node.parameters) { + if (!isIndependentVariableLikeDeclaration(parameter)) { + return false; + } + } + return true; + } + + // Returns true if the class or interface member given by the symbol is free of "this" references. The + // function may return false for symbols that are actually free of "this" references because it is not + // feasible to perform a complete analysis in all cases. In particular, property members with types + // inferred from their initializers and function members with inferred return types are convervatively + // assumed not to be free of "this" references. + function isIndependentMember(symbol: Symbol): boolean { + if (symbol.declarations && symbol.declarations.length === 1) { + let declaration = symbol.declarations[0]; + if (declaration) { + switch (declaration.kind) { + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + return isIndependentVariableLikeDeclaration(<VariableLikeDeclaration>declaration); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.Constructor: + return isIndependentFunctionLikeDeclaration(<FunctionLikeDeclaration>declaration); + } + } + } + return false; + } + function createSymbolTable(symbols: Symbol[]): SymbolTable { let result: SymbolTable = {}; for (let symbol of symbols) { @@ -3001,10 +3167,12 @@ namespace ts { return result; } - function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper): SymbolTable { + // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, + // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. + function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { let result: SymbolTable = {}; for (let symbol of symbols) { - result[symbol.name] = instantiateSymbol(symbol, mapper); + result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper); } return result; } @@ -3037,44 +3205,57 @@ namespace ts { return <InterfaceTypeWithDeclaredMembers>type; } - function resolveClassOrInterfaceMembers(type: InterfaceType): void { - let target = resolveDeclaredMembers(type); - let members = target.symbol.members; - let callSignatures = target.declaredCallSignatures; - let constructSignatures = target.declaredConstructSignatures; - let stringIndexType = target.declaredStringIndexType; - let numberIndexType = target.declaredNumberIndexType; - let baseTypes = getBaseTypes(target); + function getTypeWithThisArgument(type: ObjectType, thisArgument?: Type) { + if (type.flags & TypeFlags.Reference) { + return createTypeReference((<TypeReference>type).target, + concatenate((<TypeReference>type).typeArguments, [thisArgument || (<TypeReference>type).target.thisType])); + } + return type; + } + + function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: TypeParameter[], typeArguments: Type[]) { + let mapper = identityMapper; + let members = source.symbol.members; + let callSignatures = source.declaredCallSignatures; + let constructSignatures = source.declaredConstructSignatures; + let stringIndexType = source.declaredStringIndexType; + let numberIndexType = source.declaredNumberIndexType; + if (!rangeEquals(typeParameters, typeArguments, 0, typeParameters.length)) { + mapper = createTypeMapper(typeParameters, typeArguments); + members = createInstantiatedSymbolTable(source.declaredProperties, mapper, /*mappingThisOnly*/ typeParameters.length === 1); + callSignatures = instantiateList(source.declaredCallSignatures, mapper, instantiateSignature); + constructSignatures = instantiateList(source.declaredConstructSignatures, mapper, instantiateSignature); + stringIndexType = instantiateType(source.declaredStringIndexType, mapper); + numberIndexType = instantiateType(source.declaredNumberIndexType, mapper); + } + let baseTypes = getBaseTypes(source); if (baseTypes.length) { - members = createSymbolTable(target.declaredProperties); + if (members === source.symbol.members) { + members = createSymbolTable(source.declaredProperties); + } + let thisArgument = lastOrUndefined(typeArguments); for (let baseType of baseTypes) { - addInheritedMembers(members, getPropertiesOfObjectType(baseType)); - callSignatures = concatenate(callSignatures, getSignaturesOfType(baseType, SignatureKind.Call)); - constructSignatures = concatenate(constructSignatures, getSignaturesOfType(baseType, SignatureKind.Construct)); - stringIndexType = stringIndexType || getIndexTypeOfType(baseType, IndexKind.String); - numberIndexType = numberIndexType || getIndexTypeOfType(baseType, IndexKind.Number); + let instantiatedBaseType = thisArgument ? getTypeWithThisArgument(instantiateType(baseType, mapper), thisArgument) : baseType; + addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType)); + callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); + constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); + stringIndexType = stringIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.String); + numberIndexType = numberIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.Number); } } setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType); } + function resolveClassOrInterfaceMembers(type: InterfaceType): void { + resolveObjectTypeMembers(type, resolveDeclaredMembers(type), emptyArray, emptyArray); + } + function resolveTypeReferenceMembers(type: TypeReference): void { - let target = resolveDeclaredMembers(type.target); - let mapper = createTypeMapper(target.typeParameters, type.typeArguments); - let members = createInstantiatedSymbolTable(target.declaredProperties, mapper); - let callSignatures = instantiateList(target.declaredCallSignatures, mapper, instantiateSignature); - let constructSignatures = instantiateList(target.declaredConstructSignatures, mapper, instantiateSignature); - let stringIndexType = target.declaredStringIndexType ? instantiateType(target.declaredStringIndexType, mapper) : undefined; - let numberIndexType = target.declaredNumberIndexType ? instantiateType(target.declaredNumberIndexType, mapper) : undefined; - forEach(getBaseTypes(target), baseType => { - let instantiatedBaseType = instantiateType(baseType, mapper); - addInheritedMembers(members, getPropertiesOfObjectType(instantiatedBaseType)); - callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); - constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); - stringIndexType = stringIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.String); - numberIndexType = numberIndexType || getIndexTypeOfType(instantiatedBaseType, IndexKind.Number); - }); - setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexType, numberIndexType); + let source = resolveDeclaredMembers(type.target); + let typeParameters = concatenate(source.typeParameters, [source.thisType]); + let typeArguments = type.typeArguments && type.typeArguments.length === typeParameters.length ? + type.typeArguments : concatenate(type.typeArguments, [type]); + resolveObjectTypeMembers(type, source, typeParameters, typeArguments); } function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[], @@ -3097,7 +3278,7 @@ namespace ts { } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { - if (!getBaseTypes(classType).length) { + if (!hasClassBaseType(classType)) { return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, undefined, 0, false, false)]; } let baseConstructorType = getBaseConstructorTypeOfClass(classType); @@ -3129,14 +3310,16 @@ namespace ts { } function resolveTupleTypeMembers(type: TupleType) { - let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes, /*noSubtypeReduction*/ true))); + let arrayElementType = getUnionType(type.elementTypes, /*noSubtypeReduction*/ true); + // Make the tuple type itself the 'this' type by including an extra type argument + let arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type])); let members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType); } function findMatchingSignature(signatureList: Signature[], signature: Signature, partialMatch: boolean, ignoreReturnTypes: boolean): Signature { - for (let s of signatureList) { + for (let s of signatureList) { if (compareSignatures(s, signature, partialMatch, ignoreReturnTypes, compareTypes)) { return s; } @@ -3242,7 +3425,7 @@ namespace ts { setObjectTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexType, numberIndexType); } - function resolveAnonymousTypeMembers(type: ObjectType) { + function resolveAnonymousTypeMembers(type: AnonymousType) { let symbol = type.symbol; let members: SymbolTable; let callSignatures: Signature[]; @@ -3250,7 +3433,14 @@ namespace ts { let stringIndexType: Type; let numberIndexType: Type; - if (symbol.flags & SymbolFlags.TypeLiteral) { + if (type.target) { + members = createInstantiatedSymbolTable(getPropertiesOfObjectType(type.target), type.mapper, /*mappingThisOnly*/ false); + callSignatures = instantiateList(getSignaturesOfType(type.target, SignatureKind.Call), type.mapper, instantiateSignature); + constructSignatures = instantiateList(getSignaturesOfType(type.target, SignatureKind.Construct), type.mapper, instantiateSignature); + stringIndexType = instantiateType(getIndexTypeOfType(type.target, IndexKind.String), type.mapper); + numberIndexType = instantiateType(getIndexTypeOfType(type.target, IndexKind.Number), type.mapper); + } + else if (symbol.flags & SymbolFlags.TypeLiteral) { members = symbol.members; callSignatures = getSignaturesOfSymbol(members["__call"]); constructSignatures = getSignaturesOfSymbol(members["__new"]); @@ -3288,11 +3478,14 @@ namespace ts { function resolveStructuredTypeMembers(type: ObjectType): ResolvedType { if (!(<ResolvedType>type).members) { - if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) { + if (type.flags & TypeFlags.Reference) { + resolveTypeReferenceMembers(<TypeReference>type); + } + else if (type.flags & (TypeFlags.Class | TypeFlags.Interface)) { resolveClassOrInterfaceMembers(<InterfaceType>type); } else if (type.flags & TypeFlags.Anonymous) { - resolveAnonymousTypeMembers(<ObjectType>type); + resolveAnonymousTypeMembers(<AnonymousType>type); } else if (type.flags & TypeFlags.Tuple) { resolveTupleTypeMembers(<TupleType>type); @@ -3303,9 +3496,6 @@ namespace ts { else if (type.flags & TypeFlags.Intersection) { resolveIntersectionTypeMembers(<IntersectionType>type); } - else { - resolveTypeReferenceMembers(<TypeReference>type); - } } return <ResolvedType>type; } @@ -3550,7 +3740,9 @@ namespace ts { function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { let links = getNodeLinks(declaration); if (!links.resolvedSignature) { - let classType = declaration.kind === SyntaxKind.Constructor ? getDeclaredTypeOfClassOrInterface((<ClassDeclaration>declaration.parent).symbol) : undefined; + let classType = declaration.kind === SyntaxKind.Constructor ? + getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol)) + : undefined; let typeParameters = classType ? classType.localTypeParameters : declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : undefined; let parameters: Symbol[] = []; @@ -3771,22 +3963,24 @@ namespace ts { } function getTypeListId(types: Type[]) { - switch (types.length) { - case 1: - return "" + types[0].id; - case 2: - return types[0].id + "," + types[1].id; - default: - let result = ""; - for (let i = 0; i < types.length; i++) { - if (i > 0) { - result += ","; + if (types) { + switch (types.length) { + case 1: + return "" + types[0].id; + case 2: + return types[0].id + "," + types[1].id; + default: + let result = ""; + for (let i = 0; i < types.length; i++) { + if (i > 0) { + result += ","; + } + result += types[i].id; } - - result += types[i].id; - } - return result; + return result; + } } + return ""; } // This function is used to propagate certain flags when creating new object type references and union types. @@ -3805,7 +3999,7 @@ namespace ts { let id = getTypeListId(typeArguments); let type = target.instantiations[id]; if (!type) { - let flags = TypeFlags.Reference | getPropagatingFlagsOfTypes(typeArguments); + let flags = TypeFlags.Reference | (typeArguments ? getPropagatingFlagsOfTypes(typeArguments) : 0); type = target.instantiations[id] = <TypeReference>createObjectType(flags, target.symbol); type.target = target; type.typeArguments = typeArguments; @@ -3864,8 +4058,8 @@ namespace ts { // Get type from reference to class or interface function getTypeFromClassOrInterfaceReference(node: TypeReferenceNode | ExpressionWithTypeArguments, symbol: Symbol): Type { - let type = getDeclaredTypeOfSymbol(symbol); - let typeParameters = (<InterfaceType>type).localTypeParameters; + let type = <InterfaceType>getDeclaredTypeOfSymbol(symbol); + let typeParameters = type.localTypeParameters; if (typeParameters) { if (!node.typeArguments || node.typeArguments.length !== typeParameters.length) { error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), typeParameters.length); @@ -3874,8 +4068,7 @@ namespace ts { // In a type reference, the outer type parameters of the referenced class or interface are automatically // supplied as type arguments and the type reference only specifies arguments for the local type parameters // of the class or interface. - return createTypeReference(<GenericType>type, concatenate((<InterfaceType>type).outerTypeParameters, - map(node.typeArguments, getTypeFromTypeNode))); + return createTypeReference(<GenericType>type, concatenate(type.outerTypeParameters, map(node.typeArguments, getTypeFromTypeNode))); } if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, typeToString(type)); @@ -4031,20 +4224,20 @@ namespace ts { /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ - function createTypeFromGenericGlobalType(genericGlobalType: GenericType, elementType: Type): Type { - return <ObjectType>genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, [elementType]) : emptyObjectType; + function createTypeFromGenericGlobalType(genericGlobalType: GenericType, typeArguments: Type[]): Type { + return genericGlobalType !== emptyGenericType ? createTypeReference(genericGlobalType, typeArguments) : emptyObjectType; } function createIterableType(elementType: Type): Type { - return createTypeFromGenericGlobalType(globalIterableType, elementType); + return createTypeFromGenericGlobalType(globalIterableType, [elementType]); } function createIterableIteratorType(elementType: Type): Type { - return createTypeFromGenericGlobalType(globalIterableIteratorType, elementType); + return createTypeFromGenericGlobalType(globalIterableIteratorType, [elementType]); } function createArrayType(elementType: Type): Type { - return createTypeFromGenericGlobalType(globalArrayType, elementType); + return createTypeFromGenericGlobalType(globalArrayType, [elementType]); } function getTypeFromArrayTypeNode(node: ArrayTypeNode): Type { @@ -4057,11 +4250,12 @@ namespace ts { function createTupleType(elementTypes: Type[]) { let id = getTypeListId(elementTypes); - let type = tupleTypes[id]; - if (!type) { - type = tupleTypes[id] = <TupleType>createObjectType(TypeFlags.Tuple | getPropagatingFlagsOfTypes(elementTypes)); - type.elementTypes = elementTypes; - } + return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes)); + } + + function createNewTupleType(elementTypes: Type[]) { + let type = <TupleType>createObjectType(TypeFlags.Tuple | getPropagatingFlagsOfTypes(elementTypes)); + type.elementTypes = elementTypes; return type; } @@ -4232,6 +4426,27 @@ namespace ts { return links.resolvedType; } + function getThisType(node: TypeNode): Type { + let container = getThisContainer(node, /*includeArrowFunctions*/ false); + let parent = container && container.parent; + if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) { + if (!(container.flags & NodeFlags.Static) && + (container.kind !== SyntaxKind.Constructor || isNodeDescendentOf(node, (<ConstructorDeclaration>container).body))) { + return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType; + } + } + error(node, Diagnostics.A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface); + return unknownType; + } + + function getTypeFromThisTypeNode(node: TypeNode): Type { + let links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = getThisType(node); + } + return links.resolvedType; + } + function getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: @@ -4246,6 +4461,8 @@ namespace ts { return esSymbolType; case SyntaxKind.VoidKeyword: return voidType; + case SyntaxKind.ThisKeyword: + return getTypeFromThisTypeNode(node); case SyntaxKind.StringLiteral: return getTypeFromStringLiteral(<StringLiteral>node); case SyntaxKind.TypeReference: @@ -4348,7 +4565,7 @@ namespace ts { } return t; }; - + mapper.context = context; return mapper; } @@ -4390,7 +4607,7 @@ namespace ts { } let result = createSignature(signature.declaration, freshTypeParameters, instantiateList(signature.parameters, mapper, instantiateSymbol), - signature.resolvedReturnType ? instantiateType(signature.resolvedReturnType, mapper) : undefined, + instantiateType(signature.resolvedReturnType, mapper), freshTypePredicate, signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); result.target = signature; @@ -4422,7 +4639,7 @@ namespace ts { return result; } - function instantiateAnonymousType(type: ObjectType, mapper: TypeMapper): ObjectType { + function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): ObjectType { if (mapper.instantiations) { let cachedType = mapper.instantiations[type.id]; if (cachedType) { @@ -4433,27 +4650,21 @@ namespace ts { mapper.instantiations = []; } // Mark the anonymous type as instantiated such that our infinite instantiation detection logic can recognize it - let result = <ResolvedType>createObjectType(TypeFlags.Anonymous | TypeFlags.Instantiated, type.symbol); - result.properties = instantiateList(getPropertiesOfObjectType(type), mapper, instantiateSymbol); - result.members = createSymbolTable(result.properties); - result.callSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Call), mapper, instantiateSignature); - result.constructSignatures = instantiateList(getSignaturesOfType(type, SignatureKind.Construct), mapper, instantiateSignature); - let stringIndexType = getIndexTypeOfType(type, IndexKind.String); - let numberIndexType = getIndexTypeOfType(type, IndexKind.Number); - if (stringIndexType) result.stringIndexType = instantiateType(stringIndexType, mapper); - if (numberIndexType) result.numberIndexType = instantiateType(numberIndexType, mapper); + let result = <AnonymousType>createObjectType(TypeFlags.Anonymous | TypeFlags.Instantiated, type.symbol); + result.target = type; + result.mapper = mapper; mapper.instantiations[type.id] = result; return result; } function instantiateType(type: Type, mapper: TypeMapper): Type { - if (mapper !== identityMapper) { + if (type && mapper !== identityMapper) { if (type.flags & TypeFlags.TypeParameter) { return mapper(<TypeParameter>type); } if (type.flags & TypeFlags.Anonymous) { return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) ? - instantiateAnonymousType(<ObjectType>type, mapper) : type; + instantiateAnonymousType(<AnonymousType>type, mapper) : type; } if (type.flags & TypeFlags.Reference) { return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>type).typeArguments, mapper, instantiateType)); @@ -4650,7 +4861,9 @@ namespace ts { // and intersection types are further deconstructed on the target side, we don't want to // make the check again (as it might fail for a partial target type). Therefore we obtain // the regular source type and proceed with that. - source = getRegularTypeOfObjectLiteral(source); + if (target.flags & TypeFlags.UnionOrIntersection) { + source = getRegularTypeOfObjectLiteral(source); + } } let saveErrorInfo = errorInfo; @@ -4698,7 +4911,7 @@ namespace ts { else { if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) { // We have type references to same target type, see if relationship holds for all type arguments - if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) { + if (result = typeArgumentsRelatedTo(<TypeReference>source, <TypeReference>target, reportErrors)) { return result; } } @@ -4729,7 +4942,7 @@ namespace ts { if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) { // We have type references to same target type, see if all type arguments are identical - if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, /*reportErrors*/ false)) { + if (result = typeArgumentsRelatedTo(<TypeReference>source, <TypeReference>target, /*reportErrors*/ false)) { return result; } } @@ -4780,7 +4993,7 @@ namespace ts { // We know *exactly* where things went wrong when comparing the types. // Use this property as the error node as this will be more helpful in // reasoning about what went wrong. - errorNode = prop.valueDeclaration + errorNode = prop.valueDeclaration; reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, symbolToString(prop), typeToString(target)); @@ -4851,9 +5064,14 @@ namespace ts { return result; } - function typesRelatedTo(sources: Type[], targets: Type[], reportErrors: boolean): Ternary { + function typeArgumentsRelatedTo(source: TypeReference, target: TypeReference, reportErrors: boolean): Ternary { + let sources = source.typeArguments || emptyArray; + let targets = target.typeArguments || emptyArray; + if (sources.length !== targets.length && relation === identityRelation) { + return Ternary.False; + } let result = Ternary.True; - for (let i = 0, len = sources.length; i < len; i++) { + for (let i = 0; i < targets.length; i++) { let related = isRelatedTo(sources[i], targets[i], reportErrors); if (!related) { return Ternary.False; @@ -5080,9 +5298,9 @@ namespace ts { if (kind === SignatureKind.Construct) { // Only want to compare the construct signatures for abstractness guarantees. - + // Because the "abstractness" of a class is the same across all construct signatures - // (internally we are checking the corresponding declaration), it is enough to perform + // (internally we are checking the corresponding declaration), it is enough to perform // the check and report an error once over all pairs of source and target construct signatures. // // sourceSig and targetSig are (possibly) undefined. @@ -5121,8 +5339,8 @@ namespace ts { function abstractSignatureRelatedTo(source: Type, sourceSig: Signature, target: Type, targetSig: Signature) { if (sourceSig && targetSig) { - let sourceDecl = source.symbol && getDeclarationOfKind(source.symbol, SyntaxKind.ClassDeclaration); - let targetDecl = target.symbol && getDeclarationOfKind(target.symbol, SyntaxKind.ClassDeclaration); + let sourceDecl = source.symbol && getClassLikeDeclarationOfSymbol(source.symbol); + let targetDecl = target.symbol && getClassLikeDeclarationOfSymbol(target.symbol); if (!sourceDecl) { // If the source object isn't itself a class declaration, it can be freely assigned, regardless @@ -5136,8 +5354,8 @@ namespace ts { let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature); let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature); - let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration); - let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration); + let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getClassLikeDeclarationOfSymbol(sourceReturnType.symbol); + let targetReturnDecl = targetReturnType && targetReturnType.symbol && getClassLikeDeclarationOfSymbol(targetReturnType.symbol); let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract; let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract; @@ -5419,11 +5637,10 @@ namespace ts { // M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N source = getErasedSignature(source); target = getErasedSignature(target); - let sourceLen = source.parameters.length; let targetLen = target.parameters.length; for (let i = 0; i < targetLen; i++) { - let s = source.hasRestParameter && i === sourceLen - 1 ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]); - let t = target.hasRestParameter && i === targetLen - 1 ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]); + let s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]); + let t = isRestParameterIndex(target, i) ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]); let related = compareTypes(s, t); if (!related) { return Ternary.False; @@ -5436,6 +5653,10 @@ namespace ts { return result; } + function isRestParameterIndex(signature: Signature, parameterIndex: number) { + return signature.hasRestParameter && parameterIndex >= signature.parameters.length - 1; + } + function isSupertypeOfEach(candidate: Type, types: Type[]): boolean { for (let type of types) { if (candidate !== type && !isTypeSubtypeOf(type, candidate)) return false; @@ -5521,6 +5742,7 @@ namespace ts { regularType.constructSignatures = (<ResolvedType>type).constructSignatures; regularType.stringIndexType = (<ResolvedType>type).stringIndexType; regularType.numberIndexType = (<ResolvedType>type).numberIndexType; + (<FreshObjectLiteralType>type).regularType = regularType; } return regularType; } @@ -5751,9 +5973,10 @@ namespace ts { } else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) { // If source and target are references to the same generic type, infer from type arguments - let sourceTypes = (<TypeReference>source).typeArguments; - let targetTypes = (<TypeReference>target).typeArguments; - for (let i = 0; i < sourceTypes.length; i++) { + let sourceTypes = (<TypeReference>source).typeArguments || emptyArray; + let targetTypes = (<TypeReference>target).typeArguments || emptyArray; + let count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; + for (let i = 0; i < count; i++) { inferFromTypes(sourceTypes[i], targetTypes[i]); } } @@ -6248,7 +6471,7 @@ namespace ts { return getUnionType(assignableConstituents); } } - + if (isTypeAssignableTo(narrowedTypeCandidate, originalType)) { // Narrow to the target type if it's assignable to the current type return narrowedTypeCandidate; @@ -6455,7 +6678,7 @@ namespace ts { if (isClassLike(container.parent)) { let symbol = getSymbolOfNode(container.parent); - return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : getDeclaredTypeOfSymbol(symbol); + return container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType; } return anyType; } @@ -6475,46 +6698,46 @@ namespace ts { let classType = classDeclaration && <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(classDeclaration)); let baseClassType = classType && getBaseTypes(classType)[0]; - let container = getSuperContainer(node, /*includeFunctions*/ true); + let container = getSuperContainer(node, /*includeFunctions*/ true); let needToCaptureLexicalThis = false; - if (!isCallExpression) { - // adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting + if (!isCallExpression) { + // adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting while (container && container.kind === SyntaxKind.ArrowFunction) { container = getSuperContainer(container, /*includeFunctions*/ true); needToCaptureLexicalThis = languageVersion < ScriptTarget.ES6; } } - + let canUseSuperExpression = isLegalUsageOfSuperExpression(container); let nodeCheckFlag: NodeCheckFlags = 0; - - // always set NodeCheckFlags for 'super' expression node - if (canUseSuperExpression) { + + // always set NodeCheckFlags for 'super' expression node + if (canUseSuperExpression) { if ((container.flags & NodeFlags.Static) || isCallExpression) { nodeCheckFlag = NodeCheckFlags.SuperStatic; } else { nodeCheckFlag = NodeCheckFlags.SuperInstance; } - + getNodeLinks(node).flags |= nodeCheckFlag; - + if (needToCaptureLexicalThis) { // call expressions are allowed only in constructors so they should always capture correct 'this' // super property access expressions can also appear in arrow functions - // in this case they should also use correct lexical this captureLexicalThis(node.parent, container); - } + } } - + if (!baseClassType) { if (!classDeclaration || !getClassExtendsHeritageClauseElement(classDeclaration)) { error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class); } - return unknownType; + return unknownType; } - + if (!canUseSuperExpression) { if (container && container.kind === SyntaxKind.ComputedPropertyName) { error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name); @@ -6525,20 +6748,20 @@ namespace ts { else { error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class); } - + return unknownType; } - + if (container.kind === SyntaxKind.Constructor && isInConstructorArgumentInitializer(node, container)) { // issue custom error message for super property access in constructor arguments (to be aligned with old compiler) error(node, Diagnostics.super_cannot_be_referenced_in_constructor_arguments); return unknownType; } - + return nodeCheckFlag === NodeCheckFlags.SuperStatic ? getBaseConstructorTypeOfClass(classType) : baseClassType; - + function isLegalUsageOfSuperExpression(container: Node): boolean { if (!container) { return false; @@ -6574,15 +6797,15 @@ namespace ts { } } } - + return false; - } + } } // Return contextual type of parameter or undefined if no contextual type is available function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { - if (isFunctionExpressionOrArrowFunction(parameter.parent)) { - let func = <FunctionExpression>parameter.parent; + let func = parameter.parent; + if (isFunctionExpressionOrArrowFunction(func) || isObjectLiteralMethod(func)) { if (isContextSensitive(func)) { let contextualSignature = getContextualSignature(func); if (contextualSignature) { @@ -6595,8 +6818,9 @@ namespace ts { } // If last parameter is contextually rest parameter get its type - if (indexOfParameter === (func.parameters.length - 1) && - funcHasRestParameters && contextualSignature.hasRestParameter && func.parameters.length >= contextualSignature.parameters.length) { + if (funcHasRestParameters && + indexOfParameter === (func.parameters.length - 1) && + isRestParameterIndex(contextualSignature, func.parameters.length - 1)) { return getTypeOfSymbol(lastOrUndefined(contextualSignature.parameters)); } } @@ -6623,7 +6847,7 @@ namespace ts { } } if (isBindingPattern(declaration.name)) { - return getTypeFromBindingPattern(<BindingPattern>declaration.name); + return getTypeFromBindingPattern(<BindingPattern>declaration.name, /*includePatternInType*/ true); } } return undefined; @@ -6914,7 +7138,7 @@ namespace ts { } } - function isFunctionExpressionOrArrowFunction(node: Node): boolean { + function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression { return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction; } @@ -6933,8 +7157,8 @@ namespace ts { function getContextualSignature(node: FunctionExpression | MethodDeclaration): Signature { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); let type = isObjectLiteralMethod(node) - ? getContextualTypeForObjectLiteralMethod(<MethodDeclaration>node) - : getContextualType(<FunctionExpression>node); + ? getContextualTypeForObjectLiteralMethod(node) + : getContextualType(node); if (!type) { return undefined; } @@ -7019,11 +7243,13 @@ namespace ts { return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false); } + function hasDefaultValue(node: BindingElement | Expression): boolean { + return (node.kind === SyntaxKind.BindingElement && !!(<BindingElement>node).initializer) || + (node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken); + } + function checkArrayLiteral(node: ArrayLiteralExpression, contextualMapper?: TypeMapper): Type { let elements = node.elements; - if (!elements.length) { - return createArrayType(undefinedType); - } let hasSpreadElement = false; let elementTypes: Type[] = []; let inDestructuringPattern = isAssignmentTarget(node); @@ -7055,12 +7281,39 @@ namespace ts { hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression; } if (!hasSpreadElement) { + // If array literal is actually a destructuring pattern, mark it as an implied type. We do this such + // that we get the same behavior for "var [x, y] = []" and "[x, y] = []". + if (inDestructuringPattern && elementTypes.length) { + let type = createNewTupleType(elementTypes); + type.pattern = node; + return type; + } let contextualType = getContextualType(node); - if (contextualType && contextualTypeIsTupleLikeType(contextualType) || inDestructuringPattern) { - return createTupleType(elementTypes); + if (contextualType && contextualTypeIsTupleLikeType(contextualType)) { + let pattern = contextualType.pattern; + // If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting + // tuple type with the corresponding binding or assignment element types to make the lengths equal. + if (pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) { + let patternElements = (<BindingPattern | ArrayLiteralExpression>pattern).elements; + for (let i = elementTypes.length; i < patternElements.length; i++) { + let patternElement = patternElements[i]; + if (hasDefaultValue(patternElement)) { + elementTypes.push((<TupleType>contextualType).elementTypes[i]); + } + else { + if (patternElement.kind !== SyntaxKind.OmittedExpression) { + error(patternElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); + } + elementTypes.push(unknownType); + } + } + } + if (elementTypes.length) { + return createTupleType(elementTypes); + } } } - return createArrayType(getUnionType(elementTypes)); + return createArrayType(elementTypes.length ? getUnionType(elementTypes) : undefinedType); } function isNumericName(name: DeclarationName): boolean { @@ -7121,12 +7374,15 @@ namespace ts { } function checkObjectLiteral(node: ObjectLiteralExpression, contextualMapper?: TypeMapper): Type { + let inDestructuringPattern = isAssignmentTarget(node); // Grammar checking - checkGrammarObjectLiteralExpression(node); + checkGrammarObjectLiteralExpression(node, inDestructuringPattern); let propertiesTable: SymbolTable = {}; let propertiesArray: Symbol[] = []; let contextualType = getContextualType(node); + let contextualTypeHasPattern = contextualType && contextualType.pattern && + (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); let typeFlags: TypeFlags = 0; for (let memberDecl of node.properties) { @@ -7147,6 +7403,28 @@ namespace ts { } typeFlags |= type.flags; let prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name); + if (inDestructuringPattern) { + // If object literal is an assignment pattern and if the assignment pattern specifies a default value + // for the property, make the property optional. + const isOptional = + (memberDecl.kind === SyntaxKind.PropertyAssignment && hasDefaultValue((<PropertyAssignment>memberDecl).initializer)) || + (memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment && (<ShorthandPropertyAssignment>memberDecl).objectAssignmentInitializer); + if (isOptional) { + prop.flags |= SymbolFlags.Optional; + } + } + else if (contextualTypeHasPattern) { + // If object literal is contextually typed by the implied type of a binding pattern, and if the + // binding pattern specifies a default value for the property, make the property optional. + let impliedProp = getPropertyOfType(contextualType, member.name); + if (impliedProp) { + prop.flags |= impliedProp.flags & SymbolFlags.Optional; + } + else if (!compilerOptions.suppressExcessPropertyErrors) { + error(memberDecl.name, Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, + symbolToString(member), typeToString(contextualType)); + } + } prop.declarations = member.declarations; prop.parent = member.parent; if (member.valueDeclaration) { @@ -7173,11 +7451,29 @@ namespace ts { propertiesArray.push(member); } + // If object literal is contextually typed by the implied type of a binding pattern, augment the result + // type with those properties for which the binding pattern specifies a default value. + if (contextualTypeHasPattern) { + for (let prop of getPropertiesOfType(contextualType)) { + if (!hasProperty(propertiesTable, prop.name)) { + if (!(prop.flags & SymbolFlags.Optional)) { + error(prop.valueDeclaration || (<TransientSymbol>prop).bindingElement, + Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); + } + propertiesTable[prop.name] = prop; + propertiesArray.push(prop); + } + } + } + let stringIndexType = getIndexType(IndexKind.String); let numberIndexType = getIndexType(IndexKind.Number); let result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexType, numberIndexType); let freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshObjectLiteral; result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags); + if (inDestructuringPattern) { + result.pattern = node; + } return result; function getIndexType(kind: IndexKind) { @@ -7291,7 +7587,7 @@ namespace ts { // Maybe there's a string indexer? let indexerType = getIndexTypeOfType(elementAttributesType, IndexKind.String); if (indexerType) { - correspondingPropType = indexerType + correspondingPropType = indexerType; } else { // If there's no corresponding property with this name, error @@ -7359,7 +7655,8 @@ namespace ts { if (!links.resolvedSymbol) { if (isJsxIntrinsicIdentifier(node.tagName)) { links.resolvedSymbol = lookupIntrinsicTag(node); - } else { + } + else { links.resolvedSymbol = lookupClassTag(node); } } @@ -7399,7 +7696,9 @@ namespace ts { // Look up the value in the current scope if (valueSymbol && valueSymbol !== unknownSymbol) { links.jsxFlags |= JsxFlags.ClassElement; - getSymbolLinks(valueSymbol).referenced = true; + if (valueSymbol.flags & SymbolFlags.Alias) { + markAliasSymbolAsReferenced(valueSymbol); + } } return valueSymbol || unknownSymbol; @@ -7743,6 +8042,11 @@ namespace ts { return true; } // An instance property must be accessed through an instance of the enclosing class + if (type.flags & TypeFlags.ThisType) { + // get the original type -- represented as the type constraint of the 'this' type + type = getConstraintOfTypeParameter(<TypeParameter>type); + } + // TODO: why is the first part of this check here? if (!(getTargetType(type).flags & (TypeFlags.Class | TypeFlags.Interface) && hasBaseType(<InterfaceType>type, enclosingClass))) { error(node, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass)); @@ -7773,7 +8077,7 @@ namespace ts { let prop = getPropertyOfType(apparentType, right.text); if (!prop) { if (right.text) { - error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type)); + error(right, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(right), typeToString(type.flags & TypeFlags.ThisType ? apparentType : type)); } return unknownType; } @@ -7781,7 +8085,7 @@ namespace ts { getNodeLinks(node).resolvedSymbol = prop; if (prop.parent && prop.parent.flags & SymbolFlags.Class) { - checkClassPropertyAccess(node, left, type, prop); + checkClassPropertyAccess(node, left, apparentType, prop); } return getTypeOfSymbol(prop); } @@ -7890,6 +8194,7 @@ namespace ts { /** * If indexArgumentExpression is a string literal or number literal, returns its text. + * If indexArgumentExpression is a constant value, returns its string value. * If indexArgumentExpression is a well known symbol, returns the property name corresponding * to this symbol, as long as it is a proper symbol reference. * Otherwise, returns undefined. @@ -7898,6 +8203,12 @@ namespace ts { if (indexArgumentExpression.kind === SyntaxKind.StringLiteral || indexArgumentExpression.kind === SyntaxKind.NumericLiteral) { return (<LiteralExpression>indexArgumentExpression).text; } + if (indexArgumentExpression.kind === SyntaxKind.ElementAccessExpression || indexArgumentExpression.kind === SyntaxKind.PropertyAccessExpression) { + let value = getConstantValue(<ElementAccessExpression | PropertyAccessExpression>indexArgumentExpression); + if (value !== undefined) { + return value.toString(); + } + } if (checkThatExpressionIsProperSymbolReference(indexArgumentExpression, indexArgumentType, /*reportError*/ false)) { let rightHandSideName = (<Identifier>(<PropertyAccessExpression>indexArgumentExpression).name).text; return getPropertyNameForKnownSymbolName(rightHandSideName); @@ -7983,9 +8294,9 @@ namespace ts { function reorderCandidates(signatures: Signature[], result: Signature[]): void { let lastParent: Node; let lastSymbol: Symbol; - let cutoffIndex: number = 0; + let cutoffIndex = 0; let index: number; - let specializedIndex: number = -1; + let specializedIndex = -1; let spliceIndex: number; Debug.assert(!result.length); for (let signature of signatures) { @@ -8103,7 +8414,7 @@ namespace ts { // If spread arguments are present, check that they correspond to a rest parameter. If so, no // further checking is necessary. if (spreadArgIndex >= 0) { - return signature.hasRestParameter && spreadArgIndex >= signature.parameters.length - 1; + return isRestParameterIndex(signature, spreadArgIndex); } // Too many arguments implies incorrect arity. @@ -8332,6 +8643,12 @@ namespace ts { case SyntaxKind.SetAccessor: // A method or accessor declaration decorator will have two or three arguments (see // `PropertyDecorator` and `MethodDecorator` in core.d.ts) + + // If we are emitting decorators for ES3, we will only pass two arguments. + if (languageVersion === ScriptTarget.ES3) { + return 2; + } + // If the method decorator signature only accepts a target and a key, we will only // type check those arguments. return signature.parameters.length >= 3 ? 3 : 2; @@ -8362,39 +8679,36 @@ namespace ts { */ function getEffectiveDecoratorFirstArgumentType(node: Node): Type { // The first argument to a decorator is its `target`. - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - // For a class decorator, the `target` is the type of the class (e.g. the - // "static" or "constructor" side of the class) + if (node.kind === SyntaxKind.ClassDeclaration) { + // For a class decorator, the `target` is the type of the class (e.g. the + // "static" or "constructor" side of the class) + let classSymbol = getSymbolOfNode(node); + return getTypeOfSymbol(classSymbol); + } + + if (node.kind === SyntaxKind.Parameter) { + // For a parameter decorator, the `target` is the parent type of the + // parameter's containing method. + node = node.parent; + if (node.kind === SyntaxKind.Constructor) { let classSymbol = getSymbolOfNode(node); return getTypeOfSymbol(classSymbol); + } + } - case SyntaxKind.Parameter: - // For a parameter decorator, the `target` is the parent type of the - // parameter's containing method. - node = node.parent; - if (node.kind === SyntaxKind.Constructor) { - let classSymbol = getSymbolOfNode(node); - return getTypeOfSymbol(classSymbol); - } - - // fall-through - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // For a property or method decorator, the `target` is the - // "static"-side type of the parent of the member if the member is - // declared "static"; otherwise, it is the "instance"-side type of the - // parent of the member. - return getParentTypeOfClassElement(<ClassElement>node); - - default: - Debug.fail("Unsupported decorator target."); - return unknownType; + if (node.kind === SyntaxKind.PropertyDeclaration || + node.kind === SyntaxKind.MethodDeclaration || + node.kind === SyntaxKind.GetAccessor || + node.kind === SyntaxKind.SetAccessor) { + // For a property or method decorator, the `target` is the + // "static"-side type of the parent of the member if the member is + // declared "static"; otherwise, it is the "instance"-side type of the + // parent of the member. + return getParentTypeOfClassElement(<ClassElement>node); } + + Debug.fail("Unsupported decorator target."); + return unknownType; } /** @@ -8414,57 +8728,54 @@ namespace ts { */ function getEffectiveDecoratorSecondArgumentType(node: Node) { // The second argument to a decorator is its `propertyKey` - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - Debug.fail("Class decorators should not have a second synthetic argument."); - return unknownType; + if (node.kind === SyntaxKind.ClassDeclaration) { + Debug.fail("Class decorators should not have a second synthetic argument."); + return unknownType; + } - case SyntaxKind.Parameter: - node = node.parent; - if (node.kind === SyntaxKind.Constructor) { - // For a constructor parameter decorator, the `propertyKey` will be `undefined`. - return anyType; - } + if (node.kind === SyntaxKind.Parameter) { + node = node.parent; + if (node.kind === SyntaxKind.Constructor) { + // For a constructor parameter decorator, the `propertyKey` will be `undefined`. + return anyType; + } // For a non-constructor parameter decorator, the `propertyKey` will be either // a string or a symbol, based on the name of the parameter's containing method. + } - // fall-through - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // The `propertyKey` for a property or method decorator will be a - // string literal type if the member name is an identifier, number, or string; - // otherwise, if the member name is a computed property name it will - // be either string or symbol. - let element = <ClassElement>node; - switch (element.name.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - return getStringLiteralType(<StringLiteral>element.name); - - case SyntaxKind.ComputedPropertyName: - let nameType = checkComputedPropertyName(<ComputedPropertyName>element.name); - if (allConstituentTypesHaveKind(nameType, TypeFlags.ESSymbol)) { - return nameType; - } - else { - return stringType; - } - - default: - Debug.fail("Unsupported property name."); - return unknownType; - } + if (node.kind === SyntaxKind.PropertyDeclaration || + node.kind === SyntaxKind.MethodDeclaration || + node.kind === SyntaxKind.GetAccessor || + node.kind === SyntaxKind.SetAccessor) { + // The `propertyKey` for a property or method decorator will be a + // string literal type if the member name is an identifier, number, or string; + // otherwise, if the member name is a computed property name it will + // be either string or symbol. + let element = <ClassElement>node; + switch (element.name.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + return getStringLiteralType(<StringLiteral>element.name); + case SyntaxKind.ComputedPropertyName: + let nameType = checkComputedPropertyName(<ComputedPropertyName>element.name); + if (allConstituentTypesHaveKind(nameType, TypeFlags.ESSymbol)) { + return nameType; + } + else { + return stringType; + } - default: - Debug.fail("Unsupported decorator target."); - return unknownType; + default: + Debug.fail("Unsupported property name."); + return unknownType; + } } + + Debug.fail("Unsupported decorator target."); + return unknownType; } /** @@ -8477,31 +8788,32 @@ namespace ts { function getEffectiveDecoratorThirdArgumentType(node: Node) { // The third argument to a decorator is either its `descriptor` for a method decorator // or its `parameterIndex` for a paramter decorator - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - Debug.fail("Class decorators should not have a third synthetic argument."); - return unknownType; - - case SyntaxKind.Parameter: - // The `parameterIndex` for a parameter decorator is always a number - return numberType; + if (node.kind === SyntaxKind.ClassDeclaration) { + Debug.fail("Class decorators should not have a third synthetic argument."); + return unknownType; + } - case SyntaxKind.PropertyDeclaration: - Debug.fail("Property decorators should not have a third synthetic argument."); - return unknownType; + if (node.kind === SyntaxKind.Parameter) { + // The `parameterIndex` for a parameter decorator is always a number + return numberType; + } - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // The `descriptor` for a method decorator will be a `TypedPropertyDescriptor<T>` - // for the type of the member. - let propertyType = getTypeOfNode(node); - return createTypedPropertyDescriptorType(propertyType); + if (node.kind === SyntaxKind.PropertyDeclaration) { + Debug.fail("Property decorators should not have a third synthetic argument."); + return unknownType; + } - default: - Debug.fail("Unsupported decorator target."); - return unknownType; + if (node.kind === SyntaxKind.MethodDeclaration || + node.kind === SyntaxKind.GetAccessor || + node.kind === SyntaxKind.SetAccessor) { + // The `descriptor` for a method decorator will be a `TypedPropertyDescriptor<T>` + // for the type of the member. + let propertyType = getTypeOfNode(node); + return createTypedPropertyDescriptorType(propertyType); } + + Debug.fail("Unsupported decorator target."); + return unknownType; } /** @@ -8889,7 +9201,7 @@ namespace ts { // Note, only class declarations can be declared abstract. // In the case of a merged class-module or class-interface declaration, // only the class declaration node will have the Abstract flag set. - let valueDecl = expressionType.symbol && getDeclarationOfKind(expressionType.symbol, SyntaxKind.ClassDeclaration); + let valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol); if (valueDecl && valueDecl.flags & NodeFlags.Abstract) { error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(valueDecl.name)); return resolveErrorCall(node); @@ -9093,17 +9405,33 @@ namespace ts { let contextualParameterType = getTypeAtPosition(context, i); assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper); } - if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) { + if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) { let parameter = lastOrUndefined(signature.parameters); let contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters)); assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper); } } + // When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push + // the destructured type into the contained binding elements. + function assignBindingElementTypes(node: VariableLikeDeclaration) { + if (isBindingPattern(node.name)) { + for (let element of (<BindingPattern>node.name).elements) { + if (element.kind !== SyntaxKind.OmittedExpression) { + if (element.name.kind === SyntaxKind.Identifier) { + getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element); + } + assignBindingElementTypes(element); + } + } + } + } + function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper) { let links = getSymbolLinks(parameter); if (!links.type) { links.type = instantiateType(contextualType, mapper); + assignBindingElementTypes(<ParameterDeclaration>parameter.valueDeclaration); } else if (isInferentialContext(mapper)) { // Even if the parameter already has a type, it might be because it was given a type while @@ -9489,7 +9817,7 @@ namespace ts { return !symbol || symbol === unknownSymbol || (symbol.flags & ~SymbolFlags.EnumMember) !== 0; } case SyntaxKind.ElementAccessExpression: - // old compiler doesn't check indexed assess + // old compiler doesn't check indexed access return true; case SyntaxKind.ParenthesizedExpression: return isReferenceOrErrorExpression((<ParenthesizedExpression>n).expression); @@ -9647,32 +9975,32 @@ namespace ts { return (symbol.flags & SymbolFlags.ConstEnum) !== 0; } - function checkInstanceOfExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type { + function checkInstanceOfExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { // TypeScript 1.0 spec (April 2014): 4.15.4 // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, // and the right operand to be of type Any or a subtype of the 'Function' interface type. // The result is always of the Boolean primitive type. // NOTE: do not raise error if leftType is unknown as related error was already reported if (allConstituentTypesHaveKind(leftType, TypeFlags.Primitive)) { - error(node.left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); + error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } // NOTE: do not raise error if right is unknown as related error was already reported if (!(isTypeAny(rightType) || isTypeSubtypeOf(rightType, globalFunctionType))) { - error(node.right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type); + error(right, Diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_of_type_any_or_of_a_type_assignable_to_the_Function_interface_type); } return booleanType; } - function checkInExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type { + function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { // TypeScript 1.0 spec (April 2014): 4.15.5 // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, // and the right operand to be of type Any, an object type, or a type parameter type. // The result is always of the Boolean primitive type. if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { - error(node.left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); + error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.ObjectType | TypeFlags.TypeParameter)) { - error(node.right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); + error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } return booleanType; } @@ -9689,7 +10017,12 @@ namespace ts { isNumericLiteralName(name.text) && getIndexTypeOfType(sourceType, IndexKind.Number) || getIndexTypeOfType(sourceType, IndexKind.String); if (type) { - checkDestructuringAssignment((<PropertyAssignment>p).initializer || name, type); + if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { + checkDestructuringAssignment(<ShorthandPropertyAssignment>p, type); + } + else { + checkDestructuringAssignment((<PropertyAssignment>p).initializer || name, type); + } } else { error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(sourceType), declarationNameToString(name)); @@ -9749,7 +10082,19 @@ namespace ts { return sourceType; } - function checkDestructuringAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type { + function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, contextualMapper?: TypeMapper): Type { + let target: Expression; + if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) { + const prop = <ShorthandPropertyAssignment>exprOrAssignment; + if (prop.objectAssignmentInitializer) { + checkBinaryLikeExpression(prop.name, prop.equalsToken, prop.objectAssignmentInitializer, contextualMapper); + } + target = (<ShorthandPropertyAssignment>exprOrAssignment).name; + } + else { + target = <Expression>exprOrAssignment; + } + if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) { checkBinaryExpression(<BinaryExpression>target, contextualMapper); target = (<BinaryExpression>target).left; @@ -9772,15 +10117,21 @@ namespace ts { } function checkBinaryExpression(node: BinaryExpression, contextualMapper?: TypeMapper) { - let operator = node.operatorToken.kind; - if (operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { - return checkDestructuringAssignment(node.left, checkExpression(node.right, contextualMapper), contextualMapper); + return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, contextualMapper, node); + } + + function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, contextualMapper?: TypeMapper, errorNode?: Node) { + let operator = operatorToken.kind; + if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) { + return checkDestructuringAssignment(left, checkExpression(right, contextualMapper), contextualMapper); } - let leftType = checkExpression(node.left, contextualMapper); - let rightType = checkExpression(node.right, contextualMapper); + let leftType = checkExpression(left, contextualMapper); + let rightType = checkExpression(right, contextualMapper); switch (operator) { case SyntaxKind.AsteriskToken: + case SyntaxKind.AsteriskAsteriskToken: case SyntaxKind.AsteriskEqualsToken: + case SyntaxKind.AsteriskAsteriskEqualsToken: case SyntaxKind.SlashToken: case SyntaxKind.SlashEqualsToken: case SyntaxKind.PercentToken: @@ -9799,7 +10150,7 @@ namespace ts { case SyntaxKind.CaretEqualsToken: case SyntaxKind.AmpersandToken: case SyntaxKind.AmpersandEqualsToken: - // TypeScript 1.0 spec (April 2014): 4.15.1 + // TypeScript 1.0 spec (April 2014): 4.19.1 // These operators require their operands to be of type Any, the Number primitive type, // or an enum type. Operands of an enum type are treated // as having the primitive type Number. If one operand is the null or undefined value, @@ -9813,13 +10164,13 @@ namespace ts { // try and return them a helpful suggestion if ((leftType.flags & TypeFlags.Boolean) && (rightType.flags & TypeFlags.Boolean) && - (suggestedOperator = getSuggestedBooleanOperator(node.operatorToken.kind)) !== undefined) { - error(node, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(node.operatorToken.kind), tokenToString(suggestedOperator)); + (suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) { + error(errorNode || operatorToken, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(operatorToken.kind), tokenToString(suggestedOperator)); } else { // otherwise just check each operand separately and report errors as normal - let leftOk = checkArithmeticOperandType(node.left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type); - let rightOk = checkArithmeticOperandType(node.right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type); + let leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type); + let rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_or_an_enum_type); if (leftOk && rightOk) { checkAssignmentOperator(numberType); } @@ -9828,7 +10179,7 @@ namespace ts { return numberType; case SyntaxKind.PlusToken: case SyntaxKind.PlusEqualsToken: - // TypeScript 1.0 spec (April 2014): 4.15.2 + // TypeScript 1.0 spec (April 2014): 4.19.2 // The binary + operator requires both operands to be of the Number primitive type or an enum type, // or at least one of the operands to be of type Any or the String primitive type. @@ -9885,9 +10236,9 @@ namespace ts { } return booleanType; case SyntaxKind.InstanceOfKeyword: - return checkInstanceOfExpression(node, leftType, rightType); + return checkInstanceOfExpression(left, right, leftType, rightType); case SyntaxKind.InKeyword: - return checkInExpression(node, leftType, rightType); + return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: return rightType; case SyntaxKind.BarBarToken: @@ -9902,8 +10253,8 @@ namespace ts { // Return true if there was no error, false if there was an error. function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean { let offendingSymbolOperand = - someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? node.left : - someConstituentTypeHasKind(rightType, TypeFlags.ESSymbol) ? node.right : + someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? left : + someConstituentTypeHasKind(rightType, TypeFlags.ESSymbol) ? right : undefined; if (offendingSymbolOperand) { error(offendingSymbolOperand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(operator)); @@ -9937,17 +10288,17 @@ namespace ts { // requires VarExpr to be classified as a reference // A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1) // and the type of the non - compound operation to be assignable to the type of VarExpr. - let ok = checkReferenceExpression(node.left, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant); + let ok = checkReferenceExpression(left, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant); // Use default messages if (ok) { // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported - checkTypeAssignableTo(valueType, leftType, node.left, /*headMessage*/ undefined); + checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined); } } } function reportOperatorError() { - error(node, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(node.operatorToken.kind), typeToString(leftType), typeToString(rightType)); + error(errorNode || operatorToken, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(operatorToken.kind), typeToString(leftType), typeToString(rightType)); } } @@ -10570,7 +10921,7 @@ namespace ts { } if (!superCallStatement) { error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties); - } + } else { // In such a required super call, it is a compile-time error for argument expressions to reference this. markThisReferencesAsErrors(superCallStatement.expression); @@ -10730,7 +11081,13 @@ namespace ts { function getEffectiveDeclarationFlags(n: Node, flagsToCheck: NodeFlags): NodeFlags { let flags = getCombinedNodeFlags(n); - if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isInAmbientContext(n)) { + + // children of classes (even ambient classes) should not be marked as ambient or export + // because those flags have no useful semantics there. + if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && + n.parent.kind !== SyntaxKind.ClassDeclaration && + n.parent.kind !== SyntaxKind.ClassExpression && + isInAmbientContext(n)) { if (!(flags & NodeFlags.Ambient)) { // It is nested in an ambient context, which means it is automatically exported flags |= NodeFlags.Export; @@ -10825,11 +11182,17 @@ namespace ts { let errorNode: Node = (<FunctionLikeDeclaration>subsequentNode).name || subsequentNode; // TODO(jfreeman): These are methods, so handle computed name case if (node.name && (<FunctionLikeDeclaration>subsequentNode).name && (<Identifier>node.name).text === (<Identifier>(<FunctionLikeDeclaration>subsequentNode).name).text) { - // the only situation when this is possible (same kind\same name but different symbol) - mixed static and instance class members - Debug.assert(node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature); - Debug.assert((node.flags & NodeFlags.Static) !== (subsequentNode.flags & NodeFlags.Static)); - let diagnostic = node.flags & NodeFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; - error(errorNode, diagnostic); + const reportError = + (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && + (node.flags & NodeFlags.Static) !== (subsequentNode.flags & NodeFlags.Static); + // we can get here in two cases + // 1. mixed static and instance class members + // 2. something with the same name was defined before the set of overloads that prevents them from merging + // here we'll report error only for the first case since for second we should already report error in binder + if (reportError) { + const diagnostic = node.flags & NodeFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static; + error(errorNode, diagnostic); + } return; } else if (nodeIsPresent((<FunctionLikeDeclaration>subsequentNode).body)) { @@ -11009,7 +11372,7 @@ namespace ts { // Spaces for anyting not declared a 'default export'. let nonDefaultExportedDeclarationSpaces = exportedDeclarationSpaces | nonExportedDeclarationSpaces; - + let commonDeclarationSpacesForExportsAndLocals = exportedDeclarationSpaces & nonExportedDeclarationSpaces; let commonDeclarationSpacesForDefaultAndNonDefault = defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces; @@ -11017,7 +11380,7 @@ namespace ts { // declaration spaces for exported and non-exported declarations intersect for (let d of symbol.declarations) { let declarationSpaces = getDeclarationSpaces(d); - + // Only error on the declarations that conributed to the intersecting spaces. if (declarationSpaces & commonDeclarationSpacesForDefaultAndNonDefault) { error(d.name, Diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, declarationNameToString(d.name)); @@ -11051,7 +11414,8 @@ namespace ts { } function checkNonThenableType(type: Type, location?: Node, message?: DiagnosticMessage) { - if (!(type.flags & TypeFlags.Any) && isTypeAssignableTo(type, getGlobalThenableType())) { + type = getWidenedType(type); + if (!isTypeAny(type) && isTypeAssignableTo(type, getGlobalThenableType())) { if (location) { if (!message) { message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member; @@ -11277,9 +11641,12 @@ namespace ts { return unknownType; } - let promiseConstructor = getMergedSymbol(promiseType.symbol); + let promiseConstructor = getNodeLinks(node.type).resolvedSymbol; if (!promiseConstructor || !symbolIsValue(promiseConstructor)) { - error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeToString(promiseType)); + let typeName = promiseConstructor + ? symbolToString(promiseConstructor) + : typeToString(promiseType); + error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeName); return unknownType; } @@ -11469,10 +11836,6 @@ namespace ts { checkSignatureDeclaration(node); let isAsync = isAsyncFunctionLike(node); if (isAsync) { - if (!compilerOptions.experimentalAsyncFunctions) { - error(node, Diagnostics.Experimental_support_for_async_functions_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalAsyncFunctions_to_remove_this_warning); - } - emitAwaiter = true; } @@ -11844,7 +12207,7 @@ namespace ts { function checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node: Node) { // We only disallow modifier on a method declaration if it is a property of object-literal-expression - if (node.modifiers && node.parent.kind === SyntaxKind.ObjectLiteralExpression){ + if (node.modifiers && node.parent.kind === SyntaxKind.ObjectLiteralExpression) { if (isAsyncFunctionLike(node)) { if (node.modifiers.length > 1) { return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); @@ -12289,7 +12652,12 @@ namespace ts { if (isAsyncFunctionLike(func)) { let promisedType = getPromisedType(returnType); let awaitedType = checkAwaitedType(exprType, node.expression, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); - checkTypeAssignableTo(awaitedType, promisedType, node.expression); + if (promisedType) { + // If the function has a return type, but promisedType is + // undefined, an error will be reported in checkAsyncFunctionReturnType + // so we don't need to report one here. + checkTypeAssignableTo(awaitedType, promisedType, node.expression); + } } else { checkTypeAssignableTo(exprType, returnType, node.expression); @@ -12551,11 +12919,6 @@ namespace ts { } checkClassLikeDeclaration(node); - // Interfaces cannot be merged with non-ambient classes. - if (getSymbolOfNode(node).flags & SymbolFlags.Interface && !isInAmbientContext(node)) { - error(node, Diagnostics.Only_an_ambient_class_can_be_merged_with_an_interface); - } - forEach(node.members, checkSourceElement); } @@ -12571,6 +12934,7 @@ namespace ts { checkExportsOnMergedDeclarations(node); let symbol = getSymbolOfNode(node); let type = <InterfaceType>getDeclaredTypeOfSymbol(symbol); + let typeWithThis = getTypeWithThisArgument(type); let staticType = <ObjectType>getTypeOfSymbol(symbol); let baseTypeNode = getClassExtendsHeritageClauseElement(node); @@ -12589,7 +12953,7 @@ namespace ts { } } } - checkTypeAssignableTo(type, baseType, node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); + checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1); checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node, Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); @@ -12609,7 +12973,7 @@ namespace ts { let implementedTypeNodes = getClassImplementsHeritageClauseElements(node); if (implementedTypeNodes) { - forEach(implementedTypeNodes, typeRefNode => { + for (let typeRefNode of implementedTypeNodes) { if (!isSupportedExpressionWithTypeArguments(typeRefNode)) { error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments); } @@ -12619,14 +12983,14 @@ namespace ts { if (t !== unknownType) { let declaredType = (t.flags & TypeFlags.Reference) ? (<TypeReference>t).target : t; if (declaredType.flags & (TypeFlags.Class | TypeFlags.Interface)) { - checkTypeAssignableTo(type, t, node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); + checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(t, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_implements_interface_1); } else { error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface); } } } - }); + } } if (produceDiagnostics) { @@ -12641,6 +13005,10 @@ namespace ts { return s.flags & SymbolFlags.Instantiated ? getSymbolLinks(s).target : s; } + function getClassLikeDeclarationOfSymbol(symbol: Symbol): Declaration { + return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined); + } + function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: ObjectType): void { // TypeScript 1.0 spec (April 2014): 8.2.3 @@ -12678,14 +13046,20 @@ namespace ts { if (derived === base) { // derived class inherits base without override/redeclaration - let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration); + let derivedClassDecl = getClassLikeDeclarationOfSymbol(type.symbol); // It is an error to inherit an abstract member without implementing it or being declared abstract. // If there is no declaration for the derived class (as in the case of class expressions), // then the class cannot be declared abstract. - if ( baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) { - error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, - typeToString(type), symbolToString(baseProperty), typeToString(baseType)); + if (baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) { + if (derivedClassDecl.kind === SyntaxKind.ClassExpression) { + error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, + symbolToString(baseProperty), typeToString(baseType)); + } + else { + error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, + typeToString(type), symbolToString(baseProperty), typeToString(baseType)); + } } } else { @@ -12776,7 +13150,7 @@ namespace ts { let ok = true; for (let base of baseTypes) { - let properties = getPropertiesOfObjectType(base); + let properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType)); for (let prop of properties) { if (!hasProperty(seen, prop.name)) { seen[prop.name] = { prop: prop, containingType: base }; @@ -12821,22 +13195,13 @@ namespace ts { // Only check this symbol once if (node === firstInterfaceDecl) { let type = <InterfaceType>getDeclaredTypeOfSymbol(symbol); + let typeWithThis = getTypeWithThisArgument(type); // run subsequent checks only if first set succeeded if (checkInheritedPropertiesAreIdentical(type, node.name)) { - forEach(getBaseTypes(type), baseType => { - checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1); - }); - checkIndexConstraints(type); - } - } - - // Interfaces cannot merge with non-ambient classes. - if (symbol && symbol.declarations) { - for (let declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.ClassDeclaration && !isInAmbientContext(declaration)) { - error(node, Diagnostics.Only_an_ambient_class_can_be_merged_with_an_interface); - break; + for (let baseType of getBaseTypes(type)) { + checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1); } + checkIndexConstraints(type); } } } @@ -12868,26 +13233,41 @@ namespace ts { if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) { let enumSymbol = getSymbolOfNode(node); let enumType = getDeclaredTypeOfSymbol(enumSymbol); - let autoValue = 0; + let autoValue = 0; // set to undefined when enum member is non-constant let ambient = isInAmbientContext(node); let enumIsConst = isConst(node); - forEach(node.members, member => { - if (member.name.kind !== SyntaxKind.ComputedPropertyName && isNumericLiteralName((<Identifier>member.name).text)) { + for (const member of node.members) { + if (member.name.kind === SyntaxKind.ComputedPropertyName) { + error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums); + } + else if (isNumericLiteralName((<Identifier>member.name).text)) { error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name); } + + const previousEnumMemberIsNonConstant = autoValue === undefined; + let initializer = member.initializer; if (initializer) { autoValue = computeConstantValueForEnumMemberInitializer(initializer, enumType, enumIsConst, ambient); } else if (ambient && !enumIsConst) { + // In ambient enum declarations that specify no const modifier, enum member declarations + // that omit a value are considered computed members (as opposed to having auto-incremented values assigned). autoValue = undefined; } + else if (previousEnumMemberIsNonConstant) { + // If the member declaration specifies no value, the member is considered a constant enum member. + // If the member is the first member in the enum declaration, it is assigned the value zero. + // Otherwise, it is assigned the value of the immediately preceding member plus one, + // and an error occurs if the immediately preceding member is not a constant enum member + error(member.name, Diagnostics.Enum_member_must_have_initializer); + } if (autoValue !== undefined) { getNodeLinks(member).enumMemberValue = autoValue++; } - }); + } nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } @@ -12903,11 +13283,11 @@ namespace ts { if (enumIsConst) { error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression); } - else if (!ambient) { + else if (ambient) { + error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression); + } + else { // Only here do we need to check that the initializer is assignable to the enum type. - // If it is a constant value (not undefined), it is syntactically constrained to be a number. - // Also, we do not need to check this for ambients because there is already - // a syntax error if it is not a constant. checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*headMessage*/ undefined); } } @@ -13029,7 +13409,7 @@ namespace ts { } // illegal case: forward reference - if (!isDefinedBefore(propertyDecl, member)) { + if (!isBlockScopedNameDeclaredBeforeUse(propertyDecl, member)) { reportError = false; error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums); return undefined; @@ -13047,7 +13427,7 @@ namespace ts { } // Grammar checking - checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarEnumDeclaration(node); + checkGrammarDecorators(node) || checkGrammarModifiers(node); checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); @@ -13180,7 +13560,7 @@ namespace ts { // Checks for ambient external modules. if (isAmbientExternalModule) { if (!isGlobalSourceFile(node.parent)) { - error(node.name, Diagnostics.Ambient_modules_cannot_be_nested_in_other_modules); + error(node.name, Diagnostics.Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces); } if (isExternalModuleNameRelative(node.name.text)) { error(node.name, Diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name); @@ -13205,7 +13585,7 @@ namespace ts { Debug.assert(node.kind === SyntaxKind.Identifier); return <Identifier>node; } - + function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { let moduleName = getExternalModuleName(node); if (!nodeIsMissing(moduleName) && moduleName.kind !== SyntaxKind.StringLiteral) { @@ -13307,9 +13687,9 @@ namespace ts { } } else { - if (languageVersion >= ScriptTarget.ES6 && !isInAmbientContext(node)) { + if (modulekind === ModuleKind.ES6 && !isInAmbientContext(node)) { // Import equals declaration is deprecated in es6 or above - grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_6_or_higher_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_or_import_d_from_mod_instead); + grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_6_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead); } } } @@ -13384,11 +13764,11 @@ namespace ts { checkExternalModuleExports(<SourceFile | ModuleDeclaration>container); if (node.isExportEquals && !isInAmbientContext(node)) { - if (languageVersion >= ScriptTarget.ES6) { - // export assignment is deprecated in es6 or above - grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_6_or_higher_Consider_using_export_default_instead); + if (modulekind === ModuleKind.ES6) { + // export assignment is not supported in es6 modules + grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_6_modules_Consider_using_export_default_or_another_module_format_instead); } - else if (compilerOptions.module === ModuleKind.System) { + else if (modulekind === ModuleKind.System) { // system modules does not support export assignment grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system); } @@ -13578,13 +13958,14 @@ namespace ts { break; case SyntaxKind.ClassExpression: forEach((<ClassExpression>node).members, checkSourceElement); + forEachChild(node, checkFunctionAndClassExpressionBodies); break; case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: forEach(node.decorators, checkFunctionAndClassExpressionBodies); forEach((<MethodDeclaration>node).parameters, checkFunctionAndClassExpressionBodies); if (isObjectLiteralMethod(node)) { - checkFunctionExpressionOrObjectLiteralMethodBody(<MethodDeclaration>node); + checkFunctionExpressionOrObjectLiteralMethodBody(node); } break; case SyntaxKind.Constructor: @@ -13818,7 +14199,7 @@ namespace ts { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: // If we didn't come from static member of class or interface, - // add the type parameters into the symbol table + // add the type parameters into the symbol table // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. // Note: that the memberFlags come from previous iteration. if (!(memberFlags & NodeFlags.Static)) { @@ -13832,11 +14213,11 @@ namespace ts { } break; } - + if (introducesArgumentsExoticObject(location)) { copySymbol(argumentsSymbol, meaning); } - + memberFlags = location.flags; location = location.parent; } @@ -13951,7 +14332,21 @@ namespace ts { } if (isHeritageClauseElementIdentifier(<EntityName>entityName)) { - let meaning = entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments ? SymbolFlags.Type : SymbolFlags.Namespace; + let meaning = SymbolFlags.None; + + // In an interface or class, we're definitely interested in a type. + if (entityName.parent.kind === SyntaxKind.ExpressionWithTypeArguments) { + meaning = SymbolFlags.Type; + + // In a class 'extends' clause we are also looking for a value. + if (isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) { + meaning |= SymbolFlags.Value; + } + } + else { + meaning = SymbolFlags.Namespace; + } + meaning |= SymbolFlags.Alias; return resolveEntityName(<EntityName>entityName, meaning); } @@ -14043,7 +14438,7 @@ namespace ts { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: - let type = checkExpression(<Expression>node); + let type = isExpression(node) ? checkExpression(<Expression>node) : getTypeFromTypeNode(<TypeNode>node); return type.symbol; case SyntaxKind.ConstructorKeyword: @@ -14308,9 +14703,9 @@ namespace ts { } // const enums and modules that contain only const enums are not considered values from the emit perespective // unless 'preserveConstEnums' option is set to true - return target !== unknownSymbol && - target && - target.flags & SymbolFlags.Value && + return target !== unknownSymbol && + target && + target.flags & SymbolFlags.Value && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target)); } @@ -14381,7 +14776,7 @@ namespace ts { function isFunctionType(type: Type): boolean { return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0; } - + function getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind { // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. let valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true); @@ -14394,7 +14789,7 @@ namespace ts { let typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true); // We might not be able to resolve type symbol so use unknown type in that case (eg error case) if (!typeSymbol) { - return TypeReferenceSerializationKind.ObjectType; + return TypeReferenceSerializationKind.ObjectType; } let type = getDeclaredTypeOfSymbol(typeSymbol); if (type === unknownType) { @@ -14468,31 +14863,6 @@ namespace ts { return symbol && getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; } - function getBlockScopedVariableId(n: Identifier): number { - Debug.assert(!nodeIsSynthesized(n)); - - let isVariableDeclarationOrBindingElement = - n.parent.kind === SyntaxKind.BindingElement || (n.parent.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>n.parent).name === n); - - let symbol = - (isVariableDeclarationOrBindingElement ? getSymbolOfNode(n.parent) : undefined) || - getNodeLinks(n).resolvedSymbol || - resolveName(n, n.text, SymbolFlags.Value | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined); - - let isLetOrConst = - symbol && - (symbol.flags & SymbolFlags.BlockScopedVariable) && - symbol.valueDeclaration.parent.kind !== SyntaxKind.CatchClause; - - if (isLetOrConst) { - // side-effect of calling this method: - // assign id to symbol if it was not yet set - getSymbolLinks(symbol); - return symbol.id; - } - return undefined; - } - function instantiateSingleCallFunctionType(functionType: Type, typeArguments: Type[]): Type { if (functionType === unknownType) { return unknownType; @@ -14527,7 +14897,6 @@ namespace ts { isEntityNameVisible, getConstantValue, collectLinkedAliases, - getBlockScopedVariableId, getReferencedValueDeclaration, getTypeReferenceSerializationKind, isOptionalParameter @@ -14630,9 +14999,6 @@ namespace ts { if (!nodeCanBeDecorated(node)) { return grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_not_valid_here); } - else if (languageVersion < ScriptTarget.ES5) { - return grammarErrorOnFirstToken(node, Diagnostics.Decorators_are_only_available_when_targeting_ECMAScript_5_and_higher); - } else if (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) { let accessors = getAllAccessorDeclarations((<ClassDeclaration>node.parent).members, <AccessorDeclaration>node); if (accessors.firstAccessor.decorators && node === accessors.secondAccessor) { @@ -15141,7 +15507,7 @@ namespace ts { } } - function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression) { + function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { let seen: Map<SymbolFlags> = {}; let Property = 1; let GetAccessor = 2; @@ -15157,6 +15523,12 @@ namespace ts { continue; } + if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && (<ShorthandPropertyAssignment>prop).objectAssignmentInitializer) { + // having objectAssignmentInitializer is only valid in ObjectAssignmentPattern + // outside of destructuring it is a syntax error + return grammarErrorOnNode((<ShorthandPropertyAssignment>prop).equalsToken, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment); + } + // ECMA-262 11.1.5 Object Initialiser // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true // a.This production is contained in strict code and IsDataDescriptor(previous) is true and @@ -15547,40 +15919,6 @@ namespace ts { return false; } - function checkGrammarEnumDeclaration(enumDecl: EnumDeclaration): boolean { - let enumIsConst = (enumDecl.flags & NodeFlags.Const) !== 0; - - let hasError = false; - - // skip checks below for const enums - they allow arbitrary initializers as long as they can be evaluated to constant expressions. - // since all values are known in compile time - it is not necessary to check that constant enum section precedes computed enum members. - if (!enumIsConst) { - let inConstantEnumMemberSection = true; - let inAmbientContext = isInAmbientContext(enumDecl); - for (let node of enumDecl.members) { - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if (node.name.kind === SyntaxKind.ComputedPropertyName) { - hasError = grammarErrorOnNode(node.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums); - } - else if (inAmbientContext) { - if (node.initializer && !isIntegerLiteral(node.initializer)) { - hasError = grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers) || hasError; - } - } - else if (node.initializer) { - inConstantEnumMemberSection = isIntegerLiteral(node.initializer); - } - else if (!inConstantEnumMemberSection) { - hasError = grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer) || hasError; - } - } - } - - return hasError; - } - function hasParseDiagnostics(sourceFile: SourceFile): boolean { return sourceFile.parseDiagnostics.length > 0; } diff --git a/ts/compiler/commandLineParser.ts b/ts/compiler/commandLineParser.ts index 9b7558f..acf0474 100644 --- a/ts/compiler/commandLineParser.ts +++ b/ts/compiler/commandLineParser.ts @@ -76,10 +76,12 @@ namespace ts { "amd": ModuleKind.AMD, "system": ModuleKind.System, "umd": ModuleKind.UMD, + "es6": ModuleKind.ES6, + "es2015": ModuleKind.ES2015, }, - description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_or_umd, + description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es6, paramType: Diagnostics.KIND, - error: Diagnostics.Argument_for_module_option_must_be_commonjs_amd_system_or_umd + error: Diagnostics.Argument_for_module_option_must_be_commonjs_amd_system_umd_or_es6 }, { name: "newLine", @@ -204,7 +206,12 @@ namespace ts { { name: "target", shortName: "t", - type: { "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6 }, + type: { + "es3": ScriptTarget.ES3, + "es5": ScriptTarget.ES5, + "es6": ScriptTarget.ES6, + "es2015": ScriptTarget.ES2015, + }, description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental, paramType: Diagnostics.VERSION, error: Diagnostics.Argument_for_target_option_must_be_ES3_ES5_or_ES6 @@ -221,11 +228,6 @@ namespace ts { type: "boolean", description: Diagnostics.Watch_input_files, }, - { - name: "experimentalAsyncFunctions", - type: "boolean", - description: Diagnostics.Enables_experimental_support_for_ES7_async_functions - }, { name: "experimentalDecorators", type: "boolean", @@ -380,15 +382,15 @@ namespace ts { * Read tsconfig.json file * @param fileName The path to the config file */ - export function readConfigFile(fileName: string): { config?: any; error?: Diagnostic } { + export function readConfigFile(fileName: string, readFile: (path: string) => string): { config?: any; error?: Diagnostic } { let text = ""; try { - text = sys.readFile(fileName); + text = readFile(fileName); } catch (e) { return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) }; } - return parseConfigFileText(fileName, text); + return parseConfigFileTextToJson(fileName, text); } /** @@ -396,7 +398,7 @@ namespace ts { * @param fileName The path to the config file * @param jsonText The text of the config file */ - export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { + export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { try { return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} }; } @@ -411,7 +413,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine { let errors: Diagnostic[] = []; return { @@ -447,6 +449,9 @@ namespace ts { } if (opt.isFilePath) { value = normalizePath(combinePaths(basePath, value)); + if (value === "") { + value = "."; + } } options[opt.name] = value; } @@ -469,7 +474,7 @@ namespace ts { fileNames = map(<string[]>json["files"], s => combinePaths(basePath, s)); } else { - errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array")); + errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array")); } } else { @@ -496,4 +501,4 @@ namespace ts { return fileNames; } } -} \ No newline at end of file +} diff --git a/ts/compiler/core.ts b/ts/compiler/core.ts index a1f6565..9a4f545 100644 --- a/ts/compiler/core.ts +++ b/ts/compiler/core.ts @@ -52,7 +52,7 @@ namespace ts { function normalizeKey(key: string) { return getCanonicalFileName(normalizeSlashes(key)); } - + function clear() { files = {}; } @@ -117,7 +117,7 @@ namespace ts { return count; } - export function filter<T>(array: T[], f: (x: T) => boolean): T[]{ + export function filter<T>(array: T[], f: (x: T) => boolean): T[] { let result: T[]; if (array) { result = []; @@ -130,7 +130,7 @@ namespace ts { return result; } - export function map<T, U>(array: T[], f: (x: T) => U): U[]{ + export function map<T, U>(array: T[], f: (x: T) => U): U[] { let result: U[]; if (array) { result = []; @@ -148,7 +148,7 @@ namespace ts { return array1.concat(array2); } - export function deduplicate<T>(array: T[]): T[]{ + export function deduplicate<T>(array: T[]): T[] { let result: T[]; if (array) { result = []; @@ -437,8 +437,12 @@ namespace ts { } export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): DiagnosticMessageChain { - Debug.assert(!headChain.next); - headChain.next = tailChain; + let lastChain = headChain; + while (lastChain.next) { + lastChain = lastChain.next; + } + + lastChain.next = tailChain; return headChain; } @@ -486,7 +490,7 @@ namespace ts { return text1 ? Comparison.GreaterThan : Comparison.LessThan; } - export function sortAndDeduplicateDiagnostics(diagnostics: Diagnostic[]): Diagnostic[]{ + export function sortAndDeduplicateDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] { return deduplicateSortedDiagnostics(diagnostics.sort(compareDiagnostics)); } @@ -700,6 +704,9 @@ namespace ts { } export function getBaseFileName(path: string) { + if (!path) { + return undefined; + } let i = path.lastIndexOf(directorySeparator); return i < 0 ? path : path.substring(i + 1); } @@ -722,6 +729,23 @@ namespace ts { * List of supported extensions in order of file resolution precedence. */ export const supportedExtensions = [".ts", ".tsx", ".d.ts"]; + /** + * List of extensions that will be used to look for external modules. + * This list is kept separate from supportedExtensions to for cases when we'll allow to include .js files in compilation, + * but still would like to load only TypeScript files as modules + */ + export const moduleFileExtensions = supportedExtensions; + + export function isSupportedSourceFileName(fileName: string) { + if (!fileName) { return false; } + + for (let extension of supportedExtensions) { + if (fileExtensionIs(fileName, extension)) { + return true; + } + } + return false; + } const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; export function removeFileExtension(path: string): string { @@ -795,7 +819,7 @@ namespace ts { VeryAggressive = 3, } - export module Debug { + export namespace Debug { let currentAssertionLevel = AssertionLevel.None; export function shouldAssert(level: AssertionLevel): boolean { @@ -817,4 +841,14 @@ namespace ts { Debug.assert(false, message); } } -} + + export function copyListRemovingItem<T>(item: T, list: T[]) { + let copiedList: T[] = []; + for (var i = 0, len = list.length; i < len; i++) { + if (list[i] !== item) { + copiedList.push(list[i]); + } + } + return copiedList; + } +} \ No newline at end of file diff --git a/ts/compiler/declarationEmitter.ts b/ts/compiler/declarationEmitter.ts index e5914d1..00085f1 100644 --- a/ts/compiler/declarationEmitter.ts +++ b/ts/compiler/declarationEmitter.ts @@ -52,6 +52,7 @@ namespace ts { let enclosingDeclaration: Node; let currentSourceFile: SourceFile; let reportedDeclarationError = false; + let errorNameNode: DeclarationName; let emitJsDocComments = compilerOptions.removeComments ? function (declaration: Node) { } : writeJsDocComments; let emit = compilerOptions.stripInternal ? stripInternal : emitNode; @@ -152,6 +153,7 @@ namespace ts { function createAndSetNewTextWriterWithSymbolWriter(): EmitTextWriterWithSymbolWriter { let writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine); writer.trackSymbol = trackSymbol; + writer.reportInaccessibleThisError = reportInaccessibleThisError; writer.writeKeyword = writer.write; writer.writeOperator = writer.write; writer.writePunctuation = writer.write; @@ -178,9 +180,11 @@ namespace ts { let nodeToCheck: Node; if (declaration.kind === SyntaxKind.VariableDeclaration) { nodeToCheck = declaration.parent.parent; - } else if (declaration.kind === SyntaxKind.NamedImports || declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause) { + } + else if (declaration.kind === SyntaxKind.NamedImports || declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause) { Debug.fail("We should be getting ImportDeclaration instead to write"); - } else { + } + else { nodeToCheck = declaration; } @@ -257,6 +261,13 @@ namespace ts { handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning)); } + function reportInaccessibleThisError() { + if (errorNameNode) { + diagnostics.push(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary, + declarationNameToString(errorNameNode))); + } + } + function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; write(": "); @@ -265,7 +276,9 @@ namespace ts { emitType(type); } else { + errorNameNode = declaration.name; resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); + errorNameNode = undefined; } } @@ -277,7 +290,9 @@ namespace ts { emitType(signature.type); } else { + errorNameNode = signature.name; resolver.writeReturnTypeOfSignatureDeclaration(signature, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); + errorNameNode = undefined; } } @@ -326,6 +341,7 @@ namespace ts { case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.VoidKeyword: + case SyntaxKind.ThisKeyword: case SyntaxKind.StringLiteral: return writeTextOfNode(currentSourceFile, type); case SyntaxKind.ExpressionWithTypeArguments: @@ -1068,7 +1084,7 @@ namespace ts { // emitted: declare var c: number; // instead of declare var c:number, ; let elements: Node[] = []; for (let element of bindingPattern.elements) { - if (element.kind !== SyntaxKind.OmittedExpression){ + if (element.kind !== SyntaxKind.OmittedExpression) { elements.push(element); } } diff --git a/ts/compiler/diagnosticInformationMap.generated.ts b/ts/compiler/diagnosticInformationMap.generated.ts index 1ff650a..57c058c 100644 --- a/ts/compiler/diagnosticInformationMap.generated.ts +++ b/ts/compiler/diagnosticInformationMap.generated.ts @@ -52,7 +52,7 @@ namespace ts { Enum_member_must_have_initializer: { code: 1061, category: DiagnosticCategory.Error, key: "Enum member must have initializer." }, _0_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method: { code: 1062, category: DiagnosticCategory.Error, key: "{0} is referenced directly or indirectly in the fulfillment callback of its own 'then' method." }, An_export_assignment_cannot_be_used_in_a_namespace: { code: 1063, category: DiagnosticCategory.Error, key: "An export assignment cannot be used in a namespace." }, - Ambient_enum_elements_can_only_have_integer_literal_initializers: { code: 1066, category: DiagnosticCategory.Error, key: "Ambient enum elements can only have integer literal initializers." }, + In_ambient_enum_declarations_member_initializer_must_be_constant_expression: { code: 1066, category: DiagnosticCategory.Error, key: "In ambient enum declarations member initializer must be constant expression." }, Unexpected_token_A_constructor_method_accessor_or_property_was_expected: { code: 1068, category: DiagnosticCategory.Error, key: "Unexpected token. A constructor, method, accessor, or property was expected." }, A_0_modifier_cannot_be_used_with_an_import_declaration: { code: 1079, category: DiagnosticCategory.Error, key: "A '{0}' modifier cannot be used with an import declaration." }, Invalid_reference_directive_syntax: { code: 1084, category: DiagnosticCategory.Error, key: "Invalid 'reference' directive syntax." }, @@ -140,7 +140,7 @@ namespace ts { Property_destructuring_pattern_expected: { code: 1180, category: DiagnosticCategory.Error, key: "Property destructuring pattern expected." }, Array_element_destructuring_pattern_expected: { code: 1181, category: DiagnosticCategory.Error, key: "Array element destructuring pattern expected." }, A_destructuring_declaration_must_have_an_initializer: { code: 1182, category: DiagnosticCategory.Error, key: "A destructuring declaration must have an initializer." }, - An_implementation_cannot_be_declared_in_ambient_contexts: { code: 1184, category: DiagnosticCategory.Error, key: "An implementation cannot be declared in ambient contexts." }, + An_implementation_cannot_be_declared_in_ambient_contexts: { code: 1183, category: DiagnosticCategory.Error, key: "An implementation cannot be declared in ambient contexts." }, Modifiers_cannot_appear_here: { code: 1184, category: DiagnosticCategory.Error, key: "Modifiers cannot appear here." }, Merge_conflict_marker_encountered: { code: 1185, category: DiagnosticCategory.Error, key: "Merge conflict marker encountered." }, A_rest_element_cannot_have_an_initializer: { code: 1186, category: DiagnosticCategory.Error, key: "A rest element cannot have an initializer." }, @@ -158,10 +158,9 @@ namespace ts { An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive: { code: 1198, category: DiagnosticCategory.Error, key: "An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive." }, Unterminated_Unicode_escape_sequence: { code: 1199, category: DiagnosticCategory.Error, key: "Unterminated Unicode escape sequence." }, Line_terminator_not_permitted_before_arrow: { code: 1200, category: DiagnosticCategory.Error, key: "Line terminator not permitted before arrow." }, - Import_assignment_cannot_be_used_when_targeting_ECMAScript_6_or_higher_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_or_import_d_from_mod_instead: { code: 1202, category: DiagnosticCategory.Error, key: "Import assignment cannot be used when targeting ECMAScript 6 or higher. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"' or 'import d from \"mod\"' instead." }, - Export_assignment_cannot_be_used_when_targeting_ECMAScript_6_or_higher_Consider_using_export_default_instead: { code: 1203, category: DiagnosticCategory.Error, key: "Export assignment cannot be used when targeting ECMAScript 6 or higher. Consider using 'export default' instead." }, - Cannot_compile_modules_into_commonjs_amd_system_or_umd_when_targeting_ES6_or_higher: { code: 1204, category: DiagnosticCategory.Error, key: "Cannot compile modules into 'commonjs', 'amd', 'system' or 'umd' when targeting 'ES6' or higher." }, - Decorators_are_only_available_when_targeting_ECMAScript_5_and_higher: { code: 1205, category: DiagnosticCategory.Error, key: "Decorators are only available when targeting ECMAScript 5 and higher." }, + Import_assignment_cannot_be_used_when_targeting_ECMAScript_6_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead: { code: 1202, category: DiagnosticCategory.Error, key: "Import assignment cannot be used when targeting ECMAScript 6 modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead." }, + Export_assignment_cannot_be_used_when_targeting_ECMAScript_6_modules_Consider_using_export_default_or_another_module_format_instead: { code: 1203, category: DiagnosticCategory.Error, key: "Export assignment cannot be used when targeting ECMAScript 6 modules. Consider using 'export default' or another module format instead." }, + Cannot_compile_modules_into_es6_when_targeting_ES5_or_lower: { code: 1204, category: DiagnosticCategory.Error, key: "Cannot compile modules into 'es6' when targeting 'ES5' or lower." }, Decorators_are_not_valid_here: { code: 1206, category: DiagnosticCategory.Error, key: "Decorators are not valid here." }, Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name: { code: 1207, category: DiagnosticCategory.Error, key: "Decorators cannot be applied to multiple get/set accessors of the same name." }, Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided: { code: 1208, category: DiagnosticCategory.Error, key: "Cannot compile namespaces when the '--isolatedModules' flag is provided." }, @@ -190,10 +189,6 @@ namespace ts { An_export_declaration_can_only_be_used_in_a_module: { code: 1233, category: DiagnosticCategory.Error, key: "An export declaration can only be used in a module." }, An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file: { code: 1234, category: DiagnosticCategory.Error, key: "An ambient module declaration is only allowed at the top level in a file." }, A_namespace_declaration_is_only_allowed_in_a_namespace_or_module: { code: 1235, category: DiagnosticCategory.Error, key: "A namespace declaration is only allowed in a namespace or module." }, - Experimental_support_for_async_functions_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalAsyncFunctions_to_remove_this_warning: { code: 1236, category: DiagnosticCategory.Error, key: "Experimental support for async functions is a feature that is subject to change in a future release. Specify '--experimentalAsyncFunctions' to remove this warning." }, - with_statements_are_not_allowed_in_an_async_function_block: { code: 1300, category: DiagnosticCategory.Error, key: "'with' statements are not allowed in an async function block." }, - await_expression_is_only_allowed_within_an_async_function: { code: 1308, category: DiagnosticCategory.Error, key: "'await' expression is only allowed within an async function." }, - Async_functions_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1311, category: DiagnosticCategory.Error, key: "Async functions are only available when targeting ECMAScript 6 and higher." }, The_return_type_of_a_property_decorator_function_must_be_either_void_or_any: { code: 1236, category: DiagnosticCategory.Error, key: "The return type of a property decorator function must be either 'void' or 'any'." }, The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any: { code: 1237, category: DiagnosticCategory.Error, key: "The return type of a parameter decorator function must be either 'void' or 'any'." }, Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression: { code: 1238, category: DiagnosticCategory.Error, key: "Unable to resolve signature of class decorator when called as an expression." }, @@ -204,6 +199,10 @@ namespace ts { _0_modifier_cannot_be_used_with_1_modifier: { code: 1243, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with '{1}' modifier." }, Abstract_methods_can_only_appear_within_an_abstract_class: { code: 1244, category: DiagnosticCategory.Error, key: "Abstract methods can only appear within an abstract class." }, Method_0_cannot_have_an_implementation_because_it_is_marked_abstract: { code: 1245, category: DiagnosticCategory.Error, key: "Method '{0}' cannot have an implementation because it is marked abstract." }, + with_statements_are_not_allowed_in_an_async_function_block: { code: 1300, category: DiagnosticCategory.Error, key: "'with' statements are not allowed in an async function block." }, + await_expression_is_only_allowed_within_an_async_function: { code: 1308, category: DiagnosticCategory.Error, key: "'await' expression is only allowed within an async function." }, + Async_functions_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1311, category: DiagnosticCategory.Error, key: "Async functions are only available when targeting ECMAScript 6 and higher." }, + can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment: { code: 1312, category: DiagnosticCategory.Error, key: "'=' can only be used in an object literal property inside a destructuring assignment." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, @@ -244,7 +243,7 @@ namespace ts { Property_0_does_not_exist_on_type_1: { code: 2339, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on type '{1}'." }, Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword: { code: 2340, category: DiagnosticCategory.Error, key: "Only public and protected methods of the base class are accessible via the 'super' keyword." }, Property_0_is_private_and_only_accessible_within_class_1: { code: 2341, category: DiagnosticCategory.Error, key: "Property '{0}' is private and only accessible within class '{1}'." }, - An_index_expression_argument_must_be_of_type_string_number_symbol_or_any: { code: 2342, category: DiagnosticCategory.Error, key: "An index expression argument must be of type 'string', 'number', 'symbol, or 'any'." }, + An_index_expression_argument_must_be_of_type_string_number_symbol_or_any: { code: 2342, category: DiagnosticCategory.Error, key: "An index expression argument must be of type 'string', 'number', 'symbol', or 'any'." }, Type_0_does_not_satisfy_the_constraint_1: { code: 2344, category: DiagnosticCategory.Error, key: "Type '{0}' does not satisfy the constraint '{1}'." }, Argument_of_type_0_is_not_assignable_to_parameter_of_type_1: { code: 2345, category: DiagnosticCategory.Error, key: "Argument of type '{0}' is not assignable to parameter of type '{1}'." }, Supplied_parameters_do_not_match_any_signature_of_call_target: { code: 2346, category: DiagnosticCategory.Error, key: "Supplied parameters do not match any signature of call target." }, @@ -328,7 +327,7 @@ namespace ts { In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element: { code: 2432, category: DiagnosticCategory.Error, key: "In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element." }, A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged: { code: 2433, category: DiagnosticCategory.Error, key: "A namespace declaration cannot be in a different file from a class or function with which it is merged" }, A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged: { code: 2434, category: DiagnosticCategory.Error, key: "A namespace declaration cannot be located prior to a class or function with which it is merged" }, - Ambient_modules_cannot_be_nested_in_other_modules: { code: 2435, category: DiagnosticCategory.Error, key: "Ambient modules cannot be nested in other modules." }, + Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces: { code: 2435, category: DiagnosticCategory.Error, key: "Ambient modules cannot be nested in other modules or namespaces." }, Ambient_module_declaration_cannot_specify_relative_module_name: { code: 2436, category: DiagnosticCategory.Error, key: "Ambient module declaration cannot specify relative module name." }, Module_0_is_hidden_by_a_local_declaration_with_the_same_name: { code: 2437, category: DiagnosticCategory.Error, key: "Module '{0}' is hidden by a local declaration with the same name" }, Import_name_cannot_be_0: { code: 2438, category: DiagnosticCategory.Error, key: "Import name cannot be '{0}'" }, @@ -409,12 +408,15 @@ namespace ts { Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2: { code: 2515, category: DiagnosticCategory.Error, key: "Non-abstract class '{0}' does not implement inherited abstract member '{1}' from class '{2}'." }, All_declarations_of_an_abstract_method_must_be_consecutive: { code: 2516, category: DiagnosticCategory.Error, key: "All declarations of an abstract method must be consecutive." }, Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type: { code: 2517, category: DiagnosticCategory.Error, key: "Cannot assign an abstract constructor type to a non-abstract constructor type." }, - Only_an_ambient_class_can_be_merged_with_an_interface: { code: 2518, category: DiagnosticCategory.Error, key: "Only an ambient class can be merged with an interface." }, Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions: { code: 2520, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions." }, Expression_resolves_to_variable_declaration_0_that_compiler_uses_to_support_async_functions: { code: 2521, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '{0}' that compiler uses to support async functions." }, The_arguments_object_cannot_be_referenced_in_an_async_arrow_function_Consider_using_a_standard_async_function_expression: { code: 2522, category: DiagnosticCategory.Error, key: "The 'arguments' object cannot be referenced in an async arrow function. Consider using a standard async function expression." }, yield_expressions_cannot_be_used_in_a_parameter_initializer: { code: 2523, category: DiagnosticCategory.Error, key: "'yield' expressions cannot be used in a parameter initializer." }, await_expressions_cannot_be_used_in_a_parameter_initializer: { code: 2524, category: DiagnosticCategory.Error, key: "'await' expressions cannot be used in a parameter initializer." }, + Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value: { code: 2525, category: DiagnosticCategory.Error, key: "Initializer provides no value for this binding element and the binding element has no default value." }, + A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface: { code: 2526, category: DiagnosticCategory.Error, key: "A 'this' type is available only in a non-static member of a class or interface." }, + The_inferred_type_of_0_references_an_inaccessible_this_type_A_type_annotation_is_necessary: { code: 2527, category: DiagnosticCategory.Error, key: "The inferred type of '{0}' references an inaccessible 'this' type. A type annotation is necessary." }, + A_module_cannot_have_multiple_default_exports: { code: 2528, category: DiagnosticCategory.Error, key: "A module cannot have multiple default exports." }, JSX_element_attributes_type_0_must_be_an_object_type: { code: 2600, category: DiagnosticCategory.Error, key: "JSX element attributes type '{0}' must be an object type." }, The_return_type_of_a_JSX_element_constructor_must_return_an_object_type: { code: 2601, category: DiagnosticCategory.Error, key: "The return type of a JSX element constructor must return an object type." }, JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist: { code: 2602, category: DiagnosticCategory.Error, key: "JSX element implicitly has type 'any' because the global type 'JSX.Element' does not exist." }, @@ -427,8 +429,9 @@ namespace ts { Cannot_emit_namespaced_JSX_elements_in_React: { code: 2650, category: DiagnosticCategory.Error, key: "Cannot emit namespaced JSX elements in React" }, A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums: { code: 2651, category: DiagnosticCategory.Error, key: "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums." }, Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead: { code: 2652, category: DiagnosticCategory.Error, key: "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead." }, + Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1: { code: 2653, category: DiagnosticCategory.Error, key: "Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'." }, Exported_external_package_typings_file_cannot_contain_tripleslash_references_Please_contact_the_package_author_to_update_the_package_definition: { code: 2654, category: DiagnosticCategory.Error, key: "Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition." }, - Exported_external_package_typings_can_only_be_in_d_ts_files_Please_contact_the_package_author_to_update_the_package_definition: { code: 2655, category: DiagnosticCategory.Error, key: "Exported external package typings can only be in '.d.ts' files. Please contact the package author to update the package definition." }, + Exported_external_package_typings_file_0_is_not_a_module_Please_contact_the_package_author_to_update_the_package_definition: { code: 2656, category: DiagnosticCategory.Error, key: "Exported external package typings file '{0}' is not a module. Please contact the package author to update the package definition." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, @@ -513,7 +516,7 @@ namespace ts { Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided: { code: 5051, category: DiagnosticCategory.Error, key: "Option 'inlineSources' can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided." }, Option_0_cannot_be_specified_without_specifying_option_1: { code: 5052, category: DiagnosticCategory.Error, key: "Option '{0}' cannot be specified without specifying option '{1}'." }, Option_0_cannot_be_specified_with_option_1: { code: 5053, category: DiagnosticCategory.Error, key: "Option '{0}' cannot be specified with option '{1}'." }, - A_tsconfig_json_file_is_already_defined_at_Colon_0: { code: 5053, category: DiagnosticCategory.Error, key: "A 'tsconfig.json' file is already defined at: '{0}'." }, + A_tsconfig_json_file_is_already_defined_at_Colon_0: { code: 5054, category: DiagnosticCategory.Error, key: "A 'tsconfig.json' file is already defined at: '{0}'." }, Concatenate_and_emit_output_to_single_file: { code: 6001, category: DiagnosticCategory.Message, key: "Concatenate and emit output to single file." }, Generates_corresponding_d_ts_file: { code: 6002, category: DiagnosticCategory.Message, key: "Generates corresponding '.d.ts' file." }, Specifies_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations: { code: 6003, category: DiagnosticCategory.Message, key: "Specifies the location where debugger should locate map files instead of generated locations." }, @@ -525,7 +528,7 @@ namespace ts { Do_not_emit_comments_to_output: { code: 6009, category: DiagnosticCategory.Message, key: "Do not emit comments to output." }, Do_not_emit_outputs: { code: 6010, category: DiagnosticCategory.Message, key: "Do not emit outputs." }, Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental: { code: 6015, category: DiagnosticCategory.Message, key: "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)" }, - Specify_module_code_generation_Colon_commonjs_amd_system_or_umd: { code: 6016, category: DiagnosticCategory.Message, key: "Specify module code generation: 'commonjs', 'amd', 'system' or 'umd'" }, + Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es6: { code: 6016, category: DiagnosticCategory.Message, key: "Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es6'" }, Print_this_message: { code: 6017, category: DiagnosticCategory.Message, key: "Print this message." }, Print_the_compiler_s_version: { code: 6019, category: DiagnosticCategory.Message, key: "Print the compiler's version." }, Compile_the_project_in_the_given_directory: { code: 6020, category: DiagnosticCategory.Message, key: "Compile the project in the given directory." }, @@ -546,7 +549,7 @@ namespace ts { Generates_corresponding_map_file: { code: 6043, category: DiagnosticCategory.Message, key: "Generates corresponding '.map' file." }, Compiler_option_0_expects_an_argument: { code: 6044, category: DiagnosticCategory.Error, key: "Compiler option '{0}' expects an argument." }, Unterminated_quoted_string_in_response_file_0: { code: 6045, category: DiagnosticCategory.Error, key: "Unterminated quoted string in response file '{0}'." }, - Argument_for_module_option_must_be_commonjs_amd_system_or_umd: { code: 6046, category: DiagnosticCategory.Error, key: "Argument for '--module' option must be 'commonjs', 'amd', 'system' or 'umd'." }, + Argument_for_module_option_must_be_commonjs_amd_system_umd_or_es6: { code: 6046, category: DiagnosticCategory.Error, key: "Argument for '--module' option must be 'commonjs', 'amd', 'system', 'umd', or 'es6'." }, Argument_for_target_option_must_be_ES3_ES5_or_ES6: { code: 6047, category: DiagnosticCategory.Error, key: "Argument for '--target' option must be 'ES3', 'ES5', or 'ES6'." }, Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1: { code: 6048, category: DiagnosticCategory.Error, key: "Locale must be of the form <language> or <language>-<territory>. For example '{0}' or '{1}'." }, Unsupported_locale_0: { code: 6049, category: DiagnosticCategory.Error, key: "Unsupported locale '{0}'." }, @@ -567,7 +570,6 @@ namespace ts { Argument_for_jsx_must_be_preserve_or_react: { code: 6081, category: DiagnosticCategory.Message, key: "Argument for '--jsx' must be 'preserve' or 'react'." }, Enables_experimental_support_for_ES7_decorators: { code: 6065, category: DiagnosticCategory.Message, key: "Enables experimental support for ES7 decorators." }, Enables_experimental_support_for_emitting_type_metadata_for_decorators: { code: 6066, category: DiagnosticCategory.Message, key: "Enables experimental support for emitting type metadata for decorators." }, - Option_experimentalAsyncFunctions_cannot_be_specified_when_targeting_ES5_or_lower: { code: 6067, category: DiagnosticCategory.Message, key: "Option 'experimentalAsyncFunctions' cannot be specified when targeting ES5 or lower." }, Enables_experimental_support_for_ES7_async_functions: { code: 6068, category: DiagnosticCategory.Message, key: "Enables experimental support for ES7 async functions." }, Specifies_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6: { code: 6069, category: DiagnosticCategory.Message, key: "Specifies module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)." }, Initializes_a_TypeScript_project_and_creates_a_tsconfig_json_file: { code: 6070, category: DiagnosticCategory.Message, key: "Initializes a TypeScript project and creates a tsconfig.json file." }, @@ -615,5 +617,7 @@ namespace ts { JSX_attribute_expected: { code: 17003, category: DiagnosticCategory.Error, key: "JSX attribute expected." }, Cannot_use_JSX_unless_the_jsx_flag_is_provided: { code: 17004, category: DiagnosticCategory.Error, key: "Cannot use JSX unless the '--jsx' flag is provided." }, A_constructor_cannot_contain_a_super_call_when_its_class_extends_null: { code: 17005, category: DiagnosticCategory.Error, key: "A constructor cannot contain a 'super' call when its class extends 'null'" }, + An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17006, category: DiagnosticCategory.Error, key: "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, + A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17007, category: DiagnosticCategory.Error, key: "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, }; } \ No newline at end of file diff --git a/ts/compiler/emitter.ts b/ts/compiler/emitter.ts index a5c9b88..e418ee1 100644 --- a/ts/compiler/emitter.ts +++ b/ts/compiler/emitter.ts @@ -7,6 +7,264 @@ namespace ts { return isExternalModule(sourceFile) || isDeclarationFile(sourceFile); } + type DependencyGroup = Array<ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration>; + + let entities: Map<number> = { + "quot": 0x0022, + "amp": 0x0026, + "apos": 0x0027, + "lt": 0x003C, + "gt": 0x003E, + "nbsp": 0x00A0, + "iexcl": 0x00A1, + "cent": 0x00A2, + "pound": 0x00A3, + "curren": 0x00A4, + "yen": 0x00A5, + "brvbar": 0x00A6, + "sect": 0x00A7, + "uml": 0x00A8, + "copy": 0x00A9, + "ordf": 0x00AA, + "laquo": 0x00AB, + "not": 0x00AC, + "shy": 0x00AD, + "reg": 0x00AE, + "macr": 0x00AF, + "deg": 0x00B0, + "plusmn": 0x00B1, + "sup2": 0x00B2, + "sup3": 0x00B3, + "acute": 0x00B4, + "micro": 0x00B5, + "para": 0x00B6, + "middot": 0x00B7, + "cedil": 0x00B8, + "sup1": 0x00B9, + "ordm": 0x00BA, + "raquo": 0x00BB, + "frac14": 0x00BC, + "frac12": 0x00BD, + "frac34": 0x00BE, + "iquest": 0x00BF, + "Agrave": 0x00C0, + "Aacute": 0x00C1, + "Acirc": 0x00C2, + "Atilde": 0x00C3, + "Auml": 0x00C4, + "Aring": 0x00C5, + "AElig": 0x00C6, + "Ccedil": 0x00C7, + "Egrave": 0x00C8, + "Eacute": 0x00C9, + "Ecirc": 0x00CA, + "Euml": 0x00CB, + "Igrave": 0x00CC, + "Iacute": 0x00CD, + "Icirc": 0x00CE, + "Iuml": 0x00CF, + "ETH": 0x00D0, + "Ntilde": 0x00D1, + "Ograve": 0x00D2, + "Oacute": 0x00D3, + "Ocirc": 0x00D4, + "Otilde": 0x00D5, + "Ouml": 0x00D6, + "times": 0x00D7, + "Oslash": 0x00D8, + "Ugrave": 0x00D9, + "Uacute": 0x00DA, + "Ucirc": 0x00DB, + "Uuml": 0x00DC, + "Yacute": 0x00DD, + "THORN": 0x00DE, + "szlig": 0x00DF, + "agrave": 0x00E0, + "aacute": 0x00E1, + "acirc": 0x00E2, + "atilde": 0x00E3, + "auml": 0x00E4, + "aring": 0x00E5, + "aelig": 0x00E6, + "ccedil": 0x00E7, + "egrave": 0x00E8, + "eacute": 0x00E9, + "ecirc": 0x00EA, + "euml": 0x00EB, + "igrave": 0x00EC, + "iacute": 0x00ED, + "icirc": 0x00EE, + "iuml": 0x00EF, + "eth": 0x00F0, + "ntilde": 0x00F1, + "ograve": 0x00F2, + "oacute": 0x00F3, + "ocirc": 0x00F4, + "otilde": 0x00F5, + "ouml": 0x00F6, + "divide": 0x00F7, + "oslash": 0x00F8, + "ugrave": 0x00F9, + "uacute": 0x00FA, + "ucirc": 0x00FB, + "uuml": 0x00FC, + "yacute": 0x00FD, + "thorn": 0x00FE, + "yuml": 0x00FF, + "OElig": 0x0152, + "oelig": 0x0153, + "Scaron": 0x0160, + "scaron": 0x0161, + "Yuml": 0x0178, + "fnof": 0x0192, + "circ": 0x02C6, + "tilde": 0x02DC, + "Alpha": 0x0391, + "Beta": 0x0392, + "Gamma": 0x0393, + "Delta": 0x0394, + "Epsilon": 0x0395, + "Zeta": 0x0396, + "Eta": 0x0397, + "Theta": 0x0398, + "Iota": 0x0399, + "Kappa": 0x039A, + "Lambda": 0x039B, + "Mu": 0x039C, + "Nu": 0x039D, + "Xi": 0x039E, + "Omicron": 0x039F, + "Pi": 0x03A0, + "Rho": 0x03A1, + "Sigma": 0x03A3, + "Tau": 0x03A4, + "Upsilon": 0x03A5, + "Phi": 0x03A6, + "Chi": 0x03A7, + "Psi": 0x03A8, + "Omega": 0x03A9, + "alpha": 0x03B1, + "beta": 0x03B2, + "gamma": 0x03B3, + "delta": 0x03B4, + "epsilon": 0x03B5, + "zeta": 0x03B6, + "eta": 0x03B7, + "theta": 0x03B8, + "iota": 0x03B9, + "kappa": 0x03BA, + "lambda": 0x03BB, + "mu": 0x03BC, + "nu": 0x03BD, + "xi": 0x03BE, + "omicron": 0x03BF, + "pi": 0x03C0, + "rho": 0x03C1, + "sigmaf": 0x03C2, + "sigma": 0x03C3, + "tau": 0x03C4, + "upsilon": 0x03C5, + "phi": 0x03C6, + "chi": 0x03C7, + "psi": 0x03C8, + "omega": 0x03C9, + "thetasym": 0x03D1, + "upsih": 0x03D2, + "piv": 0x03D6, + "ensp": 0x2002, + "emsp": 0x2003, + "thinsp": 0x2009, + "zwnj": 0x200C, + "zwj": 0x200D, + "lrm": 0x200E, + "rlm": 0x200F, + "ndash": 0x2013, + "mdash": 0x2014, + "lsquo": 0x2018, + "rsquo": 0x2019, + "sbquo": 0x201A, + "ldquo": 0x201C, + "rdquo": 0x201D, + "bdquo": 0x201E, + "dagger": 0x2020, + "Dagger": 0x2021, + "bull": 0x2022, + "hellip": 0x2026, + "permil": 0x2030, + "prime": 0x2032, + "Prime": 0x2033, + "lsaquo": 0x2039, + "rsaquo": 0x203A, + "oline": 0x203E, + "frasl": 0x2044, + "euro": 0x20AC, + "image": 0x2111, + "weierp": 0x2118, + "real": 0x211C, + "trade": 0x2122, + "alefsym": 0x2135, + "larr": 0x2190, + "uarr": 0x2191, + "rarr": 0x2192, + "darr": 0x2193, + "harr": 0x2194, + "crarr": 0x21B5, + "lArr": 0x21D0, + "uArr": 0x21D1, + "rArr": 0x21D2, + "dArr": 0x21D3, + "hArr": 0x21D4, + "forall": 0x2200, + "part": 0x2202, + "exist": 0x2203, + "empty": 0x2205, + "nabla": 0x2207, + "isin": 0x2208, + "notin": 0x2209, + "ni": 0x220B, + "prod": 0x220F, + "sum": 0x2211, + "minus": 0x2212, + "lowast": 0x2217, + "radic": 0x221A, + "prop": 0x221D, + "infin": 0x221E, + "ang": 0x2220, + "and": 0x2227, + "or": 0x2228, + "cap": 0x2229, + "cup": 0x222A, + "int": 0x222B, + "there4": 0x2234, + "sim": 0x223C, + "cong": 0x2245, + "asymp": 0x2248, + "ne": 0x2260, + "equiv": 0x2261, + "le": 0x2264, + "ge": 0x2265, + "sub": 0x2282, + "sup": 0x2283, + "nsub": 0x2284, + "sube": 0x2286, + "supe": 0x2287, + "oplus": 0x2295, + "otimes": 0x2297, + "perp": 0x22A5, + "sdot": 0x22C5, + "lceil": 0x2308, + "rceil": 0x2309, + "lfloor": 0x230A, + "rfloor": 0x230B, + "lang": 0x2329, + "rang": 0x232A, + "loz": 0x25CA, + "spades": 0x2660, + "clubs": 0x2663, + "hearts": 0x2665, + "diams": 0x2666 + }; + // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { Auto = 0x00000000, // No preferred name @@ -27,12 +285,10 @@ var __extends = (this && this.__extends) || function (d, b) { // emit output for the __decorate helper function const decorateHelper = ` var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); - switch (arguments.length) { - case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); - case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); - case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); - } + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; };`; // emit output for the __metadata helper function @@ -64,6 +320,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let compilerOptions = host.getCompilerOptions(); let languageVersion = compilerOptions.target || ScriptTarget.ES3; + let modulekind = compilerOptions.module ? compilerOptions.module : languageVersion === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.None; let sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined; let diagnostics: Diagnostic[] = []; let newLine = host.getNewLine(); @@ -102,14 +359,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi sourceMaps: sourceMapDataList }; - function isNodeDescendentOf(node: Node, ancestor: Node): boolean { - while (node) { - if (node === ancestor) return true; - node = node.parent; - } - return false; - } - function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { if (node.locals && hasProperty(node.locals, name)) { @@ -186,6 +435,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** Sourcemap data that will get encoded */ let sourceMapData: SourceMapData; + /** If removeComments is true, no leading-comments needed to be emitted **/ + let emitLeadingCommentsOfPosition = compilerOptions.removeComments ? function (pos: number) { } : emitLeadingCommentsOfPositionWorker; + + let moduleEmitDelegates: Map<(node: SourceFile) => void> = { + [ModuleKind.ES6]: emitES6Module, + [ModuleKind.AMD]: emitAMDModule, + [ModuleKind.System]: emitSystemModule, + [ModuleKind.UMD]: emitUMDModule, + [ModuleKind.CommonJS]: emitCommonJSModule, + }; + if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) { initializeEmitterWithSourceMaps(); } @@ -683,7 +943,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } } - + function emitNodeWithCommentsAndWithSourcemap(node: Node) { emitNodeConsideringCommentsOption(node, emitNodeWithSourceMap); } @@ -1178,7 +1438,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitJsxElement(openingNode: JsxOpeningLikeElement, children?: JsxChild[]) { let syntheticReactRef = <Identifier>createSynthesizedNode(SyntaxKind.Identifier); - syntheticReactRef.text = 'React'; + syntheticReactRef.text = "React"; syntheticReactRef.parent = openingNode; // Call React.createElement(tag, ... @@ -1382,7 +1642,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // // The emit for the decorated computed property decorator is: // - // Object.defineProperty(C.prototype, _a, __decorate([dec], C.prototype, _a, Object.getOwnPropertyDescriptor(C.prototype, _a))); + // __decorate([dec], C.prototype, _a, Object.getOwnPropertyDescriptor(C.prototype, _a)); // if (nodeIsDecorated(node.parent)) { if (!computedPropertyNamesToGeneratedNames) { @@ -1422,6 +1682,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let parent = node.parent; switch (parent.kind) { case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.AsExpression: case SyntaxKind.BinaryExpression: case SyntaxKind.CallExpression: case SyntaxKind.CaseClause: @@ -1490,7 +1751,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (container) { if (container.kind === SyntaxKind.SourceFile) { // Identifier references module export - if (languageVersion < ScriptTarget.ES6 && compilerOptions.module !== ModuleKind.System) { + if (modulekind !== ModuleKind.ES6 && modulekind !== ModuleKind.System) { write("exports."); } } @@ -1500,28 +1761,40 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("."); } } - else if (languageVersion < ScriptTarget.ES6) { - let declaration = resolver.getReferencedImportDeclaration(node); - if (declaration) { - if (declaration.kind === SyntaxKind.ImportClause) { - // Identifier references default import - write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent)); - write(languageVersion === ScriptTarget.ES3 ? "[\"default\"]" : ".default"); - return; + else { + if (modulekind !== ModuleKind.ES6) { + let declaration = resolver.getReferencedImportDeclaration(node); + if (declaration) { + if (declaration.kind === SyntaxKind.ImportClause) { + // Identifier references default import + write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent)); + write(languageVersion === ScriptTarget.ES3 ? "[\"default\"]" : ".default"); + return; + } + else if (declaration.kind === SyntaxKind.ImportSpecifier) { + // Identifier references named import + write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent)); + let name = (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name; + let identifier = getSourceTextOfNodeFromSourceFile(currentSourceFile, name); + if (languageVersion === ScriptTarget.ES3 && identifier === "default") { + write(`["default"]`); + } + else { + write("."); + write(identifier); + } + return; + } } - else if (declaration.kind === SyntaxKind.ImportSpecifier) { - // Identifier references named import - write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent)); - write("."); - writeTextOfNode(currentSourceFile, (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name); + } + + if (languageVersion !== ScriptTarget.ES6) { + let declaration = resolver.getReferencedNestedRedeclaration(node); + if (declaration) { + write(getGeneratedNameForNode(declaration.name)); return; } } - declaration = resolver.getReferencedNestedRedeclaration(node); - if (declaration) { - write(getGeneratedNameForNode(declaration.name)); - return; - } } if (nodeIsSynthesized(node)) { @@ -2035,6 +2308,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(": "); emit(node.name); } + + if (languageVersion >= ScriptTarget.ES6 && node.objectAssignmentInitializer) { + write(" = "); + emit(node.objectAssignmentInitializer); + } } function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { @@ -2049,15 +2327,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } return false; } - + function tryGetConstEnumValue(node: Node): number { if (compilerOptions.isolatedModules) { return undefined; } - - return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression + + return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression ? resolver.getConstantValue(<PropertyAccessExpression | ElementAccessExpression>node) - : undefined + : undefined; } // Returns 'true' if the code was actually indented, false otherwise. @@ -2354,7 +2632,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi operand.kind !== SyntaxKind.PostfixUnaryExpression && operand.kind !== SyntaxKind.NewExpression && !(operand.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.NewExpression) && - !(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression)) { + !(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression) && + !(operand.kind === SyntaxKind.NumericLiteral && node.parent.kind === SyntaxKind.PropertyAccessExpression)) { emit(operand); return; } @@ -2501,6 +2780,68 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } + /** + * Emit ES7 exponentiation operator downlevel using Math.pow + * @param node a binary expression node containing exponentiationOperator (**, **=) + */ + function emitExponentiationOperator(node: BinaryExpression) { + let leftHandSideExpression = node.left; + if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { + let synthesizedLHS: ElementAccessExpression | PropertyAccessExpression; + let shouldEmitParentheses = false; + if (isElementAccessExpression(leftHandSideExpression)) { + shouldEmitParentheses = true; + write("("); + + synthesizedLHS = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression, /*startsOnNewLine*/ false); + + let identifier = emitTempVariableAssignment(leftHandSideExpression.expression, /*canDefinedTempVariablesInPlaces*/ false, /*shouldEmitCommaBeforeAssignment*/ false); + synthesizedLHS.expression = identifier; + + if (leftHandSideExpression.argumentExpression.kind !== SyntaxKind.NumericLiteral && + leftHandSideExpression.argumentExpression.kind !== SyntaxKind.StringLiteral) { + let tempArgumentExpression = createAndRecordTempVariable(TempFlags._i); + (<ElementAccessExpression>synthesizedLHS).argumentExpression = tempArgumentExpression; + emitAssignment(tempArgumentExpression, leftHandSideExpression.argumentExpression, /*shouldEmitCommaBeforeAssignment*/ true); + } + else { + (<ElementAccessExpression>synthesizedLHS).argumentExpression = leftHandSideExpression.argumentExpression; + } + write(", "); + } + else if (isPropertyAccessExpression(leftHandSideExpression)) { + shouldEmitParentheses = true; + write("("); + synthesizedLHS = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression, /*startsOnNewLine*/ false); + + let identifier = emitTempVariableAssignment(leftHandSideExpression.expression, /*canDefinedTempVariablesInPlaces*/ false, /*shouldemitCommaBeforeAssignment*/ false); + synthesizedLHS.expression = identifier; + + (<PropertyAccessExpression>synthesizedLHS).dotToken = leftHandSideExpression.dotToken; + (<PropertyAccessExpression>synthesizedLHS).name = leftHandSideExpression.name; + write(", "); + } + + emit(synthesizedLHS || leftHandSideExpression); + write(" = "); + write("Math.pow("); + emit(synthesizedLHS || leftHandSideExpression); + write(", "); + emit(node.right); + write(")"); + if (shouldEmitParentheses) { + write(")"); + } + } + else { + write("Math.pow("); + emit(leftHandSideExpression); + write(", "); + emit(node.right); + write(")"); + } + } + function emitBinaryExpression(node: BinaryExpression) { if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { @@ -2518,12 +2859,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitNodeWithoutSourceMap(node.left); write(`", `); } - emit(node.left); - let indentedBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined); - write(tokenToString(node.operatorToken.kind)); - let indentedAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " "); - emit(node.right); - decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator); + + if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { + // Downleveled emit exponentiation operator using Math.pow + emitExponentiationOperator(node); + } + else { + emit(node.left); + // Add indentation before emit the operator if the operator is on different line + // For example: + // 3 + // + 2; + // emitted as + // 3 + // + 2; + let indentedBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined); + write(tokenToString(node.operatorToken.kind)); + let indentedAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " "); + emit(node.right); + decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator); + } + if (exportChanged) { write(")"); } @@ -3045,7 +3401,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(getGeneratedNameForNode(container)); write("."); } - else if (languageVersion < ScriptTarget.ES6 && compilerOptions.module !== ModuleKind.System) { + else if (modulekind !== ModuleKind.ES6 && modulekind !== ModuleKind.System) { write("exports."); } } @@ -3065,7 +3421,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (node.parent.kind === SyntaxKind.SourceFile) { Debug.assert(!!(node.flags & NodeFlags.Default) || node.kind === SyntaxKind.ExportAssignment); // only allow export default at a source file level - if (compilerOptions.module === ModuleKind.CommonJS || compilerOptions.module === ModuleKind.AMD || compilerOptions.module === ModuleKind.UMD) { + if (modulekind === ModuleKind.CommonJS || modulekind === ModuleKind.AMD || modulekind === ModuleKind.UMD) { if (!currentSourceFile.symbol.exports["___esModule"]) { if (languageVersion === ScriptTarget.ES5) { // default value of configurable, enumerable, writable are `false`. @@ -3087,7 +3443,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitStart(node); // emit call to exporter only for top level nodes - if (compilerOptions.module === ModuleKind.System && node.parent === currentSourceFile) { + if (modulekind === ModuleKind.System && node.parent === currentSourceFile) { // emit export default <smth> as // export("default", <smth>) write(`${exportFunctionForFile}("`); @@ -3123,10 +3479,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportMemberAssignments(name: Identifier) { - if (compilerOptions.module === ModuleKind.System) { + if (modulekind === ModuleKind.System) { return; } - + if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { for (let specifier of exportSpecifiers[name.text]) { writeLine(); @@ -3141,14 +3497,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } } - + function emitExportSpecifierInSystemModule(specifier: ExportSpecifier): void { - Debug.assert(compilerOptions.module === ModuleKind.System); + Debug.assert(modulekind === ModuleKind.System); if (!resolver.getReferencedValueDeclaration(specifier.propertyName || specifier.name) && !resolver.isValueAliasDeclaration(specifier) ) { return; } - + writeLine(); emitStart(specifier.name); write(`${exportFunctionForFile}("`); @@ -3160,6 +3516,58 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(";"); } + /** + * Emit an assignment to a given identifier, 'name', with a given expression, 'value'. + * @param name an identifier as a left-hand-side operand of the assignment + * @param value an expression as a right-hand-side operand of the assignment + * @param shouldEmitCommaBeforeAssignment a boolean indicating whether to prefix an assignment with comma + */ + function emitAssignment(name: Identifier, value: Expression, shouldEmitCommaBeforeAssignment: boolean) { + if (shouldEmitCommaBeforeAssignment) { + write(", "); + } + + let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name); + + if (exportChanged) { + write(`${exportFunctionForFile}("`); + emitNodeWithCommentsAndWithoutSourcemap(name); + write(`", `); + } + + const isVariableDeclarationOrBindingElement = + name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement); + + if (isVariableDeclarationOrBindingElement) { + emitModuleMemberName(<Declaration>name.parent); + } + else { + emit(name); + } + + write(" = "); + emit(value); + + if (exportChanged) { + write(")"); + } + } + + /** + * Create temporary variable, emit an assignment of the variable the given expression + * @param expression an expression to assign to the newly created temporary variable + * @param canDefineTempVariablesInPlace a boolean indicating whether you can define the temporary variable at an assignment location + * @param shouldEmitCommaBeforeAssignment a boolean indicating whether an assignment should prefix with comma + */ + function emitTempVariableAssignment(expression: Expression, canDefineTempVariablesInPlace: boolean, shouldEmitCommaBeforeAssignment: boolean): Identifier { + let identifier = createTempVariable(TempFlags.Auto); + if (!canDefineTempVariablesInPlace) { + recordTempDeclaration(identifier); + } + emitAssignment(identifier, expression, shouldEmitCommaBeforeAssignment); + return identifier; + } + function emitDestructuring(root: BinaryExpression | VariableDeclaration | ParameterDeclaration, isAssignmentExpressionStatement: boolean, value?: Expression) { let emitCount = 0; @@ -3185,36 +3593,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitBindingElement(<BindingElement>root, value); } - function emitAssignment(name: Identifier, value: Expression) { - if (emitCount++) { - write(", "); - } - - const isVariableDeclarationOrBindingElement = - name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement); - - let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name); - - if (exportChanged) { - write(`${exportFunctionForFile}("`); - emitNodeWithCommentsAndWithoutSourcemap(name); - write(`", `); - } - - if (isVariableDeclarationOrBindingElement) { - emitModuleMemberName(<Declaration>name.parent); - } - else { - emit(name); - } - - write(" = "); - emit(value); - - if (exportChanged) { - write(")"); - } - } /** * Ensures that there exists a declared identifier whose value holds the given expression. @@ -3230,11 +3608,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return expr; } - let identifier = createTempVariable(TempFlags.Auto); - if (!canDefineTempVariablesInPlace) { - recordTempDeclaration(identifier); - } - emitAssignment(identifier, expr); + let identifier = emitTempVariableAssignment(expr, canDefineTempVariablesInPlace, emitCount > 0); + emitCount++; return identifier; } @@ -3297,7 +3672,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi for (let p of properties) { if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) { let propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name; - emitDestructuringAssignment((<PropertyAssignment>p).initializer || propName, createPropertyAccessForDestructuringProperty(value, propName)); + let target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName; + emitDestructuringAssignment(target, createPropertyAccessForDestructuringProperty(value, propName)); } } } @@ -3322,8 +3698,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } - function emitDestructuringAssignment(target: Expression, value: Expression) { - if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) { + function emitDestructuringAssignment(target: Expression | ShorthandPropertyAssignment, value: Expression) { + if (target.kind === SyntaxKind.ShorthandPropertyAssignment) { + if ((<ShorthandPropertyAssignment>target).objectAssignmentInitializer) { + value = createDefaultValueCheck(value, (<ShorthandPropertyAssignment>target).objectAssignmentInitializer); + } + target = (<ShorthandPropertyAssignment>target).name; + } + else if (target.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>target).operatorToken.kind === SyntaxKind.EqualsToken) { value = createDefaultValueCheck(value, (<BinaryExpression>target).right); target = (<BinaryExpression>target).left; } @@ -3334,7 +3716,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value); } else { - emitAssignment(<Identifier>target, value); + emitAssignment(<Identifier>target, value, /*shouldEmitCommaBeforeAssignment*/ emitCount > 0); + emitCount++; } } @@ -3403,7 +3786,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } else { - emitAssignment(<Identifier>target.name, value); + emitAssignment(<Identifier>target.name, value, /*shouldEmitCommaBeforeAssignment*/ emitCount > 0); + emitCount++; } } } @@ -3480,7 +3864,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function isES6ExportedDeclaration(node: Node) { return !!(node.flags & NodeFlags.Export) && - languageVersion >= ScriptTarget.ES6 && + modulekind === ModuleKind.ES6 && node.parent.kind === SyntaxKind.SourceFile; } @@ -3508,7 +3892,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(";"); } } - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { + if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile) { forEach(node.declarationList.declarations, emitExportVariableAssignments); } } @@ -3687,7 +4071,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitFunctionDeclaration(node: FunctionLikeDeclaration) { if (nodeIsMissing(node.body)) { - return emitOnlyPinnedOrTripleSlashComments(node); + return emitCommentsOnNotEmittedNode(node); } // TODO (yuisu) : we should not have special cases to condition emitting comments @@ -3734,7 +4118,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } emitSignatureAndBody(node); - if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.FunctionDeclaration && node.parent === currentSourceFile && node.name) { + if (modulekind !== ModuleKind.ES6 && node.kind === SyntaxKind.FunctionDeclaration && node.parent === currentSourceFile && node.name) { emitExportMemberAssignments((<FunctionDeclaration>node).name); } @@ -4164,7 +4548,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } else if (member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) { if (!(<MethodDeclaration>member).body) { - return emitOnlyPinnedOrTripleSlashComments(member); + return emitCommentsOnNotEmittedNode(member); } writeLine(); @@ -4231,7 +4615,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitMemberFunctionsForES6AndHigher(node: ClassLikeDeclaration) { for (let member of node.members) { if ((member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && !(<MethodDeclaration>member).body) { - emitOnlyPinnedOrTripleSlashComments(member); + emitCommentsOnNotEmittedNode(member); } else if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.GetAccessor || @@ -4288,7 +4672,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Emit the constructor overload pinned comments forEach(node.members, member => { if (member.kind === SyntaxKind.Constructor && !(<ConstructorDeclaration>member).body) { - emitOnlyPinnedOrTripleSlashComments(member); + emitCommentsOnNotEmittedNode(member); } // Check if there is any non-static property assignment if (member.kind === SyntaxKind.PropertyDeclaration && (<PropertyDeclaration>member).initializer && (member.flags & NodeFlags.Static) === 0) { @@ -4410,6 +4794,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { emitClassLikeDeclarationForES6AndHigher(node); } + if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile && node.name) { + emitExportMemberAssignments(node.name); + } } function emitClassLikeDeclarationForES6AndHigher(node: ClassLikeDeclaration) { @@ -4428,7 +4815,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // // let C = class { // }; - // Object.defineProperty(C, "name", { value: "C", configurable: true }); // C = __decorate([dec], C); // // * For an exported class declaration: @@ -4440,7 +4826,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // // export let C = class { // }; - // Object.defineProperty(C, "name", { value: "C", configurable: true }); // C = __decorate([dec], C); // // * For a default export of a class declaration with a name: @@ -4452,7 +4837,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // // let C = class { // } - // Object.defineProperty(C, "name", { value: "C", configurable: true }); // C = __decorate([dec], C); // export default C; // @@ -4509,8 +4893,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("class"); - // check if this is an "export default class" as it may not have a name. Do not emit the name if the class is decorated. - if ((node.name || !(node.flags & NodeFlags.Default)) && !thisNodeIsDecorated) { + // emit name if + // - node has a name + // - this is default export with static initializers + if ((node.name || (node.flags & NodeFlags.Default && staticProperties.length > 0)) && !thisNodeIsDecorated) { write(" "); emitDeclarationName(node); } @@ -4658,10 +5044,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (node.kind === SyntaxKind.ClassDeclaration) { emitExportMemberAssignment(<ClassDeclaration>node); } - - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile && node.name) { - emitExportMemberAssignments(node.name); - } } function emitClassMemberPrefix(node: ClassLikeDeclaration, member: Node) { @@ -4778,21 +5160,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // // The emit for a method is: // - // Object.defineProperty(C.prototype, "method", - // __decorate([ - // dec, - // __param(0, dec2), - // __metadata("design:type", Function), - // __metadata("design:paramtypes", [Object]), - // __metadata("design:returntype", void 0) - // ], C.prototype, "method", Object.getOwnPropertyDescriptor(C.prototype, "method"))); + // __decorate([ + // dec, + // __param(0, dec2), + // __metadata("design:type", Function), + // __metadata("design:paramtypes", [Object]), + // __metadata("design:returntype", void 0) + // ], C.prototype, "method", undefined); // // The emit for an accessor is: // - // Object.defineProperty(C.prototype, "accessor", - // __decorate([ - // dec - // ], C.prototype, "accessor", Object.getOwnPropertyDescriptor(C.prototype, "accessor"))); + // __decorate([ + // dec + // ], C.prototype, "accessor", undefined); // // The emit for a property is: // @@ -4803,18 +5183,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi writeLine(); emitStart(member); - if (member.kind !== SyntaxKind.PropertyDeclaration) { - write("Object.defineProperty("); - emitStart(member.name); - emitClassMemberPrefix(node, member); - write(", "); - emitExpressionForPropertyName(member.name); - emitEnd(member.name); - write(","); - increaseIndent(); - writeLine(); - } - write("__decorate(["); increaseIndent(); writeLine(); @@ -4838,15 +5206,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitExpressionForPropertyName(member.name); emitEnd(member.name); - if (member.kind !== SyntaxKind.PropertyDeclaration) { - write(", Object.getOwnPropertyDescriptor("); - emitStart(member.name); - emitClassMemberPrefix(node, member); - write(", "); - emitExpressionForPropertyName(member.name); - emitEnd(member.name); - write("))"); - decreaseIndent(); + if (languageVersion > ScriptTarget.ES3) { + if (member.kind !== SyntaxKind.PropertyDeclaration) { + // We emit `null` here to indicate to `__decorate` that it can invoke `Object.getOwnPropertyDescriptor` directly. + // We have this extra argument here so that we can inject an explicit property descriptor at a later date. + write(", null"); + } + else { + // We emit `void 0` here to indicate to `__decorate` that it can invoke `Object.defineProperty` directly, but that it + // should not invoke `Object.getOwnPropertyDescriptor`. + write(", void 0"); + } } write(");"); @@ -5187,7 +5557,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitInterfaceDeclaration(node: InterfaceDeclaration) { - emitOnlyPinnedOrTripleSlashComments(node); + emitCommentsOnNotEmittedNode(node); } function shouldEmitEnumDeclaration(node: EnumDeclaration) { @@ -5245,8 +5615,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(node); write(";"); } - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { - if (compilerOptions.module === ModuleKind.System && (node.flags & NodeFlags.Export)) { + if (modulekind !== ModuleKind.ES6 && node.parent === currentSourceFile) { + if (modulekind === ModuleKind.System && (node.flags & NodeFlags.Export)) { // write the call to exporter for enum writeLine(); write(`${exportFunctionForFile}("`); @@ -5309,7 +5679,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let shouldEmit = shouldEmitModuleDeclaration(node); if (!shouldEmit) { - return emitOnlyPinnedOrTripleSlashComments(node); + return emitCommentsOnNotEmittedNode(node); } let hoistedInDeclarationScope = shouldHoistDeclarationInSystemJsModule(node); let emitVarForModule = !hoistedInDeclarationScope && !isModuleMergedWithES6Class(node); @@ -5368,7 +5738,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(" = {}));"); emitEnd(node); if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && node.parent === currentSourceFile) { - if (compilerOptions.module === ModuleKind.System && (node.flags & NodeFlags.Export)) { + if (modulekind === ModuleKind.System && (node.flags & NodeFlags.Export)) { writeLine(); write(`${exportFunctionForFile}("`); emitDeclarationName(node); @@ -5379,14 +5749,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitExportMemberAssignments(<Identifier>node.name); } } - + /* - * Some bundlers (SystemJS builder) sometimes want to rename dependencies. - * Here we check if alternative name was provided for a given moduleName and return it if possible. + * Some bundlers (SystemJS builder) sometimes want to rename dependencies. + * Here we check if alternative name was provided for a given moduleName and return it if possible. */ function tryRenameExternalModule(moduleName: LiteralExpression): string { if (currentSourceFile.renamedDependencies && hasProperty(currentSourceFile.renamedDependencies, moduleName.text)) { - return `"${currentSourceFile.renamedDependencies[moduleName.text]}"` + return `"${currentSourceFile.renamedDependencies[moduleName.text]}"`; } return undefined; } @@ -5432,7 +5802,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitImportDeclaration(node: ImportDeclaration) { - if (languageVersion < ScriptTarget.ES6) { + if (modulekind !== ModuleKind.ES6) { return emitExternalImportDeclaration(node); } @@ -5483,7 +5853,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let isExportedImport = node.kind === SyntaxKind.ImportEqualsDeclaration && (node.flags & NodeFlags.Export) !== 0; let namespaceDeclaration = getNamespaceDeclarationNode(node); - if (compilerOptions.module !== ModuleKind.AMD) { + if (modulekind !== ModuleKind.AMD) { emitLeadingComments(node); emitStart(node); if (namespaceDeclaration && !isDefaultImport(node)) { @@ -5595,15 +5965,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportDeclaration(node: ExportDeclaration) { - Debug.assert(compilerOptions.module !== ModuleKind.System); + Debug.assert(modulekind !== ModuleKind.System); - if (languageVersion < ScriptTarget.ES6) { + if (modulekind !== ModuleKind.ES6) { if (node.moduleSpecifier && (!node.exportClause || resolver.isValueAliasDeclaration(node))) { emitStart(node); let generatedName = getGeneratedNameForNode(node); if (node.exportClause) { // export { x, y, ... } from "foo" - if (compilerOptions.module !== ModuleKind.AMD) { + if (modulekind !== ModuleKind.AMD) { write("var "); write(generatedName); write(" = "); @@ -5630,7 +6000,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // export * from "foo" writeLine(); write("__export("); - if (compilerOptions.module !== ModuleKind.AMD) { + if (modulekind !== ModuleKind.AMD) { emitRequire(getExternalModuleName(node)); } else { @@ -5663,7 +6033,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportOrImportSpecifierList(specifiers: ImportOrExportSpecifier[], shouldEmit: (node: Node) => boolean) { - Debug.assert(languageVersion >= ScriptTarget.ES6); + Debug.assert(modulekind === ModuleKind.ES6); let needsComma = false; for (let specifier of specifiers) { @@ -5683,7 +6053,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitExportAssignment(node: ExportAssignment) { if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { - if (languageVersion >= ScriptTarget.ES6) { + if (modulekind === ModuleKind.ES6) { writeLine(); emitStart(node); write("export default "); @@ -5698,7 +6068,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { writeLine(); emitStart(node); - if (compilerOptions.module === ModuleKind.System) { + if (modulekind === ModuleKind.System) { write(`${exportFunctionForFile}("default",`); emit(node.expression); write(")"); @@ -5708,7 +6078,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitContainingModuleName(node); if (languageVersion === ScriptTarget.ES3) { write("[\"default\"] = "); - } else { + } + else { write(".default = "); } emit(node.expression); @@ -5801,7 +6172,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function getExternalModuleNameText(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { let moduleName = getExternalModuleName(importNode); if (moduleName.kind === SyntaxKind.StringLiteral) { - return tryRenameExternalModule(<LiteralExpression>moduleName) || getLiteralText(<LiteralExpression>moduleName); + return tryRenameExternalModule(<LiteralExpression>moduleName) || getLiteralText(<LiteralExpression>moduleName); } return undefined; @@ -6144,7 +6515,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isCurrentFileSystemExternalModule() { - return compilerOptions.module === ModuleKind.System && isExternalModule(currentSourceFile); + return modulekind === ModuleKind.System && isExternalModule(currentSourceFile); } function emitSystemModuleBody(node: SourceFile, dependencyGroups: DependencyGroup[], startIndex: number): void { @@ -6203,7 +6574,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitSetters(exportStarFunction: string, dependencyGroups: DependencyGroup[]) { write("setters:["); - + for (let i = 0; i < dependencyGroups.length; ++i) { if (i !== 0) { write(","); @@ -6211,17 +6582,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi writeLine(); increaseIndent(); - + let group = dependencyGroups[i]; - + // derive a unique name for parameter from the first named entry in the group let parameterName = makeUniqueName(forEach(group, getLocalNameForExternalImport) || ""); write(`function (${parameterName}) {`); increaseIndent(); - - for(let entry of group) { + + for (let entry of group) { let importVariableName = getLocalNameForExternalImport(entry) || ""; - + switch (entry.kind) { case SyntaxKind.ImportDeclaration: if (!(<ImportDeclaration>entry).importClause) { @@ -6257,7 +6628,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(","); writeLine(); } - + let e = (<ExportDeclaration>entry).exportClause.elements[i]; write(`"`); emitNodeWithCommentsAndWithoutSourcemap(e.name); @@ -6267,7 +6638,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } decreaseIndent(); writeLine(); - write("});") + write("});"); } else { writeLine(); @@ -6302,7 +6673,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // - import declarations are not emitted since they are already handled in setters // - export declarations with module specifiers are not emitted since they were already written in setters // - export declarations without module specifiers are emitted preserving the order - case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionDeclaration: case SyntaxKind.ImportDeclaration: continue; case SyntaxKind.ExportDeclaration: @@ -6322,16 +6693,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi default: writeLine(); emit(statement); - } + } } decreaseIndent(); writeLine(); write("}"); // execute } - - type DependencyGroup = Array<ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration>; - - function emitSystemModule(node: SourceFile, startIndex: number): void { + + function emitSystemModule(node: SourceFile): void { collectExternalModuleInfo(node); // System modules has the following shape // System.register(['dep-1', ... 'dep-n'], function(exports) {/* module body function */}) @@ -6350,7 +6719,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(`"${node.moduleName}", `); } write("["); - + let groupIndices: Map<number> = {}; let dependencyGroups: DependencyGroup[] = []; @@ -6370,12 +6739,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (i !== 0) { write(", "); } - + write(text); } write(`], function(${exportFunctionForFile}) {`); writeLine(); increaseIndent(); + let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ true); emitEmitHelpers(node); emitCaptureThisForNodeIfNecessary(node); emitSystemModuleBody(node, dependencyGroups, startIndex); @@ -6384,19 +6754,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("});"); } - function emitAMDDependencies(node: SourceFile, includeNonAmdDependencies: boolean) { - // An AMD define function has the following shape: - // define(id?, dependencies?, factory); - // - // This has the shape of - // define(name, ["module1", "module2"], function (module1Alias) { - // The location of the alias in the parameter list in the factory function needs to - // match the position of the module name in the dependency list. - // - // To ensure this is true in cases of modules with no aliases, e.g.: - // `import "module"` or `<amd-dependency path= "a.css" />` - // we need to add modules without alias names to the end of the dependencies list + interface AMDDependencyNames { + aliasedModuleNames: string[]; + unaliasedModuleNames: string[]; + importAliasNames: string[]; + } + function getAMDDependencyNames(node: SourceFile, includeNonAmdDependencies: boolean): AMDDependencyNames { // names of modules with corresponding parameter in the factory function let aliasedModuleNames: string[] = []; // names of modules with no corresponding parameters in factory function @@ -6431,6 +6795,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } + return { aliasedModuleNames, unaliasedModuleNames, importAliasNames }; + } + + function emitAMDDependencies(node: SourceFile, includeNonAmdDependencies: boolean) { + // An AMD define function has the following shape: + // define(id?, dependencies?, factory); + // + // This has the shape of + // define(name, ["module1", "module2"], function (module1Alias) { + // The location of the alias in the parameter list in the factory function needs to + // match the position of the module name in the dependency list. + // + // To ensure this is true in cases of modules with no aliases, e.g.: + // `import "module"` or `<amd-dependency path= "a.css" />` + // we need to add modules without alias names to the end of the dependencies list + + let dependencyNames = getAMDDependencyNames(node, includeNonAmdDependencies); + emitAMDDependencyList(dependencyNames); + write(", "); + emitAMDFactoryHeader(dependencyNames); + } + + function emitAMDDependencyList({ aliasedModuleNames, unaliasedModuleNames }: AMDDependencyNames) { write("[\"require\", \"exports\""); if (aliasedModuleNames.length) { write(", "); @@ -6440,14 +6827,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(", "); write(unaliasedModuleNames.join(", ")); } - write("], function (require, exports"); + write("]"); + } + + function emitAMDFactoryHeader({ importAliasNames }: AMDDependencyNames) { + write("function (require, exports"); if (importAliasNames.length) { write(", "); write(importAliasNames.join(", ")); } + write(") {"); } - function emitAMDModule(node: SourceFile, startIndex: number) { + function emitAMDModule(node: SourceFile) { emitEmitHelpers(node); collectExternalModuleInfo(node); @@ -6457,8 +6849,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("\"" + node.moduleName + "\", "); } emitAMDDependencies(node, /*includeNonAmdDependencies*/ true); - write(") {"); increaseIndent(); + let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ true); emitExportStarHelper(); emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); @@ -6469,7 +6861,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("});"); } - function emitCommonJSModule(node: SourceFile, startIndex: number) { + function emitCommonJSModule(node: SourceFile) { + let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false); emitEmitHelpers(node); collectExternalModuleInfo(node); emitExportStarHelper(); @@ -6479,22 +6872,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitExportEquals(/*emitAsReturn*/ false); } - function emitUMDModule(node: SourceFile, startIndex: number) { + function emitUMDModule(node: SourceFile) { emitEmitHelpers(node); collectExternalModuleInfo(node); + let dependencyNames = getAMDDependencyNames(node, /*includeNonAmdDependencies*/ false); + // Module is detected first to support Browserify users that load into a browser with an AMD loader - writeLines(`(function (deps, factory) { + writeLines(`(function (factory) { if (typeof module === 'object' && typeof module.exports === 'object') { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === 'function' && define.amd) { - define(deps, factory); - } + define(`); + emitAMDDependencyList(dependencyNames); + write(", factory);"); + writeLines(` } })(`); - emitAMDDependencies(node, false); - write(") {"); + emitAMDFactoryHeader(dependencyNames); increaseIndent(); + let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ true); emitExportStarHelper(); emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); @@ -6505,11 +6902,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("});"); } - function emitES6Module(node: SourceFile, startIndex: number) { + function emitES6Module(node: SourceFile) { externalImports = undefined; exportSpecifiers = undefined; exportEquals = undefined; hasExportStars = false; + let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false); emitEmitHelpers(node); emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); @@ -6556,7 +6954,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (isLineBreak(c)) { if (firstNonWhitespace !== -1 && (lastNonWhitespace - firstNonWhitespace + 1 > 0)) { let part = text.substr(firstNonWhitespace, lastNonWhitespace - firstNonWhitespace + 1); - result = (result ? result + "\" + ' ' + \"" : "") + part; + result = (result ? result + "\" + ' ' + \"" : "") + escapeString(part); } firstNonWhitespace = -1; } @@ -6570,7 +6968,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (firstNonWhitespace !== -1) { let part = text.substr(firstNonWhitespace); - result = (result ? result + "\" + ' ' + \"" : "") + part; + result = (result ? result + "\" + ' ' + \"" : "") + escapeString(part); } if (result) { @@ -6698,27 +7096,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitShebang(); emitDetachedComments(node); - // emit prologue directives prior to __extends - let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false); - if (isExternalModule(node) || compilerOptions.isolatedModules) { - if (languageVersion >= ScriptTarget.ES6) { - emitES6Module(node, startIndex); - } - else if (compilerOptions.module === ModuleKind.AMD) { - emitAMDModule(node, startIndex); - } - else if (compilerOptions.module === ModuleKind.System) { - emitSystemModule(node, startIndex); - } - else if (compilerOptions.module === ModuleKind.UMD) { - emitUMDModule(node, startIndex); - } - else { - emitCommonJSModule(node, startIndex); - } + let emitModule = moduleEmitDelegates[modulekind] || moduleEmitDelegates[ModuleKind.CommonJS]; + emitModule(node); } else { + // emit prologue directives prior to __extends + let startIndex = emitDirectivePrologues(node.statements, /*startWithNewLine*/ false); externalImports = undefined; exportSpecifiers = undefined; exportEquals = undefined; @@ -6739,7 +7123,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitNodeConsideringCommentsOption(node: Node, emitNodeConsideringSourcemap: (node: Node) => void): void { if (node) { if (node.flags & NodeFlags.Ambient) { - return emitOnlyPinnedOrTripleSlashComments(node); + return emitCommentsOnNotEmittedNode(node); } if (isSpecializedCommentHandling(node)) { @@ -6796,7 +7180,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return shouldEmitEnumDeclaration(<EnumDeclaration>node); } - // If the node is emitted in specialized fashion, dont emit comments as this node will handle + // If the node is emitted in specialized fashion, dont emit comments as this node will handle // emitting comments when emitting itself Debug.assert(!isSpecializedCommentHandling(node)); @@ -7006,22 +7390,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return leadingComments; } + function isPinnedComments(comment: CommentRange) { + return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && + currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; + } + /** - * Removes all but the pinned or triple slash comments. - * @param ranges The array to be filtered - * @param onlyPinnedOrTripleSlashComments whether the filtering should be performed. - */ - function filterComments(ranges: CommentRange[], onlyPinnedOrTripleSlashComments: boolean): CommentRange[] { - // If we're removing comments, then we want to strip out all but the pinned or - // triple slash comments. - if (ranges && onlyPinnedOrTripleSlashComments) { - ranges = filter(ranges, isPinnedOrTripleSlashComment); - if (ranges.length === 0) { - return undefined; - } + * Determine if the given comment is a triple-slash + * + * @return true if the comment is a triple-slash comment else false + **/ + function isTripleSlashComment(comment: CommentRange) { + // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text + // so that we don't end up computing comment string and doing match for all // comments + if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.slash && + comment.pos + 2 < comment.end && + currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.slash) { + let textSubStr = currentSourceFile.text.substring(comment.pos, comment.end); + return textSubStr.match(fullTripleSlashReferencePathRegEx) || + textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ? + true : false; } - - return ranges; + return false; } function getLeadingCommentsToEmit(node: Node) { @@ -7049,28 +7439,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } - function emitOnlyPinnedOrTripleSlashComments(node: Node) { - emitLeadingCommentsWorker(node, /*onlyPinnedOrTripleSlashComments:*/ true); + /** + * Emit comments associated with node that will not be emitted into JS file + */ + function emitCommentsOnNotEmittedNode(node: Node) { + emitLeadingCommentsWorker(node, /*isEmittedNode:*/ false); } function emitLeadingComments(node: Node) { - return emitLeadingCommentsWorker(node, /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments); + return emitLeadingCommentsWorker(node, /*isEmittedNode:*/ true); } - function emitLeadingCommentsWorker(node: Node, onlyPinnedOrTripleSlashComments: boolean) { - // If the caller only wants pinned or triple slash comments, then always filter - // down to that set. Otherwise, filter based on the current compiler options. - let leadingComments = filterComments(getLeadingCommentsToEmit(node), onlyPinnedOrTripleSlashComments); + function emitLeadingCommentsWorker(node: Node, isEmittedNode: boolean) { + if (compilerOptions.removeComments) { + return; + } + + let leadingComments: CommentRange[]; + if (isEmittedNode) { + leadingComments = getLeadingCommentsToEmit(node); + } + else { + // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node, + // unless it is a triple slash comment at the top of the file. + // For Example: + // /// <reference-path ...> + // declare var x; + // /// <reference-path ...> + // interface F {} + // The first /// will NOT be removed while the second one will be removed eventhough both node will not be emitted + if (node.pos === 0) { + leadingComments = filter(getLeadingCommentsToEmit(node), isTripleSlashComment); + } + } emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); + emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator:*/ true, newLine, writeComment); } function emitTrailingComments(node: Node) { + if (compilerOptions.removeComments) { + return; + } + // Emit the trailing comments only if the parent's end doesn't match - let trailingComments = filterComments(getTrailingCommentsToEmit(node), /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments); + let trailingComments = getTrailingCommentsToEmit(node); // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ emitComments(currentSourceFile, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment); @@ -7082,13 +7497,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi * ^ => pos; the function will emit "comment1" in the emitJS */ function emitTrailingCommentsOfPosition(pos: number) { - let trailingComments = filterComments(getTrailingCommentRanges(currentSourceFile.text, pos), /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments); + if (compilerOptions.removeComments) { + return; + } + + let trailingComments = getTrailingCommentRanges(currentSourceFile.text, pos); // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/ emitComments(currentSourceFile, writer, trailingComments, /*trailingSeparator*/ true, newLine, writeComment); } - function emitLeadingCommentsOfPosition(pos: number) { + function emitLeadingCommentsOfPositionWorker(pos: number) { + if (compilerOptions.removeComments) { + return; + } + let leadingComments: CommentRange[]; if (hasDetachedComments(pos)) { // get comments without detached comments @@ -7099,7 +7522,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi leadingComments = getLeadingCommentRanges(currentSourceFile.text, pos); } - leadingComments = filterComments(leadingComments, compilerOptions.removeComments); emitNewLineBeforeLeadingComments(currentSourceFile, writer, { pos: pos, end: pos }, leadingComments); // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space @@ -7107,7 +7529,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDetachedComments(node: TextRange) { - let leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos); + let leadingComments: CommentRange[]; + if (compilerOptions.removeComments) { + // removeComments is true, only reserve pinned comment at the top of file + // For example: + // /*! Pinned Comment */ + // + // var x = 10; + if (node.pos === 0) { + leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComments); + } + } + else { + // removeComments is false, just get detached as normal and bypass the process to filter comment + leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos); + } + if (leadingComments) { let detachedComments: CommentRange[] = []; let lastComment: CommentRange; @@ -7150,27 +7587,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } } - + function emitShebang() { let shebang = getShebang(currentSourceFile.text); if (shebang) { write(shebang); } } - - function isPinnedOrTripleSlashComment(comment: CommentRange) { - if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) { - return currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; - } - // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text - // so that we don't end up computing comment string and doing match for all // comments - else if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.slash && - comment.pos + 2 < comment.end && - currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.slash && - currentSourceFile.text.substring(comment.pos, comment.end).match(fullTripleSlashReferencePathRegEx)) { - return true; - } - } } function emitFile(jsFilePath: string, sourceFile?: SourceFile) { @@ -7181,260 +7604,4 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } } - - var entities: Map<number> = { - "quot": 0x0022, - "amp": 0x0026, - "apos": 0x0027, - "lt": 0x003C, - "gt": 0x003E, - "nbsp": 0x00A0, - "iexcl": 0x00A1, - "cent": 0x00A2, - "pound": 0x00A3, - "curren": 0x00A4, - "yen": 0x00A5, - "brvbar": 0x00A6, - "sect": 0x00A7, - "uml": 0x00A8, - "copy": 0x00A9, - "ordf": 0x00AA, - "laquo": 0x00AB, - "not": 0x00AC, - "shy": 0x00AD, - "reg": 0x00AE, - "macr": 0x00AF, - "deg": 0x00B0, - "plusmn": 0x00B1, - "sup2": 0x00B2, - "sup3": 0x00B3, - "acute": 0x00B4, - "micro": 0x00B5, - "para": 0x00B6, - "middot": 0x00B7, - "cedil": 0x00B8, - "sup1": 0x00B9, - "ordm": 0x00BA, - "raquo": 0x00BB, - "frac14": 0x00BC, - "frac12": 0x00BD, - "frac34": 0x00BE, - "iquest": 0x00BF, - "Agrave": 0x00C0, - "Aacute": 0x00C1, - "Acirc": 0x00C2, - "Atilde": 0x00C3, - "Auml": 0x00C4, - "Aring": 0x00C5, - "AElig": 0x00C6, - "Ccedil": 0x00C7, - "Egrave": 0x00C8, - "Eacute": 0x00C9, - "Ecirc": 0x00CA, - "Euml": 0x00CB, - "Igrave": 0x00CC, - "Iacute": 0x00CD, - "Icirc": 0x00CE, - "Iuml": 0x00CF, - "ETH": 0x00D0, - "Ntilde": 0x00D1, - "Ograve": 0x00D2, - "Oacute": 0x00D3, - "Ocirc": 0x00D4, - "Otilde": 0x00D5, - "Ouml": 0x00D6, - "times": 0x00D7, - "Oslash": 0x00D8, - "Ugrave": 0x00D9, - "Uacute": 0x00DA, - "Ucirc": 0x00DB, - "Uuml": 0x00DC, - "Yacute": 0x00DD, - "THORN": 0x00DE, - "szlig": 0x00DF, - "agrave": 0x00E0, - "aacute": 0x00E1, - "acirc": 0x00E2, - "atilde": 0x00E3, - "auml": 0x00E4, - "aring": 0x00E5, - "aelig": 0x00E6, - "ccedil": 0x00E7, - "egrave": 0x00E8, - "eacute": 0x00E9, - "ecirc": 0x00EA, - "euml": 0x00EB, - "igrave": 0x00EC, - "iacute": 0x00ED, - "icirc": 0x00EE, - "iuml": 0x00EF, - "eth": 0x00F0, - "ntilde": 0x00F1, - "ograve": 0x00F2, - "oacute": 0x00F3, - "ocirc": 0x00F4, - "otilde": 0x00F5, - "ouml": 0x00F6, - "divide": 0x00F7, - "oslash": 0x00F8, - "ugrave": 0x00F9, - "uacute": 0x00FA, - "ucirc": 0x00FB, - "uuml": 0x00FC, - "yacute": 0x00FD, - "thorn": 0x00FE, - "yuml": 0x00FF, - "OElig": 0x0152, - "oelig": 0x0153, - "Scaron": 0x0160, - "scaron": 0x0161, - "Yuml": 0x0178, - "fnof": 0x0192, - "circ": 0x02C6, - "tilde": 0x02DC, - "Alpha": 0x0391, - "Beta": 0x0392, - "Gamma": 0x0393, - "Delta": 0x0394, - "Epsilon": 0x0395, - "Zeta": 0x0396, - "Eta": 0x0397, - "Theta": 0x0398, - "Iota": 0x0399, - "Kappa": 0x039A, - "Lambda": 0x039B, - "Mu": 0x039C, - "Nu": 0x039D, - "Xi": 0x039E, - "Omicron": 0x039F, - "Pi": 0x03A0, - "Rho": 0x03A1, - "Sigma": 0x03A3, - "Tau": 0x03A4, - "Upsilon": 0x03A5, - "Phi": 0x03A6, - "Chi": 0x03A7, - "Psi": 0x03A8, - "Omega": 0x03A9, - "alpha": 0x03B1, - "beta": 0x03B2, - "gamma": 0x03B3, - "delta": 0x03B4, - "epsilon": 0x03B5, - "zeta": 0x03B6, - "eta": 0x03B7, - "theta": 0x03B8, - "iota": 0x03B9, - "kappa": 0x03BA, - "lambda": 0x03BB, - "mu": 0x03BC, - "nu": 0x03BD, - "xi": 0x03BE, - "omicron": 0x03BF, - "pi": 0x03C0, - "rho": 0x03C1, - "sigmaf": 0x03C2, - "sigma": 0x03C3, - "tau": 0x03C4, - "upsilon": 0x03C5, - "phi": 0x03C6, - "chi": 0x03C7, - "psi": 0x03C8, - "omega": 0x03C9, - "thetasym": 0x03D1, - "upsih": 0x03D2, - "piv": 0x03D6, - "ensp": 0x2002, - "emsp": 0x2003, - "thinsp": 0x2009, - "zwnj": 0x200C, - "zwj": 0x200D, - "lrm": 0x200E, - "rlm": 0x200F, - "ndash": 0x2013, - "mdash": 0x2014, - "lsquo": 0x2018, - "rsquo": 0x2019, - "sbquo": 0x201A, - "ldquo": 0x201C, - "rdquo": 0x201D, - "bdquo": 0x201E, - "dagger": 0x2020, - "Dagger": 0x2021, - "bull": 0x2022, - "hellip": 0x2026, - "permil": 0x2030, - "prime": 0x2032, - "Prime": 0x2033, - "lsaquo": 0x2039, - "rsaquo": 0x203A, - "oline": 0x203E, - "frasl": 0x2044, - "euro": 0x20AC, - "image": 0x2111, - "weierp": 0x2118, - "real": 0x211C, - "trade": 0x2122, - "alefsym": 0x2135, - "larr": 0x2190, - "uarr": 0x2191, - "rarr": 0x2192, - "darr": 0x2193, - "harr": 0x2194, - "crarr": 0x21B5, - "lArr": 0x21D0, - "uArr": 0x21D1, - "rArr": 0x21D2, - "dArr": 0x21D3, - "hArr": 0x21D4, - "forall": 0x2200, - "part": 0x2202, - "exist": 0x2203, - "empty": 0x2205, - "nabla": 0x2207, - "isin": 0x2208, - "notin": 0x2209, - "ni": 0x220B, - "prod": 0x220F, - "sum": 0x2211, - "minus": 0x2212, - "lowast": 0x2217, - "radic": 0x221A, - "prop": 0x221D, - "infin": 0x221E, - "ang": 0x2220, - "and": 0x2227, - "or": 0x2228, - "cap": 0x2229, - "cup": 0x222A, - "int": 0x222B, - "there4": 0x2234, - "sim": 0x223C, - "cong": 0x2245, - "asymp": 0x2248, - "ne": 0x2260, - "equiv": 0x2261, - "le": 0x2264, - "ge": 0x2265, - "sub": 0x2282, - "sup": 0x2283, - "nsub": 0x2284, - "sube": 0x2286, - "supe": 0x2287, - "oplus": 0x2295, - "otimes": 0x2297, - "perp": 0x22A5, - "sdot": 0x22C5, - "lceil": 0x2308, - "rceil": 0x2309, - "lfloor": 0x230A, - "rfloor": 0x230B, - "lang": 0x2329, - "rang": 0x232A, - "loz": 0x25CA, - "spades": 0x2660, - "clubs": 0x2663, - "hearts": 0x2665, - "diams": 0x2666 - } } diff --git a/ts/compiler/parser.ts b/ts/compiler/parser.ts index 5314f32..a8843ad 100644 --- a/ts/compiler/parser.ts +++ b/ts/compiler/parser.ts @@ -60,11 +60,17 @@ namespace ts { return visitNode(cbNode, (<TypeParameterDeclaration>node).name) || visitNode(cbNode, (<TypeParameterDeclaration>node).constraint) || visitNode(cbNode, (<TypeParameterDeclaration>node).expression); + case SyntaxKind.ShorthandPropertyAssignment: + return visitNodes(cbNodes, node.decorators) || + visitNodes(cbNodes, node.modifiers) || + visitNode(cbNode, (<ShorthandPropertyAssignment>node).name) || + visitNode(cbNode, (<ShorthandPropertyAssignment>node).questionToken) || + visitNode(cbNode, (<ShorthandPropertyAssignment>node).equalsToken) || + visitNode(cbNode, (<ShorthandPropertyAssignment>node).objectAssignmentInitializer); case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: return visitNodes(cbNodes, node.decorators) || @@ -428,7 +434,7 @@ namespace ts { // Implement the parser as a singleton module. We do this for perf reasons because creating // parser instances can actually be expensive enough to impact us on projects with many source // files. - module Parser { + namespace Parser { // Share a single scanner across all calls to parse a source file. This helps speed things // up by avoiding the cost of creating/compiling scanners over and over again. const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true); @@ -521,7 +527,7 @@ namespace ts { // // Note: any errors at the end of the file that do not precede a regular node, should get // attached to the EOF token. - let parseErrorBeforeNextFinishedNode: boolean = false; + let parseErrorBeforeNextFinishedNode = false; export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean): SourceFile { initializeState(fileName, _sourceText, languageVersion, _syntaxCursor); @@ -859,7 +865,7 @@ namespace ts { let saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; // Note: it is not actually necessary to save/restore the context flags here. That's - // because the saving/restorating of these flags happens naturally through the recursive + // because the saving/restoring of these flags happens naturally through the recursive // descent nature of our parser. However, we still store this here just so we can // assert that that invariant holds. let saveContextFlags = contextFlags; @@ -1061,11 +1067,11 @@ namespace ts { } function parseIdentifierName(): Identifier { - return createIdentifier(isIdentifierOrKeyword()); + return createIdentifier(tokenIsIdentifierOrKeyword(token)); } function isLiteralPropertyName(): boolean { - return isIdentifierOrKeyword() || + return tokenIsIdentifierOrKeyword(token) || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral; } @@ -1089,7 +1095,7 @@ namespace ts { } function isSimplePropertyName() { - return token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral || isIdentifierOrKeyword(); + return token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral || tokenIsIdentifierOrKeyword(token); } function parseComputedPropertyName(): ComputedPropertyName { @@ -1127,7 +1133,15 @@ namespace ts { if (token === SyntaxKind.DefaultKeyword) { return nextTokenIsClassOrFunction(); } + if (token === SyntaxKind.StaticKeyword) { + nextToken(); + return canFollowModifier(); + } + nextToken(); + if (scanner.hasPrecedingLineBreak()) { + return false; + } return canFollowModifier(); } @@ -1216,9 +1230,9 @@ namespace ts { case ParsingContext.HeritageClauses: return isHeritageClause(); case ParsingContext.ImportOrExportSpecifiers: - return isIdentifierOrKeyword(); + return tokenIsIdentifierOrKeyword(token); case ParsingContext.JsxAttributes: - return isIdentifierOrKeyword() || token === SyntaxKind.OpenBraceToken; + return tokenIsIdentifierOrKeyword(token) || token === SyntaxKind.OpenBraceToken; case ParsingContext.JsxChildren: return true; case ParsingContext.JSDocFunctionParameters: @@ -1257,7 +1271,7 @@ namespace ts { function nextTokenIsIdentifierOrKeyword() { nextToken(); - return isIdentifierOrKeyword(); + return tokenIsIdentifierOrKeyword(token); } function isHeritageClauseExtendsOrImplementsKeyword(): boolean { @@ -1827,7 +1841,7 @@ namespace ts { // the code would be implicitly: "name.identifierOrKeyword; identifierNameOrKeyword". // In the first case though, ASI will not take effect because there is not a // line terminator after the identifier or keyword. - if (scanner.hasPrecedingLineBreak() && isIdentifierOrKeyword()) { + if (scanner.hasPrecedingLineBreak() && tokenIsIdentifierOrKeyword(token)) { let matchesPattern = lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine); if (matchesPattern) { @@ -2285,7 +2299,7 @@ namespace ts { } } - if (isIdentifierOrKeyword()) { + if (tokenIsIdentifierOrKeyword(token)) { return parsePropertyOrMethodSignature(); } } @@ -2363,6 +2377,7 @@ namespace ts { let node = tryParse(parseKeywordAndNoDot); return node || parseTypeReferenceOrTypePredicate(); case SyntaxKind.VoidKeyword: + case SyntaxKind.ThisKeyword: return parseTokenNode<TypeNode>(); case SyntaxKind.TypeOfKeyword: return parseTypeQuery(); @@ -2385,6 +2400,7 @@ namespace ts { case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.VoidKeyword: + case SyntaxKind.ThisKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.OpenBraceToken: case SyntaxKind.OpenBracketToken: @@ -3018,7 +3034,31 @@ namespace ts { let newPrecedence = getBinaryOperatorPrecedence(); // Check the precedence to see if we should "take" this operator - if (newPrecedence <= precedence) { + // - For left associative operator (all operator but **), consume the operator, + // recursively call the function below, and parse binaryExpression as a rightOperand + // of the caller if the new precendence of the operator is greater then or equal to the current precendence. + // For example: + // a - b - c; + // ^token; leftOperand = b. Return b to the caller as a rightOperand + // a * b - c + // ^token; leftOperand = b. Return b to the caller as a rightOperand + // a - b * c; + // ^token; leftOperand = b. Return b * c to the caller as a rightOperand + // - For right associative operator (**), consume the operator, recursively call the function + // and parse binaryExpression as a rightOperand of the caller if the new precendence of + // the operator is strictly grater than the current precendence + // For example: + // a ** b ** c; + // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand + // a - b ** c; + // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand + // a ** b - c + // ^token; leftOperand = b. Return b to the caller as a rightOperand + const consumeCurrentOperator = token === SyntaxKind.AsteriskAsteriskToken ? + newPrecedence >= precedence : + newPrecedence > precedence; + + if (!consumeCurrentOperator) { break; } @@ -3092,6 +3132,8 @@ namespace ts { case SyntaxKind.SlashToken: case SyntaxKind.PercentToken: return 10; + case SyntaxKind.AsteriskAsteriskToken: + return 11; } // -1 is lower than all other precedences. Returning it will cause binary expression @@ -3118,28 +3160,29 @@ namespace ts { let node = <PrefixUnaryExpression>createNode(SyntaxKind.PrefixUnaryExpression); node.operator = token; nextToken(); - node.operand = parseUnaryExpressionOrHigher(); + node.operand = parseSimpleUnaryExpression(); + return finishNode(node); } function parseDeleteExpression() { let node = <DeleteExpression>createNode(SyntaxKind.DeleteExpression); nextToken(); - node.expression = parseUnaryExpressionOrHigher(); + node.expression = parseSimpleUnaryExpression(); return finishNode(node); } function parseTypeOfExpression() { let node = <TypeOfExpression>createNode(SyntaxKind.TypeOfExpression); nextToken(); - node.expression = parseUnaryExpressionOrHigher(); + node.expression = parseSimpleUnaryExpression(); return finishNode(node); } function parseVoidExpression() { let node = <VoidExpression>createNode(SyntaxKind.VoidExpression); nextToken(); - node.expression = parseUnaryExpressionOrHigher(); + node.expression = parseSimpleUnaryExpression(); return finishNode(node); } @@ -3159,22 +3202,63 @@ namespace ts { function parseAwaitExpression() { const node = <AwaitExpression>createNode(SyntaxKind.AwaitExpression); nextToken(); - node.expression = parseUnaryExpressionOrHigher(); + node.expression = parseSimpleUnaryExpression(); return finishNode(node); } - function parseUnaryExpressionOrHigher(): UnaryExpression { + /** + * Parse ES7 unary expression and await expression + * + * ES7 UnaryExpression: + * 1) SimpleUnaryExpression[?yield] + * 2) IncrementExpression[?yield] ** UnaryExpression[?yield] + */ + function parseUnaryExpressionOrHigher(): UnaryExpression | BinaryExpression { if (isAwaitExpression()) { return parseAwaitExpression(); } + if (isIncrementExpression()) { + let incrementExpression = parseIncrementExpression(); + return token === SyntaxKind.AsteriskAsteriskToken ? + <BinaryExpression>parseBinaryExpressionRest(getBinaryOperatorPrecedence(), incrementExpression) : + incrementExpression; + } + + let unaryOperator = token; + let simpleUnaryExpression = parseSimpleUnaryExpression(); + if (token === SyntaxKind.AsteriskAsteriskToken) { + let diagnostic: Diagnostic; + let start = skipTrivia(sourceText, simpleUnaryExpression.pos); + if (simpleUnaryExpression.kind === SyntaxKind.TypeAssertionExpression) { + parseErrorAtPosition(start, simpleUnaryExpression.end - start, Diagnostics.A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses); + } + else { + parseErrorAtPosition(start, simpleUnaryExpression.end - start, Diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, tokenToString(unaryOperator)); + } + } + return simpleUnaryExpression; + } + + /** + * Parse ES7 simple-unary expression or higher: + * + * ES7 SimpleUnaryExpression: + * 1) IncrementExpression[?yield] + * 2) delete UnaryExpression[?yield] + * 3) void UnaryExpression[?yield] + * 4) typeof UnaryExpression[?yield] + * 5) + UnaryExpression[?yield] + * 6) - UnaryExpression[?yield] + * 7) ~ UnaryExpression[?yield] + * 8) ! UnaryExpression[?yield] + */ + function parseSimpleUnaryExpression(): UnaryExpression { switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: case SyntaxKind.ExclamationToken: - case SyntaxKind.PlusPlusToken: - case SyntaxKind.MinusMinusToken: return parsePrefixUnaryExpression(); case SyntaxKind.DeleteKeyword: return parseDeleteExpression(); @@ -3183,19 +3267,73 @@ namespace ts { case SyntaxKind.VoidKeyword: return parseVoidExpression(); case SyntaxKind.LessThanToken: + // This is modified UnaryExpression grammar in TypeScript + // UnaryExpression (modified): + // < type > UnaryExpression + return parseTypeAssertion(); + default: + return parseIncrementExpression(); + } + } + + /** + * Check if the current token can possibly be an ES7 increment expression. + * + * ES7 IncrementExpression: + * LeftHandSideExpression[?Yield] + * LeftHandSideExpression[?Yield][no LineTerminator here]++ + * LeftHandSideExpression[?Yield][no LineTerminator here]-- + * ++LeftHandSideExpression[?Yield] + * --LeftHandSideExpression[?Yield] + */ + function isIncrementExpression(): boolean { + // This function is called inside parseUnaryExpression to decide + // whether to call parseSimpleUnaryExpression or call parseIncrmentExpression directly + switch (token) { + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + case SyntaxKind.TildeToken: + case SyntaxKind.ExclamationToken: + case SyntaxKind.DeleteKeyword: + case SyntaxKind.TypeOfKeyword: + case SyntaxKind.VoidKeyword: + return false; + case SyntaxKind.LessThanToken: + // If we are not in JSX context, we are parsing TypeAssertion which is an UnaryExpression if (sourceFile.languageVariant !== LanguageVariant.JSX) { - return parseTypeAssertion(); - } - if (lookAhead(nextTokenIsIdentifierOrKeyword)) { - return parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ true); + return false; } + // We are in JSX context and the token is part of JSXElement. // Fall through default: - return parsePostfixExpressionOrHigher(); + return true; } } - function parsePostfixExpressionOrHigher(): PostfixExpression { + /** + * Parse ES7 IncrementExpression. IncrementExpression is used instead of ES6's PostFixExpression. + * + * ES7 IncrementExpression[yield]: + * 1) LeftHandSideExpression[?yield] + * 2) LeftHandSideExpression[?yield] [[no LineTerminator here]]++ + * 3) LeftHandSideExpression[?yield] [[no LineTerminator here]]-- + * 4) ++LeftHandSideExpression[?yield] + * 5) --LeftHandSideExpression[?yield] + * In TypeScript (2), (3) are parsed as PostfixUnaryExpression. (4), (5) are parsed as PrefixUnaryExpression + */ + function parseIncrementExpression(): IncrementExpression { + if (token === SyntaxKind.PlusPlusToken || token === SyntaxKind.MinusMinusToken) { + let node = <PrefixUnaryExpression>createNode(SyntaxKind.PrefixUnaryExpression); + node.operator = token; + nextToken(); + node.operand = parseLeftHandSideExpressionOrHigher(); + return finishNode(node); + } + else if (sourceFile.languageVariant === LanguageVariant.JSX && token === SyntaxKind.LessThanToken && lookAhead(nextTokenIsIdentifierOrKeyword)) { + // JSXElement is part of primaryExpression + return parseJsxElementOrSelfClosingElement(/*inExpressionContext*/ true); + } + let expression = parseLeftHandSideExpressionOrHigher(); Debug.assert(isLeftHandSideExpression(expression)); @@ -3492,7 +3630,7 @@ namespace ts { parseExpected(SyntaxKind.LessThanToken); node.type = parseType(); parseExpected(SyntaxKind.GreaterThanToken); - node.expression = parseUnaryExpressionOrHigher(); + node.expression = parseSimpleUnaryExpression(); return finishNode(node); } @@ -3751,11 +3889,23 @@ namespace ts { return parseMethodDeclaration(fullStart, decorators, modifiers, asteriskToken, propertyName, questionToken); } - // Parse to check if it is short-hand property assignment or normal property assignment - if ((token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBraceToken) && tokenIsIdentifier) { + // check if it is short-hand property assignment or normal property assignment + // NOTE: if token is EqualsToken it is interpreted as CoverInitializedName production + // CoverInitializedName[Yield] : + // IdentifierReference[?Yield] Initializer[In, ?Yield] + // this is necessary because ObjectLiteral productions are also used to cover grammar for ObjectAssignmentPattern + const isShorthandPropertyAssignment = + tokenIsIdentifier && (token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBraceToken || token === SyntaxKind.EqualsToken); + + if (isShorthandPropertyAssignment) { let shorthandDeclaration = <ShorthandPropertyAssignment>createNode(SyntaxKind.ShorthandPropertyAssignment, fullStart); shorthandDeclaration.name = <Identifier>propertyName; shorthandDeclaration.questionToken = questionToken; + const equalsToken = parseOptionalToken(SyntaxKind.EqualsToken); + if (equalsToken) { + shorthandDeclaration.equalsToken = equalsToken; + shorthandDeclaration.objectAssignmentInitializer = allowInAnd(parseAssignmentExpressionOrHigher); + } return finishNode(shorthandDeclaration); } else { @@ -3950,7 +4100,8 @@ namespace ts { forOfStatement.expression = allowInAnd(parseAssignmentExpressionOrHigher); parseExpected(SyntaxKind.CloseParenToken); forOrForInOrForOfStatement = forOfStatement; - } else { + } + else { let forStatement = <ForStatement>createNode(SyntaxKind.ForStatement, pos); forStatement.initializer = initializer; parseExpected(SyntaxKind.SemicolonToken); @@ -4113,13 +4264,9 @@ namespace ts { } } - function isIdentifierOrKeyword() { - return token >= SyntaxKind.Identifier; - } - function nextTokenIsIdentifierOrKeywordOnSameLine() { nextToken(); - return isIdentifierOrKeyword() && !scanner.hasPrecedingLineBreak(); + return tokenIsIdentifierOrKeyword(token) && !scanner.hasPrecedingLineBreak(); } function nextTokenIsFunctionKeywordOnSameLine() { @@ -4129,7 +4276,7 @@ namespace ts { function nextTokenIsIdentifierOrKeywordOrNumberOnSameLine() { nextToken(); - return (isIdentifierOrKeyword() || token === SyntaxKind.NumericLiteral) && !scanner.hasPrecedingLineBreak(); + return (tokenIsIdentifierOrKeyword(token) || token === SyntaxKind.NumericLiteral) && !scanner.hasPrecedingLineBreak(); } function isDeclaration(): boolean { @@ -4170,8 +4317,12 @@ namespace ts { case SyntaxKind.ModuleKeyword: case SyntaxKind.NamespaceKeyword: return nextTokenIsIdentifierOrStringLiteralOnSameLine(); + case SyntaxKind.AbstractKeyword: case SyntaxKind.AsyncKeyword: case SyntaxKind.DeclareKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.PublicKeyword: nextToken(); // ASI takes effect for this modifier. if (scanner.hasPrecedingLineBreak()) { @@ -4182,7 +4333,7 @@ namespace ts { case SyntaxKind.ImportKeyword: nextToken(); return token === SyntaxKind.StringLiteral || token === SyntaxKind.AsteriskToken || - token === SyntaxKind.OpenBraceToken || isIdentifierOrKeyword(); + token === SyntaxKind.OpenBraceToken || tokenIsIdentifierOrKeyword(token); case SyntaxKind.ExportKeyword: nextToken(); if (token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken || @@ -4191,11 +4342,7 @@ namespace ts { } continue; - case SyntaxKind.PublicKeyword: - case SyntaxKind.PrivateKeyword: - case SyntaxKind.ProtectedKeyword: case SyntaxKind.StaticKeyword: - case SyntaxKind.AbstractKeyword: nextToken(); continue; default: @@ -4789,7 +4936,7 @@ namespace ts { // It is very important that we check this *after* checking indexers because // the [ token can start an index signature or a computed property name - if (isIdentifierOrKeyword() || + if (tokenIsIdentifierOrKeyword(token) || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral || token === SyntaxKind.AsteriskToken || @@ -4825,7 +4972,7 @@ namespace ts { node.decorators = decorators; setModifiers(node, modifiers); parseExpected(SyntaxKind.ClassKeyword); - node.name = parseOptionalIdentifier(); + node.name = parseNameOfClassDeclarationOrExpression(); node.typeParameters = parseTypeParameters(); node.heritageClauses = parseHeritageClauses(/*isClassHeritageClause*/ true); @@ -4842,6 +4989,21 @@ namespace ts { return finishNode(node); } + function parseNameOfClassDeclarationOrExpression(): Identifier { + // implements is a future reserved word so + // 'class implements' might mean either + // - class expression with omitted name, 'implements' starts heritage clause + // - class with name 'implements' + // 'isImplementsClause' helps to disambiguate between these two cases + return isIdentifier() && !isImplementsClause() + ? parseIdentifier() + : undefined; + } + + function isImplementsClause() { + return token === SyntaxKind.ImplementsKeyword && lookAhead(nextTokenIsIdentifierOrKeyword); + } + function parseHeritageClauses(isClassHeritageClause: boolean): NodeArray<HeritageClause> { // ClassTail[Yield,Await] : (Modified) See 14.5 // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } @@ -4953,12 +5115,15 @@ namespace ts { function parseModuleOrNamespaceDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray, flags: NodeFlags): ModuleDeclaration { let node = <ModuleDeclaration>createNode(SyntaxKind.ModuleDeclaration, fullStart); + // If we are parsing a dotted namespace name, we want to + // propagate the 'Namespace' flag across the names if set. + let namespaceFlag = flags & NodeFlags.Namespace; node.decorators = decorators; setModifiers(node, modifiers); node.flags |= flags; node.name = parseIdentifier(); node.body = parseOptional(SyntaxKind.DotToken) - ? parseModuleOrNamespaceDeclaration(getNodePos(), /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.Export) + ? parseModuleOrNamespaceDeclaration(getNodePos(), /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.Export | namespaceFlag) : parseModuleBlock(); return finishNode(node); } @@ -5313,7 +5478,7 @@ namespace ts { Unknown } - export module JSDocParser { + export namespace JSDocParser { export function isJSDocType() { switch (token) { case SyntaxKind.AsteriskToken: @@ -5329,7 +5494,7 @@ namespace ts { return true; } - return isIdentifierOrKeyword(); + return tokenIsIdentifierOrKeyword(token); } export function parseJSDocTypeExpressionForTests(content: string, start: number, length: number) { @@ -5958,7 +6123,7 @@ namespace ts { } } - module IncrementalParser { + namespace IncrementalParser { export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); diff --git a/ts/compiler/program.ts b/ts/compiler/program.ts index 0acb81f..a360ae6 100644 --- a/ts/compiler/program.ts +++ b/ts/compiler/program.ts @@ -9,10 +9,10 @@ namespace ts { /* @internal */ export let ioWriteTime = 0; /** The version of the TypeScript compiler release */ - + let emptyArray: any[] = []; - - export const version = "1.6.2"; + + export const version = "1.7.3"; export function findConfigFile(searchPath: string): string { let fileName = "tsconfig.json"; @@ -29,37 +29,37 @@ namespace ts { } return undefined; } - + export function resolveTripleslashReference(moduleName: string, containingFile: string): string { let basePath = getDirectoryPath(containingFile); let referencedFileName = isRootedDiskPath(moduleName) ? moduleName : combinePaths(basePath, moduleName); return normalizePath(referencedFileName); } - + export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { - let moduleResolution = compilerOptions.moduleResolution !== undefined + let moduleResolution = compilerOptions.moduleResolution !== undefined ? compilerOptions.moduleResolution : compilerOptions.module === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic; - + switch (moduleResolution) { case ModuleResolutionKind.NodeJs: return nodeModuleNameResolver(moduleName, containingFile, host); case ModuleResolutionKind.Classic: return classicNameResolver(moduleName, containingFile, compilerOptions, host); } } - + export function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { - let containingDirectory = getDirectoryPath(containingFile); + let containingDirectory = getDirectoryPath(containingFile); if (getRootLength(moduleName) !== 0 || nameStartsWithDotSlashOrDotDotSlash(moduleName)) { let failedLookupLocations: string[] = []; let candidate = normalizePath(combinePaths(containingDirectory, moduleName)); - let resolvedFileName = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ false, failedLookupLocations, host); - + let resolvedFileName = loadNodeModuleFromFile(candidate, failedLookupLocations, host); + if (resolvedFileName) { return { resolvedModule: { resolvedFileName }, failedLookupLocations }; } - - resolvedFileName = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ false, failedLookupLocations, host); + + resolvedFileName = loadNodeModuleFromDirectory(candidate, failedLookupLocations, host); return resolvedFileName ? { resolvedModule: { resolvedFileName }, failedLookupLocations } : { resolvedModule: undefined, failedLookupLocations }; @@ -68,15 +68,10 @@ namespace ts { return loadModuleFromNodeModules(moduleName, containingDirectory, host); } } - - function loadNodeModuleFromFile(candidate: string, loadOnlyDts: boolean, failedLookupLocation: string[], host: ModuleResolutionHost): string { - if (loadOnlyDts) { - return tryLoad(".d.ts"); - } - else { - return forEach(supportedExtensions, tryLoad); - } - + + function loadNodeModuleFromFile(candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string { + return forEach(moduleFileExtensions, tryLoad); + function tryLoad(ext: string): string { let fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext; if (host.fileExists(fileName)) { @@ -88,13 +83,13 @@ namespace ts { } } } - - function loadNodeModuleFromDirectory(candidate: string, loadOnlyDts: boolean, failedLookupLocation: string[], host: ModuleResolutionHost): string { + + function loadNodeModuleFromDirectory(candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string { let packageJsonPath = combinePaths(candidate, "package.json"); if (host.fileExists(packageJsonPath)) { - + let jsonContent: { typings?: string }; - + try { let jsonText = host.readFile(packageJsonPath); jsonContent = jsonText ? <{ typings?: string }>JSON.parse(jsonText) : { typings: undefined }; @@ -103,9 +98,9 @@ namespace ts { // gracefully handle if readFile fails or returns not JSON jsonContent = { typings: undefined }; } - + if (jsonContent.typings) { - let result = loadNodeModuleFromFile(normalizePath(combinePaths(candidate, jsonContent.typings)), loadOnlyDts, failedLookupLocation, host); + let result = loadNodeModuleFromFile(normalizePath(combinePaths(candidate, jsonContent.typings)), failedLookupLocation, host); if (result) { return result; } @@ -115,52 +110,52 @@ namespace ts { // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results failedLookupLocation.push(packageJsonPath); } - - return loadNodeModuleFromFile(combinePaths(candidate, "index"), loadOnlyDts, failedLookupLocation, host); + + return loadNodeModuleFromFile(combinePaths(candidate, "index"), failedLookupLocation, host); } - + function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { - let failedLookupLocations: string[] = []; + let failedLookupLocations: string[] = []; directory = normalizeSlashes(directory); while (true) { let baseName = getBaseFileName(directory); if (baseName !== "node_modules") { let nodeModulesFolder = combinePaths(directory, "node_modules"); let candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName)); - let result = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ true, failedLookupLocations, host); + let result = loadNodeModuleFromFile(candidate, failedLookupLocations, host); if (result) { return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations }; } - - result = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ true, failedLookupLocations, host); + + result = loadNodeModuleFromDirectory(candidate, failedLookupLocations, host); if (result) { return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations }; } } - + let parentPath = getDirectoryPath(directory); if (parentPath === directory) { break; } - + directory = parentPath; } - + return { resolvedModule: undefined, failedLookupLocations }; } - + function nameStartsWithDotSlashOrDotDotSlash(name: string) { let i = name.lastIndexOf("./", 1); return i === 0 || (i === 1 && name.charCodeAt(0) === CharacterCodes.dot); } export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { - + // module names that contain '!' are used to reference resources and are not resolved to actual files on disk - if (moduleName.indexOf('!') != -1) { + if (moduleName.indexOf("!") != -1) { return { resolvedModule: undefined, failedLookupLocations: [] }; } - + let searchPath = getDirectoryPath(containingFile); let searchName: string; @@ -175,7 +170,7 @@ namespace ts { // 'logical not' handles both undefined and None cases return undefined; } - + let candidate = searchName + extension; if (host.fileExists(candidate)) { return candidate; @@ -277,8 +272,7 @@ namespace ts { } const newLine = getNewLineCharacter(options); - - + return { getSourceFile, getDefaultLibFileName: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), getDefaultLibFileName(options)), @@ -347,26 +341,26 @@ namespace ts { let start = new Date().getTime(); host = host || createCompilerHost(options); - + const resolveModuleNamesWorker = host.resolveModuleNames ? ((moduleNames: string[], containingFile: string) => host.resolveModuleNames(moduleNames, containingFile)) : ((moduleNames: string[], containingFile: string) => map(moduleNames, moduleName => resolveModuleName(moduleName, containingFile, options, host).resolvedModule)); let filesByName = createFileMap<SourceFile>(fileName => host.getCanonicalFileName(fileName)); - + if (oldProgram) { // check properties that can affect structure of the program or module resolution strategy // if any of these properties has changed - structure cannot be reused let oldOptions = oldProgram.getCompilerOptions(); - if ((oldOptions.module !== options.module) || - (oldOptions.noResolve !== options.noResolve) || - (oldOptions.target !== options.target) || + if ((oldOptions.module !== options.module) || + (oldOptions.noResolve !== options.noResolve) || + (oldOptions.target !== options.target) || (oldOptions.noLib !== options.noLib) || (oldOptions.jsx !== options.jsx)) { oldProgram = undefined; } } - + if (!tryReuseStructureFromOldProgram()) { forEach(rootNames, name => processRootFile(name, false)); // Do not process the default library if: @@ -427,7 +421,7 @@ namespace ts { if (!oldProgram) { return false; } - + Debug.assert(!oldProgram.structureIsReused); // there is an old program, check if we can reuse its structure @@ -435,7 +429,7 @@ namespace ts { if (!arrayIsEqualTo(oldRootNames, rootNames)) { return false; } - + // check if program source files has changed in the way that can affect structure of the program let newSourceFiles: SourceFile[] = []; let modifiedSourceFiles: SourceFile[] = []; @@ -445,7 +439,7 @@ namespace ts { return false; } - if (oldSourceFile !== newSourceFile) { + if (oldSourceFile !== newSourceFile) { if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) { // value of no-default-lib has changed // this will affect if default library is injected into the list of files @@ -457,14 +451,14 @@ namespace ts { // tripleslash references has changed return false; } - + // check imports collectExternalModuleReferences(newSourceFile); if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) { // imports has changed return false; } - + if (resolveModuleNamesWorker) { let moduleNames = map(newSourceFile.imports, name => name.text); let resolutions = resolveModuleNamesWorker(moduleNames, newSourceFile.fileName); @@ -473,11 +467,11 @@ namespace ts { let newResolution = resolutions[i]; let oldResolution = getResolvedModule(oldSourceFile, moduleNames[i]); let resolutionChanged = oldResolution - ? !newResolution || + ? !newResolution || oldResolution.resolvedFileName !== newResolution.resolvedFileName || !!oldResolution.isExternalLibraryImport !== !!newResolution.isExternalLibraryImport : newResolution; - + if (resolutionChanged) { return false; } @@ -491,24 +485,24 @@ namespace ts { // file has no changes - use it as is newSourceFile = oldSourceFile; } - + // if file has passed all checks it should be safe to reuse it newSourceFiles.push(newSourceFile); } - + // update fileName -> file mapping for (let file of newSourceFiles) { filesByName.set(file.fileName, file); } - + files = newSourceFiles; fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics(); - + for (let modifiedFile of modifiedSourceFiles) { fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile); } oldProgram.structureIsReused = true; - + return true; } @@ -554,7 +548,7 @@ namespace ts { // This is because in the -out scenario all files need to be emitted, and therefore all // files need to be type checked. And the way to specify that all files need to be type // checked is to not pass the file to getEmitResolver. - let emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out)? undefined : sourceFile); + let emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); let start = new Date().getTime(); @@ -568,7 +562,9 @@ namespace ts { } function getSourceFile(fileName: string) { - return filesByName.get(fileName); + // first try to use file name as is to find file + // then try to convert relative file name to absolute and use it to retrieve source file + return filesByName.get(fileName) || filesByName.get(getNormalizedAbsolutePath(fileName, host.getCurrentDirectory())); } function getDiagnosticsHelper( @@ -656,7 +652,7 @@ namespace ts { function getOptionsDiagnostics(): Diagnostic[] { let allDiagnostics: Diagnostic[] = []; - addRange(allDiagnostics, fileProcessingDiagnostics.getGlobalDiagnostics()) + addRange(allDiagnostics, fileProcessingDiagnostics.getGlobalDiagnostics()); addRange(allDiagnostics, programDiagnostics.getGlobalDiagnostics()); return sortAndDeduplicateDiagnostics(allDiagnostics); } @@ -673,23 +669,29 @@ namespace ts { function processRootFile(fileName: string, isDefaultLib: boolean) { processSourceFile(normalizePath(fileName), isDefaultLib); - } - + } + function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean { return a.fileName === b.fileName; } - + function moduleNameIsEqualTo(a: LiteralExpression, b: LiteralExpression): boolean { return a.text === b.text; } - + function collectExternalModuleReferences(file: SourceFile): void { if (file.imports) { return; } - + let imports: LiteralExpression[]; for (let node of file.statements) { + collect(node, /* allowRelativeModuleNames */ true); + } + + file.imports = imports || emptyArray; + + function collect(node: Node, allowRelativeModuleNames: boolean): void { switch (node.kind) { case SyntaxKind.ImportDeclaration: case SyntaxKind.ImportEqualsDeclaration: @@ -702,7 +704,9 @@ namespace ts { break; } - (imports || (imports = [])).push(<LiteralExpression>moduleNameExpr); + if (allowRelativeModuleNames || !isExternalModuleNameRelative((<LiteralExpression>moduleNameExpr).text)) { + (imports || (imports = [])).push(<LiteralExpression>moduleNameExpr); + } break; case SyntaxKind.ModuleDeclaration: if ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) { @@ -712,23 +716,15 @@ namespace ts { // The StringLiteral must specify a top - level external module name. // Relative external module names are not permitted forEachChild((<ModuleDeclaration>node).body, node => { - if (isExternalModuleImportEqualsDeclaration(node) && - getExternalModuleImportEqualsDeclarationExpression(node).kind === SyntaxKind.StringLiteral) { - let moduleName = <LiteralExpression>getExternalModuleImportEqualsDeclarationExpression(node); - // TypeScript 1.0 spec (April 2014): 12.1.6 - // An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules - // only through top - level external module names. Relative external module names are not permitted. - if (moduleName) { - (imports || (imports = [])).push(moduleName); - } - } + // TypeScript 1.0 spec (April 2014): 12.1.6 + // An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules + // only through top - level external module names. Relative external module names are not permitted. + collect(node, /* allowRelativeModuleNames */ false); }); } break; } } - - file.imports = imports || emptyArray; } function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) { @@ -775,60 +771,62 @@ namespace ts { // Get source file from normalized fileName function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { - let canonicalName = host.getCanonicalFileName(normalizeSlashes(fileName)); - if (filesByName.contains(canonicalName)) { + if (filesByName.contains(fileName)) { // We've already looked for this file, use cached result - return getSourceFileFromCache(fileName, canonicalName, /*useAbsolutePath*/ false); + return getSourceFileFromCache(fileName, /*useAbsolutePath*/ false); } - else { - let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory()); - let canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath); - if (filesByName.contains(canonicalAbsolutePath)) { - return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true); + + let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory()); + if (filesByName.contains(normalizedAbsolutePath)) { + const file = getSourceFileFromCache(normalizedAbsolutePath, /*useAbsolutePath*/ true); + // we don't have resolution for this relative file name but the match was found by absolute file name + // store resolution for relative name as well + filesByName.set(fileName, file); + return file; + } + + // We haven't looked for this file, do so now and cache result + let file = host.getSourceFile(fileName, options.target, hostErrorMessage => { + if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { + fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, + Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); + } + else { + fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); } + }); - // We haven't looked for this file, do so now and cache result - let file = host.getSourceFile(fileName, options.target, hostErrorMessage => { - if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { - fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, - Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); - } - else { - fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); - } - }); - filesByName.set(canonicalName, file); - if (file) { - skipDefaultLib = skipDefaultLib || file.hasNoDefaultLib; - - // Set the source file for normalized absolute path - filesByName.set(canonicalAbsolutePath, file); - - let basePath = getDirectoryPath(fileName); - if (!options.noResolve) { - processReferencedFiles(file, basePath); - } + filesByName.set(fileName, file); + if (file) { + skipDefaultLib = skipDefaultLib || file.hasNoDefaultLib; - // always process imported modules to record module name resolutions - processImportedModules(file, basePath); + // Set the source file for normalized absolute path + filesByName.set(normalizedAbsolutePath, file); - if (isDefaultLib) { - file.isDefaultLib = true; - files.unshift(file); - } - else { - files.push(file); - } + let basePath = getDirectoryPath(fileName); + if (!options.noResolve) { + processReferencedFiles(file, basePath); } - return file; + // always process imported modules to record module name resolutions + processImportedModules(file, basePath); + + if (isDefaultLib) { + file.isDefaultLib = true; + files.unshift(file); + } + else { + files.push(file); + } } - function getSourceFileFromCache(fileName: string, canonicalName: string, useAbsolutePath: boolean): SourceFile { - let file = filesByName.get(canonicalName); + return file; + + function getSourceFileFromCache(fileName: string, useAbsolutePath: boolean): SourceFile { + let file = filesByName.get(fileName); if (file && host.useCaseSensitiveFileNames()) { let sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()) : file.fileName; - if (canonicalName !== sourceFileName) { + if (normalizeSlashes(fileName) !== normalizeSlashes(sourceFileName)) { if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, fileName, sourceFileName)); @@ -848,7 +846,7 @@ namespace ts { processSourceFile(referencedFileName, /* isDefaultLib */ false, file, ref.pos, ref.end); }); } - + function processImportedModules(file: SourceFile, basePath: string) { collectExternalModuleReferences(file); if (file.imports.length) { @@ -862,12 +860,8 @@ namespace ts { const importedFile = findModuleSourceFile(resolution.resolvedFileName, file.imports[i]); if (importedFile && resolution.isExternalLibraryImport) { if (!isExternalModule(importedFile)) { - let start = getTokenPosOfNode(file.imports[i], file) - fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.File_0_is_not_a_module, importedFile.fileName)); - } - else if (!fileExtensionIs(importedFile.fileName, ".d.ts")) { - let start = getTokenPosOfNode(file.imports[i], file) - fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.Exported_external_package_typings_can_only_be_in_d_ts_files_Please_contact_the_package_author_to_update_the_package_definition)); + let start = getTokenPosOfNode(file.imports[i], file); + fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.Exported_external_package_typings_file_0_is_not_a_module_Please_contact_the_package_author_to_update_the_package_definition, importedFile.fileName)); } else if (importedFile.referencedFiles.length) { let firstRef = importedFile.referencedFiles[0]; @@ -1022,9 +1016,9 @@ namespace ts { programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_unless_the_module_flag_is_provided)); } - // Cannot specify module gen target when in es6 or above - if (options.module && languageVersion >= ScriptTarget.ES6) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_compile_modules_into_commonjs_amd_system_or_umd_when_targeting_ES6_or_higher)); + // Cannot specify module gen target of es6 when below es6 + if (options.module === ModuleKind.ES6 && languageVersion < ScriptTarget.ES6) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_compile_modules_into_es6_when_targeting_ES5_or_lower)); } // there has to be common source directory if user specified --outdir || --sourceRoot @@ -1073,11 +1067,6 @@ namespace ts { !options.experimentalDecorators) { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators")); } - - if (options.experimentalAsyncFunctions && - options.target !== ScriptTarget.ES6) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_experimentalAsyncFunctions_cannot_be_specified_when_targeting_ES5_or_lower)); - } } } } diff --git a/ts/compiler/scanner.ts b/ts/compiler/scanner.ts index 199e7e9..a827d75 100644 --- a/ts/compiler/scanner.ts +++ b/ts/compiler/scanner.ts @@ -6,6 +6,11 @@ namespace ts { (message: DiagnosticMessage, length: number): void; } + /* @internal */ + export function tokenIsIdentifierOrKeyword(token: SyntaxKind): boolean { + return token >= SyntaxKind.Identifier; + } + export interface Scanner { getStartPos(): number; getToken(): SyntaxKind; @@ -131,6 +136,7 @@ namespace ts { "=>": SyntaxKind.EqualsGreaterThanToken, "+": SyntaxKind.PlusToken, "-": SyntaxKind.MinusToken, + "**": SyntaxKind.AsteriskAsteriskToken, "*": SyntaxKind.AsteriskToken, "/": SyntaxKind.SlashToken, "%": SyntaxKind.PercentToken, @@ -153,6 +159,7 @@ namespace ts { "+=": SyntaxKind.PlusEqualsToken, "-=": SyntaxKind.MinusEqualsToken, "*=": SyntaxKind.AsteriskEqualsToken, + "**=": SyntaxKind.AsteriskAsteriskEqualsToken, "/=": SyntaxKind.SlashEqualsToken, "%=": SyntaxKind.PercentEqualsToken, "<<=": SyntaxKind.LessThanLessThanEqualsToken, @@ -219,7 +226,7 @@ namespace ts { } // Perform binary search in one of the Unicode range maps - let lo: number = 0; + let lo = 0; let hi: number = map.length; let mid: number; @@ -652,7 +659,7 @@ namespace ts { export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] { return getCommentRanges(text, pos, /*trailing*/ true); } - + /** Optionally, get the shebang */ export function getShebang(text: string): string { return shebangTriviaRegex.test(text) @@ -734,18 +741,6 @@ namespace ts { } } - function isIdentifierStart(ch: number): boolean { - return ch >= CharacterCodes.A && ch <= CharacterCodes.Z || ch >= CharacterCodes.a && ch <= CharacterCodes.z || - ch === CharacterCodes.$ || ch === CharacterCodes._ || - ch > CharacterCodes.maxAsciiCharacter && isUnicodeIdentifierStart(ch, languageVersion); - } - - function isIdentifierPart(ch: number): boolean { - return ch >= CharacterCodes.A && ch <= CharacterCodes.Z || ch >= CharacterCodes.a && ch <= CharacterCodes.z || - ch >= CharacterCodes._0 && ch <= CharacterCodes._9 || ch === CharacterCodes.$ || ch === CharacterCodes._ || - ch > CharacterCodes.maxAsciiCharacter && isUnicodeIdentifierPart(ch, languageVersion); - } - function scanNumber(): number { let start = pos; while (isDigit(text.charCodeAt(pos))) pos++; @@ -1059,12 +1054,12 @@ namespace ts { let start = pos; while (pos < end) { let ch = text.charCodeAt(pos); - if (isIdentifierPart(ch)) { + if (isIdentifierPart(ch, languageVersion)) { pos++; } else if (ch === CharacterCodes.backslash) { ch = peekUnicodeEscape(); - if (!(ch >= 0 && isIdentifierPart(ch))) { + if (!(ch >= 0 && isIdentifierPart(ch, languageVersion))) { break; } result += text.substring(start, pos); @@ -1207,6 +1202,12 @@ namespace ts { if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { return pos += 2, token = SyntaxKind.AsteriskEqualsToken; } + if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { + if (text.charCodeAt(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.AsteriskAsteriskEqualsToken; + } + return pos += 2, token = SyntaxKind.AsteriskAsteriskToken; + } return pos++, token = SyntaxKind.AsteriskToken; case CharacterCodes.plus: if (text.charCodeAt(pos + 1) === CharacterCodes.plus) { @@ -1368,7 +1369,9 @@ namespace ts { if (text.charCodeAt(pos + 1) === CharacterCodes.equals) { return pos += 2, token = SyntaxKind.LessThanEqualsToken; } - if (text.charCodeAt(pos + 1) === CharacterCodes.slash && languageVariant === LanguageVariant.JSX) { + if (languageVariant === LanguageVariant.JSX && + text.charCodeAt(pos + 1) === CharacterCodes.slash && + text.charCodeAt(pos + 2) !== CharacterCodes.asterisk) { return pos += 2, token = SyntaxKind.LessThanSlashToken; } return pos++, token = SyntaxKind.LessThanToken; @@ -1434,7 +1437,7 @@ namespace ts { return pos++, token = SyntaxKind.AtToken; case CharacterCodes.backslash: let cookedChar = peekUnicodeEscape(); - if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { + if (cookedChar >= 0 && isIdentifierStart(cookedChar, languageVersion)) { pos += 6; tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts(); return token = getIdentifierToken(); @@ -1442,9 +1445,9 @@ namespace ts { error(Diagnostics.Invalid_character); return pos++, token = SyntaxKind.Unknown; default: - if (isIdentifierStart(ch)) { + if (isIdentifierStart(ch, languageVersion)) { pos++; - while (pos < end && isIdentifierPart(ch = text.charCodeAt(pos))) pos++; + while (pos < end && isIdentifierPart(ch = text.charCodeAt(pos), languageVersion)) pos++; tokenValue = text.substring(tokenPos, pos); if (ch === CharacterCodes.backslash) { tokenValue += scanIdentifierParts(); @@ -1531,7 +1534,7 @@ namespace ts { p++; } - while (p < end && isIdentifierPart(text.charCodeAt(p))) { + while (p < end && isIdentifierPart(text.charCodeAt(p), languageVersion)) { p++; } pos = p; @@ -1590,11 +1593,11 @@ namespace ts { // Scans a JSX identifier; these differ from normal identifiers in that // they allow dashes function scanJsxIdentifier(): SyntaxKind { - if (token === SyntaxKind.Identifier) { + if (tokenIsIdentifierOrKeyword(token)) { let firstCharPosition = pos; while (pos < end) { let ch = text.charCodeAt(pos); - if (ch === CharacterCodes.minus || ((firstCharPosition === pos) ? isIdentifierStart(ch) : isIdentifierPart(ch))) { + if (ch === CharacterCodes.minus || ((firstCharPosition === pos) ? isIdentifierStart(ch, languageVersion) : isIdentifierPart(ch, languageVersion))) { pos++; } else { diff --git a/ts/compiler/sys.ts b/ts/compiler/sys.ts index 5b46324..0872a2e 100644 --- a/ts/compiler/sys.ts +++ b/ts/compiler/sys.ts @@ -8,7 +8,8 @@ namespace ts { write(s: string): void; readFile(path: string, encoding?: string): string; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - watchFile?(path: string, callback: (path: string) => void): FileWatcher; + watchFile?(path: string, callback: (path: string, removed?: boolean) => void): FileWatcher; + watchDirectory?(path: string, callback: (path: string) => void, recursive?: boolean): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; directoryExists(path: string): boolean; @@ -20,6 +21,12 @@ namespace ts { exit(exitCode?: number): void; } + interface WatchedFile { + fileName: string; + callback: (fileName: string, removed?: boolean) => void; + mtime: Date; + } + export interface FileWatcher { close(): void; } @@ -29,8 +36,8 @@ namespace ts { declare var process: any; declare var global: any; declare var __filename: string; - declare var Buffer: { - new (str: string, encoding?: string): any; + declare var Buffer: { + new (str: string, encoding?: string): any; }; declare class Enumerator { @@ -116,7 +123,7 @@ namespace ts { return path.toLowerCase(); } - function getNames(collection: any): string[]{ + function getNames(collection: any): string[] { let result: string[] = []; for (let e = new Enumerator(collection); !e.atEnd(); e.moveNext()) { result.push(e.item().Name); @@ -192,6 +199,103 @@ namespace ts { const _path = require("path"); const _os = require("os"); + // average async stat takes about 30 microseconds + // set chunk size to do 30 files in < 1 millisecond + function createWatchedFileSet(interval = 2500, chunkSize = 30) { + let watchedFiles: WatchedFile[] = []; + let nextFileToCheck = 0; + let watchTimer: any; + + function getModifiedTime(fileName: string): Date { + return _fs.statSync(fileName).mtime; + } + + function poll(checkedIndex: number) { + let watchedFile = watchedFiles[checkedIndex]; + if (!watchedFile) { + return; + } + + _fs.stat(watchedFile.fileName, (err: any, stats: any) => { + if (err) { + watchedFile.callback(watchedFile.fileName); + } + else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { + watchedFile.mtime = getModifiedTime(watchedFile.fileName); + watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0); + } + }); + } + + // this implementation uses polling and + // stat due to inconsistencies of fs.watch + // and efficiency of stat on modern filesystems + function startWatchTimer() { + watchTimer = setInterval(() => { + let count = 0; + let nextToCheck = nextFileToCheck; + let firstCheck = -1; + while ((count < chunkSize) && (nextToCheck !== firstCheck)) { + poll(nextToCheck); + if (firstCheck < 0) { + firstCheck = nextToCheck; + } + nextToCheck++; + if (nextToCheck === watchedFiles.length) { + nextToCheck = 0; + } + count++; + } + nextFileToCheck = nextToCheck; + }, interval); + } + + function addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile { + let file: WatchedFile = { + fileName, + callback, + mtime: getModifiedTime(fileName) + }; + + watchedFiles.push(file); + if (watchedFiles.length === 1) { + startWatchTimer(); + } + return file; + } + + function removeFile(file: WatchedFile) { + watchedFiles = copyListRemovingItem(file, watchedFiles); + } + + return { + getModifiedTime: getModifiedTime, + poll: poll, + startWatchTimer: startWatchTimer, + addFile: addFile, + removeFile: removeFile + }; + } + + // REVIEW: for now this implementation uses polling. + // The advantage of polling is that it works reliably + // on all os and with network mounted files. + // For 90 referenced files, the average time to detect + // changes is 2*msInterval (by default 5 seconds). + // The overhead of this is .04 percent (1/2500) with + // average pause of < 1 millisecond (and max + // pause less than 1.5 milliseconds); question is + // do we anticipate reference sets in the 100s and + // do we care about waiting 10-20 seconds to detect + // changes for large reference sets? If so, do we want + // to increase the chunk size or decrease the interval + // time dynamically to match the large reference set? + let watchedFileSet = createWatchedFileSet(); + + function isNode4OrLater(): Boolean { + return parseInt(process.version.charAt(1)) >= 4; + } + const platform: string = _os.platform(); // win32\win64 are case insensitive platforms, MacOS (darwin) by default is also case insensitive const useCaseSensitiveFileNames = platform !== "win32" && platform !== "win64" && platform !== "darwin"; @@ -270,9 +374,9 @@ namespace ts { args: process.argv.slice(2), newLine: _os.EOL, useCaseSensitiveFileNames: useCaseSensitiveFileNames, - write(s: string): void { - const buffer = new Buffer(s, "utf8"); - let offset: number = 0; + write(s: string): void { + const buffer = new Buffer(s, "utf8"); + let offset = 0; let toWrite: number = buffer.length; let written = 0; // 1 is a standard descriptor for stdout @@ -280,24 +384,40 @@ namespace ts { offset += written; toWrite -= written; } - }, + }, readFile, writeFile, watchFile: (fileName, callback) => { - // watchFile polls a file every 250ms, picking up file notifications. - _fs.watchFile(fileName, { persistent: true, interval: 250 }, fileChanged); + // Node 4.0 stablized the `fs.watch` function on Windows which avoids polling + // and is more efficient than `fs.watchFile` (ref: https://github.com/nodejs/node/pull/2649 + // and https://github.com/Microsoft/TypeScript/issues/4643), therefore + // if the current node.js version is newer than 4, use `fs.watch` instead. + if (isNode4OrLater()) { + // Note: in node the callback of fs.watch is given only the relative file name as a parameter + return _fs.watch(fileName, (eventName: string, relativeFileName: string) => callback(fileName)); + } + let watchedFile = watchedFileSet.addFile(fileName, callback); return { - close() { _fs.unwatchFile(fileName, fileChanged); } + close: () => watchedFileSet.removeFile(watchedFile) }; - - function fileChanged(curr: any, prev: any) { - if (+curr.mtime <= +prev.mtime) { - return; + }, + watchDirectory: (path, callback, recursive) => { + // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows + // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) + return _fs.watch( + path, + { persisten: true, recursive: !!recursive }, + (eventName: string, relativeFileName: string) => { + // In watchDirectory we only care about adding and removing files (when event name is + // "rename"); changes made within files are handled by corresponding fileWatchers (when + // event name is "change") + if (eventName === "rename") { + // When deleting a file, the passed baseFileName is null + callback(!relativeFileName ? relativeFileName : normalizePath(ts.combinePaths(path, relativeFileName))); + }; } - - callback(fileName); - } + ); }, resolvePath: function (path: string): string { return _path.resolve(path); diff --git a/ts/compiler/types.ts b/ts/compiler/types.ts index 12ed448..64a8b9d 100644 --- a/ts/compiler/types.ts +++ b/ts/compiler/types.ts @@ -67,6 +67,7 @@ namespace ts { PlusToken, MinusToken, AsteriskToken, + AsteriskAsteriskToken, SlashToken, PercentToken, PlusPlusToken, @@ -89,6 +90,7 @@ namespace ts { PlusEqualsToken, MinusEqualsToken, AsteriskEqualsToken, + AsteriskAsteriskEqualsToken, SlashEqualsToken, PercentEqualsToken, LessThanLessThanEqualsToken, @@ -379,6 +381,7 @@ namespace ts { OctalLiteral = 0x00010000, // Octal numeric literal Namespace = 0x00020000, // Namespace declaration ExportContext = 0x00040000, // Export context (initialized by binding) + ContainsThis = 0x00080000, // Interface contains references to "this" Modifier = Export | Ambient | Public | Private | Protected | Static | Abstract | Default | Async, AccessibilityModifier = Public | Private | Protected, @@ -564,6 +567,10 @@ namespace ts { export interface ShorthandPropertyAssignment extends ObjectLiteralElement { name: Identifier; questionToken?: Node; + // used when ObjectLiteralExpression is used in ObjectAssignmentPattern + // it is grammar error to appear in actual object initializer + equalsToken?: Node; + objectAssignmentInitializer?: Expression; } // SyntaxKind.VariableDeclaration @@ -709,12 +716,16 @@ namespace ts { _unaryExpressionBrand: any; } - export interface PrefixUnaryExpression extends UnaryExpression { + export interface IncrementExpression extends UnaryExpression { + _incrementExpressionBrand: any; + } + + export interface PrefixUnaryExpression extends IncrementExpression { operator: SyntaxKind; operand: UnaryExpression; } - export interface PostfixUnaryExpression extends PostfixExpression { + export interface PostfixUnaryExpression extends IncrementExpression { operand: LeftHandSideExpression; operator: SyntaxKind; } @@ -723,7 +734,7 @@ namespace ts { _postfixExpressionBrand: any; } - export interface LeftHandSideExpression extends PostfixExpression { + export interface LeftHandSideExpression extends IncrementExpression { _leftHandSideExpressionBrand: any; } @@ -1247,7 +1258,7 @@ namespace ts { moduleName: string; referencedFiles: FileReference[]; languageVariant: LanguageVariant; - + // this map is used by transpiler to supply alternative names for dependencies (i.e. in case of bundling) /* @internal */ renamedDependencies?: Map<string>; @@ -1315,12 +1326,12 @@ namespace ts { } export interface Program extends ScriptReferenceHost { - + /** * Get a list of root file names that were passed to a 'createProgram' */ - getRootFileNames(): string[] - + getRootFileNames(): string[]; + /** * Get a list of files in the program */ @@ -1499,6 +1510,7 @@ namespace ts { // declaration emitter to help determine if it should patch up the final declaration file // with import statements it previously saw (but chose not to emit). trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; + reportInaccessibleThisError(): void; } export const enum TypeFormatFlags { @@ -1599,9 +1611,8 @@ namespace ts { isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; - getBlockScopedVariableId(node: Identifier): number; getReferencedValueDeclaration(reference: Identifier): Declaration; - getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind; + getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind; isOptionalParameter(node: ParameterDeclaration): boolean; } @@ -1720,6 +1731,7 @@ namespace ts { resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked isNestedRedeclaration?: boolean; // True if symbol is block scoped redeclaration + bindingElement?: BindingElement; // Binding element associated with property symbol } /* @internal */ @@ -1801,6 +1813,7 @@ namespace ts { /* @internal */ ContainsAnyFunctionType = 0x00800000, // Type is or contains object literal type ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6 + ThisType = 0x02000000, // This type /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null, @@ -1817,11 +1830,14 @@ namespace ts { PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType } + export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; + // Properties common to all types export interface Type { - flags: TypeFlags; // Flags - /* @internal */ id: number; // Unique ID - symbol?: Symbol; // Symbol associated with type (if any) + flags: TypeFlags; // Flags + /* @internal */ id: number; // Unique ID + symbol?: Symbol; // Symbol associated with type (if any) + pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any) } /* @internal */ @@ -1843,6 +1859,7 @@ namespace ts { typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) outerTypeParameters: TypeParameter[]; // Outer type parameters (undefined if none) localTypeParameters: TypeParameter[]; // Local type parameters (undefined if none) + thisType: TypeParameter; // The "this" type (undefined if none) /* @internal */ resolvedBaseConstructorType?: Type; // Resolved base constructor type of class /* @internal */ @@ -1857,10 +1874,17 @@ namespace ts { declaredNumberIndexType: Type; // Declared numeric index type } - // Type references (TypeFlags.Reference) + // Type references (TypeFlags.Reference). When a class or interface has type parameters or + // a "this" type, references to the class or interface are made using type references. The + // typeArguments property specififes the types to substitute for the type parameters of the + // class or interface and optionally includes an extra element that specifies the type to + // substitute for "this" in the resulting instantiation. When no extra argument is present, + // the type reference itself is substituted for "this". The typeArguments property is undefined + // if the class or interface has no type parameters and the reference isn't specifying an + // explicit "this" argument. export interface TypeReference extends ObjectType { target: GenericType; // Type reference target - typeArguments: Type[]; // Type reference type arguments + typeArguments: Type[]; // Type reference type arguments (undefined if none) } // Generic class and interface types @@ -1870,8 +1894,7 @@ namespace ts { } export interface TupleType extends ObjectType { - elementTypes: Type[]; // Element types - baseArrayType: TypeReference; // Array<T> where T is best common type of element types + elementTypes: Type[]; // Element types } export interface UnionOrIntersectionType extends Type { @@ -1886,6 +1909,13 @@ namespace ts { export interface IntersectionType extends UnionOrIntersectionType { } + /* @internal */ + // An instantiated anonymous type has a target and a mapper + export interface AnonymousType extends ObjectType { + target?: AnonymousType; // Instantiation target + mapper?: TypeMapper; // Instantiation mapper + } + /* @internal */ // Resolved object, union, or intersection type export interface ResolvedType extends ObjectType, UnionOrIntersectionType { @@ -2016,12 +2046,12 @@ namespace ts { Error, Message, } - + export const enum ModuleResolutionKind { Classic = 1, NodeJs = 2 } - + export interface CompilerOptions { allowNonTsExtensions?: boolean; charset?: string; @@ -2061,9 +2091,8 @@ namespace ts { watch?: boolean; isolatedModules?: boolean; experimentalDecorators?: boolean; - experimentalAsyncFunctions?: boolean; emitDecoratorMetadata?: boolean; - moduleResolution?: ModuleResolutionKind + moduleResolution?: ModuleResolutionKind; /* @internal */ stripInternal?: boolean; // Skip checking lib.d.ts to help speed up tests. @@ -2078,6 +2107,8 @@ namespace ts { AMD = 2, UMD = 3, System = 4, + ES6 = 5, + ES2015 = ES6, } export const enum JsxEmit { @@ -2103,12 +2134,13 @@ namespace ts { ES3 = 0, ES5 = 1, ES6 = 2, + ES2015 = ES6, Latest = ES6, } export const enum LanguageVariant { Standard, - JSX + JSX, } export interface ParsedCommandLine { @@ -2118,7 +2150,7 @@ namespace ts { } /* @internal */ - interface CommandLineOptionBase { + export interface CommandLineOptionBase { name: string; type: string | Map<number>; // "string", "number", "boolean", or an object literal mapping named values to actual values isFilePath?: boolean; // True if option value is a path or fileName @@ -2277,15 +2309,15 @@ namespace ts { byteOrderMark = 0xFEFF, tab = 0x09, // \t verticalTab = 0x0B, // \v - } - + } + export interface ModuleResolutionHost { fileExists(fileName: string): boolean; // readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json' // to determine location of bundled typings for node module readFile(fileName: string): string; } - + export interface ResolvedModule { resolvedFileName: string; /* @@ -2296,12 +2328,12 @@ namespace ts { */ isExternalLibraryImport?: boolean; } - + export interface ResolvedModuleWithFailedLookupLocations { resolvedModule: ResolvedModule; failedLookupLocations: string[]; } - + export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; getCancellationToken?(): CancellationToken; @@ -2311,7 +2343,7 @@ namespace ts { getCanonicalFileName(fileName: string): string; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - + /* * CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of * module name resolution) or provide implementation for methods from ModuleResolutionHost (in this case compiler @@ -2350,7 +2382,7 @@ namespace ts { // operation caused diagnostics to be returned by storing and comparing the return value // of this method before/after the operation is performed. getModificationCount(): number; - + /* @internal */ reattachFileDiagnostics(newFile: SourceFile): void; } } diff --git a/ts/compiler/utilities.ts b/ts/compiler/utilities.ts index 5332c8f..1aee57e 100644 --- a/ts/compiler/utilities.ts +++ b/ts/compiler/utilities.ts @@ -1,4 +1,5 @@ /// <reference path="binder.ts" /> +/// <reference path="sys.ts" /> /* @internal */ namespace ts { @@ -64,7 +65,8 @@ namespace ts { increaseIndent: () => { }, decreaseIndent: () => { }, clear: () => str = "", - trackSymbol: () => { } + trackSymbol: () => { }, + reportInaccessibleThisError: () => { } }; } @@ -97,8 +99,8 @@ namespace ts { } return true; - } - + } + export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); } @@ -164,16 +166,16 @@ namespace ts { return node.pos; } - // Returns true if this node is missing from the actual source code. 'missing' is different - // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes - // in the tree), it is definitel missing. HOwever, a node may be defined, but still be + // Returns true if this node is missing from the actual source code. A 'missing' node is different + // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes + // in the tree), it is definitely missing. However, a node may be defined, but still be // missing. This happens whenever the parser knows it needs to parse something, but can't - // get anything in the source code that it expects at that location. For example: + // get anything in the source code that it expects at that location. For example: // // let a: ; // // Here, the Type in the Type-Annotation is not-optional (as there is a colon in the source - // code). So the parser will attempt to parse out a type, and will create an actual node. + // code). So the parser will attempt to parse out a type, and will create an actual node. // However, this node will be 'missing' in the sense that no actual source-code/tokens are // contained within it. export function nodeIsMissing(node: Node) { @@ -435,6 +437,7 @@ namespace ts { } export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/; + export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/; export function isTypeNode(node: Node): boolean { if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) { @@ -466,13 +469,12 @@ namespace ts { else if (node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node) { node = node.parent; } - // fall through - case SyntaxKind.QualifiedName: - case SyntaxKind.PropertyAccessExpression: // At this point, node is either a qualified name or an identifier Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression, "'node' was expected to be a qualified name, identifier or property access in 'isTypeNode'."); - + case SyntaxKind.QualifiedName: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ThisKeyword: let parent = node.parent; if (parent.kind === SyntaxKind.TypeQuery) { return false; @@ -619,7 +621,7 @@ namespace ts { return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression); } - export function isFunctionLike(node: Node): boolean { + export function isFunctionLike(node: Node): node is FunctionLikeDeclaration { if (node) { switch (node.kind) { case SyntaxKind.Constructor: @@ -659,7 +661,7 @@ namespace ts { return node && node.kind === SyntaxKind.Block && isFunctionLike(node.parent); } - export function isObjectLiteralMethod(node: Node) { + export function isObjectLiteralMethod(node: Node): node is MethodDeclaration { return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression; } @@ -731,6 +733,9 @@ namespace ts { case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: case SyntaxKind.EnumDeclaration: case SyntaxKind.SourceFile: return node; @@ -891,9 +896,16 @@ namespace ts { return nodeIsDecorated(node) || childIsDecorated(node); } + export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { + return node.kind === SyntaxKind.PropertyAccessExpression; + } + + export function isElementAccessExpression(node: Node): node is ElementAccessExpression { + return node.kind === SyntaxKind.ElementAccessExpression; + } + export function isExpression(node: Node): boolean { switch (node.kind) { - case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.TrueKeyword: @@ -926,6 +938,7 @@ namespace ts { case SyntaxKind.JsxElement: case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.YieldExpression: + case SyntaxKind.AwaitExpression: return true; case SyntaxKind.QualifiedName: while (node.parent.kind === SyntaxKind.QualifiedName) { @@ -939,6 +952,7 @@ namespace ts { // fall through case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: + case SyntaxKind.ThisKeyword: let parent = node.parent; switch (parent.kind) { case SyntaxKind.VariableDeclaration: @@ -979,6 +993,7 @@ namespace ts { return node === (<ComputedPropertyName>parent).expression; case SyntaxKind.Decorator: case SyntaxKind.JsxExpression: + case SyntaxKind.JsxSpreadAttribute: return true; case SyntaxKind.ExpressionWithTypeArguments: return (<ExpressionWithTypeArguments>parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent); @@ -991,6 +1006,12 @@ namespace ts { return false; } + export function isExternalModuleNameRelative(moduleName: string): boolean { + // TypeScript 1.0 spec (April 2014): 11.2.1 + // An external module name is "relative" if the first term is "." or "..". + return moduleName.substr(0, 2) === "./" || moduleName.substr(0, 3) === "../" || moduleName.substr(0, 2) === ".\\" || moduleName.substr(0, 3) === "..\\"; + } + export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { let moduleState = getModuleInstanceState(node); return moduleState === ModuleInstanceState.Instantiated || @@ -1130,6 +1151,14 @@ namespace ts { return !!node && (node.kind === SyntaxKind.ArrayBindingPattern || node.kind === SyntaxKind.ObjectBindingPattern); } + export function isNodeDescendentOf(node: Node, ancestor: Node): boolean { + while (node) { + if (node === ancestor) return true; + node = node.parent; + } + return false; + } + export function isInAmbientContext(node: Node): boolean { while (node) { if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) { @@ -1401,7 +1430,7 @@ namespace ts { * where Symbol is literally the word "Symbol", and name is any identifierName */ export function isWellKnownSymbolSyntactically(node: Expression): boolean { - return node.kind === SyntaxKind.PropertyAccessExpression && isESSymbolIdentifier((<PropertyAccessExpression>node).expression); + return isPropertyAccessExpression(node) && isESSymbolIdentifier(node.expression); } export function getPropertyNameForPropertyNameNode(name: DeclarationName): string { @@ -1514,12 +1543,12 @@ namespace ts { function getModificationCount() { return modificationCount; } - + function reattachFileDiagnostics(newFile: SourceFile): void { if (!hasProperty(fileDiagnostics, newFile.fileName)) { return; } - + for (let diagnostic of fileDiagnostics[newFile.fileName]) { diagnostic.file = newFile; } @@ -2034,8 +2063,8 @@ namespace ts { if (node.kind === SyntaxKind.Identifier) { return true; } - else if (node.kind === SyntaxKind.PropertyAccessExpression) { - return isSupportedExpressionWithTypeArgumentsRest((<PropertyAccessExpression>node).expression); + else if (isPropertyAccessExpression(node)) { + return isSupportedExpressionWithTypeArgumentsRest(node.expression); } else { return false; @@ -2393,4 +2422,16 @@ namespace ts { } } } + + export function arrayStructurallyIsEqualTo<T>(array1: Array<T>, array2: Array<T>): boolean { + if (!array1 || !array2) { + return false; + } + + if (array1.length !== array2.length) { + return false; + } + + return arrayIsEqualTo(array1.sort(), array2.sort()); + } } diff --git a/ts/main.ts b/ts/main.ts index 1387f0c..498a1c2 100644 --- a/ts/main.ts +++ b/ts/main.ts @@ -111,7 +111,8 @@ class Program { } else if (/\.json$/.test(fileName)) { // tsconfig.json var pch: ts.ParseConfigHost = { readDirectory: () => [] }; var basePath = ts.getDirectoryPath(fileName); - this.host.config = ts.parseConfigFile(JSON.parse(newText), pch, basePath).options; + var json = ts.parseConfigFileTextToJson(fileName, newText).config || {}; + this.host.config = ts.parseJsonConfigFileContent(json, pch, basePath).options; } } deleteFile(fileName: string) { @@ -461,6 +462,7 @@ class Program { TabSize: tabSize, NewLineCharacter: '\n', ConvertTabsToSpaces: expandTabs, + IndentStyle: ts.IndentStyle.Smart, InsertSpaceAfterCommaDelimiter: true, InsertSpaceAfterSemicolonInForStatements: true, InsertSpaceBeforeAndAfterBinaryOperators: true, diff --git a/ts/services/formatting/formatting.ts b/ts/services/formatting/formatting.ts index 66cdbd5..4a7033c 100644 --- a/ts/services/formatting/formatting.ts +++ b/ts/services/formatting/formatting.ts @@ -32,7 +32,7 @@ namespace ts.formatting { */ interface DynamicIndentation { getIndentationForToken(tokenLine: number, tokenKind: SyntaxKind): number; - getIndentationForComment(owningToken: SyntaxKind): number; + getIndentationForComment(owningToken: SyntaxKind, tokenIndentation: number): number; /** * Indentation for open and close tokens of the node if it is block or another node that needs special indentation * ... { @@ -325,7 +325,7 @@ namespace ts.formatting { let lastIndentedLine: number; let indentationOnLastIndentedLine: number; - + let edits: TextChange[] = []; formattingScanner.advance(); @@ -354,12 +354,12 @@ namespace ts.formatting { * If list element is in the range - its indentation will be equal * to inherited indentation from its predecessors. */ - function tryComputeIndentationForListItem(startPos: number, - endPos: number, - parentStartLine: number, - range: TextRange, + function tryComputeIndentationForListItem(startPos: number, + endPos: number, + parentStartLine: number, + range: TextRange, inheritedIndentation: number): number { - + if (rangeOverlapsWithStartEnd(range, startPos, endPos)) { if (inheritedIndentation !== Constants.Unknown) { return inheritedIndentation; @@ -376,7 +376,7 @@ namespace ts.formatting { return Constants.Unknown; } - + function computeIndentation( node: TextRangeWithKind, startLine: number, @@ -419,8 +419,8 @@ namespace ts.formatting { // if node is located on the same line with the parent // - inherit indentation from the parent // - push children if either parent of node itself has non-zero delta - indentation = startLine === lastIndentedLine - ? indentationOnLastIndentedLine + indentation = startLine === lastIndentedLine + ? indentationOnLastIndentedLine : parentDynamicIndentation.getIndentation(); delta = Math.min(options.IndentSize, parentDynamicIndentation.getDelta() + delta); } @@ -455,7 +455,7 @@ namespace ts.formatting { function getDynamicIndentation(node: Node, nodeStartLine: number, indentation: number, delta: number): DynamicIndentation { return { - getIndentationForComment: kind => { + getIndentationForComment: (kind, tokenIndentation) => { switch (kind) { // preceding comment to the token that closes the indentation scope inherits the indentation from the scope // .. { @@ -463,9 +463,10 @@ namespace ts.formatting { // } case SyntaxKind.CloseBraceToken: case SyntaxKind.CloseBracketToken: + case SyntaxKind.CloseParenToken: return indentation + delta; } - return indentation; + return tokenIndentation !== Constants.Unknown ? tokenIndentation : indentation; }, getIndentationForToken: (line, kind) => { if (nodeStartLine !== line && node.decorators) { @@ -585,7 +586,7 @@ namespace ts.formatting { if (!rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) { return inheritedIndentation; } - + if (child.getFullWidth() === 0) { return inheritedIndentation; } @@ -623,8 +624,8 @@ namespace ts.formatting { return inheritedIndentation; } - function processChildNodes(nodes: NodeArray<Node>, - parent: Node, + function processChildNodes(nodes: NodeArray<Node>, + parent: Node, parentStartLine: number, parentDynamicIndentation: DynamicIndentation): void { @@ -716,8 +717,14 @@ namespace ts.formatting { } if (indentToken) { - let indentNextTokenOrTrivia = true; + let tokenIndentation = (isTokenInRange && !rangeContainsError(currentTokenInfo.token)) ? + dynamicIndentation.getIndentationForToken(tokenStart.line, currentTokenInfo.token.kind) : + Constants.Unknown; + if (currentTokenInfo.leadingTrivia) { + let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation); + let indentNextTokenOrTrivia = true; + for (let triviaItem of currentTokenInfo.leadingTrivia) { if (!rangeContainsRange(originalRange, triviaItem)) { continue; @@ -725,13 +732,11 @@ namespace ts.formatting { switch (triviaItem.kind) { case SyntaxKind.MultiLineCommentTrivia: - let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind); indentMultilineComment(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia); indentNextTokenOrTrivia = false; break; case SyntaxKind.SingleLineCommentTrivia: if (indentNextTokenOrTrivia) { - let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind); insertIndentation(triviaItem.pos, commentIndentation, /*lineAdded*/ false); indentNextTokenOrTrivia = false; } @@ -744,10 +749,9 @@ namespace ts.formatting { } // indent token only if is it is in target range and does not overlap with any error ranges - if (isTokenInRange && !rangeContainsError(currentTokenInfo.token)) { - let tokenIndentation = dynamicIndentation.getIndentationForToken(tokenStart.line, currentTokenInfo.token.kind); + if (tokenIndentation !== Constants.Unknown) { insertIndentation(currentTokenInfo.token.pos, tokenIndentation, lineAdded); - + lastIndentedLine = tokenStart.line; indentationOnLastIndentedLine = tokenIndentation; } @@ -768,12 +772,12 @@ namespace ts.formatting { } } - function processRange(range: TextRangeWithKind, - rangeStart: LineAndCharacter, - parent: Node, - contextNode: Node, + function processRange(range: TextRangeWithKind, + rangeStart: LineAndCharacter, + parent: Node, + contextNode: Node, dynamicIndentation: DynamicIndentation): boolean { - + let rangeHasError = rangeContainsError(range); let lineAdded: boolean; if (!rangeHasError && !previousRangeHasError) { @@ -783,7 +787,7 @@ namespace ts.formatting { trimTrailingWhitespacesForLines(originalStart.line, rangeStart.line); } else { - lineAdded = + lineAdded = processPair(range, rangeStart.line, parent, previousRange, previousRangeStartLine, previousParent, contextNode, dynamicIndentation) } } @@ -929,8 +933,8 @@ namespace ts.formatting { let lineStartPosition = getStartPositionOfLine(line, sourceFile); let lineEndPosition = getEndLinePosition(line, sourceFile); - // do not trim whitespaces in comments - if (range && isComment(range.kind) && range.pos <= lineEndPosition && range.end > lineEndPosition) { + // do not trim whitespaces in comments or template expression + if (range && (isComment(range.kind) || isStringOrRegularExpressionOrTemplateLiteral(range.kind)) && range.pos <= lineEndPosition && range.end > lineEndPosition) { continue; } diff --git a/ts/services/formatting/formattingScanner.ts b/ts/services/formatting/formattingScanner.ts index 6f6167d..58e2f30 100644 --- a/ts/services/formatting/formattingScanner.ts +++ b/ts/services/formatting/formattingScanner.ts @@ -3,8 +3,14 @@ /* @internal */ namespace ts.formatting { - let scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false); - + const standardScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.Standard); + const jsxScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.JSX); + + /** + * Scanner that is currently used for formatting + */ + let scanner: Scanner; + export interface FormattingScanner { advance(): void; isOnToken(): boolean; @@ -22,6 +28,8 @@ namespace ts.formatting { } export function getFormattingScanner(sourceFile: SourceFile, startPos: number, endPos: number): FormattingScanner { + Debug.assert(scanner === undefined); + scanner = sourceFile.languageVariant === LanguageVariant.JSX ? jsxScanner : standardScanner; scanner.setText(sourceFile.text); scanner.setTextPos(startPos); @@ -40,12 +48,17 @@ namespace ts.formatting { isOnToken: isOnToken, lastTrailingTriviaWasNewLine: () => wasNewLine, close: () => { + Debug.assert(scanner !== undefined); + lastTokenInfo = undefined; scanner.setText(undefined); + scanner = undefined; } } function advance(): void { + Debug.assert(scanner !== undefined); + lastTokenInfo = undefined; let isStarted = scanner.getStartPos() !== startPos; @@ -138,6 +151,8 @@ namespace ts.formatting { } function readTokenInfo(n: Node): TokenInfo { + Debug.assert(scanner !== undefined); + if (!isOnToken()) { // scanner is not on the token (either advance was not called yet or scanner is already past the end position) return { @@ -245,6 +260,8 @@ namespace ts.formatting { } function isOnToken(): boolean { + Debug.assert(scanner !== undefined); + let current = (lastTokenInfo && lastTokenInfo.token.kind) || scanner.getToken(); let startPos = (lastTokenInfo && lastTokenInfo.token.pos) || scanner.getStartPos(); return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current); diff --git a/ts/services/formatting/rules.ts b/ts/services/formatting/rules.ts index 876efa3..12efb77 100644 --- a/ts/services/formatting/rules.ts +++ b/ts/services/formatting/rules.ts @@ -213,27 +213,14 @@ namespace ts.formatting { public NoSpaceBetweenYieldKeywordAndStar: Rule; public SpaceBetweenYieldOrYieldStarAndOperand: Rule; - // Async-await + // Async functions + public SpaceBetweenAsyncAndOpenParen: Rule; public SpaceBetweenAsyncAndFunctionKeyword: Rule; - public NoSpaceBetweenAsyncAndFunctionKeyword: Rule; - public SpaceAfterAwaitKeyword: Rule; - public NoSpaceAfterAwaitKeyword: Rule; - // Type alias declaration - public SpaceAfterTypeKeyword: Rule; - public NoSpaceAfterTypeKeyword: Rule; - - // Tagged template string + // Template strings public SpaceBetweenTagAndTemplateString: Rule; - public NoSpaceBetweenTagAndTemplateString: Rule; - - // Type operation - public SpaceBeforeBar: Rule; - public NoSpaceBeforeBar: Rule; - public SpaceAfterBar: Rule; - public NoSpaceAfterBar: Rule; - public SpaceBeforeAmpersand: Rule; - public SpaceAfterAmpersand: Rule; + public NoSpaceAfterTemplateHeadAndMiddle: Rule; + public NoSpaceBeforeTemplateMiddleAndTail: Rule; constructor() { /// @@ -315,7 +302,7 @@ namespace ts.formatting { this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space)); this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete)); this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); @@ -348,7 +335,7 @@ namespace ts.formatting { this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // Add a space around certain TypeScript keywords - this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, SyntaxKind.TypeKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { @@ -383,26 +370,13 @@ namespace ts.formatting { this.SpaceBetweenYieldOrYieldStarAndOperand = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsYieldOrYieldStarWithOperand), RuleAction.Space)); // Async-await + this.SpaceBetweenAsyncAndOpenParen = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsArrowFunctionContext, Rules.IsSameLineTokenContext), RuleAction.Space)); this.SpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceAfterAwaitKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.AwaitKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceAfterAwaitKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.AwaitKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - - // Type alias declaration - this.SpaceAfterTypeKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.TypeKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceAfterTypeKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.TypeKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // template string this.SpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - - // type operation - this.SpaceBeforeBar = new Rule(RuleDescriptor.create3(SyntaxKind.BarToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceBeforeBar = new Rule(RuleDescriptor.create3(SyntaxKind.BarToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceAfterBar = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.BarToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceAfterBar = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.BarToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceBeforeAmpersand = new Rule(RuleDescriptor.create3(SyntaxKind.AmpersandToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.SpaceAfterAmpersand = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.AmpersandToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // These rules are higher in priority than user-configurable rules. this.HighPriorityCommonRules = @@ -430,12 +404,8 @@ namespace ts.formatting { this.NoSpaceBeforeOpenParenInFuncCall, this.SpaceBeforeBinaryKeywordOperator, this.SpaceAfterBinaryKeywordOperator, this.SpaceAfterVoidOperator, - this.SpaceBetweenAsyncAndFunctionKeyword, this.NoSpaceBetweenAsyncAndFunctionKeyword, - this.SpaceAfterAwaitKeyword, this.NoSpaceAfterAwaitKeyword, - this.SpaceAfterTypeKeyword, this.NoSpaceAfterTypeKeyword, - this.SpaceBetweenTagAndTemplateString, this.NoSpaceBetweenTagAndTemplateString, - this.SpaceBeforeBar, this.NoSpaceBeforeBar, this.SpaceAfterBar, this.NoSpaceAfterBar, - this.SpaceBeforeAmpersand, this.SpaceAfterAmpersand, + this.SpaceBetweenAsyncAndOpenParen, this.SpaceBetweenAsyncAndFunctionKeyword, + this.SpaceBetweenTagAndTemplateString, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail, // TypeScript-specific rules this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport, @@ -539,6 +509,8 @@ namespace ts.formatting { case SyntaxKind.ConditionalExpression: case SyntaxKind.AsExpression: case SyntaxKind.TypePredicate: + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: return true; // equals in binding elements: function foo([[x, y] = [1, 2]]) @@ -733,6 +705,10 @@ namespace ts.formatting { return context.currentTokenSpan.kind !== SyntaxKind.CommaToken; } + static IsArrowFunctionContext(context: FormattingContext): boolean { + return context.contextNode.kind === SyntaxKind.ArrowFunction; + } + static IsSameLineTokenContext(context: FormattingContext): boolean { return context.TokensAreOnSameLine(); } diff --git a/ts/services/formatting/smartIndenter.ts b/ts/services/formatting/smartIndenter.ts index 8355fac..3b68cd0 100644 --- a/ts/services/formatting/smartIndenter.ts +++ b/ts/services/formatting/smartIndenter.ts @@ -13,25 +13,45 @@ namespace ts.formatting { return 0; // past EOF } + // no indentation when the indent style is set to none, + // so we can return fast + if (options.IndentStyle === IndentStyle.None) { + return 0; + } + let precedingToken = findPrecedingToken(position, sourceFile); if (!precedingToken) { return 0; } // no indentation in string \regex\template literals - let precedingTokenIsLiteral = - precedingToken.kind === SyntaxKind.StringLiteral || - precedingToken.kind === SyntaxKind.RegularExpressionLiteral || - precedingToken.kind === SyntaxKind.NoSubstitutionTemplateLiteral || - precedingToken.kind === SyntaxKind.TemplateHead || - precedingToken.kind === SyntaxKind.TemplateMiddle || - precedingToken.kind === SyntaxKind.TemplateTail; + let precedingTokenIsLiteral = isStringOrRegularExpressionOrTemplateLiteral(precedingToken.kind); if (precedingTokenIsLiteral && precedingToken.getStart(sourceFile) <= position && precedingToken.end > position) { return 0; } let lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line; + // indentation is first non-whitespace character in a previous line + // for block indentation, we should look for a line which contains something that's not + // whitespace. + if (options.IndentStyle === IndentStyle.Block) { + + // move backwards until we find a line with a non-whitespace character, + // then find the first non-whitespace character for that line. + let current = position; + while (current > 0){ + let char = sourceFile.text.charCodeAt(current); + if (!isWhiteSpace(char) && !isLineBreak(char)) { + break; + } + current--; + } + + let lineStart = ts.getLineStartPositionForPosition(current, sourceFile); + return SmartIndenter.findFirstNonWhitespaceColumn(lineStart, current, sourceFile, options); + } + if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) { // previous token is comma that separates items in list - find the previous item and try to derive indentation from it let actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options); @@ -224,7 +244,7 @@ namespace ts.formatting { function getStartLineAndCharacterForNode(n: Node, sourceFile: SourceFile): LineAndCharacter { return sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)); } - + export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFile): boolean { if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) { let elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile); @@ -325,7 +345,7 @@ namespace ts.formatting { } return Value.Unknown; - + function getStartingExpression(node: PropertyAccessExpression | CallExpression | ElementAccessExpression) { while (true) { switch (node.kind) { @@ -405,6 +425,7 @@ namespace ts.formatting { function nodeContentIsAlwaysIndented(kind: SyntaxKind): boolean { switch (kind) { + case SyntaxKind.ExpressionStatement: case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: case SyntaxKind.InterfaceDeclaration: @@ -470,4 +491,4 @@ namespace ts.formatting { } } } -} \ No newline at end of file +} diff --git a/ts/services/services.ts b/ts/services/services.ts index 135f95e..8942eaf 100644 --- a/ts/services/services.ts +++ b/ts/services/services.ts @@ -728,7 +728,7 @@ namespace ts { } getBaseTypes(): ObjectType[] { return this.flags & (TypeFlags.Class | TypeFlags.Interface) - ? this.checker.getBaseTypes(<TypeObject & InterfaceType>this) + ? this.checker.getBaseTypes(<InterfaceType><Type>this) : undefined; } } @@ -1192,6 +1192,13 @@ namespace ts { TabSize: number; NewLineCharacter: string; ConvertTabsToSpaces: boolean; + IndentStyle: IndentStyle; + } + + export enum IndentStyle { + None = 0, + Block = 1, + Smart = 2, } export interface FormatCodeOptions extends EditorOptions { @@ -1855,8 +1862,8 @@ namespace ts { // so pass --noResolve to avoid reporting missing file errors. options.noResolve = true; - // Parse - let inputFileName = transpileOptions.fileName || "module.ts"; + // if jsx is specified then treat file as .tsx + let inputFileName = transpileOptions.fileName || (options.jsx ? "module.tsx" : "module.ts"); let sourceFile = createSourceFile(inputFileName, input, options.target); if (transpileOptions.moduleName) { sourceFile.moduleName = transpileOptions.moduleName; @@ -3111,6 +3118,7 @@ namespace ts { let node = currentToken; let isRightOfDot = false; let isRightOfOpenTag = false; + let isStartingCloseTag = false; let location = getTouchingPropertyName(sourceFile, position); if (contextToken) { @@ -3136,9 +3144,14 @@ namespace ts { return undefined; } } - else if (kind === SyntaxKind.LessThanToken && sourceFile.languageVariant === LanguageVariant.JSX) { - isRightOfOpenTag = true; - location = contextToken; + else if (sourceFile.languageVariant === LanguageVariant.JSX) { + if (kind === SyntaxKind.LessThanToken) { + isRightOfOpenTag = true; + location = contextToken; + } + else if (kind === SyntaxKind.SlashToken && contextToken.parent.kind === SyntaxKind.JsxClosingElement) { + isStartingCloseTag = true; + } } } @@ -3161,6 +3174,13 @@ namespace ts { isMemberCompletion = true; isNewIdentifierLocation = false; } + else if (isStartingCloseTag) { + let tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName; + symbols = [typeChecker.getSymbolAtLocation(tagName)]; + + isMemberCompletion = true; + isNewIdentifierLocation = false; + } else { // For JavaScript or TypeScript, if we're not after a dot, then just try to get the // global symbols in scope. These results should be valid for either language as @@ -3317,11 +3337,29 @@ namespace ts { let start = new Date().getTime(); let result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) || isSolelyIdentifierDefinitionLocation(contextToken) || - isDotOfNumericLiteral(contextToken); + isDotOfNumericLiteral(contextToken) || + isInJsxText(contextToken); log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start)); return result; } + function isInJsxText(contextToken: Node): boolean { + if (contextToken.kind === SyntaxKind.JsxText) { + return true; + } + + if (contextToken.kind === SyntaxKind.GreaterThanToken && contextToken.parent) { + if (contextToken.parent.kind === SyntaxKind.JsxOpeningElement) { + return true; + } + + if (contextToken.parent.kind === SyntaxKind.JsxClosingElement || contextToken.parent.kind === SyntaxKind.JsxSelfClosingElement) { + return contextToken.parent.parent && contextToken.parent.parent.kind === SyntaxKind.JsxElement; + } + } + return false; + } + function isNewIdentifierDefinitionLocation(previousToken: Node): boolean { if (previousToken) { let containingNodeKind = previousToken.parent.kind; @@ -3555,6 +3593,9 @@ namespace ts { if (parent && (parent.kind === SyntaxKind.JsxSelfClosingElement || parent.kind === SyntaxKind.JsxOpeningElement)) { return <JsxOpeningLikeElement>parent; } + else if (parent.kind === SyntaxKind.JsxAttribute) { + return <JsxOpeningLikeElement>parent.parent; + } break; // The context token is the closing } or " of an attribute, which means @@ -3665,9 +3706,9 @@ namespace ts { return containingNodeKind === SyntaxKind.Parameter; case SyntaxKind.AsKeyword: - containingNodeKind === SyntaxKind.ImportSpecifier || - containingNodeKind === SyntaxKind.ExportSpecifier || - containingNodeKind === SyntaxKind.NamespaceImport; + return containingNodeKind === SyntaxKind.ImportSpecifier || + containingNodeKind === SyntaxKind.ExportSpecifier || + containingNodeKind === SyntaxKind.NamespaceImport; case SyntaxKind.ClassKeyword: case SyntaxKind.EnumKeyword: @@ -3686,14 +3727,20 @@ namespace ts { // Previous token may have been a keyword that was converted to an identifier. switch (contextToken.getText()) { + case "abstract": + case "async": case "class": - case "interface": + case "const": + case "declare": case "enum": case "function": - case "var": - case "static": + case "interface": case "let": - case "const": + case "private": + case "protected": + case "public": + case "static": + case "var": case "yield": return true; } @@ -4105,8 +4152,9 @@ namespace ts { let useConstructSignatures = callExpression.kind === SyntaxKind.NewExpression || callExpression.expression.kind === SyntaxKind.SuperKeyword; let allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); - if (!contains(allSignatures, signature.target || signature)) { - // Get the first signature if there + if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) { + // Get the first signature if there is one -- allSignatures may contain + // either the original signature or its target, so check for either signature = allSignatures.length ? allSignatures[0] : undefined; } @@ -5918,6 +5966,7 @@ namespace ts { result.push(getReferenceEntryFromNode(node)); } break; + case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: // Make sure the container belongs to the same class // and has the appropriate static modifier from the original container. @@ -6261,7 +6310,8 @@ namespace ts { } return node.parent.kind === SyntaxKind.TypeReference || - (node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent)); + (node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent)) || + node.kind === SyntaxKind.ThisKeyword && !isExpression(node); } function isNamespaceReference(node: Node): boolean { @@ -7009,8 +7059,12 @@ namespace ts { * Checks if position points to a valid position to add JSDoc comments, and if so, * returns the appropriate template. Otherwise returns an empty string. * Valid positions are - * - outside of comments, statements, and expressions, and - * - preceding a function declaration. + * - outside of comments, statements, and expressions, and + * - preceding a: + * - function/constructor/method declaration + * - class declarations + * - variable statements + * - namespace declarations * * Hosts should ideally check that: * - The line is all whitespace up to 'position' before performing the insertion. @@ -7037,16 +7091,37 @@ namespace ts { } // TODO: add support for: - // - methods - // - constructors - // - class decls - let containingFunction = <FunctionDeclaration>getAncestor(tokenAtPos, SyntaxKind.FunctionDeclaration); + // - enums/enum members + // - interfaces + // - property declarations + // - potentially property assignments + let commentOwner: Node; + findOwner: for (commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) { + switch (commentOwner.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.Constructor: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.VariableStatement: + break findOwner; + case SyntaxKind.SourceFile: + return undefined; + case SyntaxKind.ModuleDeclaration: + // If in walking up the tree, we hit a a nested namespace declaration, + // then we must be somewhere within a dotted namespace name; however we don't + // want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'. + if (commentOwner.parent.kind === SyntaxKind.ModuleDeclaration) { + return undefined; + } + break findOwner; + } + } - if (!containingFunction || containingFunction.getStart() < position) { + if (!commentOwner || commentOwner.getStart() < position) { return undefined; } - let parameters = containingFunction.parameters; + let parameters = getParametersForJsDocOwningNode(commentOwner); let posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position); let lineStart = sourceFile.getLineStarts()[posLineAndChar.line]; @@ -7055,9 +7130,15 @@ namespace ts { // TODO: call a helper method instead once PR #4133 gets merged in. const newLine = host.getNewLine ? host.getNewLine() : "\r\n"; - let docParams = parameters.reduce((prev, cur, index) => - prev + - indentationStr + " * @param " + (cur.name.kind === SyntaxKind.Identifier ? (<Identifier>cur.name).text : "param" + index) + newLine, ""); + let docParams = ""; + for (let i = 0, numParams = parameters.length; i < numParams; i++) { + const currentName = parameters[i].name; + const paramName = currentName.kind === SyntaxKind.Identifier ? + (<Identifier>currentName).text : + "param" + i; + + docParams += `${indentationStr} * @param ${paramName}${newLine}`; + } // A doc comment consists of the following // * The opening comment line @@ -7077,6 +7158,52 @@ namespace ts { return { newText: result, caretOffset: preamble.length }; } + function getParametersForJsDocOwningNode(commentOwner: Node): ParameterDeclaration[] { + if (isFunctionLike(commentOwner)) { + return commentOwner.parameters; + } + + if (commentOwner.kind === SyntaxKind.VariableStatement) { + const varStatement = <VariableStatement>commentOwner; + const varDeclarations = varStatement.declarationList.declarations; + + if (varDeclarations.length === 1 && varDeclarations[0].initializer) { + return getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer); + } + } + + return emptyArray; + } + + /** + * Digs into an an initializer or RHS operand of an assignment operation + * to get the parameters of an apt signature corresponding to a + * function expression or a class expression. + * + * @param rightHandSide the expression which may contain an appropriate set of parameters + * @returns the parameters of a signature found on the RHS if one exists; otherwise 'emptyArray'. + */ + function getParametersFromRightHandSideOfAssignment(rightHandSide: Expression): ParameterDeclaration[] { + while (rightHandSide.kind === SyntaxKind.ParenthesizedExpression) { + rightHandSide = (<ParenthesizedExpression>rightHandSide).expression; + } + + switch (rightHandSide.kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return (<FunctionExpression>rightHandSide).parameters; + case SyntaxKind.ClassExpression: + for (let member of (<ClassExpression>rightHandSide).members) { + if (member.kind === SyntaxKind.Constructor) { + return (<ConstructorDeclaration>member).parameters; + } + } + break; + } + + return emptyArray; + } + function getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] { // Note: while getting todo comments seems like a syntactic operation, we actually // treat it as a semantic operation here. This is because we expect our host to call @@ -7746,6 +7873,7 @@ namespace ts { case SyntaxKind.GreaterThanEqualsToken: case SyntaxKind.InstanceOfKeyword: case SyntaxKind.InKeyword: + case SyntaxKind.AsKeyword: case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: diff --git a/ts/services/utilities.ts b/ts/services/utilities.ts index 6a41ab2..0121a14 100644 --- a/ts/services/utilities.ts +++ b/ts/services/utilities.ts @@ -12,7 +12,7 @@ namespace ts { export function getEndLinePosition(line: number, sourceFile: SourceFile): number { Debug.assert(line >= 0); let lineStarts = sourceFile.getLineStarts(); - + let lineIndex = line; if (lineIndex + 1 === lineStarts.length) { // last line - return EOF @@ -131,7 +131,8 @@ namespace ts { return isCompletedNode((<IfStatement>n).thenStatement, sourceFile); case SyntaxKind.ExpressionStatement: - return isCompletedNode((<ExpressionStatement>n).expression, sourceFile); + return isCompletedNode((<ExpressionStatement>n).expression, sourceFile) || + hasChildOfKind(n, SyntaxKind.SemicolonToken); case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.ArrayBindingPattern: @@ -173,7 +174,7 @@ namespace ts { case SyntaxKind.VoidExpression: case SyntaxKind.YieldExpression: case SyntaxKind.SpreadElementExpression: - let unaryWordExpression = (<TypeOfExpression|DeleteExpression|VoidExpression|YieldExpression|SpreadElementExpression>n); + let unaryWordExpression = (<TypeOfExpression | DeleteExpression | VoidExpression | YieldExpression | SpreadElementExpression>n); return isCompletedNode(unaryWordExpression.expression, sourceFile); case SyntaxKind.TaggedTemplateExpression: @@ -255,7 +256,7 @@ namespace ts { }); // Either we didn't find an appropriate list, or the list must contain us. - Debug.assert(!syntaxList || contains(syntaxList.getChildren(), node)); + Debug.assert(!syntaxList || contains(syntaxList.getChildren(), node)); return syntaxList; } @@ -391,7 +392,7 @@ namespace ts { // if this is the case - then we should assume that token in question is located in previous child. if (position < child.end && (nodeHasTokens(child) || child.kind === SyntaxKind.JsxText)) { const start = child.getStart(sourceFile); - const lookInPreviousChild = + const lookInPreviousChild = (start >= position) || // cursor in the leading trivia (child.kind === SyntaxKind.JsxText && start === child.end); // whitespace only JsxText @@ -428,7 +429,7 @@ namespace ts { } } } - + export function isInString(sourceFile: SourceFile, position: number) { let token = getTokenAtPosition(sourceFile, position); return token && token.kind === SyntaxKind.StringLiteral && position > token.getStart(); @@ -476,7 +477,7 @@ namespace ts { let commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos); return forEach(commentRanges, jsDocPrefix); - + function jsDocPrefix(c: CommentRange): boolean { var text = sourceFile.text; return text.length >= c.pos + 3 && text[c.pos] === '/' && text[c.pos + 1] === '*' && text[c.pos + 2] === '*'; @@ -566,6 +567,15 @@ namespace ts { return kind === SyntaxKind.SingleLineCommentTrivia || kind === SyntaxKind.MultiLineCommentTrivia; } + export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind): boolean { + if (kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.RegularExpressionLiteral + || isTemplateLiteralKind(kind)) { + return true; + } + return false; + } + export function isPunctuation(kind: SyntaxKind): boolean { return SyntaxKind.FirstPunctuation <= kind && kind <= SyntaxKind.LastPunctuation; } @@ -630,7 +640,8 @@ namespace ts { increaseIndent: () => { indent++; }, decreaseIndent: () => { indent--; }, clear: resetWriter, - trackSymbol: () => { } + trackSymbol: () => { }, + reportInaccessibleThisError: () => { } }; function writeIndent() { @@ -693,7 +704,7 @@ namespace ts { } export function displayPart(text: string, kind: SymbolDisplayPartKind, symbol?: Symbol): SymbolDisplayPart { - return <SymbolDisplayPart> { + return <SymbolDisplayPart>{ text: text, kind: SymbolDisplayPartKind[kind] };