From 3519af0bab9c8b7b4ca612da0cb7b5693f28713a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 25 Oct 2021 10:53:41 -0700 Subject: [PATCH] Fix crash pulling on global types before they're initialized (#46471) * Add failing test * Dumb fix * Compute error message info more lazily * One more laziness --- src/compiler/checker.ts | 39 ++++++++++++++----- .../importEqualsError45874.errors.txt | 15 +++++++ .../reference/importEqualsError45874.js | 21 ++++++++++ .../reference/importEqualsError45874.symbols | 18 +++++++++ .../reference/importEqualsError45874.types | 22 +++++++++++ .../cases/compiler/importEqualsError45874.ts | 9 +++++ 6 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/importEqualsError45874.errors.txt create mode 100644 tests/baselines/reference/importEqualsError45874.js create mode 100644 tests/baselines/reference/importEqualsError45874.symbols create mode 100644 tests/baselines/reference/importEqualsError45874.types create mode 100644 tests/cases/compiler/importEqualsError45874.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 217eb47cfa6..80122a58463 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3275,21 +3275,40 @@ namespace ts { const namespaceName = getFullyQualifiedName(namespace); const declarationName = declarationNameToString(right); const suggestionForNonexistentModule = getSuggestedSymbolForNonexistentModule(right, namespace); - const exportedTypeSymbol = getMergedSymbol(getSymbol(getExportsOfSymbol(namespace), right.escapedText, SymbolFlags.Type)); - const containingQualifiedName = isQualifiedName(name) && getContainingQualifiedNameNode(name); - const canSuggestTypeof = containingQualifiedName && !isTypeOfExpression(containingQualifiedName.parent) && tryGetQualifiedNameAsValue(containingQualifiedName); if (suggestionForNonexistentModule) { error(right, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, symbolToString(suggestionForNonexistentModule)); + return undefined; } - else if (canSuggestTypeof) { - error(containingQualifiedName, Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, entityNameToString(containingQualifiedName)); + + const containingQualifiedName = isQualifiedName(name) && getContainingQualifiedNameNode(name); + const canSuggestTypeof = globalObjectType // <-- can't pull on types if global types aren't initialized yet + && (meaning & SymbolFlags.Type) + && containingQualifiedName + && !isTypeOfExpression(containingQualifiedName.parent) + && tryGetQualifiedNameAsValue(containingQualifiedName); + if (canSuggestTypeof) { + error( + containingQualifiedName, + Diagnostics._0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, + entityNameToString(containingQualifiedName) + ); + return undefined; } - else if (meaning & SymbolFlags.Namespace && exportedTypeSymbol && isQualifiedName(name.parent)) { - error(name.parent.right, Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, symbolToString(exportedTypeSymbol), unescapeLeadingUnderscores(name.parent.right.escapedText)); - } - else { - error(right, Diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName); + + if (meaning & SymbolFlags.Namespace && isQualifiedName(name.parent)) { + const exportedTypeSymbol = getMergedSymbol(getSymbol(getExportsOfSymbol(namespace), right.escapedText, SymbolFlags.Type)); + if (exportedTypeSymbol) { + error( + name.parent.right, + Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, + symbolToString(exportedTypeSymbol), + unescapeLeadingUnderscores(name.parent.right.escapedText) + ); + return undefined; + } } + + error(right, Diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName); } return undefined; } diff --git a/tests/baselines/reference/importEqualsError45874.errors.txt b/tests/baselines/reference/importEqualsError45874.errors.txt new file mode 100644 index 00000000000..82718c71c3f --- /dev/null +++ b/tests/baselines/reference/importEqualsError45874.errors.txt @@ -0,0 +1,15 @@ +/globals.ts(5,22): error TS2694: Namespace 'globals' has no exported member 'toString'. + + +==== /globals.ts (1 errors) ==== + namespace globals { + export type Foo = {}; + export const Bar = {}; + } + import Foo = globals.toString.Blah; + ~~~~~~~~ +!!! error TS2694: Namespace 'globals' has no exported member 'toString'. + +==== /index.ts (0 errors) ==== + const Foo = {}; + \ No newline at end of file diff --git a/tests/baselines/reference/importEqualsError45874.js b/tests/baselines/reference/importEqualsError45874.js new file mode 100644 index 00000000000..7f153fe422f --- /dev/null +++ b/tests/baselines/reference/importEqualsError45874.js @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/importEqualsError45874.ts] //// + +//// [globals.ts] +namespace globals { + export type Foo = {}; + export const Bar = {}; +} +import Foo = globals.toString.Blah; + +//// [index.ts] +const Foo = {}; + + +//// [globals.js] +var globals; +(function (globals) { + globals.Bar = {}; +})(globals || (globals = {})); +var Foo = globals.toString.Blah; +//// [index.js] +var Foo = {}; diff --git a/tests/baselines/reference/importEqualsError45874.symbols b/tests/baselines/reference/importEqualsError45874.symbols new file mode 100644 index 00000000000..59fb03ce510 --- /dev/null +++ b/tests/baselines/reference/importEqualsError45874.symbols @@ -0,0 +1,18 @@ +=== /globals.ts === +namespace globals { +>globals : Symbol(globals, Decl(globals.ts, 0, 0)) + + export type Foo = {}; +>Foo : Symbol(Foo, Decl(globals.ts, 0, 19)) + + export const Bar = {}; +>Bar : Symbol(Bar, Decl(globals.ts, 2, 14)) +} +import Foo = globals.toString.Blah; +>Foo : Symbol(Foo, Decl(globals.ts, 3, 1)) +>globals : Symbol(globals, Decl(globals.ts, 0, 0)) + +=== /index.ts === +const Foo = {}; +>Foo : Symbol(Foo, Decl(index.ts, 0, 5)) + diff --git a/tests/baselines/reference/importEqualsError45874.types b/tests/baselines/reference/importEqualsError45874.types new file mode 100644 index 00000000000..9e3c9c2945e --- /dev/null +++ b/tests/baselines/reference/importEqualsError45874.types @@ -0,0 +1,22 @@ +=== /globals.ts === +namespace globals { +>globals : typeof globals + + export type Foo = {}; +>Foo : Foo + + export const Bar = {}; +>Bar : {} +>{} : {} +} +import Foo = globals.toString.Blah; +>Foo : any +>globals : typeof globals +>toString : any +>Blah : any + +=== /index.ts === +const Foo = {}; +>Foo : {} +>{} : {} + diff --git a/tests/cases/compiler/importEqualsError45874.ts b/tests/cases/compiler/importEqualsError45874.ts new file mode 100644 index 00000000000..5bb214ee22a --- /dev/null +++ b/tests/cases/compiler/importEqualsError45874.ts @@ -0,0 +1,9 @@ +// @Filename: /globals.ts +namespace globals { + export type Foo = {}; + export const Bar = {}; +} +import Foo = globals.toString.Blah; + +// @Filename: /index.ts +const Foo = {};