From c850390991786be8a9d68a7c230cd53b8c554fcd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:59:29 -0700 Subject: [PATCH 1/4] More did-you-mean errors on classes in plain JS Classes - that are declared with class declarations or expressions, but not constructor functions - and which have nothing in their heritage clauses Now provide spelling suggestions on misspelled property accesses. Because of JS support for assignment-as-declaration, it's still easy to mistakenly declare a property with a typo and not get any suggestions. --- src/compiler/checker.ts | 5 +- tests/cases/fourslash/codeFixSpellingJs9.ts | 56 ++++++++++++++++++--- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aae152cacaf2a..bc20377379485 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29277,9 +29277,10 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") if (file) { if (compilerOptions.checkJs === undefined && file.checkJsDirective === undefined && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); + const suggestionHasNoExtends = !suggestion?.valueDeclaration || !isClassLike(suggestion.valueDeclaration) || suggestion.valueDeclaration.heritageClauses?.length return !(file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile)) - && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class) - && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword); + && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class && suggestionHasNoExtends) + && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword && suggestionHasNoExtends); } } return false; diff --git a/tests/cases/fourslash/codeFixSpellingJs9.ts b/tests/cases/fourslash/codeFixSpellingJs9.ts index 74d78c827c243..bbdc16d99d643 100644 --- a/tests/cases/fourslash/codeFixSpellingJs9.ts +++ b/tests/cases/fourslash/codeFixSpellingJs9.ts @@ -1,10 +1,52 @@ /// +// @allowJs: true +// @Filename: codeFixSpellingJs9.js +//// class C { +//// numble = 1 +//// mumble() { +//// return this.[|numbles|] +//// } +//// } +//// class D extends C { } +//// const c = new C() +//// c.[|numbles|] = 3 +//// c.[|mumbles|]() +//// const d = new D() +//// d.[|numbles|] = 4 +//// d.[|mumbles()|] +//// class Person { +//// getFavoriteColor() { +//// +//// } +//// } +//// +//// const person = new Person(); +//// person.[|getFavoriteColour|](); +//// person.[|getFavoriteColoxr|](); +verify.codeFixAll({ + fixId: "fixSpelling", + fixAllDescription: "Fix all detected spelling errors", + newFileContent: + `class C { + numble = 1 + mumble() { + return this.numble + } +} +class D extends C { } +const c = new C() +c.numble = 3 +c.mumble() +const d = new D() +d.numbles = 4 +d.mumbles() +class Person { + getFavoriteColor() { -// @allowjs: true -// @noEmit: true + } +} -// @filename: noSuggestionWithoutDidYouMean.js -//// let a = {}; -//// console.log(a.apple); -verify.noErrors() -verify.getSuggestionDiagnostics([]) +const person = new Person(); +person.getFavoriteColor(); +person.getFavoriteColor();`, +}); From 1b723014fcff8acdf6134da21170cbd214beb11b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:32:41 -0700 Subject: [PATCH 2/4] fix semicolon lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc20377379485..4e8a2f06fffe3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -29277,7 +29277,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") if (file) { if (compilerOptions.checkJs === undefined && file.checkJsDirective === undefined && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); - const suggestionHasNoExtends = !suggestion?.valueDeclaration || !isClassLike(suggestion.valueDeclaration) || suggestion.valueDeclaration.heritageClauses?.length + const suggestionHasNoExtends = !suggestion?.valueDeclaration || !isClassLike(suggestion.valueDeclaration) || suggestion.valueDeclaration.heritageClauses?.length; return !(file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile)) && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class && suggestionHasNoExtends) && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword && suggestionHasNoExtends); From 19e4a47c636ff9f276c7fcd6428ec75c5c9e703c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:28:29 -0700 Subject: [PATCH 3/4] Post-merge update --- src/compiler/checker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8af1cc2e36d0a..7357c63578852 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31683,9 +31683,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (file) { if (compilerOptions.checkJs === undefined && file.checkJsDirective === undefined && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); + const suggestionHasNoExtends = !suggestion?.valueDeclaration || !isClassLike(suggestion.valueDeclaration) || suggestion.valueDeclaration.heritageClauses?.length return !(file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile)) - && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class) - && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword); + && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class && suggestionHasNoExtends) + && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword && suggestionHasNoExtends); } } return false; From ce4726971899bcfc9443d80ff0d234d02266eb2c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 26 Jul 2023 09:29:01 -0700 Subject: [PATCH 4/4] Also exclude class with decorators Since decorators can introduce arbitrary properties --- src/compiler/checker.ts | 9 +++++--- tests/cases/fourslash/codeFixSpellingJs9.ts | 24 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7357c63578852..e3efdf4d237c4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31683,10 +31683,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (file) { if (compilerOptions.checkJs === undefined && file.checkJsDirective === undefined && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); - const suggestionHasNoExtends = !suggestion?.valueDeclaration || !isClassLike(suggestion.valueDeclaration) || suggestion.valueDeclaration.heritageClauses?.length + const suggestionHasNoExtendsOrDecorators = !suggestion?.valueDeclaration + || !isClassLike(suggestion.valueDeclaration) + || suggestion.valueDeclaration.heritageClauses?.length + || classOrConstructorParameterIsDecorated(/*useLegacyDecorators*/ false, suggestion.valueDeclaration); return !(file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile)) - && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class && suggestionHasNoExtends) - && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword && suggestionHasNoExtends); + && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class && suggestionHasNoExtendsOrDecorators) + && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword && suggestionHasNoExtendsOrDecorators); } } return false; diff --git a/tests/cases/fourslash/codeFixSpellingJs9.ts b/tests/cases/fourslash/codeFixSpellingJs9.ts index bbdc16d99d643..876d31caefac2 100644 --- a/tests/cases/fourslash/codeFixSpellingJs9.ts +++ b/tests/cases/fourslash/codeFixSpellingJs9.ts @@ -23,6 +23,17 @@ //// const person = new Person(); //// person.[|getFavoriteColour|](); //// person.[|getFavoriteColoxr|](); +//// function deco() { } +//// @deco +//// class Art { +//// style = true +//// } +//// const a = new Art() +//// a.[|stylo|] +//// @deco +//// class Double extends Art { } +//// const db = new Double() +//// db.[|stylo|] verify.codeFixAll({ fixId: "fixSpelling", fixAllDescription: "Fix all detected spelling errors", @@ -48,5 +59,16 @@ class Person { const person = new Person(); person.getFavoriteColor(); -person.getFavoriteColor();`, +person.getFavoriteColor(); +function deco() { } +@deco +class Art { + style = true +} +const a = new Art() +a.stylo +@deco +class Double extends Art { } +const db = new Double() +db.stylo`, });