diff --git a/Jakefile.js b/Jakefile.js index 1f786fb230..46f2a0fca9 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -309,7 +309,7 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts options += " --stripInternal"; } - if (useBuiltCompiler && !/^(no?|f(alse)?|0|-)$/i.test(process.env.USE_TRANSFORMS)) { + if (useBuiltCompiler && !environmentVariableIsDisabled("USE_TRANSFORMS")) { console.warn("\u001b[93mwarning: 'USE_TRANSFORMS' environment variable is not set to 'false'. Experimental transforms will be enabled by default.\u001b[0m"); } @@ -826,14 +826,17 @@ function runTestsAndWriteOutput(file) { }); } -function runConsoleTests(defaultReporter, defaultSubsets) { - cleanTestDirs(); +function runConsoleTests(defaultReporter, defaultSubsets, dirty) { + if (!dirty) { + cleanTestDirs(); + } + var debug = process.env.debug || process.env.d; tests = process.env.test || process.env.tests || process.env.t; var light = process.env.light || false; var stackTraceLimit = process.env.stackTraceLimit || 1; var testConfigFile = 'test.config'; - if(fs.existsSync(testConfigFile)) { + if (fs.existsSync(testConfigFile)) { fs.unlinkSync(testConfigFile); } @@ -866,7 +869,7 @@ function runConsoleTests(defaultReporter, defaultSubsets) { console.log(cmd); exec(cmd, function () { deleteTemporaryProjectOutput(); - if (i === 0) { + if (i === 0 && !dirty) { var lint = jake.Task['lint']; lint.addListener('complete', function () { complete(); @@ -894,6 +897,9 @@ task("runtests", ["build-rules", "tests", builtLocalDirectory], function() { task("runtests-file", ["build-rules", "tests", builtLocalDirectory], function () { runTestsAndWriteOutput("tests/baselines/local/testresults.tap"); }, { async: true }); +task("runtests-dirty", ["build-rules", "tests", builtLocalDirectory], function () { + runConsoleTests("mocha-fivemat-progress-reporter", [], /*dirty*/ true); +}, { async: true }); desc("Generates code coverage data via instanbul"); task("generate-code-coverage", ["tests", builtLocalDirectory], function () { @@ -1258,4 +1264,12 @@ ProgressBar.prototype = { this._lastProgress = progress; this.visible = true; } -}; \ No newline at end of file +}; + +function environmentVariableIsEnabled(name) { + return /^(y(es)?|t(rue)?|on|enabled?|1|\+)$/.test(process.env[name]); +} + +function environmentVariableIsDisabled(name) { + return /^(no?|f(alse)?|off|disabled?|0|-)$/.test(process.env[name]); +} \ No newline at end of file diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aeaad03267..e339cd390f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1118,7 +1118,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean): Symbol { + function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, location?: Node): Symbol { if (nodeIsMissing(name)) { return undefined; } @@ -1127,7 +1127,7 @@ namespace ts { if (name.kind === SyntaxKind.Identifier) { const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0; - symbol = resolveName(name, (name).text, meaning, ignoreErrors ? undefined : message, name); + symbol = resolveName(location || name, (name).text, meaning, ignoreErrors ? undefined : message, name); if (!symbol) { return undefined; } @@ -1136,7 +1136,7 @@ namespace ts { const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression; const right = name.kind === SyntaxKind.QualifiedName ? (name).right : (name).name; - const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors); + const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, location); if (!namespace || namespace === unknownSymbol || nodeIsMissing(right)) { return undefined; } @@ -16042,7 +16042,14 @@ namespace ts { // Emitter support function isArgumentsLocalBinding(node: Identifier): boolean { - return getReferencedValueSymbol(node) === argumentsSymbol; + if (!isGeneratedIdentifier(node)) { + node = getSourceTreeNodeOfType(node, isIdentifier); + if (node) { + return getReferencedValueSymbol(node) === argumentsSymbol; + } + } + + return false; } function moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean { @@ -16077,37 +16084,49 @@ namespace ts { // When resolved as an expression identifier, if the given node references an exported entity, return the declaration // node of the exported entity's container. Otherwise, return undefined. function getReferencedExportContainer(node: Identifier, prefixLocals?: boolean): SourceFile | ModuleDeclaration | EnumDeclaration { - let symbol = getReferencedValueSymbol(node); - if (symbol) { - if (symbol.flags & SymbolFlags.ExportValue) { - // If we reference an exported entity within the same module declaration, then whether - // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the - // kinds that we do NOT prefix. - const exportSymbol = getMergedSymbol(symbol.exportSymbol); - if (exportSymbol.flags & SymbolFlags.ExportHasLocal && !prefixLocals) { - return undefined; + node = getSourceTreeNodeOfType(node, isIdentifier); + if (node) { + let symbol = getReferencedValueSymbol(node); + if (symbol) { + if (symbol.flags & SymbolFlags.ExportValue) { + // If we reference an exported entity within the same module declaration, then whether + // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the + // kinds that we do NOT prefix. + const exportSymbol = getMergedSymbol(symbol.exportSymbol); + if (exportSymbol.flags & SymbolFlags.ExportHasLocal && !prefixLocals) { + return undefined; + } + symbol = exportSymbol; } - symbol = exportSymbol; - } - const parentSymbol = getParentOfSymbol(symbol); - if (parentSymbol) { - if (parentSymbol.flags & SymbolFlags.ValueModule && parentSymbol.valueDeclaration.kind === SyntaxKind.SourceFile) { - return parentSymbol.valueDeclaration; - } - for (let n = node.parent; n; n = n.parent) { - if ((n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.EnumDeclaration) && getSymbolOfNode(n) === parentSymbol) { - return n; + const parentSymbol = getParentOfSymbol(symbol); + if (parentSymbol) { + if (parentSymbol.flags & SymbolFlags.ValueModule && parentSymbol.valueDeclaration.kind === SyntaxKind.SourceFile) { + return parentSymbol.valueDeclaration; + } + for (let n = node.parent; n; n = n.parent) { + if ((n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.EnumDeclaration) && getSymbolOfNode(n) === parentSymbol) { + return n; + } } } } } + + return undefined; } // When resolved as an expression identifier, if the given node references an import, return the declaration of // that import. Otherwise, return undefined. function getReferencedImportDeclaration(node: Identifier): Declaration { - const symbol = getReferencedValueSymbol(node); - return symbol && symbol.flags & SymbolFlags.Alias ? getDeclarationOfAliasSymbol(symbol) : undefined; + node = getSourceTreeNodeOfType(node, isIdentifier); + if (node) { + const symbol = getReferencedValueSymbol(node); + if (symbol && symbol.flags & SymbolFlags.Alias) { + return getDeclarationOfAliasSymbol(symbol); + } + } + + return undefined; } function isSymbolOfDeclarationWithCollidingName(symbol: Symbol): boolean { @@ -16157,35 +16176,57 @@ namespace ts { // a name that either hides an existing name or might hide it when compiled downlevel, // return the declaration of that entity. Otherwise, return undefined. function getReferencedDeclarationWithCollidingName(node: Identifier): Declaration { - const symbol = getReferencedValueSymbol(node); - return symbol && isSymbolOfDeclarationWithCollidingName(symbol) ? symbol.valueDeclaration : undefined; + if (!isGeneratedIdentifier(node)) { + node = getSourceTreeNodeOfType(node, isIdentifier); + if (node) { + const symbol = getReferencedValueSymbol(node); + if (symbol && isSymbolOfDeclarationWithCollidingName(symbol)) { + return symbol.valueDeclaration; + } + } + } + + return undefined; } // Return true if the given node is a declaration of a nested block scoped entity with a name that either hides an // existing name or might hide a name when compiled downlevel function isDeclarationWithCollidingName(node: Declaration): boolean { - return isSymbolOfDeclarationWithCollidingName(getSymbolOfNode(node)); + node = getSourceTreeNodeOfType(node, isDeclaration); + if (node) { + const symbol = getSymbolOfNode(node); + if (symbol) { + return isSymbolOfDeclarationWithCollidingName(symbol); + } + } + + return false; } function isValueAliasDeclaration(node: Node): boolean { + node = getSourceTreeNode(node); switch (node.kind) { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ImportClause: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - return isAliasResolvedToValue(getSymbolOfNode(node)); + return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol); case SyntaxKind.ExportDeclaration: const exportClause = (node).exportClause; return exportClause && forEach(exportClause.elements, isValueAliasDeclaration); case SyntaxKind.ExportAssignment: - return (node).expression && (node).expression.kind === SyntaxKind.Identifier ? isAliasResolvedToValue(getSymbolOfNode(node)) : true; + return (node).expression + && (node).expression.kind === SyntaxKind.Identifier + ? isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol) + : true; } return false; } function isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean { - if (node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) { + node = getSourceTreeNodeOfType(node, isImportEqualsDeclaration); + if (node === undefined || node.parent.kind !== SyntaxKind.SourceFile || !isInternalModuleImportEqualsDeclaration(node)) { // parent is not source file or it is not reference to internal module return false; } @@ -16212,9 +16253,10 @@ namespace ts { } function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { + node = getSourceTreeNode(node); if (isAliasSymbolDeclaration(node)) { const symbol = getSymbolOfNode(node); - if (getSymbolLinks(symbol).referenced) { + if (symbol && getSymbolLinks(symbol).referenced) { return true; } } @@ -16247,7 +16289,8 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { - return getNodeLinks(node).flags; + node = getSourceTreeNode(node); + return node ? getNodeLinks(node).flags : undefined; } function getEnumMemberValue(node: EnumMember): number { @@ -16275,16 +16318,16 @@ namespace ts { return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0; } - function getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind { + function getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind { // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. - const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true); + const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, location); const constructorType = valueSymbol ? getTypeOfSymbol(valueSymbol) : undefined; if (constructorType && isConstructorType(constructorType)) { return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue; } // Resolve the symbol as a type so that we can provide a more useful hint for the type serializer. - const typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true); + const typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true, location); // We might not be able to resolve type symbol so use unknown type in that case (eg error case) if (!typeSymbol) { return TypeReferenceSerializationKind.ObjectType; @@ -16363,9 +16406,17 @@ namespace ts { } function getReferencedValueDeclaration(reference: Identifier): Declaration { - Debug.assert(!nodeIsSynthesized(reference)); - const symbol = getReferencedValueSymbol(reference); - return symbol && getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; + if (!isGeneratedIdentifier(reference)) { + reference = getSourceTreeNodeOfType(reference, isIdentifier); + if (reference) { + const symbol = getReferencedValueSymbol(reference); + if (symbol) { + return getExportSymbolOfValueSymbolIfExported(symbol).valueDeclaration; + } + } + } + + return undefined; } function createResolver(): EmitResolver { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index bb4d179704..e3f9838cf2 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2240,41 +2240,49 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emit(node.right); } - function emitQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean) { + function emitQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean, location?: Node) { if (node.left.kind === SyntaxKind.Identifier) { - emitEntityNameAsExpression(node.left, useFallback); + emitEntityNameAsExpression(node.left, useFallback, location); } else if (useFallback) { const temp = createAndRecordTempVariable(TempFlags.Auto); write("("); emitNodeWithoutSourceMap(temp); write(" = "); - emitEntityNameAsExpression(node.left, /*useFallback*/ true); + emitEntityNameAsExpression(node.left, /*useFallback*/ true, location); write(") && "); emitNodeWithoutSourceMap(temp); } else { - emitEntityNameAsExpression(node.left, /*useFallback*/ false); + emitEntityNameAsExpression(node.left, /*useFallback*/ false, location); } write("."); emit(node.right); } - function emitEntityNameAsExpression(node: EntityName | Expression, useFallback: boolean) { + function emitEntityNameAsExpression(node: EntityName | Expression, useFallback: boolean, location?: Node) { switch (node.kind) { case SyntaxKind.Identifier: + let name = node; + if (location) { + // to resolve the expression to the correct container, create a shallow + // clone of `node` with a new parent. + name = clone(name); + name.parent = location; + } + if (useFallback) { write("typeof "); - emitExpressionIdentifier(node); + emitExpressionIdentifier(name); write(" !== 'undefined' && "); } - emitExpressionIdentifier(node); + emitExpressionIdentifier(name); break; case SyntaxKind.QualifiedName: - emitQualifiedNameAsExpression(node, useFallback); + emitQualifiedNameAsExpression(node, useFallback, location); break; default: @@ -5943,22 +5951,21 @@ const _super = (function (geti, seti) { } // Clone the type name and parent it to a location outside of the current declaration. - const typeName = cloneEntityName(node.typeName, location); - const result = resolver.getTypeReferenceSerializationKind(typeName); + const result = resolver.getTypeReferenceSerializationKind(node.typeName, location); switch (result) { case TypeReferenceSerializationKind.Unknown: let temp = createAndRecordTempVariable(TempFlags.Auto); write("(typeof ("); emitNodeWithoutSourceMap(temp); write(" = "); - emitEntityNameAsExpression(typeName, /*useFallback*/ true); + emitEntityNameAsExpression(node.typeName, /*useFallback*/ true, location); write(") === 'function' && "); emitNodeWithoutSourceMap(temp); write(") || Object"); break; case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue: - emitEntityNameAsExpression(typeName, /*useFallback*/ false); + emitEntityNameAsExpression(node.typeName, /*useFallback*/ false, location); break; case TypeReferenceSerializationKind.VoidType: diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index f126f512bf..796dd9529a 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -467,7 +467,7 @@ namespace ts { if (isGeneratedIdentifier(node)) { return node; } - if (node.text !== "arguments" && !resolver.isArgumentsLocalBinding(getOriginalNode(node))) { + if (node.text !== "arguments" && !resolver.isArgumentsLocalBinding(node)) { return node; } return convertedLoopState.argumentsName || (convertedLoopState.argumentsName = createUniqueName("arguments")); @@ -1426,10 +1426,7 @@ namespace ts { // * Why loop initializer is excluded? // - Since we've introduced a fresh name it already will be undefined. - const original = getOriginalNode(node); - Debug.assert(isVariableDeclaration(original)); - - const flags = resolver.getNodeCheckFlags(original); + const flags = resolver.getNodeCheckFlags(node); const isCapturedInFunction = flags & NodeCheckFlags.CapturedBlockScopedBinding; const isDeclaredInLoop = flags & NodeCheckFlags.BlockScopedBindingInLoop; const emittedAsTopLevel = @@ -1443,7 +1440,7 @@ namespace ts { !emittedAsTopLevel && enclosingBlockScopeContainer.kind !== SyntaxKind.ForInStatement && enclosingBlockScopeContainer.kind !== SyntaxKind.ForOfStatement - && (!resolver.isDeclarationWithCollidingName(original) + && (!resolver.isDeclarationWithCollidingName(node) || (isDeclaredInLoop && !isCapturedInFunction && !isIterationStatement(enclosingBlockScopeContainer, /*lookInLabeledStatements*/ false))); @@ -1721,7 +1718,7 @@ namespace ts { } function shouldConvertIterationStatementBody(node: IterationStatement): boolean { - return (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0; + return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0; } /** @@ -2614,8 +2611,8 @@ namespace ts { // Only substitute the identifier if we have enabled substitutions for block-scoped // bindings. if (enabledSubstitutions & ES6SubstitutionFlags.BlockScopedBindings) { - const original = getOriginalNode(node); - if (isIdentifier(original) && !nodeIsSynthesized(original) && original.parent && isNameOfDeclarationWithCollidingName(original)) { + const original = getSourceTreeNodeOfType(node, isIdentifier); + if (original && isNameOfDeclarationWithCollidingName(original)) { return getGeneratedNameForNode(original); } } @@ -2668,12 +2665,9 @@ namespace ts { */ function substituteExpressionIdentifier(node: Identifier): Identifier { if (enabledSubstitutions & ES6SubstitutionFlags.BlockScopedBindings) { - const original = getOriginalNode(node); - if (isIdentifier(original)) { - const declaration = resolver.getReferencedDeclarationWithCollidingName(original); - if (declaration) { - return getGeneratedNameForNode(declaration.name); - } + const declaration = resolver.getReferencedDeclarationWithCollidingName(node); + if (declaration) { + return getGeneratedNameForNode(declaration.name); } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 440aec62e3..1de4de782c 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -460,8 +460,7 @@ namespace ts { function visitExportAssignment(node: ExportAssignment): VisitResult { if (!node.isExportEquals) { - const original = getOriginalNode(node); - if (nodeIsSynthesized(original) || resolver.isValueAliasDeclaration(original)) { + if (nodeIsSynthesized(node) || resolver.isValueAliasDeclaration(node)) { const statements: Statement[] = []; addExportDefault(statements, node.expression, /*location*/ node); return statements; @@ -713,46 +712,43 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier): Expression { - const original = getOriginalNode(node); - if (isIdentifier(original)) { - const container = resolver.getReferencedExportContainer(original, (getNodeEmitFlags(node) & NodeEmitFlags.PrefixExportedLocal) !== 0); - if (container) { - if (container.kind === SyntaxKind.SourceFile) { - return createPropertyAccess( - createIdentifier("exports"), - getSynthesizedClone(node), - /*location*/ node - ); - } + const container = resolver.getReferencedExportContainer(node, (getNodeEmitFlags(node) & NodeEmitFlags.ExportName) !== 0); + if (container) { + if (container.kind === SyntaxKind.SourceFile) { + return createPropertyAccess( + createIdentifier("exports"), + getSynthesizedClone(node), + /*location*/ node + ); } - else { - const declaration = resolver.getReferencedImportDeclaration(node.parent ? node : original); - if (declaration) { - if (declaration.kind === SyntaxKind.ImportClause) { - if (languageVersion >= ScriptTarget.ES5) { - return createPropertyAccess( - getGeneratedNameForNode(declaration.parent), - createIdentifier("default"), - /*location*/ node - ); - } - else { - return createElementAccess( - getGeneratedNameForNode(declaration.parent), - createLiteral("default"), - /*location*/ node - ); - } - } - else if (declaration.kind === SyntaxKind.ImportSpecifier) { - const name = (declaration).propertyName - || (declaration).name; + } + else { + const declaration = resolver.getReferencedImportDeclaration(node); + if (declaration) { + if (declaration.kind === SyntaxKind.ImportClause) { + if (languageVersion >= ScriptTarget.ES5) { return createPropertyAccess( - getGeneratedNameForNode(declaration.parent.parent.parent), - getSynthesizedClone(name), + getGeneratedNameForNode(declaration.parent), + createIdentifier("default"), /*location*/ node ); } + else { + return createElementAccess( + getGeneratedNameForNode(declaration.parent), + createLiteral("default"), + /*location*/ node + ); + } + } + else if (declaration.kind === SyntaxKind.ImportSpecifier) { + const name = (declaration).propertyName + || (declaration).name; + return createPropertyAccess( + getGeneratedNameForNode(declaration.parent.parent.parent), + getSynthesizedClone(name), + /*location*/ node + ); } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index be9390beba..f7bea99420 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -573,8 +573,7 @@ namespace ts { function visitExportAssignment(node: ExportAssignment): Statement { if (!node.isExportEquals) { - const original = getOriginalNode(node); - if (nodeIsSynthesized(original) || resolver.isValueAliasDeclaration(original)) { + if (nodeIsSynthesized(node) || resolver.isValueAliasDeclaration(node)) { return createExportStatement( createLiteral("default"), node.expression @@ -1015,8 +1014,7 @@ namespace ts { const left = node.left; switch (left.kind) { case SyntaxKind.Identifier: - const originalNode = getOriginalNode(left); - const exportDeclaration = resolver.getReferencedExportContainer(originalNode); + const exportDeclaration = resolver.getReferencedExportContainer(left); if (exportDeclaration) { return createExportExpression(left, node); } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 0b8c11a66e..710edd7750 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -596,7 +596,7 @@ namespace ts { // Record an alias to avoid class double-binding. let decoratedClassAlias: Identifier; - if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.DecoratedClassWithSelfReference) { + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.DecoratedClassWithSelfReference) { enableExpressionSubstitutionForDecoratedClasses(); decoratedClassAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias; @@ -1380,7 +1380,6 @@ namespace ts { function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { - let properties: ObjectLiteralElement[]; if (shouldAddTypeMetadata(node)) { decoratorExpressions.push(createMetadataHelper("design:type", serializeTypeOfNode(node))); } @@ -1614,11 +1613,9 @@ namespace ts { * @param node The type reference node. */ function serializeTypeReferenceNode(node: TypeReferenceNode) { - // Clone the type name and parent it to a location outside of the current declaration. - const typeName = cloneEntityName(node.typeName, currentScope); - switch (resolver.getTypeReferenceSerializationKind(typeName)) { + switch (resolver.getTypeReferenceSerializationKind(node.typeName, currentScope)) { case TypeReferenceSerializationKind.Unknown: - const serialized = serializeEntityNameAsExpression(typeName, /*useFallback*/ true); + const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true); const temp = createTempVariable(hoistVariableDeclaration); return createLogicalOr( createLogicalAnd( @@ -1634,7 +1631,7 @@ namespace ts { ); case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue: - return serializeEntityNameAsExpression(typeName, /*useFallback*/ false); + return serializeEntityNameAsExpression(node.typeName, /*useFallback*/ false); case TypeReferenceSerializationKind.VoidType: return createVoidZero(); @@ -1675,17 +1672,20 @@ namespace ts { function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression { switch (node.kind) { case SyntaxKind.Identifier: + const name = getMutableClone(node); + name.original = undefined; + name.parent = currentScope; if (useFallback) { return createLogicalAnd( createStrictInequality( - createTypeOf(node), + createTypeOf(name), createLiteral("undefined") ), - node + name ); } - return node; + return name; case SyntaxKind.QualifiedName: return serializeQualifiedNameAsExpression(node, useFallback); @@ -2644,9 +2644,9 @@ namespace ts { return getNamespaceMemberName(name); } else { - // We set the "PrefixExportedLocal" flag to indicate to any module transformer + // We set the "ExportName" flag to indicate to any module transformer // downstream that any `exports.` prefix should be added. - setNodeEmitFlags(name, getNodeEmitFlags(name) | NodeEmitFlags.PrefixExportedLocal); + setNodeEmitFlags(name, getNodeEmitFlags(name) | NodeEmitFlags.ExportName); return name; } } @@ -2738,41 +2738,52 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier): Expression { + return trySubstituteDecoratedClassName(node) + || trySubstituteNamespaceExportedName(node) + || node; + } + + function trySubstituteDecoratedClassName(node: Identifier): Expression { if (enabledSubstitutions & TypeScriptSubstitutionFlags.DecoratedClasses) { - const original = getOriginalNode(node); - if (isIdentifier(original)) { - if (resolver.getNodeCheckFlags(original) & NodeCheckFlags.SelfReferenceInDecoratedClass) { - // Due to the emit for class decorators, any reference to the class from inside of the class body - // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind - // behavior of class names in ES6. - const declaration = resolver.getReferencedValueDeclaration(original); - if (declaration) { - const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; - if (classAlias) { - return getRelocatedClone(classAlias, /*location*/ node); - } + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.SelfReferenceInDecoratedClass) { + // Due to the emit for class decorators, any reference to the class from inside of the class body + // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind + // behavior of class names in ES6. + const declaration = resolver.getReferencedValueDeclaration(node); + if (declaration) { + const classAlias = currentDecoratedClassAliases[getNodeId(declaration)]; + if (classAlias) { + return getRelocatedClone(classAlias, /*location*/ node); } } } } + return undefined; + } + + function trySubstituteNamespaceExportedName(node: Identifier): Expression { if (enabledSubstitutions & applicableSubstitutions) { + // If this is explicitly a local name, do not substitute. + if (getNodeEmitFlags(node) & NodeEmitFlags.LocalName) { + return node; + } + // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. - const original = getOriginalNode(node); - if (isIdentifier(original) && original.parent) { - const container = resolver.getReferencedExportContainer(original); - if (container) { - const substitute = - (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || - (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration); - if (substitute) { - return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node); - } + const original = getSourceTreeNodeOfType(node, isIdentifier); + const container = resolver.getReferencedExportContainer(original, /*prefixLocals*/ false); + if (container && original !== container.name) { + const substitute = + (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) || + (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration); + if (substitute) { + return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node); } } } - return node; + + return undefined; } function substituteCallExpression(node: CallExpression): Expression { @@ -2900,7 +2911,7 @@ namespace ts { function getSuperContainerAsyncMethodFlags() { return currentSuperContainer !== undefined - && resolver.getNodeCheckFlags(getOriginalNode(currentSuperContainer)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); + && resolver.getNodeCheckFlags(currentSuperContainer) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); } } } \ No newline at end of file diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7723abfe27..ab63484a81 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1963,7 +1963,7 @@ namespace ts { // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; getReferencedValueDeclaration(reference: Identifier): Declaration; - getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind; + getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind; isOptionalParameter(node: ParameterDeclaration): boolean; moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean; isArgumentsLocalBinding(node: Identifier): boolean; @@ -2875,8 +2875,9 @@ namespace ts { NoSourceMap = 1 << 10, // Do not emit a source map location for this node. NoNestedSourceMaps = 1 << 11, // Do not emit source map locations for children of this node. NoComments = 1 << 12, // Do not emit comments for this node. - PrefixExportedLocal = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - Indented = 1 << 14, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). } /** Additional context provided to `visitEachChild` */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a3d0714287..bf6881b981 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1711,24 +1711,6 @@ namespace ts { || kind === SyntaxKind.SourceFile; } - /** - * Creates a deep clone of an EntityName, with new parent pointers. - * @param node The EntityName to clone. - * @param parent The parent for the cloned node. - */ - export function cloneEntityName(node: EntityName, parent?: Node): EntityName { - const clone = getMutableClone(node); - clone.parent = parent; - if (isQualifiedName(clone)) { - const { left, right } = clone; - clone.left = cloneEntityName(left, clone); - clone.right = getMutableClone(right); - clone.right.parent = clone; - } - - return clone; - } - export function nodeIsSynthesized(node: TextRange): boolean { return positionIsSynthesized(node.pos) || positionIsSynthesized(node.end); @@ -1741,13 +1723,31 @@ namespace ts { } export function getOriginalNode(node: Node): Node { - while (node.original !== undefined) { - node = node.original; + if (node) { + while (node.original !== undefined) { + node = node.original; + } } return node; } + export function getSourceTreeNode(node: Node): Node { + node = getOriginalNode(node); + if (node) { + if (node.parent || node.kind === SyntaxKind.SourceFile) { + return node; + } + } + + return undefined; + } + + export function getSourceTreeNodeOfType(node: T, nodeTest: (node: Node) => node is T): T { + const source = getSourceTreeNode(node); + return source && nodeTest(source) ? source : undefined; + } + export function getOriginalNodeId(node: Node) { node = getOriginalNode(node); return node ? getNodeId(node) : 0; @@ -2999,7 +2999,7 @@ namespace ts { break; case SyntaxKind.ImportEqualsDeclaration: - if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference && resolver.isReferencedAliasDeclaration(getOriginalNode(node))) { + if ((node).moduleReference.kind === SyntaxKind.ExternalModuleReference && resolver.isReferencedAliasDeclaration(node)) { // import x = require("mod") where x is referenced externalImports.push(node); } @@ -3014,7 +3014,7 @@ namespace ts { hasExportStarsToExportValues = true; } } - else if (resolver.isValueAliasDeclaration(getOriginalNode(node))) { + else if (resolver.isValueAliasDeclaration(node)) { // export { x, y } from "mod" where at least one export is a value symbol externalImports.push(node); } @@ -3416,6 +3416,10 @@ namespace ts { || kind === SyntaxKind.ModuleDeclaration; } + export function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { + return node.kind === SyntaxKind.ImportEqualsDeclaration; + } + export function isImportClause(node: Node): node is ImportClause { return node.kind === SyntaxKind.ImportClause; }