Skip to content

Conversation

mkantor
Copy link
Contributor

@mkantor mkantor commented Jan 8, 2021

Avoid suggesting the global keyword (from global scope augmentation declarations) as typo fix when resolving names.

Fixes #42209.

@ghost
Copy link

ghost commented Jan 8, 2021

CLA assistant check
All CLA requirements met.

@typescript-bot typescript-bot added the For Backlog Bug PRs that fix a backlog bug label Jan 8, 2021
@mkantor
Copy link
Contributor Author

mkantor commented Jan 8, 2021

As I noted in a comment on #42209 this is my first foray into the compiler so I'm not sure this is the best approach. This check could also happen inside getSuggestedSymbolForNonexistentSymbol, for example, or maybe there's a more general notion of "things that shouldn't be suggested for value positions" that should be captured somewhere, or maybe some other fix I haven't considered.

Let me know!

@@ -0,0 +1,3 @@
export {}
declare global { const x: any }
global.x // should not suggest `global` (GH#42209)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, it should probably suggest globalThis.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you miss my reply after you suggested that on the issue?

globalThis.x is not valid here.

Copy link
Contributor

@ExE-Boss ExE-Boss Jan 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is valid when you do:

declare global { var x: any }

So that case should be covered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make sure we're on the same page:

Are you saying spellingSuggestionGlobal1.ts (as written, with const x) should produce the error message Cannot find name 'global'. Did you mean 'globalThis'??

If so, I disagree. Users will follow that suggestion and change global.x to globalThis.x, then get another error (Property 'x' does not exist on type 'typeof globalThis'.), then be confused/frustrated and have to research why it doesn't work, then probably end up ignoring the suggestion anyway and write x instead of globalThis.x.

Or are you saying that in situations where x is a known property of globalThis and the user writes global.x that it would be nice to say Cannot find name 'global'. Did you mean 'globalThis'??

If so, I agree, but unfortunately that would be tricky to implement (unless I'm missing something). I'd bet that this situation occurs rarely in the wild, so it might not be worth the complexity cost to maintain a dedicated suggestion for it. I could be wrong, though.

In any case it seems orthogonal to the bugfix in this pull request. Currently if you write something like global.Math the error message won't suggest globalThis, either.


Regardless, I should add a test case that uses var in the global declaration. I'll push a commit for that soon.

// Avoid suggesting "global" if it refers to a global scope augmentation declaration.
if (suggestion && suggestion.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration)) {
suggestion = undefined;
}
if (suggestion) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since getSuggestedLibForNonExistentName doesn't make sense for global, I would just invert the predicate and move it here. Or possibly return undefined.

Either way, I think it would be better to extract the predicate to its own variable -- that way you can probably delete the comment on the first line of the change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that getSuggestedLibForNonExistentName doesn't make sense for global, but execution needs to fall through to the else case on line 2049/2053 in order to get any error message at all.

I don't like overwriting suggestion in-place either, though. Maybe it'd be better to duplicate the error call up here?

const isGlobalScopeAugmentationDeclaration = suggestion && suggestion.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration);
if (isGlobalScopeAugmentationDeclaration) {
    if (nameArg) {
        error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
    }
}
else if (suggestion) {
    // ...
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooh, I forgot that there weren't any other error calls afterward. I guess it's better to keep it the way it is.

@@ -2028,6 +2028,10 @@ namespace ts {
let suggestion: Symbol | undefined;
if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) {
suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning);
const isGlobalScopeAugmentationDeclaration = suggestion && suggestion.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration);
Copy link
Contributor Author

@mkantor mkantor Jan 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In hindsight I wish I'd named this suggestionIs..., but the name is already obnoxiously long.

Maybe suggestionIsGlobalDeclaration? suggestionIsGlobalModule? Something else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in context, I think this is a pretty good name.

@@ -2028,6 +2028,10 @@ namespace ts {
let suggestion: Symbol | undefined;
if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) {
suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning);
const isGlobalScopeAugmentationDeclaration = suggestion && suggestion.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in context, I think this is a pretty good name.

// Avoid suggesting "global" if it refers to a global scope augmentation declaration.
if (suggestion && suggestion.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration)) {
suggestion = undefined;
}
if (suggestion) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooh, I forgot that there weren't any other error calls afterward. I guess it's better to keep it the way it is.

@sandersn sandersn merged commit 424b805 into microsoft:master Jan 20, 2021
@mkantor mkantor deleted the global-suggestion branch January 22, 2021 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Incorrect suggestion: "Cannot find name 'global'. Did you mean 'global'?"
4 participants