From d01f4d140a6f70b89147fcce9b19a8bac4c404f3 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 11 Dec 2017 13:32:16 -0500 Subject: [PATCH] Reimplement #20320 differently to handle multiple check orders better (#20588) * Reimplement #20320 less elegantly but handle odd check orders better * Consolidate 2 of 3 conditions --- src/compiler/checker.ts | 28 ++++---- .../reference/importAliasFromNamespace.js | 68 +++++++++++++++++++ .../importAliasFromNamespace.symbols | 52 ++++++++++++++ .../reference/importAliasFromNamespace.types | 56 +++++++++++++++ .../compiler/importAliasFromNamespace.ts | 21 ++++++ 5 files changed, 213 insertions(+), 12 deletions(-) create mode 100644 tests/baselines/reference/importAliasFromNamespace.js create mode 100644 tests/baselines/reference/importAliasFromNamespace.symbols create mode 100644 tests/baselines/reference/importAliasFromNamespace.types create mode 100644 tests/cases/compiler/importAliasFromNamespace.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4cc0bb79531..319caa180c3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13123,6 +13123,12 @@ namespace ts { return type; } + function markAliasReferenced(symbol: Symbol, location: Node) { + if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { + markAliasSymbolAsReferenced(symbol); + } + } + function checkIdentifier(node: Identifier): Type { const symbol = getResolvedSymbol(node); if (symbol === unknownSymbol) { @@ -13151,9 +13157,9 @@ namespace ts { } // We should only mark aliases as referenced if there isn't a local value declaration - // for the symbol. - if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { - markAliasSymbolAsReferenced(symbol); + // for the symbol. Also, don't mark any property access expression LHS - checkPropertyAccessExpression will handle that + if (!(node.parent && isPropertyAccessExpression(node.parent) && node.parent.expression === node)) { + markAliasReferenced(symbol, node); } const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); @@ -15711,15 +15717,20 @@ namespace ts { function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { let propType: Type; - let leftSymbol = getNodeLinks(left) && getNodeLinks(left).resolvedSymbol; - const leftWasReferenced = leftSymbol && getSymbolLinks(leftSymbol).referenced; const leftType = checkNonNullExpression(left); + const parentSymbol = getNodeLinks(left).resolvedSymbol; const apparentType = getApparentType(getWidenedType(leftType)); if (isTypeAny(apparentType) || apparentType === silentNeverType) { + if (isIdentifier(left) && parentSymbol) { + markAliasReferenced(parentSymbol, node); + } return apparentType; } const assignmentKind = getAssignmentTargetKind(node); const prop = getPropertyOfType(apparentType, right.escapedText); + if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) { + markAliasReferenced(parentSymbol, node); + } if (!prop) { const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String); if (!(indexInfo && indexInfo.type)) { @@ -15736,13 +15747,6 @@ namespace ts { else { checkPropertyNotUsedBeforeDeclaration(prop, node, right); markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword); - // Reset the referenced-ness of the LHS expression if this access refers to a const enum or const enum only module - leftSymbol = getNodeLinks(left) && getNodeLinks(left).resolvedSymbol; - if (leftSymbol && !leftWasReferenced && getSymbolLinks(leftSymbol).referenced && - !(isNonLocalAlias(leftSymbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(prop)) - ) { - getSymbolLinks(leftSymbol).referenced = undefined; - } getNodeLinks(node).resolvedSymbol = prop; checkPropertyAccessibility(node, left, apparentType, prop); if (assignmentKind) { diff --git a/tests/baselines/reference/importAliasFromNamespace.js b/tests/baselines/reference/importAliasFromNamespace.js new file mode 100644 index 00000000000..57bfab58d9a --- /dev/null +++ b/tests/baselines/reference/importAliasFromNamespace.js @@ -0,0 +1,68 @@ +//// [tests/cases/compiler/importAliasFromNamespace.ts] //// + +//// [internal.ts] +namespace My.Internal { + export function getThing(): void {} + export const enum WhichThing { + A, B, C + } +} + +//// [usage.ts] +/// +namespace SomeOther.Thing { + import Internal = My.Internal; + export class Foo { + private _which: Internal.WhichThing; + constructor() { + Internal.getThing(); + Internal.WhichThing.A ? "foo" : "bar"; + } + } +} + +//// [internal.js] +var My; +(function (My) { + var Internal; + (function (Internal) { + function getThing() { } + Internal.getThing = getThing; + })(Internal = My.Internal || (My.Internal = {})); +})(My || (My = {})); +//// [usage.js] +/// +var SomeOther; +(function (SomeOther) { + var Thing; + (function (Thing) { + var Internal = My.Internal; + var Foo = /** @class */ (function () { + function Foo() { + Internal.getThing(); + 0 /* A */ ? "foo" : "bar"; + } + return Foo; + }()); + Thing.Foo = Foo; + })(Thing = SomeOther.Thing || (SomeOther.Thing = {})); +})(SomeOther || (SomeOther = {})); + + +//// [internal.d.ts] +declare namespace My.Internal { + function getThing(): void; + const enum WhichThing { + A = 0, + B = 1, + C = 2, + } +} +//// [usage.d.ts] +/// +declare namespace SomeOther.Thing { + class Foo { + private _which; + constructor(); + } +} diff --git a/tests/baselines/reference/importAliasFromNamespace.symbols b/tests/baselines/reference/importAliasFromNamespace.symbols new file mode 100644 index 00000000000..11cbb52d895 --- /dev/null +++ b/tests/baselines/reference/importAliasFromNamespace.symbols @@ -0,0 +1,52 @@ +=== tests/cases/compiler/usage.ts === +/// +namespace SomeOther.Thing { +>SomeOther : Symbol(SomeOther, Decl(usage.ts, 0, 0)) +>Thing : Symbol(Thing, Decl(usage.ts, 1, 20)) + + import Internal = My.Internal; +>Internal : Symbol(Internal, Decl(usage.ts, 1, 27)) +>My : Symbol(My, Decl(internal.ts, 0, 0)) +>Internal : Symbol(Internal, Decl(internal.ts, 0, 13)) + + export class Foo { +>Foo : Symbol(Foo, Decl(usage.ts, 2, 34)) + + private _which: Internal.WhichThing; +>_which : Symbol(Foo._which, Decl(usage.ts, 3, 22)) +>Internal : Symbol(Internal, Decl(usage.ts, 1, 27)) +>WhichThing : Symbol(Internal.WhichThing, Decl(internal.ts, 1, 39)) + + constructor() { + Internal.getThing(); +>Internal.getThing : Symbol(Internal.getThing, Decl(internal.ts, 0, 23)) +>Internal : Symbol(Internal, Decl(usage.ts, 1, 27)) +>getThing : Symbol(Internal.getThing, Decl(internal.ts, 0, 23)) + + Internal.WhichThing.A ? "foo" : "bar"; +>Internal.WhichThing.A : Symbol(Internal.WhichThing.A, Decl(internal.ts, 2, 34)) +>Internal.WhichThing : Symbol(Internal.WhichThing, Decl(internal.ts, 1, 39)) +>Internal : Symbol(Internal, Decl(usage.ts, 1, 27)) +>WhichThing : Symbol(Internal.WhichThing, Decl(internal.ts, 1, 39)) +>A : Symbol(Internal.WhichThing.A, Decl(internal.ts, 2, 34)) + } + } +} +=== tests/cases/compiler/internal.ts === +namespace My.Internal { +>My : Symbol(My, Decl(internal.ts, 0, 0)) +>Internal : Symbol(Internal, Decl(internal.ts, 0, 13)) + + export function getThing(): void {} +>getThing : Symbol(getThing, Decl(internal.ts, 0, 23)) + + export const enum WhichThing { +>WhichThing : Symbol(WhichThing, Decl(internal.ts, 1, 39)) + + A, B, C +>A : Symbol(WhichThing.A, Decl(internal.ts, 2, 34)) +>B : Symbol(WhichThing.B, Decl(internal.ts, 3, 10)) +>C : Symbol(WhichThing.C, Decl(internal.ts, 3, 13)) + } +} + diff --git a/tests/baselines/reference/importAliasFromNamespace.types b/tests/baselines/reference/importAliasFromNamespace.types new file mode 100644 index 00000000000..9bc6cbf0d18 --- /dev/null +++ b/tests/baselines/reference/importAliasFromNamespace.types @@ -0,0 +1,56 @@ +=== tests/cases/compiler/usage.ts === +/// +namespace SomeOther.Thing { +>SomeOther : typeof SomeOther +>Thing : typeof Thing + + import Internal = My.Internal; +>Internal : typeof Internal +>My : typeof My +>Internal : typeof Internal + + export class Foo { +>Foo : Foo + + private _which: Internal.WhichThing; +>_which : Internal.WhichThing +>Internal : any +>WhichThing : Internal.WhichThing + + constructor() { + Internal.getThing(); +>Internal.getThing() : void +>Internal.getThing : () => void +>Internal : typeof Internal +>getThing : () => void + + Internal.WhichThing.A ? "foo" : "bar"; +>Internal.WhichThing.A ? "foo" : "bar" : "foo" | "bar" +>Internal.WhichThing.A : Internal.WhichThing.A +>Internal.WhichThing : typeof Internal.WhichThing +>Internal : typeof Internal +>WhichThing : typeof Internal.WhichThing +>A : Internal.WhichThing.A +>"foo" : "foo" +>"bar" : "bar" + } + } +} +=== tests/cases/compiler/internal.ts === +namespace My.Internal { +>My : typeof My +>Internal : typeof Internal + + export function getThing(): void {} +>getThing : () => void + + export const enum WhichThing { +>WhichThing : WhichThing + + A, B, C +>A : WhichThing.A +>B : WhichThing.B +>C : WhichThing.C + } +} + diff --git a/tests/cases/compiler/importAliasFromNamespace.ts b/tests/cases/compiler/importAliasFromNamespace.ts new file mode 100644 index 00000000000..c7958be7e2c --- /dev/null +++ b/tests/cases/compiler/importAliasFromNamespace.ts @@ -0,0 +1,21 @@ +// @declaration: true +// @filename: internal.ts +namespace My.Internal { + export function getThing(): void {} + export const enum WhichThing { + A, B, C + } +} + +// @filename: usage.ts +/// +namespace SomeOther.Thing { + import Internal = My.Internal; + export class Foo { + private _which: Internal.WhichThing; + constructor() { + Internal.getThing(); + Internal.WhichThing.A ? "foo" : "bar"; + } + } +} \ No newline at end of file