Skip to content

Commit

Permalink
Feat/quick fix for types (#42126)
Browse files Browse the repository at this point in the history
* feat: add quick fix for types

* feat: add test case for quick fix of types

* feat: add did-you-mean error when Cannot_find_name_0 and Cannot_find_namespace_0

* feat: add Cannot_find_namespace_0_Did_you_mean_1 error and only suggest when resolve type

* feat: update baselines

* feat: update baselines

* feat: update baselines

* chore: fix style problem

* Always suggest spelling corrections

* suggest primitives instead of their wrappers

* Add primitives to suggestions

Instead of altering wrappers to look like primitives.

* add semicolons

* revert unneeded change

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
  • Loading branch information
chenjigeng and sandersn committed Oct 5, 2021
1 parent ee24e2e commit d60747f
Show file tree
Hide file tree
Showing 16 changed files with 95 additions and 39 deletions.
34 changes: 23 additions & 11 deletions src/compiler/checker.ts
Expand Up @@ -1764,9 +1764,8 @@ namespace ts {
nameNotFoundMessage: DiagnosticMessage | undefined,
nameArg: __String | Identifier | undefined,
isUse: boolean,
excludeGlobals = false,
issueSuggestions?: boolean): Symbol | undefined {
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSymbol, issueSuggestions);
excludeGlobals = false): Symbol | undefined {
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSymbol);
}

function resolveNameHelper(
Expand All @@ -1777,7 +1776,7 @@ namespace ts {
nameArg: __String | Identifier | undefined,
isUse: boolean,
excludeGlobals: boolean,
lookup: typeof getSymbol, issueSuggestions?: boolean): Symbol | undefined {
lookup: typeof getSymbol): Symbol | undefined {
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
let result: Symbol | undefined;
let lastLocation: Node | undefined;
Expand Down Expand Up @@ -2116,7 +2115,7 @@ namespace ts {
!checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) &&
!checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
let suggestion: Symbol | undefined;
if (issueSuggestions && suggestionCount < maximumSuggestionCount) {
if (suggestionCount < maximumSuggestionCount) {
suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning);
const isGlobalScopeAugmentationDeclaration = suggestion?.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration);
if (isGlobalScopeAugmentationDeclaration) {
Expand All @@ -2125,10 +2124,11 @@ namespace ts {
if (suggestion) {
const suggestionName = symbolToString(suggestion);
const isUncheckedJS = isUncheckedJSSuggestion(originalLocation, suggestion, /*excludeClasses*/ false);
const message = isUncheckedJS ? Diagnostics.Could_not_find_name_0_Did_you_mean_1 : Diagnostics.Cannot_find_name_0_Did_you_mean_1;
const message = meaning === SymbolFlags.Namespace || nameArg && typeof nameArg !== "string" && nodeIsSynthesized(nameArg) ? Diagnostics.Cannot_find_namespace_0_Did_you_mean_1
: isUncheckedJS ? Diagnostics.Could_not_find_name_0_Did_you_mean_1
: Diagnostics.Cannot_find_name_0_Did_you_mean_1;
const diagnostic = createError(errorLocation, message, diagnosticName(nameArg!), suggestionName);
addErrorOrSuggestion(!isUncheckedJS, diagnostic);

if (suggestion.valueDeclaration) {
addRelatedInfo(
diagnostic,
Expand Down Expand Up @@ -3238,7 +3238,7 @@ namespace ts {
if (name.kind === SyntaxKind.Identifier) {
const message = meaning === namespaceMeaning || nodeIsSynthesized(name) ? Diagnostics.Cannot_find_namespace_0 : getCannotFindNameDiagnosticForName(getFirstIdentifier(name));
const symbolFromJSPrototype = isInJSFile(name) && !nodeIsSynthesized(name) ? resolveEntityNameFromAssignmentDeclaration(name, meaning) : undefined;
symbol = getMergedSymbol(resolveName(location || name, name.escapedText, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, name, /*isUse*/ true));
symbol = getMergedSymbol(resolveName(location || name, name.escapedText, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, name, /*isUse*/ true, false));
if (!symbol) {
return getMergedSymbol(symbolFromJSPrototype);
}
Expand Down Expand Up @@ -22449,8 +22449,7 @@ namespace ts {
getCannotFindNameDiagnosticForName(node),
node,
!isWriteOnlyAccess(node),
/*excludeGlobals*/ false,
/*issueSuggestions*/ true) || unknownSymbol;
/*excludeGlobals*/ false) || unknownSymbol;
}
return links.resolvedSymbol;
}
Expand Down Expand Up @@ -28535,7 +28534,20 @@ namespace ts {
// Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function
// So the table *contains* `x` but `x` isn't actually in scope.
// However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion.
return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning);
if (symbol) return symbol;
let candidates: Symbol[];
if (symbols === globals) {
const primitives = mapDefined(
["string", "number", "boolean", "object", "bigint", "symbol"],
s => symbols.has((s.charAt(0).toUpperCase() + s.slice(1)) as __String)
? createSymbol(SymbolFlags.TypeAlias, s as __String) as Symbol
: undefined);
candidates = primitives.concat(arrayFrom(symbols.values()));
}
else {
candidates = arrayFrom(symbols.values());
}
return getSpellingSuggestionForName(unescapeLeadingUnderscores(name), candidates, meaning);
});
return result;
}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -3337,6 +3337,10 @@
"category": "Error",
"code": 2822
},
"Cannot find namespace '{0}'. Did you mean '{1}'?": {
"category": "Error",
"code": 2833
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
1 change: 1 addition & 0 deletions src/services/codefixes/fixSpelling.ts
Expand Up @@ -6,6 +6,7 @@ namespace ts.codefix {
Diagnostics.Property_0_may_not_exist_on_type_1_Did_you_mean_2.code,
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code,
Diagnostics.Could_not_find_name_0_Did_you_mean_1.code,
Diagnostics.Cannot_find_namespace_0_Did_you_mean_1.code,
Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code,
Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code,
Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2.code,
Expand Down
@@ -1,10 +1,13 @@
tests/cases/compiler/declarationEmitUnknownImport.ts(1,1): error TS2303: Circular definition of import alias 'Foo'.
tests/cases/compiler/declarationEmitUnknownImport.ts(1,14): error TS2304: Cannot find name 'SomeNonExistingName'.
tests/cases/compiler/declarationEmitUnknownImport.ts(1,14): error TS2503: Cannot find namespace 'SomeNonExistingName'.
tests/cases/compiler/declarationEmitUnknownImport.ts(1,14): error TS4000: Import declaration 'Foo' is using private name 'SomeNonExistingName'.


==== tests/cases/compiler/declarationEmitUnknownImport.ts (3 errors) ====
==== tests/cases/compiler/declarationEmitUnknownImport.ts (4 errors) ====
import Foo = SomeNonExistingName
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2303: Circular definition of import alias 'Foo'.
~~~~~~~~~~~~~~~~~~~
!!! error TS2304: Cannot find name 'SomeNonExistingName'.
~~~~~~~~~~~~~~~~~~~
Expand Down
@@ -1,12 +1,15 @@
tests/cases/compiler/declarationEmitUnknownImport2.ts(1,1): error TS2303: Circular definition of import alias 'Foo'.
tests/cases/compiler/declarationEmitUnknownImport2.ts(1,12): error TS1005: '=' expected.
tests/cases/compiler/declarationEmitUnknownImport2.ts(1,12): error TS2304: Cannot find name 'From'.
tests/cases/compiler/declarationEmitUnknownImport2.ts(1,12): error TS2503: Cannot find namespace 'From'.
tests/cases/compiler/declarationEmitUnknownImport2.ts(1,12): error TS4000: Import declaration 'Foo' is using private name 'From'.
tests/cases/compiler/declarationEmitUnknownImport2.ts(1,17): error TS1005: ';' expected.


==== tests/cases/compiler/declarationEmitUnknownImport2.ts (5 errors) ====
==== tests/cases/compiler/declarationEmitUnknownImport2.ts (6 errors) ====
import Foo From './Foo'; // Syntax error
~~~~~~~~~~~~~~~
!!! error TS2303: Circular definition of import alias 'Foo'.
~~~~
!!! error TS1005: '=' expected.
~~~~
Expand Down
10 changes: 6 additions & 4 deletions tests/baselines/reference/extendArray.errors.txt
@@ -1,5 +1,5 @@
tests/cases/compiler/extendArray.ts(7,19): error TS2304: Cannot find name '_element'.
tests/cases/compiler/extendArray.ts(7,32): error TS2304: Cannot find name '_element'.
tests/cases/compiler/extendArray.ts(7,19): error TS2552: Cannot find name '_element'. Did you mean 'Element'?
tests/cases/compiler/extendArray.ts(7,32): error TS2552: Cannot find name '_element'. Did you mean 'Element'?


==== tests/cases/compiler/extendArray.ts (2 errors) ====
Expand All @@ -11,9 +11,11 @@ tests/cases/compiler/extendArray.ts(7,32): error TS2304: Cannot find name '_elem
interface Array {
collect(fn:(e:_element) => _element[]) : any[];
~~~~~~~~
!!! error TS2304: Cannot find name '_element'.
!!! error TS2552: Cannot find name '_element'. Did you mean 'Element'?
!!! related TS2728 /.ts/lib.dom.d.ts:4792:13: 'Element' is declared here.
~~~~~~~~
!!! error TS2304: Cannot find name '_element'.
!!! error TS2552: Cannot find name '_element'. Did you mean 'Element'?
!!! related TS2728 /.ts/lib.dom.d.ts:4792:13: 'Element' is declared here.
}
}

Expand Down
@@ -1,4 +1,4 @@
tests/cases/compiler/importedModuleAddToGlobal.ts(15,23): error TS2503: Cannot find namespace 'b'.
tests/cases/compiler/importedModuleAddToGlobal.ts(15,23): error TS2833: Cannot find namespace 'b'. Did you mean 'B'?


==== tests/cases/compiler/importedModuleAddToGlobal.ts (1 errors) ====
Expand All @@ -18,5 +18,6 @@ tests/cases/compiler/importedModuleAddToGlobal.ts(15,23): error TS2503: Cannot f
import a = A;
function hello(): b.B { return null; }
~
!!! error TS2503: Cannot find namespace 'b'.
!!! error TS2833: Cannot find namespace 'b'. Did you mean 'B'?
!!! related TS2728 tests/cases/compiler/importedModuleAddToGlobal.ts:8:8: 'B' is declared here.
}
@@ -1,6 +1,6 @@
tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedModule.ts(2,18): error TS2300: Duplicate identifier 'Point'.
tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedModule.ts(3,16): error TS2300: Duplicate identifier 'Point'.
tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedModule.ts(12,8): error TS2503: Cannot find namespace 'm'.
tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedModule.ts(12,8): error TS2833: Cannot find namespace 'm'. Did you mean 'M'?


==== tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedModule.ts (3 errors) ====
Expand All @@ -21,7 +21,8 @@ tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedMo
var m = M2;
var p: m.Point; // Error
~
!!! error TS2503: Cannot find namespace 'm'.
!!! error TS2833: Cannot find namespace 'm'. Did you mean 'M'?
!!! related TS2728 tests/cases/conformance/internalModules/moduleDeclarations/invalidInstantiatedModule.ts:1:8: 'M' is declared here.



4 changes: 2 additions & 2 deletions tests/baselines/reference/jsdocPropertyTagInvalid.errors.txt
@@ -1,12 +1,12 @@
/a.js(3,15): error TS2304: Cannot find name 'sting'.
/a.js(3,15): error TS2552: Cannot find name 'sting'. Did you mean 'string'?


==== /a.js (1 errors) ====
/**
* @typedef MyType
* @property {sting} [x]
~~~~~
!!! error TS2304: Cannot find name 'sting'.
!!! error TS2552: Cannot find name 'sting'. Did you mean 'string'?
*/

/** @param {MyType} p */
Expand Down
@@ -1,4 +1,4 @@
tests/cases/compiler/test.tsx(9,17): error TS2304: Cannot find name 'createElement'.
tests/cases/compiler/test.tsx(9,17): error TS2552: Cannot find name 'createElement'. Did you mean 'frameElement'?


==== tests/cases/compiler/test.tsx (1 errors) ====
Expand All @@ -12,7 +12,8 @@ tests/cases/compiler/test.tsx(9,17): error TS2304: Cannot find name 'createEleme
render() {
return <div />;
~~~
!!! error TS2304: Cannot find name 'createElement'.
!!! error TS2552: Cannot find name 'createElement'. Did you mean 'frameElement'?
!!! related TS2728 /.ts/lib.dom.d.ts:17075:13: 'frameElement' is declared here.
}
}

@@ -1,4 +1,4 @@
tests/cases/compiler/test.tsx(9,17): error TS2304: Cannot find name 'MyElement'.
tests/cases/compiler/test.tsx(9,17): error TS2552: Cannot find name 'MyElement'. Did you mean 'Element'?


==== tests/cases/compiler/test.tsx (1 errors) ====
Expand All @@ -12,6 +12,7 @@ tests/cases/compiler/test.tsx(9,17): error TS2304: Cannot find name 'MyElement'.
render(createElement) {
return <div />;
~~~
!!! error TS2304: Cannot find name 'MyElement'.
!!! error TS2552: Cannot find name 'MyElement'. Did you mean 'Element'?
!!! related TS2728 /.ts/lib.dom.d.ts:4792:13: 'Element' is declared here.
}
}
@@ -1,11 +1,11 @@
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnfinishedTypeNameBeforeKeyword1.ts(1,8): error TS2503: Cannot find namespace 'TypeModule1'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnfinishedTypeNameBeforeKeyword1.ts(1,8): error TS2833: Cannot find namespace 'TypeModule1'. Did you mean 'TypeModule2'?
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnfinishedTypeNameBeforeKeyword1.ts(1,20): error TS1003: Identifier expected.


==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnfinishedTypeNameBeforeKeyword1.ts (2 errors) ====
var x: TypeModule1.
~~~~~~~~~~~
!!! error TS2503: Cannot find namespace 'TypeModule1'.
!!! error TS2833: Cannot find namespace 'TypeModule1'. Did you mean 'TypeModule2'?

!!! error TS1003: Identifier expected.
module TypeModule2 {
Expand Down
@@ -1,14 +1,14 @@
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric1.ts(2,23): error TS2304: Cannot find name 'IPromise'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric1.ts(2,45): error TS2304: Cannot find name 'IPromise'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric1.ts(2,23): error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric1.ts(2,45): error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric1.ts(2,54): error TS1005: '>' expected.


==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric1.ts (3 errors) ====
interface IQService {
all(promises: IPromise < any > []): IPromise<
~~~~~~~~
!!! error TS2304: Cannot find name 'IPromise'.
!!! error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?
~~~~~~~~
!!! error TS2304: Cannot find name 'IPromise'.
!!! error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?

!!! error TS1005: '>' expected.
Expand Up @@ -10,8 +10,8 @@ tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGener
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(4,37): error TS2693: 'any' only refers to a type, but is being used as a value here.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(4,41): error TS1005: ';' expected.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(4,43): error TS2693: 'any' only refers to a type, but is being used as a value here.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(8,23): error TS2304: Cannot find name 'IPromise'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(8,45): error TS2304: Cannot find name 'IPromise'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(8,23): error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(8,45): error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGeneric2.ts(8,54): error TS1005: '>' expected.


Expand Down Expand Up @@ -49,8 +49,8 @@ tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserUnterminatedGener
interface IQService {
all(promises: IPromise < any > []): IPromise<
~~~~~~~~
!!! error TS2304: Cannot find name 'IPromise'.
!!! error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?
~~~~~~~~
!!! error TS2304: Cannot find name 'IPromise'.
!!! error TS2552: Cannot find name 'IPromise'. Did you mean 'Promise'?

!!! error TS1005: '>' expected.
5 changes: 3 additions & 2 deletions tests/baselines/reference/primaryExpressionMods.errors.txt
@@ -1,5 +1,5 @@
tests/cases/compiler/primaryExpressionMods.ts(7,8): error TS2709: Cannot use namespace 'M' as a type.
tests/cases/compiler/primaryExpressionMods.ts(11,8): error TS2503: Cannot find namespace 'm'.
tests/cases/compiler/primaryExpressionMods.ts(11,8): error TS2833: Cannot find namespace 'm'. Did you mean 'M'?


==== tests/cases/compiler/primaryExpressionMods.ts (2 errors) ====
Expand All @@ -17,5 +17,6 @@ tests/cases/compiler/primaryExpressionMods.ts(11,8): error TS2503: Cannot find n
var x2 = m.a; // Same as M.a
var q: m.P; // Error
~
!!! error TS2503: Cannot find namespace 'm'.
!!! error TS2833: Cannot find namespace 'm'. Did you mean 'M'?
!!! related TS2728 tests/cases/compiler/primaryExpressionMods.ts:1:8: 'M' is declared here.

26 changes: 26 additions & 0 deletions tests/cases/fourslash/codeFixSpellingCaseSensitive4.ts
@@ -0,0 +1,26 @@
/// <reference path='fourslash.ts' />

//// declare let a: numbers;
//// declare let b: Numbers;
//// declare let c: objects;
//// declare let d: Objects;
//// declare let e: RegEx;
//// namespace yadda {
//// export type Thing = string;
//// }
//// let f: yaddas.Thing;

verify.codeFixAll({
fixId: "fixSpelling",
fixAllDescription: "Fix all detected spelling errors",
newFileContent:
`declare let a: number;
declare let b: Number;
declare let c: object;
declare let d: Object;
declare let e: RegExp;
namespace yadda {
export type Thing = string;
}
let f: yadda.Thing;`,
});

0 comments on commit d60747f

Please sign in to comment.