From 141de3f570f05cdf6e9e90af99219c3f1bd02347 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 26 May 2021 10:11:48 -0700 Subject: [PATCH 01/16] Always issue cannot find name did-you-mean error This PR issues "cannot find ${name}, did you mean ${name}" errors for identifiers and propery access expressions in JS files *without* `// @ts-check` and without `// @ts-nocheck`. This brings some benefits of Typescript's binder to all Javascript users, even those who haven't opted into Typescript checking. ```js export var inModule = 1 inmodule.toFixed() // errors on exports function f() { var locals = 2 locale.toFixed() // errors on locals } var object = { spaaace: 3 } object.spaaaace // error on read object.spaace = 2 // error on write object.fresh = 12 // OK, no spelling correction to offer ``` To disable the errors, add `// @ts-nocheck` to the file. To get the normal checkJs experience, add `// @ts-check`. == Why This Works == In a word: precision. This change has low recall — it misses lots of correct errors that would be nice to show — but it has high precision: almost all the errors it shows are correct. And they come with a suggested correction. Here are the ingredients: 1. For unchecked JS files, the compiler suppresses all errors except two did-you-mean name resolution errors. 2. Did-you-mean spelling correction is already tuned for high precision/low recall, and doesn't show many bogus errors even in JS. 3. For identifiers, the error is suppressed for suggestions from global files. These are often DOM feature detection, for example. 4. For property accesses, the error is suppressed for suggestions from other files, for the same reason. 5. For property accesses, the error is suppressed for `this` property accesses because the compiler doesn't understand JS constructor functions well enough. In particular, it doesn't understand any inheritance patterns. == Work Remaining == 1. Code cleanup. 2. Fix a couple of failures in existing tests. 3. Suppress errors on property access suggestions from large objects. 4. Combine (3) and (4) above to suppress errors on suggestions from other, global files. 5. A little more testing on random files to make sure that precision is good there too. 6. Have people from the regular Code editor meeting test the code and suggest ideas. --- src/compiler/checker.ts | 39 ++++- src/compiler/program.ts | 15 +- .../reference/spellingCheckedJS.errors.txt | 83 +++++++++++ .../reference/spellingCheckedJS.symbols | 92 ++++++++++++ .../reference/spellingCheckedJS.types | 140 ++++++++++++++++++ .../reference/spellingNocheckJS.errors.txt | 53 +++++++ .../reference/spellingNocheckJS.symbols | 92 ++++++++++++ .../reference/spellingNocheckJS.types | 140 ++++++++++++++++++ .../reference/spellingUncheckedJS.errors.txt | 67 +++++++++ .../reference/spellingUncheckedJS.symbols | 91 ++++++++++++ .../reference/spellingUncheckedJS.types | 139 +++++++++++++++++ .../conformance/salsa/spellingCheckedJS.ts | 48 ++++++ .../conformance/salsa/spellingNocheckJS.ts | 48 ++++++ .../conformance/salsa/spellingUncheckedJS.ts | 47 ++++++ 14 files changed, 1081 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/spellingCheckedJS.errors.txt create mode 100644 tests/baselines/reference/spellingCheckedJS.symbols create mode 100644 tests/baselines/reference/spellingCheckedJS.types create mode 100644 tests/baselines/reference/spellingNocheckJS.errors.txt create mode 100644 tests/baselines/reference/spellingNocheckJS.symbols create mode 100644 tests/baselines/reference/spellingNocheckJS.types create mode 100644 tests/baselines/reference/spellingUncheckedJS.errors.txt create mode 100644 tests/baselines/reference/spellingUncheckedJS.symbols create mode 100644 tests/baselines/reference/spellingUncheckedJS.types create mode 100644 tests/cases/conformance/salsa/spellingCheckedJS.ts create mode 100644 tests/cases/conformance/salsa/spellingNocheckJS.ts create mode 100644 tests/cases/conformance/salsa/spellingUncheckedJS.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b9af8b6dd7395..04fcae05e9509 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2057,10 +2057,22 @@ 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); + const isGlobalScopeAugmentationDeclaration = suggestion?.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration); if (isGlobalScopeAugmentationDeclaration) { suggestion = undefined; } + // (1) when allowjs is on but checkjs is not, for JS[X] files that don't have either ts-check OR ts-nocheck + // skip suggestions when the suggestion comes from a global scope + // This will prevent it from always showing up + const file = getSourceFileOfNode(lastLocation) + const suggestionFile = getSourceFileOfNode(suggestion?.valueDeclaration) + if (file && suggestionFile) { + const isCheckJs = isCheckJsEnabledForFile(file, compilerOptions) + const isTsNoCheck = !!file.checkJsDirective && file.checkJsDirective.enabled === false + if (!isCheckJs && !isTsNoCheck && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX) && isGlobalSourceFile(suggestionFile) && file !== suggestionFile) { + suggestion = undefined + } + } if (suggestion) { const suggestionName = symbolToString(suggestion); const diagnostic = error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName); @@ -14570,22 +14582,33 @@ namespace ts { * Should all count as literals and not print errors on access or assignment of possibly existing properties. * This mirrors the behavior of the index signature propagation, to which this behaves similarly (but doesn't affect assignability or inference). */ - function isJSLiteralType(type: Type): boolean { + function isJSLiteralType(type: Type, reference?: Node): boolean { if (noImplicitAny) { return false; // Flag is meaningless under `noImplicitAny` mode } + // TODO: Move this check outside isJSLiteralType; it doesn't make sense here + const file = getSourceFileOfNode(reference) + if (file) { + const isCheckJs = isCheckJsEnabledForFile(file, compilerOptions) + const isTsNoCheck = !!file.checkJsDirective && file.checkJsDirective.enabled === false + if (!isCheckJs && !isTsNoCheck && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { + const parentFile = forEach(type.symbol?.declarations, getSourceFileOfNode) + return file !== parentFile || !!reference && isPropertyAccessExpression(reference) && reference.expression.kind === SyntaxKind.ThisKeyword + } + } + if (getObjectFlags(type) & ObjectFlags.JSLiteral) { return true; } if (type.flags & TypeFlags.Union) { - return every((type as UnionType).types, isJSLiteralType); + return every((type as UnionType).types, t => isJSLiteralType(t, reference)); } if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, isJSLiteralType); + return some((type as IntersectionType).types, t => isJSLiteralType(t, reference)); } if (type.flags & TypeFlags.Instantiable) { const constraint = getResolvedBaseConstraint(type); - return constraint !== type && isJSLiteralType(constraint); + return constraint !== type && isJSLiteralType(constraint, reference); } return false; } @@ -14684,7 +14707,7 @@ namespace ts { if (indexType.flags & TypeFlags.Never) { return neverType; } - if (isJSLiteralType(objectType)) { + if (isJSLiteralType(objectType, accessNode)) { return anyType; } if (accessExpression && !isConstEnumObjectType(objectType)) { @@ -14755,7 +14778,7 @@ namespace ts { return undefined; } } - if (isJSLiteralType(objectType)) { + if (isJSLiteralType(objectType, accessNode)) { return anyType; } if (accessNode) { @@ -27419,7 +27442,7 @@ namespace ts { if (!prop) { const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; if (!(indexInfo && indexInfo.type)) { - if (isJSLiteralType(leftType)) { + if (isJSLiteralType(leftType, node)) { return anyType; } if (leftType.symbol === globalThisSymbol) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index caf3ebb1c7d90..6385d947d65fb 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1916,11 +1916,16 @@ namespace ts { const isCheckJs = isCheckJsEnabledForFile(sourceFile, options); const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) - const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX || - sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred); - const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; - const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; - + const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX || sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX + || sourceFile.scriptKind === ScriptKind.External || sourceFile.scriptKind === ScriptKind.Deferred); + let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; + let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; + if (!isCheckJs && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX)) { + // TODO: It can just be checkDiagnostics srsly + // TODO: Recheck user tests and random compilation to see how much this adds + bindDiagnostics = bindDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) // MORE TO COME + checkDiagnostics = checkDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) + } return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined); }); } diff --git a/tests/baselines/reference/spellingCheckedJS.errors.txt b/tests/baselines/reference/spellingCheckedJS.errors.txt new file mode 100644 index 0000000000000..511ac91052e48 --- /dev/null +++ b/tests/baselines/reference/spellingCheckedJS.errors.txt @@ -0,0 +1,83 @@ +tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +tests/cases/conformance/salsa/spellingCheckedJS.js(3,1): error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? +tests/cases/conformance/salsa/spellingCheckedJS.js(7,5): error TS2552: Cannot find name 'locale'. Did you mean 'locals'? +tests/cases/conformance/salsa/spellingCheckedJS.js(13,21): error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? +tests/cases/conformance/salsa/spellingCheckedJS.js(19,22): error TS2339: Property 'none' does not exist on type 'Classe'. +tests/cases/conformance/salsa/spellingCheckedJS.js(31,12): error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? +tests/cases/conformance/salsa/spellingCheckedJS.js(34,14): error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? +tests/cases/conformance/salsa/spellingCheckedJS.js(35,1): error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? +tests/cases/conformance/salsa/spellingCheckedJS.js(37,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? + + +==== tests/cases/conformance/salsa/spellingCheckedJS.js (8 errors) ==== + // @ts-check + export var inModule = 1 + inmodule.toFixed() + ~~~~~~~~ +!!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? + + function f() { + var locals = 2 + locale.toFixed() + ~~~~~~ +!!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? +!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS.js:6:9: 'locals' is declared here. + } + class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + ~~~~ +!!! error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? +!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS.js:10:5: 'non' is declared here. + } + } + class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + ~~~~ +!!! error TS2339: Property 'none' does not exist on type 'Classe'. + } + } + + + var object = { + spaaace: 3 + } + object.spaaaace // error on read + object.spaace = 12 // error on write + object.fresh = 12 // OK + other.puuuce // OK, from another file + new Date().getGMTDate() // OK, from another file + ~~~~~~~~~~ +!!! error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? +!!! related TS2728 /.ts/lib.es5.d.ts:757:5: 'getUTCDate' is declared here. + + // No suggestions for globals from other files + const atoc = setIntegral(() => console.log('ok'), 500) + ~~~~~~~~~~~ +!!! error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? +!!! related TS2728 /.ts/lib.dom.d.ts:19626:18: 'setInterval' is declared here. + AudioBuffin // etc + ~~~~~~~~~~~ +!!! error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? +!!! related TS2728 /.ts/lib.dom.d.ts:2246:13: 'AudioBuffer' is declared here. + Jimmy + Jon + ~~~ +!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. + +==== tests/cases/conformance/salsa/other.js (1 errors) ==== + var Jimmy = 1 + var John = 2 + Jon // error, it's from the same file + ~~~ +!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. + var other = { + puuce: 4 + } + \ No newline at end of file diff --git a/tests/baselines/reference/spellingCheckedJS.symbols b/tests/baselines/reference/spellingCheckedJS.symbols new file mode 100644 index 0000000000000..51cae91cffe47 --- /dev/null +++ b/tests/baselines/reference/spellingCheckedJS.symbols @@ -0,0 +1,92 @@ +=== tests/cases/conformance/salsa/spellingCheckedJS.js === +// @ts-check +export var inModule = 1 +>inModule : Symbol(inModule, Decl(spellingCheckedJS.js, 1, 10)) + +inmodule.toFixed() + +function f() { +>f : Symbol(f, Decl(spellingCheckedJS.js, 2, 18)) + + var locals = 2 +>locals : Symbol(locals, Decl(spellingCheckedJS.js, 5, 7)) + + locale.toFixed() +} +class Classe { +>Classe : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) + + non = 'oui' +>non : Symbol(Classe.non, Decl(spellingCheckedJS.js, 8, 14)) + + methode() { +>methode : Symbol(Classe.methode, Decl(spellingCheckedJS.js, 9, 15)) + + // no error on 'this' references + return this.none +>this : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) + } +} +class Derivee extends Classe { +>Derivee : Symbol(Derivee, Decl(spellingCheckedJS.js, 14, 1)) +>Classe : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) + + methode() { +>methode : Symbol(Derivee.methode, Decl(spellingCheckedJS.js, 15, 30)) + + // no error on 'super' references + return super.none +>super : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) + } +} + + +var object = { +>object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) + + spaaace: 3 +>spaaace : Symbol(spaaace, Decl(spellingCheckedJS.js, 23, 14)) +} +object.spaaaace // error on read +>object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) + +object.spaace = 12 // error on write +>object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) + +object.fresh = 12 // OK +>object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) + +other.puuuce // OK, from another file +>other : Symbol(other, Decl(other.js, 3, 3)) + +new Date().getGMTDate() // OK, from another file +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : Symbol(atoc, Decl(spellingCheckedJS.js, 33, 5)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +AudioBuffin // etc +Jimmy +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +Jon + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +var John = 2 +>John : Symbol(John, Decl(other.js, 1, 3)) + +Jon // error, it's from the same file +var other = { +>other : Symbol(other, Decl(other.js, 3, 3)) + + puuce: 4 +>puuce : Symbol(puuce, Decl(other.js, 3, 13)) +} + diff --git a/tests/baselines/reference/spellingCheckedJS.types b/tests/baselines/reference/spellingCheckedJS.types new file mode 100644 index 0000000000000..d514df1558ac4 --- /dev/null +++ b/tests/baselines/reference/spellingCheckedJS.types @@ -0,0 +1,140 @@ +=== tests/cases/conformance/salsa/spellingCheckedJS.js === +// @ts-check +export var inModule = 1 +>inModule : number +>1 : 1 + +inmodule.toFixed() +>inmodule.toFixed() : any +>inmodule.toFixed : any +>inmodule : any +>toFixed : any + +function f() { +>f : () => void + + var locals = 2 +>locals : number +>2 : 2 + + locale.toFixed() +>locale.toFixed() : any +>locale.toFixed : any +>locale : any +>toFixed : any +} +class Classe { +>Classe : Classe + + non = 'oui' +>non : string +>'oui' : "oui" + + methode() { +>methode : () => any + + // no error on 'this' references + return this.none +>this.none : any +>this : this +>none : any + } +} +class Derivee extends Classe { +>Derivee : Derivee +>Classe : Classe + + methode() { +>methode : () => any + + // no error on 'super' references + return super.none +>super.none : any +>super : Classe +>none : any + } +} + + +var object = { +>object : { spaaace: number; } +>{ spaaace: 3} : { spaaace: number; } + + spaaace: 3 +>spaaace : number +>3 : 3 +} +object.spaaaace // error on read +>object.spaaaace : any +>object : { spaaace: number; } +>spaaaace : any + +object.spaace = 12 // error on write +>object.spaace = 12 : 12 +>object.spaace : any +>object : { spaaace: number; } +>spaace : any +>12 : 12 + +object.fresh = 12 // OK +>object.fresh = 12 : 12 +>object.fresh : any +>object : { spaaace: number; } +>fresh : any +>12 : 12 + +other.puuuce // OK, from another file +>other.puuuce : any +>other : { puuce: number; } +>puuuce : any + +new Date().getGMTDate() // OK, from another file +>new Date().getGMTDate() : any +>new Date().getGMTDate : any +>new Date() : Date +>Date : DateConstructor +>getGMTDate : any + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : any +>setIntegral(() => console.log('ok'), 500) : any +>setIntegral : any +>() => console.log('ok') : () => void +>console.log('ok') : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>'ok' : "ok" +>500 : 500 + +AudioBuffin // etc +>AudioBuffin : any + +Jimmy +>Jimmy : number + +Jon +>Jon : any + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : number +>1 : 1 + +var John = 2 +>John : number +>2 : 2 + +Jon // error, it's from the same file +>Jon : any + +var other = { +>other : { puuce: number; } +>{ puuce: 4} : { puuce: number; } + + puuce: 4 +>puuce : number +>4 : 4 +} + diff --git a/tests/baselines/reference/spellingNocheckJS.errors.txt b/tests/baselines/reference/spellingNocheckJS.errors.txt new file mode 100644 index 0000000000000..1d42c836268fd --- /dev/null +++ b/tests/baselines/reference/spellingNocheckJS.errors.txt @@ -0,0 +1,53 @@ +tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? + + +==== tests/cases/conformance/salsa/spellingNocheckJS.js (0 errors) ==== + // @ts-nocheck + export var inModule = 1 + inmodule.toFixed() + + function f() { + var locals = 2 + locale.toFixed() + } + class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } + } + class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } + } + + + var object = { + spaaace: 3 + } + object.spaaaace // error on read + object.spaace = 12 // error on write + object.fresh = 12 // OK + other.puuuce // OK, from another file + new Date().getGMTDate() // OK, from another file + + // No suggestions for globals from other files + const atoc = setIntegral(() => console.log('ok'), 500) + AudioBuffin // etc + Jimmy + Jon + +==== tests/cases/conformance/salsa/other.js (1 errors) ==== + var Jimmy = 1 + var John = 2 + Jon // error, it's from the same file + ~~~ +!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. + var other = { + puuce: 4 + } + \ No newline at end of file diff --git a/tests/baselines/reference/spellingNocheckJS.symbols b/tests/baselines/reference/spellingNocheckJS.symbols new file mode 100644 index 0000000000000..df99d4e2a4288 --- /dev/null +++ b/tests/baselines/reference/spellingNocheckJS.symbols @@ -0,0 +1,92 @@ +=== tests/cases/conformance/salsa/spellingNocheckJS.js === +// @ts-nocheck +export var inModule = 1 +>inModule : Symbol(inModule, Decl(spellingNocheckJS.js, 1, 10)) + +inmodule.toFixed() + +function f() { +>f : Symbol(f, Decl(spellingNocheckJS.js, 2, 18)) + + var locals = 2 +>locals : Symbol(locals, Decl(spellingNocheckJS.js, 5, 7)) + + locale.toFixed() +} +class Classe { +>Classe : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) + + non = 'oui' +>non : Symbol(Classe.non, Decl(spellingNocheckJS.js, 8, 14)) + + methode() { +>methode : Symbol(Classe.methode, Decl(spellingNocheckJS.js, 9, 15)) + + // no error on 'this' references + return this.none +>this : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) + } +} +class Derivee extends Classe { +>Derivee : Symbol(Derivee, Decl(spellingNocheckJS.js, 14, 1)) +>Classe : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) + + methode() { +>methode : Symbol(Derivee.methode, Decl(spellingNocheckJS.js, 15, 30)) + + // no error on 'super' references + return super.none +>super : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) + } +} + + +var object = { +>object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) + + spaaace: 3 +>spaaace : Symbol(spaaace, Decl(spellingNocheckJS.js, 23, 14)) +} +object.spaaaace // error on read +>object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) + +object.spaace = 12 // error on write +>object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) + +object.fresh = 12 // OK +>object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) + +other.puuuce // OK, from another file +>other : Symbol(other, Decl(other.js, 3, 3)) + +new Date().getGMTDate() // OK, from another file +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : Symbol(atoc, Decl(spellingNocheckJS.js, 33, 5)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +AudioBuffin // etc +Jimmy +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +Jon + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +var John = 2 +>John : Symbol(John, Decl(other.js, 1, 3)) + +Jon // error, it's from the same file +var other = { +>other : Symbol(other, Decl(other.js, 3, 3)) + + puuce: 4 +>puuce : Symbol(puuce, Decl(other.js, 3, 13)) +} + diff --git a/tests/baselines/reference/spellingNocheckJS.types b/tests/baselines/reference/spellingNocheckJS.types new file mode 100644 index 0000000000000..0d3489d62560b --- /dev/null +++ b/tests/baselines/reference/spellingNocheckJS.types @@ -0,0 +1,140 @@ +=== tests/cases/conformance/salsa/spellingNocheckJS.js === +// @ts-nocheck +export var inModule = 1 +>inModule : number +>1 : 1 + +inmodule.toFixed() +>inmodule.toFixed() : any +>inmodule.toFixed : any +>inmodule : any +>toFixed : any + +function f() { +>f : () => void + + var locals = 2 +>locals : number +>2 : 2 + + locale.toFixed() +>locale.toFixed() : any +>locale.toFixed : any +>locale : any +>toFixed : any +} +class Classe { +>Classe : Classe + + non = 'oui' +>non : string +>'oui' : "oui" + + methode() { +>methode : () => any + + // no error on 'this' references + return this.none +>this.none : any +>this : this +>none : any + } +} +class Derivee extends Classe { +>Derivee : Derivee +>Classe : Classe + + methode() { +>methode : () => any + + // no error on 'super' references + return super.none +>super.none : any +>super : Classe +>none : any + } +} + + +var object = { +>object : { spaaace: number; } +>{ spaaace: 3} : { spaaace: number; } + + spaaace: 3 +>spaaace : number +>3 : 3 +} +object.spaaaace // error on read +>object.spaaaace : any +>object : { spaaace: number; } +>spaaaace : any + +object.spaace = 12 // error on write +>object.spaace = 12 : 12 +>object.spaace : any +>object : { spaaace: number; } +>spaace : any +>12 : 12 + +object.fresh = 12 // OK +>object.fresh = 12 : 12 +>object.fresh : any +>object : { spaaace: number; } +>fresh : any +>12 : 12 + +other.puuuce // OK, from another file +>other.puuuce : any +>other : { puuce: number; } +>puuuce : any + +new Date().getGMTDate() // OK, from another file +>new Date().getGMTDate() : any +>new Date().getGMTDate : any +>new Date() : Date +>Date : DateConstructor +>getGMTDate : any + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : any +>setIntegral(() => console.log('ok'), 500) : any +>setIntegral : any +>() => console.log('ok') : () => void +>console.log('ok') : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>'ok' : "ok" +>500 : 500 + +AudioBuffin // etc +>AudioBuffin : any + +Jimmy +>Jimmy : number + +Jon +>Jon : any + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : number +>1 : 1 + +var John = 2 +>John : number +>2 : 2 + +Jon // error, it's from the same file +>Jon : any + +var other = { +>other : { puuce: number; } +>{ puuce: 4} : { puuce: number; } + + puuce: 4 +>puuce : number +>4 : 4 +} + diff --git a/tests/baselines/reference/spellingUncheckedJS.errors.txt b/tests/baselines/reference/spellingUncheckedJS.errors.txt new file mode 100644 index 0000000000000..7005e3e4d1c9d --- /dev/null +++ b/tests/baselines/reference/spellingUncheckedJS.errors.txt @@ -0,0 +1,67 @@ +tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +tests/cases/conformance/salsa/spellingUncheckedJS.js(2,1): error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? +tests/cases/conformance/salsa/spellingUncheckedJS.js(6,5): error TS2552: Cannot find name 'locale'. Did you mean 'locals'? +tests/cases/conformance/salsa/spellingUncheckedJS.js(26,8): error TS2551: Property 'spaaaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? +tests/cases/conformance/salsa/spellingUncheckedJS.js(27,8): error TS2551: Property 'spaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? + + +==== tests/cases/conformance/salsa/spellingUncheckedJS.js (4 errors) ==== + export var inModule = 1 + inmodule.toFixed() + ~~~~~~~~ +!!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? + + function f() { + var locals = 2 + locale.toFixed() + ~~~~~~ +!!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? +!!! related TS2728 tests/cases/conformance/salsa/spellingUncheckedJS.js:5:9: 'locals' is declared here. + } + class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } + } + class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } + } + + + var object = { + spaaace: 3 + } + object.spaaaace // error on read + ~~~~~~~~ +!!! error TS2551: Property 'spaaaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? +!!! related TS2728 tests/cases/conformance/salsa/spellingUncheckedJS.js:24:5: 'spaaace' is declared here. + object.spaace = 12 // error on write + ~~~~~~ +!!! error TS2551: Property 'spaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? +!!! related TS2728 tests/cases/conformance/salsa/spellingUncheckedJS.js:24:5: 'spaaace' is declared here. + object.fresh = 12 // OK + other.puuuce // OK, from another file + new Date().getGMTDate() // OK, from another file + + // No suggestions for globals from other files + const atoc = setIntegral(() => console.log('ok'), 500) + AudioBuffin // etc + Jimmy + Jon + +==== tests/cases/conformance/salsa/other.js (1 errors) ==== + var Jimmy = 1 + var John = 2 + Jon // error, it's from the same file + ~~~ +!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. + var other = { + puuce: 4 + } + \ No newline at end of file diff --git a/tests/baselines/reference/spellingUncheckedJS.symbols b/tests/baselines/reference/spellingUncheckedJS.symbols new file mode 100644 index 0000000000000..4f323e06cb283 --- /dev/null +++ b/tests/baselines/reference/spellingUncheckedJS.symbols @@ -0,0 +1,91 @@ +=== tests/cases/conformance/salsa/spellingUncheckedJS.js === +export var inModule = 1 +>inModule : Symbol(inModule, Decl(spellingUncheckedJS.js, 0, 10)) + +inmodule.toFixed() + +function f() { +>f : Symbol(f, Decl(spellingUncheckedJS.js, 1, 18)) + + var locals = 2 +>locals : Symbol(locals, Decl(spellingUncheckedJS.js, 4, 7)) + + locale.toFixed() +} +class Classe { +>Classe : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) + + non = 'oui' +>non : Symbol(Classe.non, Decl(spellingUncheckedJS.js, 7, 14)) + + methode() { +>methode : Symbol(Classe.methode, Decl(spellingUncheckedJS.js, 8, 15)) + + // no error on 'this' references + return this.none +>this : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) + } +} +class Derivee extends Classe { +>Derivee : Symbol(Derivee, Decl(spellingUncheckedJS.js, 13, 1)) +>Classe : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) + + methode() { +>methode : Symbol(Derivee.methode, Decl(spellingUncheckedJS.js, 14, 30)) + + // no error on 'super' references + return super.none +>super : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) + } +} + + +var object = { +>object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) + + spaaace: 3 +>spaaace : Symbol(spaaace, Decl(spellingUncheckedJS.js, 22, 14)) +} +object.spaaaace // error on read +>object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) + +object.spaace = 12 // error on write +>object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) + +object.fresh = 12 // OK +>object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) + +other.puuuce // OK, from another file +>other : Symbol(other, Decl(other.js, 3, 3)) + +new Date().getGMTDate() // OK, from another file +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : Symbol(atoc, Decl(spellingUncheckedJS.js, 32, 5)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +AudioBuffin // etc +Jimmy +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +Jon + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +var John = 2 +>John : Symbol(John, Decl(other.js, 1, 3)) + +Jon // error, it's from the same file +var other = { +>other : Symbol(other, Decl(other.js, 3, 3)) + + puuce: 4 +>puuce : Symbol(puuce, Decl(other.js, 3, 13)) +} + diff --git a/tests/baselines/reference/spellingUncheckedJS.types b/tests/baselines/reference/spellingUncheckedJS.types new file mode 100644 index 0000000000000..ec966ea82477e --- /dev/null +++ b/tests/baselines/reference/spellingUncheckedJS.types @@ -0,0 +1,139 @@ +=== tests/cases/conformance/salsa/spellingUncheckedJS.js === +export var inModule = 1 +>inModule : number +>1 : 1 + +inmodule.toFixed() +>inmodule.toFixed() : any +>inmodule.toFixed : any +>inmodule : any +>toFixed : any + +function f() { +>f : () => void + + var locals = 2 +>locals : number +>2 : 2 + + locale.toFixed() +>locale.toFixed() : any +>locale.toFixed : any +>locale : any +>toFixed : any +} +class Classe { +>Classe : Classe + + non = 'oui' +>non : string +>'oui' : "oui" + + methode() { +>methode : () => any + + // no error on 'this' references + return this.none +>this.none : any +>this : this +>none : any + } +} +class Derivee extends Classe { +>Derivee : Derivee +>Classe : Classe + + methode() { +>methode : () => any + + // no error on 'super' references + return super.none +>super.none : any +>super : Classe +>none : any + } +} + + +var object = { +>object : { spaaace: number; } +>{ spaaace: 3} : { spaaace: number; } + + spaaace: 3 +>spaaace : number +>3 : 3 +} +object.spaaaace // error on read +>object.spaaaace : any +>object : { spaaace: number; } +>spaaaace : any + +object.spaace = 12 // error on write +>object.spaace = 12 : 12 +>object.spaace : any +>object : { spaaace: number; } +>spaace : any +>12 : 12 + +object.fresh = 12 // OK +>object.fresh = 12 : 12 +>object.fresh : any +>object : { spaaace: number; } +>fresh : any +>12 : 12 + +other.puuuce // OK, from another file +>other.puuuce : any +>other : { puuce: number; } +>puuuce : any + +new Date().getGMTDate() // OK, from another file +>new Date().getGMTDate() : any +>new Date().getGMTDate : any +>new Date() : Date +>Date : DateConstructor +>getGMTDate : any + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : any +>setIntegral(() => console.log('ok'), 500) : any +>setIntegral : any +>() => console.log('ok') : () => void +>console.log('ok') : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>'ok' : "ok" +>500 : 500 + +AudioBuffin // etc +>AudioBuffin : any + +Jimmy +>Jimmy : number + +Jon +>Jon : any + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : number +>1 : 1 + +var John = 2 +>John : number +>2 : 2 + +Jon // error, it's from the same file +>Jon : any + +var other = { +>other : { puuce: number; } +>{ puuce: 4} : { puuce: number; } + + puuce: 4 +>puuce : number +>4 : 4 +} + diff --git a/tests/cases/conformance/salsa/spellingCheckedJS.ts b/tests/cases/conformance/salsa/spellingCheckedJS.ts new file mode 100644 index 0000000000000..a79eeaf122850 --- /dev/null +++ b/tests/cases/conformance/salsa/spellingCheckedJS.ts @@ -0,0 +1,48 @@ +// @noEmit: true +// @allowJs: true +// @filename: spellingCheckedJS.js +// @ts-check +export var inModule = 1 +inmodule.toFixed() + +function f() { + var locals = 2 + locale.toFixed() +} +class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } +} +class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } +} + + +var object = { + spaaace: 3 +} +object.spaaaace // error on read +object.spaace = 12 // error on write +object.fresh = 12 // OK +other.puuuce // OK, from another file +new Date().getGMTDate() // OK, from another file + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +AudioBuffin // etc +Jimmy +Jon + +// @filename: other.js +var Jimmy = 1 +var John = 2 +Jon // error, it's from the same file +var other = { + puuce: 4 +} diff --git a/tests/cases/conformance/salsa/spellingNocheckJS.ts b/tests/cases/conformance/salsa/spellingNocheckJS.ts new file mode 100644 index 0000000000000..6b96c05686aaf --- /dev/null +++ b/tests/cases/conformance/salsa/spellingNocheckJS.ts @@ -0,0 +1,48 @@ +// @noEmit: true +// @allowJs: true +// @filename: spellingNocheckJS.js +// @ts-nocheck +export var inModule = 1 +inmodule.toFixed() + +function f() { + var locals = 2 + locale.toFixed() +} +class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } +} +class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } +} + + +var object = { + spaaace: 3 +} +object.spaaaace // error on read +object.spaace = 12 // error on write +object.fresh = 12 // OK +other.puuuce // OK, from another file +new Date().getGMTDate() // OK, from another file + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +AudioBuffin // etc +Jimmy +Jon + +// @filename: other.js +var Jimmy = 1 +var John = 2 +Jon // error, it's from the same file +var other = { + puuce: 4 +} diff --git a/tests/cases/conformance/salsa/spellingUncheckedJS.ts b/tests/cases/conformance/salsa/spellingUncheckedJS.ts new file mode 100644 index 0000000000000..166b99492c560 --- /dev/null +++ b/tests/cases/conformance/salsa/spellingUncheckedJS.ts @@ -0,0 +1,47 @@ +// @noEmit: true +// @allowJs: true +// @filename: spellingUncheckedJS.js +export var inModule = 1 +inmodule.toFixed() + +function f() { + var locals = 2 + locale.toFixed() +} +class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } +} +class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } +} + + +var object = { + spaaace: 3 +} +object.spaaaace // error on read +object.spaace = 12 // error on write +object.fresh = 12 // OK +other.puuuce // OK, from another file +new Date().getGMTDate() // OK, from another file + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +AudioBuffin // etc +Jimmy +Jon + +// @filename: other.js +var Jimmy = 1 +var John = 2 +Jon // error, it's from the same file +var other = { + puuce: 4 +} From ac5834ae1a4b5139d374ba8f275076e8f1cd2d19 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 28 May 2021 12:01:21 -0700 Subject: [PATCH 02/16] all (most?) tests pass --- src/compiler/checker.ts | 54 ++++--- src/compiler/program.ts | 19 ++- src/compiler/utilities.ts | 2 +- .../argumentsReferenceInConstructor3_Js.types | 4 +- .../jsObjectsMarkedAsOpenEnded.types | 4 +- .../reference/methodsReturningThis.types | 2 +- .../misspelledJsDocTypedefTags.types | 4 +- .../reference/multipleDeclarations.types | 4 +- .../reference/spellingCheckedJS2.errors.txt | 82 +++++++++++ .../reference/spellingCheckedJS2.symbols | 91 ++++++++++++ .../reference/spellingCheckedJS2.types | 139 ++++++++++++++++++ .../reference/spellingNocheckJS2.symbols | 91 ++++++++++++ .../reference/spellingNocheckJS2.types | 139 ++++++++++++++++++ .../reference/spellingUncheckedJS.errors.txt | 2 +- .../reference/spellingUncheckedJS.symbols | 2 +- .../reference/spellingUncheckedJS.types | 6 +- .../conformance/salsa/spellingCheckedJS2.ts | 48 ++++++ .../conformance/salsa/spellingNocheckJS2.ts | 48 ++++++ .../conformance/salsa/spellingUncheckedJS.ts | 2 +- 19 files changed, 693 insertions(+), 50 deletions(-) create mode 100644 tests/baselines/reference/spellingCheckedJS2.errors.txt create mode 100644 tests/baselines/reference/spellingCheckedJS2.symbols create mode 100644 tests/baselines/reference/spellingCheckedJS2.types create mode 100644 tests/baselines/reference/spellingNocheckJS2.symbols create mode 100644 tests/baselines/reference/spellingNocheckJS2.types create mode 100644 tests/cases/conformance/salsa/spellingCheckedJS2.ts create mode 100644 tests/cases/conformance/salsa/spellingNocheckJS2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04fcae05e9509..888f0489de88c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2061,17 +2061,8 @@ namespace ts { if (isGlobalScopeAugmentationDeclaration) { suggestion = undefined; } - // (1) when allowjs is on but checkjs is not, for JS[X] files that don't have either ts-check OR ts-nocheck - // skip suggestions when the suggestion comes from a global scope - // This will prevent it from always showing up - const file = getSourceFileOfNode(lastLocation) - const suggestionFile = getSourceFileOfNode(suggestion?.valueDeclaration) - if (file && suggestionFile) { - const isCheckJs = isCheckJsEnabledForFile(file, compilerOptions) - const isTsNoCheck = !!file.checkJsDirective && file.checkJsDirective.enabled === false - if (!isCheckJs && !isTsNoCheck && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX) && isGlobalSourceFile(suggestionFile) && file !== suggestionFile) { - suggestion = undefined - } + if (originalLocation && isBadUncheckedJSSuggestion(lastLocation, /*parent*/ undefined, suggestion?.valueDeclaration ? [suggestion.valueDeclaration] : [])) { + suggestion = undefined } if (suggestion) { const suggestionName = symbolToString(suggestion); @@ -14582,33 +14573,23 @@ namespace ts { * Should all count as literals and not print errors on access or assignment of possibly existing properties. * This mirrors the behavior of the index signature propagation, to which this behaves similarly (but doesn't affect assignability or inference). */ - function isJSLiteralType(type: Type, reference?: Node): boolean { + function isJSLiteralType(type: Type): boolean { if (noImplicitAny) { return false; // Flag is meaningless under `noImplicitAny` mode } - // TODO: Move this check outside isJSLiteralType; it doesn't make sense here - const file = getSourceFileOfNode(reference) - if (file) { - const isCheckJs = isCheckJsEnabledForFile(file, compilerOptions) - const isTsNoCheck = !!file.checkJsDirective && file.checkJsDirective.enabled === false - if (!isCheckJs && !isTsNoCheck && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { - const parentFile = forEach(type.symbol?.declarations, getSourceFileOfNode) - return file !== parentFile || !!reference && isPropertyAccessExpression(reference) && reference.expression.kind === SyntaxKind.ThisKeyword - } - } if (getObjectFlags(type) & ObjectFlags.JSLiteral) { return true; } if (type.flags & TypeFlags.Union) { - return every((type as UnionType).types, t => isJSLiteralType(t, reference)); + return every((type as UnionType).types, isJSLiteralType); } if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, t => isJSLiteralType(t, reference)); + return some((type as IntersectionType).types, isJSLiteralType); } if (type.flags & TypeFlags.Instantiable) { const constraint = getResolvedBaseConstraint(type); - return constraint !== type && isJSLiteralType(constraint, reference); + return constraint !== type && isJSLiteralType(constraint); } return false; } @@ -14707,7 +14688,7 @@ namespace ts { if (indexType.flags & TypeFlags.Never) { return neverType; } - if (isJSLiteralType(objectType, accessNode)) { + if (isJSLiteralType(objectType)) { return anyType; } if (accessExpression && !isConstEnumObjectType(objectType)) { @@ -14778,7 +14759,7 @@ namespace ts { return undefined; } } - if (isJSLiteralType(objectType, accessNode)) { + if (isJSLiteralType(objectType)) { return anyType; } if (accessNode) { @@ -27442,7 +27423,8 @@ namespace ts { if (!prop) { const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; if (!(indexInfo && indexInfo.type)) { - if (isJSLiteralType(leftType, node)) { + const isBadJSSuggestion = isBadUncheckedJSSuggestion(node, leftType.symbol, leftType.symbol?.declarations) + if (isBadJSSuggestion === undefined ? isJSLiteralType(leftType) : isBadJSSuggestion) { return anyType; } if (leftType.symbol === globalThisSymbol) { @@ -27488,6 +27470,22 @@ namespace ts { return getFlowTypeOfAccessExpression(node, prop, propType, right, checkMode); } + /** + * Only applies to unchecked JS files without checkJS, // @ts-check or // @ts-nocheck + * @returns undefined when not applicable, true for bad suggestions, false for good ones + * TODO: Should probably only pass one declaration + */ + function isBadUncheckedJSSuggestion(node: Node | undefined, parent: Symbol | undefined, declarations: Node[] | undefined): boolean | undefined { + const file = getSourceFileOfNode(node) + if (file) { + if (compilerOptions.checkJs === undefined && !file.checkJsDirective && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { + const parentFile = forEach(declarations, getSourceFileOfNode) + return file !== parentFile && !!parentFile && isGlobalSourceFile(parentFile) + || !!(parent && parent.flags & SymbolFlags.Class) + } + } + } + function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node, checkMode: CheckMode | undefined) { // Only compute control flow type if this is a property access expression that isn't an // assignment target, and the referenced property was declared as a variable, property, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 6385d947d65fb..db42c04cbb120 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1913,20 +1913,25 @@ namespace ts { Debug.assert(!!sourceFile.bindDiagnostics); - const isCheckJs = isCheckJsEnabledForFile(sourceFile, options); + const isCheckJsTrue = isCheckJsEnabledForFile(sourceFile, options); + const isCheckJsUndefined = sourceFile.checkJsDirective === undefined && options.checkJs === undefined; const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) - const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX || sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX - || sourceFile.scriptKind === ScriptKind.External || sourceFile.scriptKind === ScriptKind.Deferred); + const includeBindAndCheckDiagnostics = !isTsNoCheck && + (sourceFile.scriptKind === ScriptKind.TS + || sourceFile.scriptKind === ScriptKind.TSX + || isCheckJsTrue + || (isCheckJsUndefined && sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX) + || sourceFile.scriptKind === ScriptKind.External + || sourceFile.scriptKind === ScriptKind.Deferred); let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; - if (!isCheckJs && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX)) { + if (isCheckJsUndefined && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX)) { // TODO: It can just be checkDiagnostics srsly - // TODO: Recheck user tests and random compilation to see how much this adds - bindDiagnostics = bindDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) // MORE TO COME + bindDiagnostics = bindDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) checkDiagnostics = checkDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) } - return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined); + return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJsTrue ? sourceFile.jsDocDiagnostics : undefined); }); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0e1fde7d4cdcc..23d86e3e0bce2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6779,7 +6779,7 @@ namespace ts { export function tryGetExtensionFromPath(path: string): Extension | undefined { return find(extensionsToRemove, e => fileExtensionIs(path, e)); } - + // TODO: Callers of this function in services are probably what *actually* need to change export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs; } diff --git a/tests/baselines/reference/argumentsReferenceInConstructor3_Js.types b/tests/baselines/reference/argumentsReferenceInConstructor3_Js.types index ccdf9b660674d..4b6557cd1b972 100644 --- a/tests/baselines/reference/argumentsReferenceInConstructor3_Js.types +++ b/tests/baselines/reference/argumentsReferenceInConstructor3_Js.types @@ -43,11 +43,11 @@ class B extends A { * @type object */ this.bar = super.arguments.foo; ->this.bar = super.arguments.foo : any +>this.bar = super.arguments.foo : error >this.bar : any >this : this >bar : any ->super.arguments.foo : any +>super.arguments.foo : error >super.arguments : { bar: {}; } >super : A >arguments : { bar: {}; } diff --git a/tests/baselines/reference/jsObjectsMarkedAsOpenEnded.types b/tests/baselines/reference/jsObjectsMarkedAsOpenEnded.types index 777f779e9c78c..adb07b7bae781 100644 --- a/tests/baselines/reference/jsObjectsMarkedAsOpenEnded.types +++ b/tests/baselines/reference/jsObjectsMarkedAsOpenEnded.types @@ -27,7 +27,7 @@ class C { this.member.a = 0; >this.member.a = 0 : 0 ->this.member.a : any +>this.member.a : error >this.member : {} >this : this >member : {} @@ -48,7 +48,7 @@ var obj = { obj.property.a = 0; >obj.property.a = 0 : 0 ->obj.property.a : any +>obj.property.a : error >obj.property : {} >obj : { property: {}; } >property : {} diff --git a/tests/baselines/reference/methodsReturningThis.types b/tests/baselines/reference/methodsReturningThis.types index 544139497e088..856ea6925330f 100644 --- a/tests/baselines/reference/methodsReturningThis.types +++ b/tests/baselines/reference/methodsReturningThis.types @@ -13,7 +13,7 @@ Class.prototype.containsError = function () { return this.notPresent; }; >prototype : any >containsError : any >function () { return this.notPresent; } : () => any ->this.notPresent : error +>this.notPresent : any >this : this >notPresent : any diff --git a/tests/baselines/reference/misspelledJsDocTypedefTags.types b/tests/baselines/reference/misspelledJsDocTypedefTags.types index 9a42e2d1cd8b0..5facd80a2675d 100644 --- a/tests/baselines/reference/misspelledJsDocTypedefTags.types +++ b/tests/baselines/reference/misspelledJsDocTypedefTags.types @@ -1,7 +1,7 @@ === tests/cases/compiler/a.js === /** @typedef {{ endTime: number, screenshots: number}} A.*/ Animation.AnimationModel.ScreenshotCapture.Request; ->Animation.AnimationModel.ScreenshotCapture.Request : error +>Animation.AnimationModel.ScreenshotCapture.Request : any >Animation.AnimationModel.ScreenshotCapture : any >Animation.AnimationModel : any >Animation : { new (effect?: AnimationEffect, timeline?: AnimationTimeline): Animation; prototype: Animation; } @@ -11,7 +11,7 @@ Animation.AnimationModel.ScreenshotCapture.Request; /** @typedef {{ endTime: number, screenshots: !B.}} */ Animation.AnimationModel.ScreenshotCapture.Request; ->Animation.AnimationModel.ScreenshotCapture.Request : error +>Animation.AnimationModel.ScreenshotCapture.Request : any >Animation.AnimationModel.ScreenshotCapture : any >Animation.AnimationModel : any >Animation : { new (effect?: AnimationEffect, timeline?: AnimationTimeline): Animation; prototype: Animation; } diff --git a/tests/baselines/reference/multipleDeclarations.types b/tests/baselines/reference/multipleDeclarations.types index 12aa74ecaf096..dc695bca44552 100644 --- a/tests/baselines/reference/multipleDeclarations.types +++ b/tests/baselines/reference/multipleDeclarations.types @@ -19,8 +19,8 @@ C.prototype.m = function() { >function() { this.nothing();} : () => void this.nothing(); ->this.nothing() : error ->this.nothing : error +>this.nothing() : any +>this.nothing : any >this : this >nothing : any } diff --git a/tests/baselines/reference/spellingCheckedJS2.errors.txt b/tests/baselines/reference/spellingCheckedJS2.errors.txt new file mode 100644 index 0000000000000..39e996c71cb09 --- /dev/null +++ b/tests/baselines/reference/spellingCheckedJS2.errors.txt @@ -0,0 +1,82 @@ +tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(2,1): error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(6,5): error TS2552: Cannot find name 'locale'. Did you mean 'locals'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(12,21): error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(18,22): error TS2339: Property 'none' does not exist on type 'Classe'. +tests/cases/conformance/salsa/spellingCheckedJS2.js(30,12): error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(33,14): error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(34,1): error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? +tests/cases/conformance/salsa/spellingCheckedJS2.js(36,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? + + +==== tests/cases/conformance/salsa/spellingCheckedJS2.js (8 errors) ==== + export var inModule = 1 + inmodule.toFixed() + ~~~~~~~~ +!!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? + + function f() { + var locals = 2 + locale.toFixed() + ~~~~~~ +!!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? +!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS2.js:5:9: 'locals' is declared here. + } + class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + ~~~~ +!!! error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? +!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS2.js:9:5: 'non' is declared here. + } + } + class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + ~~~~ +!!! error TS2339: Property 'none' does not exist on type 'Classe'. + } + } + + + var object = { + spaaace: 3 + } + object.spaaaace // error on read + object.spaace = 12 // error on write + object.fresh = 12 // OK + other.puuuce // OK, from another file + new Date().getGMTDate() // OK, from another file + ~~~~~~~~~~ +!!! error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? +!!! related TS2728 /.ts/lib.es5.d.ts:757:5: 'getUTCDate' is declared here. + + // No suggestions for globals from other files + const atoc = setIntegral(() => console.log('ok'), 500) + ~~~~~~~~~~~ +!!! error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? +!!! related TS2728 /.ts/lib.dom.d.ts:19626:18: 'setInterval' is declared here. + AudioBuffin // etc + ~~~~~~~~~~~ +!!! error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? +!!! related TS2728 /.ts/lib.dom.d.ts:2246:13: 'AudioBuffer' is declared here. + Jimmy + Jon + ~~~ +!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. + +==== tests/cases/conformance/salsa/other.js (1 errors) ==== + var Jimmy = 1 + var John = 2 + Jon // error, it's from the same file + ~~~ +!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? +!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. + var other = { + puuce: 4 + } + \ No newline at end of file diff --git a/tests/baselines/reference/spellingCheckedJS2.symbols b/tests/baselines/reference/spellingCheckedJS2.symbols new file mode 100644 index 0000000000000..5512ba82b740c --- /dev/null +++ b/tests/baselines/reference/spellingCheckedJS2.symbols @@ -0,0 +1,91 @@ +=== tests/cases/conformance/salsa/spellingCheckedJS2.js === +export var inModule = 1 +>inModule : Symbol(inModule, Decl(spellingCheckedJS2.js, 0, 10)) + +inmodule.toFixed() + +function f() { +>f : Symbol(f, Decl(spellingCheckedJS2.js, 1, 18)) + + var locals = 2 +>locals : Symbol(locals, Decl(spellingCheckedJS2.js, 4, 7)) + + locale.toFixed() +} +class Classe { +>Classe : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) + + non = 'oui' +>non : Symbol(Classe.non, Decl(spellingCheckedJS2.js, 7, 14)) + + methode() { +>methode : Symbol(Classe.methode, Decl(spellingCheckedJS2.js, 8, 15)) + + // no error on 'this' references + return this.none +>this : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) + } +} +class Derivee extends Classe { +>Derivee : Symbol(Derivee, Decl(spellingCheckedJS2.js, 13, 1)) +>Classe : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) + + methode() { +>methode : Symbol(Derivee.methode, Decl(spellingCheckedJS2.js, 14, 30)) + + // no error on 'super' references + return super.none +>super : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) + } +} + + +var object = { +>object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) + + spaaace: 3 +>spaaace : Symbol(spaaace, Decl(spellingCheckedJS2.js, 22, 14)) +} +object.spaaaace // error on read +>object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) + +object.spaace = 12 // error on write +>object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) + +object.fresh = 12 // OK +>object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) + +other.puuuce // OK, from another file +>other : Symbol(other, Decl(other.js, 3, 3)) + +new Date().getGMTDate() // OK, from another file +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : Symbol(atoc, Decl(spellingCheckedJS2.js, 32, 5)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +AudioBuffin // etc +Jimmy +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +Jon + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +var John = 2 +>John : Symbol(John, Decl(other.js, 1, 3)) + +Jon // error, it's from the same file +var other = { +>other : Symbol(other, Decl(other.js, 3, 3)) + + puuce: 4 +>puuce : Symbol(puuce, Decl(other.js, 3, 13)) +} + diff --git a/tests/baselines/reference/spellingCheckedJS2.types b/tests/baselines/reference/spellingCheckedJS2.types new file mode 100644 index 0000000000000..0abf06c6bad09 --- /dev/null +++ b/tests/baselines/reference/spellingCheckedJS2.types @@ -0,0 +1,139 @@ +=== tests/cases/conformance/salsa/spellingCheckedJS2.js === +export var inModule = 1 +>inModule : number +>1 : 1 + +inmodule.toFixed() +>inmodule.toFixed() : any +>inmodule.toFixed : any +>inmodule : any +>toFixed : any + +function f() { +>f : () => void + + var locals = 2 +>locals : number +>2 : 2 + + locale.toFixed() +>locale.toFixed() : any +>locale.toFixed : any +>locale : any +>toFixed : any +} +class Classe { +>Classe : Classe + + non = 'oui' +>non : string +>'oui' : "oui" + + methode() { +>methode : () => any + + // no error on 'this' references + return this.none +>this.none : any +>this : this +>none : any + } +} +class Derivee extends Classe { +>Derivee : Derivee +>Classe : Classe + + methode() { +>methode : () => any + + // no error on 'super' references + return super.none +>super.none : any +>super : Classe +>none : any + } +} + + +var object = { +>object : { spaaace: number; } +>{ spaaace: 3} : { spaaace: number; } + + spaaace: 3 +>spaaace : number +>3 : 3 +} +object.spaaaace // error on read +>object.spaaaace : any +>object : { spaaace: number; } +>spaaaace : any + +object.spaace = 12 // error on write +>object.spaace = 12 : 12 +>object.spaace : any +>object : { spaaace: number; } +>spaace : any +>12 : 12 + +object.fresh = 12 // OK +>object.fresh = 12 : 12 +>object.fresh : any +>object : { spaaace: number; } +>fresh : any +>12 : 12 + +other.puuuce // OK, from another file +>other.puuuce : any +>other : { puuce: number; } +>puuuce : any + +new Date().getGMTDate() // OK, from another file +>new Date().getGMTDate() : any +>new Date().getGMTDate : any +>new Date() : Date +>Date : DateConstructor +>getGMTDate : any + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : any +>setIntegral(() => console.log('ok'), 500) : any +>setIntegral : any +>() => console.log('ok') : () => void +>console.log('ok') : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>'ok' : "ok" +>500 : 500 + +AudioBuffin // etc +>AudioBuffin : any + +Jimmy +>Jimmy : number + +Jon +>Jon : any + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : number +>1 : 1 + +var John = 2 +>John : number +>2 : 2 + +Jon // error, it's from the same file +>Jon : any + +var other = { +>other : { puuce: number; } +>{ puuce: 4} : { puuce: number; } + + puuce: 4 +>puuce : number +>4 : 4 +} + diff --git a/tests/baselines/reference/spellingNocheckJS2.symbols b/tests/baselines/reference/spellingNocheckJS2.symbols new file mode 100644 index 0000000000000..5ea2c5065bab9 --- /dev/null +++ b/tests/baselines/reference/spellingNocheckJS2.symbols @@ -0,0 +1,91 @@ +=== tests/cases/conformance/salsa/spellingNocheckJS2.js === +export var inModule = 1 +>inModule : Symbol(inModule, Decl(spellingNocheckJS2.js, 0, 10)) + +inmodule.toFixed() + +function f() { +>f : Symbol(f, Decl(spellingNocheckJS2.js, 1, 18)) + + var locals = 2 +>locals : Symbol(locals, Decl(spellingNocheckJS2.js, 4, 7)) + + locale.toFixed() +} +class Classe { +>Classe : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) + + non = 'oui' +>non : Symbol(Classe.non, Decl(spellingNocheckJS2.js, 7, 14)) + + methode() { +>methode : Symbol(Classe.methode, Decl(spellingNocheckJS2.js, 8, 15)) + + // no error on 'this' references + return this.none +>this : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) + } +} +class Derivee extends Classe { +>Derivee : Symbol(Derivee, Decl(spellingNocheckJS2.js, 13, 1)) +>Classe : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) + + methode() { +>methode : Symbol(Derivee.methode, Decl(spellingNocheckJS2.js, 14, 30)) + + // no error on 'super' references + return super.none +>super : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) + } +} + + +var object = { +>object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) + + spaaace: 3 +>spaaace : Symbol(spaaace, Decl(spellingNocheckJS2.js, 22, 14)) +} +object.spaaaace // error on read +>object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) + +object.spaace = 12 // error on write +>object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) + +object.fresh = 12 // OK +>object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) + +other.puuuce // OK, from another file +>other : Symbol(other, Decl(other.js, 3, 3)) + +new Date().getGMTDate() // OK, from another file +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : Symbol(atoc, Decl(spellingNocheckJS2.js, 32, 5)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + +AudioBuffin // etc +Jimmy +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +Jon + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) + +var John = 2 +>John : Symbol(John, Decl(other.js, 1, 3)) + +Jon // error, it's from the same file +var other = { +>other : Symbol(other, Decl(other.js, 3, 3)) + + puuce: 4 +>puuce : Symbol(puuce, Decl(other.js, 3, 13)) +} + diff --git a/tests/baselines/reference/spellingNocheckJS2.types b/tests/baselines/reference/spellingNocheckJS2.types new file mode 100644 index 0000000000000..bd77605b79e5a --- /dev/null +++ b/tests/baselines/reference/spellingNocheckJS2.types @@ -0,0 +1,139 @@ +=== tests/cases/conformance/salsa/spellingNocheckJS2.js === +export var inModule = 1 +>inModule : number +>1 : 1 + +inmodule.toFixed() +>inmodule.toFixed() : error +>inmodule.toFixed : error +>inmodule : any +>toFixed : any + +function f() { +>f : () => void + + var locals = 2 +>locals : number +>2 : 2 + + locale.toFixed() +>locale.toFixed() : error +>locale.toFixed : error +>locale : any +>toFixed : any +} +class Classe { +>Classe : Classe + + non = 'oui' +>non : string +>'oui' : "oui" + + methode() { +>methode : () => any + + // no error on 'this' references + return this.none +>this.none : error +>this : this +>none : any + } +} +class Derivee extends Classe { +>Derivee : Derivee +>Classe : Classe + + methode() { +>methode : () => any + + // no error on 'super' references + return super.none +>super.none : error +>super : Classe +>none : any + } +} + + +var object = { +>object : { spaaace: number; } +>{ spaaace: 3} : { spaaace: number; } + + spaaace: 3 +>spaaace : number +>3 : 3 +} +object.spaaaace // error on read +>object.spaaaace : any +>object : { spaaace: number; } +>spaaaace : any + +object.spaace = 12 // error on write +>object.spaace = 12 : 12 +>object.spaace : any +>object : { spaaace: number; } +>spaace : any +>12 : 12 + +object.fresh = 12 // OK +>object.fresh = 12 : 12 +>object.fresh : any +>object : { spaaace: number; } +>fresh : any +>12 : 12 + +other.puuuce // OK, from another file +>other.puuuce : any +>other : { puuce: number; } +>puuuce : any + +new Date().getGMTDate() // OK, from another file +>new Date().getGMTDate() : error +>new Date().getGMTDate : error +>new Date() : Date +>Date : DateConstructor +>getGMTDate : any + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +>atoc : error +>setIntegral(() => console.log('ok'), 500) : error +>setIntegral : error +>() => console.log('ok') : () => void +>console.log('ok') : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>'ok' : "ok" +>500 : 500 + +AudioBuffin // etc +>AudioBuffin : error + +Jimmy +>Jimmy : number + +Jon +>Jon : error + +=== tests/cases/conformance/salsa/other.js === +var Jimmy = 1 +>Jimmy : number +>1 : 1 + +var John = 2 +>John : number +>2 : 2 + +Jon // error, it's from the same file +>Jon : error + +var other = { +>other : { puuce: number; } +>{ puuce: 4} : { puuce: number; } + + puuce: 4 +>puuce : number +>4 : 4 +} + diff --git a/tests/baselines/reference/spellingUncheckedJS.errors.txt b/tests/baselines/reference/spellingUncheckedJS.errors.txt index 7005e3e4d1c9d..549ca5edb810b 100644 --- a/tests/baselines/reference/spellingUncheckedJS.errors.txt +++ b/tests/baselines/reference/spellingUncheckedJS.errors.txt @@ -12,7 +12,7 @@ tests/cases/conformance/salsa/spellingUncheckedJS.js(27,8): error TS2551: Proper !!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? function f() { - var locals = 2 + var locals = 2 + true locale.toFixed() ~~~~~~ !!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? diff --git a/tests/baselines/reference/spellingUncheckedJS.symbols b/tests/baselines/reference/spellingUncheckedJS.symbols index 4f323e06cb283..af754a34f3efc 100644 --- a/tests/baselines/reference/spellingUncheckedJS.symbols +++ b/tests/baselines/reference/spellingUncheckedJS.symbols @@ -7,7 +7,7 @@ inmodule.toFixed() function f() { >f : Symbol(f, Decl(spellingUncheckedJS.js, 1, 18)) - var locals = 2 + var locals = 2 + true >locals : Symbol(locals, Decl(spellingUncheckedJS.js, 4, 7)) locale.toFixed() diff --git a/tests/baselines/reference/spellingUncheckedJS.types b/tests/baselines/reference/spellingUncheckedJS.types index ec966ea82477e..0c568b7f1e360 100644 --- a/tests/baselines/reference/spellingUncheckedJS.types +++ b/tests/baselines/reference/spellingUncheckedJS.types @@ -12,9 +12,11 @@ inmodule.toFixed() function f() { >f : () => void - var locals = 2 ->locals : number + var locals = 2 + true +>locals : any +>2 + true : any >2 : 2 +>true : true locale.toFixed() >locale.toFixed() : any diff --git a/tests/cases/conformance/salsa/spellingCheckedJS2.ts b/tests/cases/conformance/salsa/spellingCheckedJS2.ts new file mode 100644 index 0000000000000..1c10ac36f5baf --- /dev/null +++ b/tests/cases/conformance/salsa/spellingCheckedJS2.ts @@ -0,0 +1,48 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @filename: spellingCheckedJS2.js +export var inModule = 1 +inmodule.toFixed() + +function f() { + var locals = 2 + locale.toFixed() +} +class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } +} +class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } +} + + +var object = { + spaaace: 3 +} +object.spaaaace // error on read +object.spaace = 12 // error on write +object.fresh = 12 // OK +other.puuuce // OK, from another file +new Date().getGMTDate() // OK, from another file + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +AudioBuffin // etc +Jimmy +Jon + +// @filename: other.js +var Jimmy = 1 +var John = 2 +Jon // error, it's from the same file +var other = { + puuce: 4 +} diff --git a/tests/cases/conformance/salsa/spellingNocheckJS2.ts b/tests/cases/conformance/salsa/spellingNocheckJS2.ts new file mode 100644 index 0000000000000..538a0e599b5e2 --- /dev/null +++ b/tests/cases/conformance/salsa/spellingNocheckJS2.ts @@ -0,0 +1,48 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: false +// @filename: spellingNocheckJS2.js +export var inModule = 1 +inmodule.toFixed() + +function f() { + var locals = 2 + locale.toFixed() +} +class Classe { + non = 'oui' + methode() { + // no error on 'this' references + return this.none + } +} +class Derivee extends Classe { + methode() { + // no error on 'super' references + return super.none + } +} + + +var object = { + spaaace: 3 +} +object.spaaaace // error on read +object.spaace = 12 // error on write +object.fresh = 12 // OK +other.puuuce // OK, from another file +new Date().getGMTDate() // OK, from another file + +// No suggestions for globals from other files +const atoc = setIntegral(() => console.log('ok'), 500) +AudioBuffin // etc +Jimmy +Jon + +// @filename: other.js +var Jimmy = 1 +var John = 2 +Jon // error, it's from the same file +var other = { + puuce: 4 +} diff --git a/tests/cases/conformance/salsa/spellingUncheckedJS.ts b/tests/cases/conformance/salsa/spellingUncheckedJS.ts index 166b99492c560..f2b382df9b4f1 100644 --- a/tests/cases/conformance/salsa/spellingUncheckedJS.ts +++ b/tests/cases/conformance/salsa/spellingUncheckedJS.ts @@ -5,7 +5,7 @@ export var inModule = 1 inmodule.toFixed() function f() { - var locals = 2 + var locals = 2 + true locale.toFixed() } class Classe { From 0384117abb88b4e4a073199d5be8840a1a514c12 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 28 May 2021 12:02:47 -0700 Subject: [PATCH 03/16] NOW they all pass --- .../jsFileClassPropertyInitalizationInObjectLiteral.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types b/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types index 52922bcefb6c0..184845e1e4bcc 100644 --- a/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types +++ b/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types @@ -15,7 +15,7 @@ module.exports = function () { c: A.b = 1, >c : number >A.b = 1 : 1 ->A.b : error +>A.b : any >A : typeof A >b : any >1 : 1 From 2f5ba2bf0bed93be9957e8b9905fa8aa149a6e27 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 28 May 2021 12:03:52 -0700 Subject: [PATCH 04/16] add tonnes of semi-colons --- src/compiler/checker.ts | 10 +++++----- src/compiler/program.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 888f0489de88c..16e48993ca8b6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2062,7 +2062,7 @@ namespace ts { suggestion = undefined; } if (originalLocation && isBadUncheckedJSSuggestion(lastLocation, /*parent*/ undefined, suggestion?.valueDeclaration ? [suggestion.valueDeclaration] : [])) { - suggestion = undefined + suggestion = undefined; } if (suggestion) { const suggestionName = symbolToString(suggestion); @@ -27423,7 +27423,7 @@ namespace ts { if (!prop) { const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; if (!(indexInfo && indexInfo.type)) { - const isBadJSSuggestion = isBadUncheckedJSSuggestion(node, leftType.symbol, leftType.symbol?.declarations) + const isBadJSSuggestion = isBadUncheckedJSSuggestion(node, leftType.symbol, leftType.symbol?.declarations); if (isBadJSSuggestion === undefined ? isJSLiteralType(leftType) : isBadJSSuggestion) { return anyType; } @@ -27476,12 +27476,12 @@ namespace ts { * TODO: Should probably only pass one declaration */ function isBadUncheckedJSSuggestion(node: Node | undefined, parent: Symbol | undefined, declarations: Node[] | undefined): boolean | undefined { - const file = getSourceFileOfNode(node) + const file = getSourceFileOfNode(node); if (file) { if (compilerOptions.checkJs === undefined && !file.checkJsDirective && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { - const parentFile = forEach(declarations, getSourceFileOfNode) + const parentFile = forEach(declarations, getSourceFileOfNode); return file !== parentFile && !!parentFile && isGlobalSourceFile(parentFile) - || !!(parent && parent.flags & SymbolFlags.Class) + || !!(parent && parent.flags & SymbolFlags.Class); } } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index db42c04cbb120..4b609077a2d4b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1928,8 +1928,8 @@ namespace ts { let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; if (isCheckJsUndefined && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX)) { // TODO: It can just be checkDiagnostics srsly - bindDiagnostics = bindDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) - checkDiagnostics = checkDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code) + bindDiagnostics = bindDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code); + checkDiagnostics = checkDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code); } return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJsTrue ? sourceFile.jsDocDiagnostics : undefined); }); From bb589a477201763d970ff189e42a5360d28ccc79 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:43:31 -0700 Subject: [PATCH 05/16] restore this.x check+add a test case --- src/compiler/checker.ts | 4 ++-- .../reference/spellingUncheckedJS.errors.txt | 4 ++++ .../reference/spellingUncheckedJS.symbols | 10 ++++++++++ .../reference/spellingUncheckedJS.types | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 16e48993ca8b6..e8f3c50fdd837 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14577,7 +14577,6 @@ namespace ts { if (noImplicitAny) { return false; // Flag is meaningless under `noImplicitAny` mode } - if (getObjectFlags(type) & ObjectFlags.JSLiteral) { return true; } @@ -27481,7 +27480,8 @@ namespace ts { if (compilerOptions.checkJs === undefined && !file.checkJsDirective && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const parentFile = forEach(declarations, getSourceFileOfNode); return file !== parentFile && !!parentFile && isGlobalSourceFile(parentFile) - || !!(parent && parent.flags & SymbolFlags.Class); + || !!(parent && parent.flags & SymbolFlags.Class) + || !!node && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword; } } } diff --git a/tests/baselines/reference/spellingUncheckedJS.errors.txt b/tests/baselines/reference/spellingUncheckedJS.errors.txt index 549ca5edb810b..7e2f6746f2568 100644 --- a/tests/baselines/reference/spellingUncheckedJS.errors.txt +++ b/tests/baselines/reference/spellingUncheckedJS.errors.txt @@ -53,6 +53,8 @@ tests/cases/conformance/salsa/spellingUncheckedJS.js(27,8): error TS2551: Proper AudioBuffin // etc Jimmy Jon + window.argle + self.blargle ==== tests/cases/conformance/salsa/other.js (1 errors) ==== var Jimmy = 1 @@ -64,4 +66,6 @@ tests/cases/conformance/salsa/spellingUncheckedJS.js(27,8): error TS2551: Proper var other = { puuce: 4 } + window.argle + self.blargle \ No newline at end of file diff --git a/tests/baselines/reference/spellingUncheckedJS.symbols b/tests/baselines/reference/spellingUncheckedJS.symbols index af754a34f3efc..e8925ea763c5f 100644 --- a/tests/baselines/reference/spellingUncheckedJS.symbols +++ b/tests/baselines/reference/spellingUncheckedJS.symbols @@ -73,6 +73,11 @@ Jimmy >Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) Jon +window.argle +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + +self.blargle +>self : Symbol(self, Decl(lib.dom.d.ts, --, --)) === tests/cases/conformance/salsa/other.js === var Jimmy = 1 @@ -88,4 +93,9 @@ var other = { puuce: 4 >puuce : Symbol(puuce, Decl(other.js, 3, 13)) } +window.argle +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + +self.blargle +>self : Symbol(self, Decl(lib.dom.d.ts, --, --)) diff --git a/tests/baselines/reference/spellingUncheckedJS.types b/tests/baselines/reference/spellingUncheckedJS.types index 0c568b7f1e360..0c9e28132b5bd 100644 --- a/tests/baselines/reference/spellingUncheckedJS.types +++ b/tests/baselines/reference/spellingUncheckedJS.types @@ -118,6 +118,16 @@ Jimmy Jon >Jon : any +window.argle +>window.argle : any +>window : Window & typeof globalThis +>argle : any + +self.blargle +>self.blargle : any +>self : Window & typeof globalThis +>blargle : any + === tests/cases/conformance/salsa/other.js === var Jimmy = 1 >Jimmy : number @@ -138,4 +148,13 @@ var other = { >puuce : number >4 : 4 } +window.argle +>window.argle : any +>window : Window & typeof globalThis +>argle : any + +self.blargle +>self.blargle : any +>self : Window & typeof globalThis +>blargle : any From 37ec4500cf40f0777affe41557b8eafc131f2cb2 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 1 Jun 2021 10:57:55 -0700 Subject: [PATCH 06/16] make ts-ignore/no-check codefix work in unchecked js --- .../codeFixDisableJsDiagnosticsInFile11.ts | 29 +++++++++++++++++++ .../codeFixDisableJsDiagnosticsInFile12.ts | 29 +++++++++++++++++++ ...eFixUnusedIdentifier_delete_templateTag.ts | 1 + ...eFixUnusedIdentifier_jsdocTypeParameter.ts | 1 + .../convertToEs6Class_emptyCatchClause.ts | 1 + 5 files changed, 61 insertions(+) create mode 100644 tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts create mode 100644 tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts diff --git a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts new file mode 100644 index 0000000000000..283bdcee3ecfc --- /dev/null +++ b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts @@ -0,0 +1,29 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// function f() { +//// var locals = 2 +//// [|locale|].toFixed() +//// return locals +//// } + +verify.codeFixAvailable([ + { description: "Change spelling to 'locals'" }, + { description: "Ignore this error message" }, + { description: "Disable checking for this file" }, +]); + +verify.codeFix({ + description: ts.Diagnostics.Ignore_this_error_message.message, + index: 1, + newFileContent: +`function f() { + var locals = 2 + // @ts-ignore + locale.toFixed() + return locals +}`, +}); diff --git a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts new file mode 100644 index 0000000000000..5d706f7a7e104 --- /dev/null +++ b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts @@ -0,0 +1,29 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// function f() { +//// var locals = 2 +//// [|locale|].toFixed() +//// return locals +//// } + +verify.codeFixAvailable([ + { description: "Change spelling to 'locals'" }, + { description: "Ignore this error message" }, + { description: "Disable checking for this file" }, +]); + +verify.codeFix({ + description: ts.Diagnostics.Disable_checking_for_this_file.message, + index: 2, + newFileContent: +`// @ts-nocheck +function f() { + var locals = 2 + locale.toFixed() + return locals +}`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts index 5c0dae5b521dd..8d4466c777b29 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_delete_templateTag.ts @@ -53,6 +53,7 @@ function second(p) { return p; }`, goTo.file("/both.js"); verify.codeFix({ description: "Remove template tag", + index: 0, newFileContent: `/** * */ diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_jsdocTypeParameter.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_jsdocTypeParameter.ts index 9367a1214d8f0..4d6d624bd3f9a 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_jsdocTypeParameter.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_jsdocTypeParameter.ts @@ -10,6 +10,7 @@ verify.codeFix({ description: "Remove type parameters", + index: 0, newFileContent: `/** * @type {() => void} diff --git a/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts b/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts index fa6353397ca39..bc3f69ba78724 100644 --- a/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts +++ b/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts @@ -9,6 +9,7 @@ verify.codeFix({ description: "Convert function to an ES2015 class", + index: 2, newFileContent: `class MyClass { constructor() { } From 60fc7bb92b01f38d855d7ea010d5f3749282efb8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 2 Jun 2021 09:46:16 -0700 Subject: [PATCH 07/16] Issues errors only in the language service --- src/compiler/builder.ts | 4 +- src/compiler/program.ts | 41 ++--- src/compiler/types.ts | 2 +- src/services/codeFixProvider.ts | 2 +- src/services/codefixes/addMissingAwait.ts | 2 +- .../codefixes/disableJsDiagnostics.ts | 3 +- src/services/services.ts | 2 +- .../reference/spellingCheckedJS.errors.txt | 83 ----------- .../reference/spellingCheckedJS.symbols | 92 ------------ .../reference/spellingCheckedJS.types | 140 ------------------ .../reference/spellingCheckedJS2.errors.txt | 82 ---------- .../reference/spellingCheckedJS2.symbols | 91 ------------ .../reference/spellingCheckedJS2.types | 139 ----------------- .../reference/spellingNocheckJS.errors.txt | 53 ------- .../reference/spellingNocheckJS.symbols | 92 ------------ .../reference/spellingNocheckJS.types | 140 ------------------ .../reference/spellingNocheckJS2.symbols | 91 ------------ .../reference/spellingNocheckJS2.types | 139 ----------------- .../conformance/salsa/spellingCheckedJS.ts | 48 ------ .../conformance/salsa/spellingCheckedJS2.ts | 48 ------ .../conformance/salsa/spellingNocheckJS.ts | 48 ------ .../conformance/salsa/spellingNocheckJS2.ts | 48 ------ tests/cases/fourslash/codeFixSpellingJs1.ts | 28 ++++ tests/cases/fourslash/codeFixSpellingJs2.ts | 16 ++ tests/cases/fourslash/codeFixSpellingJs3.ts | 21 +++ tests/cases/fourslash/codeFixSpellingJs4.ts | 23 +++ tests/cases/fourslash/codeFixSpellingJs5.ts | 26 ++++ tests/cases/fourslash/codeFixSpellingJs6.ts | 56 +++++++ tests/cases/fourslash/codeFixSpellingJs7.ts | 57 +++++++ 29 files changed, 257 insertions(+), 1360 deletions(-) delete mode 100644 tests/baselines/reference/spellingCheckedJS.errors.txt delete mode 100644 tests/baselines/reference/spellingCheckedJS.symbols delete mode 100644 tests/baselines/reference/spellingCheckedJS.types delete mode 100644 tests/baselines/reference/spellingCheckedJS2.errors.txt delete mode 100644 tests/baselines/reference/spellingCheckedJS2.symbols delete mode 100644 tests/baselines/reference/spellingCheckedJS2.types delete mode 100644 tests/baselines/reference/spellingNocheckJS.errors.txt delete mode 100644 tests/baselines/reference/spellingNocheckJS.symbols delete mode 100644 tests/baselines/reference/spellingNocheckJS.types delete mode 100644 tests/baselines/reference/spellingNocheckJS2.symbols delete mode 100644 tests/baselines/reference/spellingNocheckJS2.types delete mode 100644 tests/cases/conformance/salsa/spellingCheckedJS.ts delete mode 100644 tests/cases/conformance/salsa/spellingCheckedJS2.ts delete mode 100644 tests/cases/conformance/salsa/spellingNocheckJS.ts delete mode 100644 tests/cases/conformance/salsa/spellingNocheckJS2.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs1.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs2.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs3.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs4.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs5.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs6.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs7.ts diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 1b6bdf5eecb4e..619e8ff38a10b 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -693,7 +693,7 @@ namespace ts { const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path); // Report the bind and check diagnostics from the cache if we already have those diagnostics present if (cachedDiagnostics) { - return filterSemanticDiagnotics(cachedDiagnostics, state.compilerOptions); + return filterSemanticDiagnostics(cachedDiagnostics, state.compilerOptions); } } @@ -702,7 +702,7 @@ namespace ts { if (state.semanticDiagnosticsPerFile) { state.semanticDiagnosticsPerFile.set(path, diagnostics); } - return filterSemanticDiagnotics(diagnostics, state.compilerOptions); + return filterSemanticDiagnostics(diagnostics, state.compilerOptions); } export type ProgramBuildInfoDiagnostic = number | [fileId: number, diagnostics: readonly ReusableDiagnostic[]]; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4b609077a2d4b..94469d9d3f799 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1802,25 +1802,26 @@ namespace ts { function getDiagnosticsHelper( sourceFile: SourceFile | undefined, - getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined) => readonly T[], - cancellationToken: CancellationToken | undefined): readonly T[] { + getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined) => readonly T[], + cancellationToken: CancellationToken | undefined, + includeUncheckedJS: boolean | undefined): readonly T[] { if (sourceFile) { - return getDiagnostics(sourceFile, cancellationToken); + return getDiagnostics(sourceFile, cancellationToken, includeUncheckedJS); } return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => { if (cancellationToken) { cancellationToken.throwIfCancellationRequested(); } - return getDiagnostics(sourceFile, cancellationToken); + return getDiagnostics(sourceFile, cancellationToken, includeUncheckedJS); })); } function getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { - return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken); + return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken, /*includeUncheckedJS*/ undefined); } - function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { - return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken); + function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[] { + return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken, includeUncheckedJS); } function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined { @@ -1830,7 +1831,7 @@ namespace ts { } function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { - return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken); + return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken, /*includeUncheckedJS*/ undefined); } function getProgramDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] { @@ -1853,7 +1854,7 @@ namespace ts { return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken); } else { - return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken); + return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken, /*includeUncheckedJS*/ undefined); } } @@ -1892,18 +1893,18 @@ namespace ts { } } - function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { + function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined): readonly Diagnostic[] { return concatenate( - filterSemanticDiagnotics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken), options), + filterSemanticDiagnostics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken, includeUncheckedJS), options), getProgramDiagnostics(sourceFile) ); } - function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { - return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache); + function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined): readonly Diagnostic[] { + return getAndCacheDiagnostics(sourceFile, cancellationToken, includeUncheckedJS, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache); } - function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { + function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined): readonly Diagnostic[] { return runWithCancellationToken(() => { if (skipTypeChecking(sourceFile, options, program)) { return emptyArray; @@ -1914,7 +1915,7 @@ namespace ts { Debug.assert(!!sourceFile.bindDiagnostics); const isCheckJsTrue = isCheckJsEnabledForFile(sourceFile, options); - const isCheckJsUndefined = sourceFile.checkJsDirective === undefined && options.checkJs === undefined; + const isCheckJsUndefined = includeUncheckedJS && sourceFile.checkJsDirective === undefined && options.checkJs === undefined; const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) const includeBindAndCheckDiagnostics = !isTsNoCheck && @@ -1944,6 +1945,7 @@ namespace ts { const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics); for (const errorExpectation of directives.getUnusedExpectations()) { + // TODO: Need to test ts-expect-error (although it's not a good idea but w/e) diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive)); } @@ -2195,7 +2197,7 @@ namespace ts { } function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] { - return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache); + return getAndCacheDiagnostics(sourceFile, cancellationToken, /*includeUncheckedJS*/ undefined, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache); } function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] { @@ -2209,8 +2211,9 @@ namespace ts { function getAndCacheDiagnostics( sourceFile: T, cancellationToken: CancellationToken | undefined, + includeUncheckedJS: boolean | undefined, cache: DiagnosticCache, - getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[], + getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined) => readonly U[], ): readonly U[] { const cachedResult = sourceFile @@ -2220,7 +2223,7 @@ namespace ts { if (cachedResult) { return cachedResult; } - const result = getDiagnostics(sourceFile, cancellationToken); + const result = getDiagnostics(sourceFile, cancellationToken, includeUncheckedJS); if (sourceFile) { (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result); } @@ -3896,7 +3899,7 @@ namespace ts { } /*@internal*/ - export function filterSemanticDiagnotics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] { + export function filterSemanticDiagnostics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] { return filter(diagnostic, d => !d.skippedOn || !option[d.skippedOn]); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f395014baad1d..626ee5147aace 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3900,7 +3900,7 @@ namespace ts { getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; /** The first time this is called, it will return global diagnostics (no location). */ - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; getConfigFileParsingDiagnostics(): readonly Diagnostic[]; /* @internal */ getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index 5baf4734002fc..2c6abcf703eea 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -95,7 +95,7 @@ namespace ts.codefix { function getDiagnostics({ program, sourceFile, cancellationToken }: CodeFixContextBase) { return [ - ...program.getSemanticDiagnostics(sourceFile, cancellationToken), + ...program.getSemanticDiagnostics(sourceFile, cancellationToken, /*includeUncheckedJS*/ true), ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), ...computeSuggestionDiagnostics(sourceFile, program, cancellationToken) ]; diff --git a/src/services/codefixes/addMissingAwait.ts b/src/services/codefixes/addMissingAwait.ts index e3a15d8864144..08008dc2c150a 100644 --- a/src/services/codefixes/addMissingAwait.ts +++ b/src/services/codefixes/addMissingAwait.ts @@ -156,7 +156,7 @@ namespace ts.codefix { continue; } - const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); + const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken, /*includeUncheckedJS*/ true); const isUsedElsewhere = FindAllReferences.Core.eachSymbolReferenceInFile(variableName, checker, sourceFile, reference => { return identifier !== reference && !symbolReferenceIsAlsoMissingAwait(reference, diagnostics, sourceFile, checker); }); diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 4469ffb6b716f..1ce68c81f7c68 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -12,7 +12,8 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile, program, span, host, formatContext } = context; - if (!isInJSFile(sourceFile) || !isCheckJsEnabledForFile(sourceFile, program.getCompilerOptions())) { + const isCheckJsUndefined = sourceFile.checkJsDirective === undefined && program.getCompilerOptions().checkJs === undefined; + if (!isInJSFile(sourceFile) || !isCheckJsEnabledForFile(sourceFile, program.getCompilerOptions()) && !isCheckJsUndefined) { return undefined; } diff --git a/src/services/services.ts b/src/services/services.ts index afe43e32a79c2..1e9cf13f7a064 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1560,7 +1560,7 @@ namespace ts { // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file. // Therefore only get diagnostics for given file. - const semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken); + const semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken, /*includeUncheckedJS*/ true); if (!getEmitDeclarations(program.getCompilerOptions())) { return semanticDiagnostics.slice(); } diff --git a/tests/baselines/reference/spellingCheckedJS.errors.txt b/tests/baselines/reference/spellingCheckedJS.errors.txt deleted file mode 100644 index 511ac91052e48..0000000000000 --- a/tests/baselines/reference/spellingCheckedJS.errors.txt +++ /dev/null @@ -1,83 +0,0 @@ -tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -tests/cases/conformance/salsa/spellingCheckedJS.js(3,1): error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? -tests/cases/conformance/salsa/spellingCheckedJS.js(7,5): error TS2552: Cannot find name 'locale'. Did you mean 'locals'? -tests/cases/conformance/salsa/spellingCheckedJS.js(13,21): error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? -tests/cases/conformance/salsa/spellingCheckedJS.js(19,22): error TS2339: Property 'none' does not exist on type 'Classe'. -tests/cases/conformance/salsa/spellingCheckedJS.js(31,12): error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? -tests/cases/conformance/salsa/spellingCheckedJS.js(34,14): error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? -tests/cases/conformance/salsa/spellingCheckedJS.js(35,1): error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? -tests/cases/conformance/salsa/spellingCheckedJS.js(37,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? - - -==== tests/cases/conformance/salsa/spellingCheckedJS.js (8 errors) ==== - // @ts-check - export var inModule = 1 - inmodule.toFixed() - ~~~~~~~~ -!!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? - - function f() { - var locals = 2 - locale.toFixed() - ~~~~~~ -!!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? -!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS.js:6:9: 'locals' is declared here. - } - class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - ~~~~ -!!! error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? -!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS.js:10:5: 'non' is declared here. - } - } - class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - ~~~~ -!!! error TS2339: Property 'none' does not exist on type 'Classe'. - } - } - - - var object = { - spaaace: 3 - } - object.spaaaace // error on read - object.spaace = 12 // error on write - object.fresh = 12 // OK - other.puuuce // OK, from another file - new Date().getGMTDate() // OK, from another file - ~~~~~~~~~~ -!!! error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? -!!! related TS2728 /.ts/lib.es5.d.ts:757:5: 'getUTCDate' is declared here. - - // No suggestions for globals from other files - const atoc = setIntegral(() => console.log('ok'), 500) - ~~~~~~~~~~~ -!!! error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? -!!! related TS2728 /.ts/lib.dom.d.ts:19626:18: 'setInterval' is declared here. - AudioBuffin // etc - ~~~~~~~~~~~ -!!! error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? -!!! related TS2728 /.ts/lib.dom.d.ts:2246:13: 'AudioBuffer' is declared here. - Jimmy - Jon - ~~~ -!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. - -==== tests/cases/conformance/salsa/other.js (1 errors) ==== - var Jimmy = 1 - var John = 2 - Jon // error, it's from the same file - ~~~ -!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. - var other = { - puuce: 4 - } - \ No newline at end of file diff --git a/tests/baselines/reference/spellingCheckedJS.symbols b/tests/baselines/reference/spellingCheckedJS.symbols deleted file mode 100644 index 51cae91cffe47..0000000000000 --- a/tests/baselines/reference/spellingCheckedJS.symbols +++ /dev/null @@ -1,92 +0,0 @@ -=== tests/cases/conformance/salsa/spellingCheckedJS.js === -// @ts-check -export var inModule = 1 ->inModule : Symbol(inModule, Decl(spellingCheckedJS.js, 1, 10)) - -inmodule.toFixed() - -function f() { ->f : Symbol(f, Decl(spellingCheckedJS.js, 2, 18)) - - var locals = 2 ->locals : Symbol(locals, Decl(spellingCheckedJS.js, 5, 7)) - - locale.toFixed() -} -class Classe { ->Classe : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) - - non = 'oui' ->non : Symbol(Classe.non, Decl(spellingCheckedJS.js, 8, 14)) - - methode() { ->methode : Symbol(Classe.methode, Decl(spellingCheckedJS.js, 9, 15)) - - // no error on 'this' references - return this.none ->this : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) - } -} -class Derivee extends Classe { ->Derivee : Symbol(Derivee, Decl(spellingCheckedJS.js, 14, 1)) ->Classe : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) - - methode() { ->methode : Symbol(Derivee.methode, Decl(spellingCheckedJS.js, 15, 30)) - - // no error on 'super' references - return super.none ->super : Symbol(Classe, Decl(spellingCheckedJS.js, 7, 1)) - } -} - - -var object = { ->object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) - - spaaace: 3 ->spaaace : Symbol(spaaace, Decl(spellingCheckedJS.js, 23, 14)) -} -object.spaaaace // error on read ->object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) - -object.spaace = 12 // error on write ->object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) - -object.fresh = 12 // OK ->object : Symbol(object, Decl(spellingCheckedJS.js, 23, 3), Decl(spellingCheckedJS.js, 26, 15), Decl(spellingCheckedJS.js, 27, 18)) - -other.puuuce // OK, from another file ->other : Symbol(other, Decl(other.js, 3, 3)) - -new Date().getGMTDate() // OK, from another file ->Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : Symbol(atoc, Decl(spellingCheckedJS.js, 33, 5)) ->console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->console : Symbol(console, Decl(lib.dom.d.ts, --, --)) ->log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) - -AudioBuffin // etc -Jimmy ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -Jon - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -var John = 2 ->John : Symbol(John, Decl(other.js, 1, 3)) - -Jon // error, it's from the same file -var other = { ->other : Symbol(other, Decl(other.js, 3, 3)) - - puuce: 4 ->puuce : Symbol(puuce, Decl(other.js, 3, 13)) -} - diff --git a/tests/baselines/reference/spellingCheckedJS.types b/tests/baselines/reference/spellingCheckedJS.types deleted file mode 100644 index d514df1558ac4..0000000000000 --- a/tests/baselines/reference/spellingCheckedJS.types +++ /dev/null @@ -1,140 +0,0 @@ -=== tests/cases/conformance/salsa/spellingCheckedJS.js === -// @ts-check -export var inModule = 1 ->inModule : number ->1 : 1 - -inmodule.toFixed() ->inmodule.toFixed() : any ->inmodule.toFixed : any ->inmodule : any ->toFixed : any - -function f() { ->f : () => void - - var locals = 2 ->locals : number ->2 : 2 - - locale.toFixed() ->locale.toFixed() : any ->locale.toFixed : any ->locale : any ->toFixed : any -} -class Classe { ->Classe : Classe - - non = 'oui' ->non : string ->'oui' : "oui" - - methode() { ->methode : () => any - - // no error on 'this' references - return this.none ->this.none : any ->this : this ->none : any - } -} -class Derivee extends Classe { ->Derivee : Derivee ->Classe : Classe - - methode() { ->methode : () => any - - // no error on 'super' references - return super.none ->super.none : any ->super : Classe ->none : any - } -} - - -var object = { ->object : { spaaace: number; } ->{ spaaace: 3} : { spaaace: number; } - - spaaace: 3 ->spaaace : number ->3 : 3 -} -object.spaaaace // error on read ->object.spaaaace : any ->object : { spaaace: number; } ->spaaaace : any - -object.spaace = 12 // error on write ->object.spaace = 12 : 12 ->object.spaace : any ->object : { spaaace: number; } ->spaace : any ->12 : 12 - -object.fresh = 12 // OK ->object.fresh = 12 : 12 ->object.fresh : any ->object : { spaaace: number; } ->fresh : any ->12 : 12 - -other.puuuce // OK, from another file ->other.puuuce : any ->other : { puuce: number; } ->puuuce : any - -new Date().getGMTDate() // OK, from another file ->new Date().getGMTDate() : any ->new Date().getGMTDate : any ->new Date() : Date ->Date : DateConstructor ->getGMTDate : any - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : any ->setIntegral(() => console.log('ok'), 500) : any ->setIntegral : any ->() => console.log('ok') : () => void ->console.log('ok') : void ->console.log : (...data: any[]) => void ->console : Console ->log : (...data: any[]) => void ->'ok' : "ok" ->500 : 500 - -AudioBuffin // etc ->AudioBuffin : any - -Jimmy ->Jimmy : number - -Jon ->Jon : any - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : number ->1 : 1 - -var John = 2 ->John : number ->2 : 2 - -Jon // error, it's from the same file ->Jon : any - -var other = { ->other : { puuce: number; } ->{ puuce: 4} : { puuce: number; } - - puuce: 4 ->puuce : number ->4 : 4 -} - diff --git a/tests/baselines/reference/spellingCheckedJS2.errors.txt b/tests/baselines/reference/spellingCheckedJS2.errors.txt deleted file mode 100644 index 39e996c71cb09..0000000000000 --- a/tests/baselines/reference/spellingCheckedJS2.errors.txt +++ /dev/null @@ -1,82 +0,0 @@ -tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(2,1): error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(6,5): error TS2552: Cannot find name 'locale'. Did you mean 'locals'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(12,21): error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(18,22): error TS2339: Property 'none' does not exist on type 'Classe'. -tests/cases/conformance/salsa/spellingCheckedJS2.js(30,12): error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(33,14): error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(34,1): error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? -tests/cases/conformance/salsa/spellingCheckedJS2.js(36,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? - - -==== tests/cases/conformance/salsa/spellingCheckedJS2.js (8 errors) ==== - export var inModule = 1 - inmodule.toFixed() - ~~~~~~~~ -!!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? - - function f() { - var locals = 2 - locale.toFixed() - ~~~~~~ -!!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? -!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS2.js:5:9: 'locals' is declared here. - } - class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - ~~~~ -!!! error TS2551: Property 'none' does not exist on type 'Classe'. Did you mean 'non'? -!!! related TS2728 tests/cases/conformance/salsa/spellingCheckedJS2.js:9:5: 'non' is declared here. - } - } - class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - ~~~~ -!!! error TS2339: Property 'none' does not exist on type 'Classe'. - } - } - - - var object = { - spaaace: 3 - } - object.spaaaace // error on read - object.spaace = 12 // error on write - object.fresh = 12 // OK - other.puuuce // OK, from another file - new Date().getGMTDate() // OK, from another file - ~~~~~~~~~~ -!!! error TS2551: Property 'getGMTDate' does not exist on type 'Date'. Did you mean 'getUTCDate'? -!!! related TS2728 /.ts/lib.es5.d.ts:757:5: 'getUTCDate' is declared here. - - // No suggestions for globals from other files - const atoc = setIntegral(() => console.log('ok'), 500) - ~~~~~~~~~~~ -!!! error TS2552: Cannot find name 'setIntegral'. Did you mean 'setInterval'? -!!! related TS2728 /.ts/lib.dom.d.ts:19626:18: 'setInterval' is declared here. - AudioBuffin // etc - ~~~~~~~~~~~ -!!! error TS2552: Cannot find name 'AudioBuffin'. Did you mean 'AudioBuffer'? -!!! related TS2728 /.ts/lib.dom.d.ts:2246:13: 'AudioBuffer' is declared here. - Jimmy - Jon - ~~~ -!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. - -==== tests/cases/conformance/salsa/other.js (1 errors) ==== - var Jimmy = 1 - var John = 2 - Jon // error, it's from the same file - ~~~ -!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. - var other = { - puuce: 4 - } - \ No newline at end of file diff --git a/tests/baselines/reference/spellingCheckedJS2.symbols b/tests/baselines/reference/spellingCheckedJS2.symbols deleted file mode 100644 index 5512ba82b740c..0000000000000 --- a/tests/baselines/reference/spellingCheckedJS2.symbols +++ /dev/null @@ -1,91 +0,0 @@ -=== tests/cases/conformance/salsa/spellingCheckedJS2.js === -export var inModule = 1 ->inModule : Symbol(inModule, Decl(spellingCheckedJS2.js, 0, 10)) - -inmodule.toFixed() - -function f() { ->f : Symbol(f, Decl(spellingCheckedJS2.js, 1, 18)) - - var locals = 2 ->locals : Symbol(locals, Decl(spellingCheckedJS2.js, 4, 7)) - - locale.toFixed() -} -class Classe { ->Classe : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) - - non = 'oui' ->non : Symbol(Classe.non, Decl(spellingCheckedJS2.js, 7, 14)) - - methode() { ->methode : Symbol(Classe.methode, Decl(spellingCheckedJS2.js, 8, 15)) - - // no error on 'this' references - return this.none ->this : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) - } -} -class Derivee extends Classe { ->Derivee : Symbol(Derivee, Decl(spellingCheckedJS2.js, 13, 1)) ->Classe : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) - - methode() { ->methode : Symbol(Derivee.methode, Decl(spellingCheckedJS2.js, 14, 30)) - - // no error on 'super' references - return super.none ->super : Symbol(Classe, Decl(spellingCheckedJS2.js, 6, 1)) - } -} - - -var object = { ->object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) - - spaaace: 3 ->spaaace : Symbol(spaaace, Decl(spellingCheckedJS2.js, 22, 14)) -} -object.spaaaace // error on read ->object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) - -object.spaace = 12 // error on write ->object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) - -object.fresh = 12 // OK ->object : Symbol(object, Decl(spellingCheckedJS2.js, 22, 3), Decl(spellingCheckedJS2.js, 25, 15), Decl(spellingCheckedJS2.js, 26, 18)) - -other.puuuce // OK, from another file ->other : Symbol(other, Decl(other.js, 3, 3)) - -new Date().getGMTDate() // OK, from another file ->Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : Symbol(atoc, Decl(spellingCheckedJS2.js, 32, 5)) ->console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->console : Symbol(console, Decl(lib.dom.d.ts, --, --)) ->log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) - -AudioBuffin // etc -Jimmy ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -Jon - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -var John = 2 ->John : Symbol(John, Decl(other.js, 1, 3)) - -Jon // error, it's from the same file -var other = { ->other : Symbol(other, Decl(other.js, 3, 3)) - - puuce: 4 ->puuce : Symbol(puuce, Decl(other.js, 3, 13)) -} - diff --git a/tests/baselines/reference/spellingCheckedJS2.types b/tests/baselines/reference/spellingCheckedJS2.types deleted file mode 100644 index 0abf06c6bad09..0000000000000 --- a/tests/baselines/reference/spellingCheckedJS2.types +++ /dev/null @@ -1,139 +0,0 @@ -=== tests/cases/conformance/salsa/spellingCheckedJS2.js === -export var inModule = 1 ->inModule : number ->1 : 1 - -inmodule.toFixed() ->inmodule.toFixed() : any ->inmodule.toFixed : any ->inmodule : any ->toFixed : any - -function f() { ->f : () => void - - var locals = 2 ->locals : number ->2 : 2 - - locale.toFixed() ->locale.toFixed() : any ->locale.toFixed : any ->locale : any ->toFixed : any -} -class Classe { ->Classe : Classe - - non = 'oui' ->non : string ->'oui' : "oui" - - methode() { ->methode : () => any - - // no error on 'this' references - return this.none ->this.none : any ->this : this ->none : any - } -} -class Derivee extends Classe { ->Derivee : Derivee ->Classe : Classe - - methode() { ->methode : () => any - - // no error on 'super' references - return super.none ->super.none : any ->super : Classe ->none : any - } -} - - -var object = { ->object : { spaaace: number; } ->{ spaaace: 3} : { spaaace: number; } - - spaaace: 3 ->spaaace : number ->3 : 3 -} -object.spaaaace // error on read ->object.spaaaace : any ->object : { spaaace: number; } ->spaaaace : any - -object.spaace = 12 // error on write ->object.spaace = 12 : 12 ->object.spaace : any ->object : { spaaace: number; } ->spaace : any ->12 : 12 - -object.fresh = 12 // OK ->object.fresh = 12 : 12 ->object.fresh : any ->object : { spaaace: number; } ->fresh : any ->12 : 12 - -other.puuuce // OK, from another file ->other.puuuce : any ->other : { puuce: number; } ->puuuce : any - -new Date().getGMTDate() // OK, from another file ->new Date().getGMTDate() : any ->new Date().getGMTDate : any ->new Date() : Date ->Date : DateConstructor ->getGMTDate : any - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : any ->setIntegral(() => console.log('ok'), 500) : any ->setIntegral : any ->() => console.log('ok') : () => void ->console.log('ok') : void ->console.log : (...data: any[]) => void ->console : Console ->log : (...data: any[]) => void ->'ok' : "ok" ->500 : 500 - -AudioBuffin // etc ->AudioBuffin : any - -Jimmy ->Jimmy : number - -Jon ->Jon : any - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : number ->1 : 1 - -var John = 2 ->John : number ->2 : 2 - -Jon // error, it's from the same file ->Jon : any - -var other = { ->other : { puuce: number; } ->{ puuce: 4} : { puuce: number; } - - puuce: 4 ->puuce : number ->4 : 4 -} - diff --git a/tests/baselines/reference/spellingNocheckJS.errors.txt b/tests/baselines/reference/spellingNocheckJS.errors.txt deleted file mode 100644 index 1d42c836268fd..0000000000000 --- a/tests/baselines/reference/spellingNocheckJS.errors.txt +++ /dev/null @@ -1,53 +0,0 @@ -tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? - - -==== tests/cases/conformance/salsa/spellingNocheckJS.js (0 errors) ==== - // @ts-nocheck - export var inModule = 1 - inmodule.toFixed() - - function f() { - var locals = 2 - locale.toFixed() - } - class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - } - } - class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - } - } - - - var object = { - spaaace: 3 - } - object.spaaaace // error on read - object.spaace = 12 // error on write - object.fresh = 12 // OK - other.puuuce // OK, from another file - new Date().getGMTDate() // OK, from another file - - // No suggestions for globals from other files - const atoc = setIntegral(() => console.log('ok'), 500) - AudioBuffin // etc - Jimmy - Jon - -==== tests/cases/conformance/salsa/other.js (1 errors) ==== - var Jimmy = 1 - var John = 2 - Jon // error, it's from the same file - ~~~ -!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. - var other = { - puuce: 4 - } - \ No newline at end of file diff --git a/tests/baselines/reference/spellingNocheckJS.symbols b/tests/baselines/reference/spellingNocheckJS.symbols deleted file mode 100644 index df99d4e2a4288..0000000000000 --- a/tests/baselines/reference/spellingNocheckJS.symbols +++ /dev/null @@ -1,92 +0,0 @@ -=== tests/cases/conformance/salsa/spellingNocheckJS.js === -// @ts-nocheck -export var inModule = 1 ->inModule : Symbol(inModule, Decl(spellingNocheckJS.js, 1, 10)) - -inmodule.toFixed() - -function f() { ->f : Symbol(f, Decl(spellingNocheckJS.js, 2, 18)) - - var locals = 2 ->locals : Symbol(locals, Decl(spellingNocheckJS.js, 5, 7)) - - locale.toFixed() -} -class Classe { ->Classe : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) - - non = 'oui' ->non : Symbol(Classe.non, Decl(spellingNocheckJS.js, 8, 14)) - - methode() { ->methode : Symbol(Classe.methode, Decl(spellingNocheckJS.js, 9, 15)) - - // no error on 'this' references - return this.none ->this : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) - } -} -class Derivee extends Classe { ->Derivee : Symbol(Derivee, Decl(spellingNocheckJS.js, 14, 1)) ->Classe : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) - - methode() { ->methode : Symbol(Derivee.methode, Decl(spellingNocheckJS.js, 15, 30)) - - // no error on 'super' references - return super.none ->super : Symbol(Classe, Decl(spellingNocheckJS.js, 7, 1)) - } -} - - -var object = { ->object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) - - spaaace: 3 ->spaaace : Symbol(spaaace, Decl(spellingNocheckJS.js, 23, 14)) -} -object.spaaaace // error on read ->object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) - -object.spaace = 12 // error on write ->object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) - -object.fresh = 12 // OK ->object : Symbol(object, Decl(spellingNocheckJS.js, 23, 3), Decl(spellingNocheckJS.js, 26, 15), Decl(spellingNocheckJS.js, 27, 18)) - -other.puuuce // OK, from another file ->other : Symbol(other, Decl(other.js, 3, 3)) - -new Date().getGMTDate() // OK, from another file ->Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : Symbol(atoc, Decl(spellingNocheckJS.js, 33, 5)) ->console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->console : Symbol(console, Decl(lib.dom.d.ts, --, --)) ->log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) - -AudioBuffin // etc -Jimmy ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -Jon - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -var John = 2 ->John : Symbol(John, Decl(other.js, 1, 3)) - -Jon // error, it's from the same file -var other = { ->other : Symbol(other, Decl(other.js, 3, 3)) - - puuce: 4 ->puuce : Symbol(puuce, Decl(other.js, 3, 13)) -} - diff --git a/tests/baselines/reference/spellingNocheckJS.types b/tests/baselines/reference/spellingNocheckJS.types deleted file mode 100644 index 0d3489d62560b..0000000000000 --- a/tests/baselines/reference/spellingNocheckJS.types +++ /dev/null @@ -1,140 +0,0 @@ -=== tests/cases/conformance/salsa/spellingNocheckJS.js === -// @ts-nocheck -export var inModule = 1 ->inModule : number ->1 : 1 - -inmodule.toFixed() ->inmodule.toFixed() : any ->inmodule.toFixed : any ->inmodule : any ->toFixed : any - -function f() { ->f : () => void - - var locals = 2 ->locals : number ->2 : 2 - - locale.toFixed() ->locale.toFixed() : any ->locale.toFixed : any ->locale : any ->toFixed : any -} -class Classe { ->Classe : Classe - - non = 'oui' ->non : string ->'oui' : "oui" - - methode() { ->methode : () => any - - // no error on 'this' references - return this.none ->this.none : any ->this : this ->none : any - } -} -class Derivee extends Classe { ->Derivee : Derivee ->Classe : Classe - - methode() { ->methode : () => any - - // no error on 'super' references - return super.none ->super.none : any ->super : Classe ->none : any - } -} - - -var object = { ->object : { spaaace: number; } ->{ spaaace: 3} : { spaaace: number; } - - spaaace: 3 ->spaaace : number ->3 : 3 -} -object.spaaaace // error on read ->object.spaaaace : any ->object : { spaaace: number; } ->spaaaace : any - -object.spaace = 12 // error on write ->object.spaace = 12 : 12 ->object.spaace : any ->object : { spaaace: number; } ->spaace : any ->12 : 12 - -object.fresh = 12 // OK ->object.fresh = 12 : 12 ->object.fresh : any ->object : { spaaace: number; } ->fresh : any ->12 : 12 - -other.puuuce // OK, from another file ->other.puuuce : any ->other : { puuce: number; } ->puuuce : any - -new Date().getGMTDate() // OK, from another file ->new Date().getGMTDate() : any ->new Date().getGMTDate : any ->new Date() : Date ->Date : DateConstructor ->getGMTDate : any - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : any ->setIntegral(() => console.log('ok'), 500) : any ->setIntegral : any ->() => console.log('ok') : () => void ->console.log('ok') : void ->console.log : (...data: any[]) => void ->console : Console ->log : (...data: any[]) => void ->'ok' : "ok" ->500 : 500 - -AudioBuffin // etc ->AudioBuffin : any - -Jimmy ->Jimmy : number - -Jon ->Jon : any - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : number ->1 : 1 - -var John = 2 ->John : number ->2 : 2 - -Jon // error, it's from the same file ->Jon : any - -var other = { ->other : { puuce: number; } ->{ puuce: 4} : { puuce: number; } - - puuce: 4 ->puuce : number ->4 : 4 -} - diff --git a/tests/baselines/reference/spellingNocheckJS2.symbols b/tests/baselines/reference/spellingNocheckJS2.symbols deleted file mode 100644 index 5ea2c5065bab9..0000000000000 --- a/tests/baselines/reference/spellingNocheckJS2.symbols +++ /dev/null @@ -1,91 +0,0 @@ -=== tests/cases/conformance/salsa/spellingNocheckJS2.js === -export var inModule = 1 ->inModule : Symbol(inModule, Decl(spellingNocheckJS2.js, 0, 10)) - -inmodule.toFixed() - -function f() { ->f : Symbol(f, Decl(spellingNocheckJS2.js, 1, 18)) - - var locals = 2 ->locals : Symbol(locals, Decl(spellingNocheckJS2.js, 4, 7)) - - locale.toFixed() -} -class Classe { ->Classe : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) - - non = 'oui' ->non : Symbol(Classe.non, Decl(spellingNocheckJS2.js, 7, 14)) - - methode() { ->methode : Symbol(Classe.methode, Decl(spellingNocheckJS2.js, 8, 15)) - - // no error on 'this' references - return this.none ->this : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) - } -} -class Derivee extends Classe { ->Derivee : Symbol(Derivee, Decl(spellingNocheckJS2.js, 13, 1)) ->Classe : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) - - methode() { ->methode : Symbol(Derivee.methode, Decl(spellingNocheckJS2.js, 14, 30)) - - // no error on 'super' references - return super.none ->super : Symbol(Classe, Decl(spellingNocheckJS2.js, 6, 1)) - } -} - - -var object = { ->object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) - - spaaace: 3 ->spaaace : Symbol(spaaace, Decl(spellingNocheckJS2.js, 22, 14)) -} -object.spaaaace // error on read ->object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) - -object.spaace = 12 // error on write ->object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) - -object.fresh = 12 // OK ->object : Symbol(object, Decl(spellingNocheckJS2.js, 22, 3), Decl(spellingNocheckJS2.js, 25, 15), Decl(spellingNocheckJS2.js, 26, 18)) - -other.puuuce // OK, from another file ->other : Symbol(other, Decl(other.js, 3, 3)) - -new Date().getGMTDate() // OK, from another file ->Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --)) - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : Symbol(atoc, Decl(spellingNocheckJS2.js, 32, 5)) ->console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) ->console : Symbol(console, Decl(lib.dom.d.ts, --, --)) ->log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) - -AudioBuffin // etc -Jimmy ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -Jon - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) - -var John = 2 ->John : Symbol(John, Decl(other.js, 1, 3)) - -Jon // error, it's from the same file -var other = { ->other : Symbol(other, Decl(other.js, 3, 3)) - - puuce: 4 ->puuce : Symbol(puuce, Decl(other.js, 3, 13)) -} - diff --git a/tests/baselines/reference/spellingNocheckJS2.types b/tests/baselines/reference/spellingNocheckJS2.types deleted file mode 100644 index bd77605b79e5a..0000000000000 --- a/tests/baselines/reference/spellingNocheckJS2.types +++ /dev/null @@ -1,139 +0,0 @@ -=== tests/cases/conformance/salsa/spellingNocheckJS2.js === -export var inModule = 1 ->inModule : number ->1 : 1 - -inmodule.toFixed() ->inmodule.toFixed() : error ->inmodule.toFixed : error ->inmodule : any ->toFixed : any - -function f() { ->f : () => void - - var locals = 2 ->locals : number ->2 : 2 - - locale.toFixed() ->locale.toFixed() : error ->locale.toFixed : error ->locale : any ->toFixed : any -} -class Classe { ->Classe : Classe - - non = 'oui' ->non : string ->'oui' : "oui" - - methode() { ->methode : () => any - - // no error on 'this' references - return this.none ->this.none : error ->this : this ->none : any - } -} -class Derivee extends Classe { ->Derivee : Derivee ->Classe : Classe - - methode() { ->methode : () => any - - // no error on 'super' references - return super.none ->super.none : error ->super : Classe ->none : any - } -} - - -var object = { ->object : { spaaace: number; } ->{ spaaace: 3} : { spaaace: number; } - - spaaace: 3 ->spaaace : number ->3 : 3 -} -object.spaaaace // error on read ->object.spaaaace : any ->object : { spaaace: number; } ->spaaaace : any - -object.spaace = 12 // error on write ->object.spaace = 12 : 12 ->object.spaace : any ->object : { spaaace: number; } ->spaace : any ->12 : 12 - -object.fresh = 12 // OK ->object.fresh = 12 : 12 ->object.fresh : any ->object : { spaaace: number; } ->fresh : any ->12 : 12 - -other.puuuce // OK, from another file ->other.puuuce : any ->other : { puuce: number; } ->puuuce : any - -new Date().getGMTDate() // OK, from another file ->new Date().getGMTDate() : error ->new Date().getGMTDate : error ->new Date() : Date ->Date : DateConstructor ->getGMTDate : any - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : error ->setIntegral(() => console.log('ok'), 500) : error ->setIntegral : error ->() => console.log('ok') : () => void ->console.log('ok') : void ->console.log : (...data: any[]) => void ->console : Console ->log : (...data: any[]) => void ->'ok' : "ok" ->500 : 500 - -AudioBuffin // etc ->AudioBuffin : error - -Jimmy ->Jimmy : number - -Jon ->Jon : error - -=== tests/cases/conformance/salsa/other.js === -var Jimmy = 1 ->Jimmy : number ->1 : 1 - -var John = 2 ->John : number ->2 : 2 - -Jon // error, it's from the same file ->Jon : error - -var other = { ->other : { puuce: number; } ->{ puuce: 4} : { puuce: number; } - - puuce: 4 ->puuce : number ->4 : 4 -} - diff --git a/tests/cases/conformance/salsa/spellingCheckedJS.ts b/tests/cases/conformance/salsa/spellingCheckedJS.ts deleted file mode 100644 index a79eeaf122850..0000000000000 --- a/tests/cases/conformance/salsa/spellingCheckedJS.ts +++ /dev/null @@ -1,48 +0,0 @@ -// @noEmit: true -// @allowJs: true -// @filename: spellingCheckedJS.js -// @ts-check -export var inModule = 1 -inmodule.toFixed() - -function f() { - var locals = 2 - locale.toFixed() -} -class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - } -} -class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - } -} - - -var object = { - spaaace: 3 -} -object.spaaaace // error on read -object.spaace = 12 // error on write -object.fresh = 12 // OK -other.puuuce // OK, from another file -new Date().getGMTDate() // OK, from another file - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) -AudioBuffin // etc -Jimmy -Jon - -// @filename: other.js -var Jimmy = 1 -var John = 2 -Jon // error, it's from the same file -var other = { - puuce: 4 -} diff --git a/tests/cases/conformance/salsa/spellingCheckedJS2.ts b/tests/cases/conformance/salsa/spellingCheckedJS2.ts deleted file mode 100644 index 1c10ac36f5baf..0000000000000 --- a/tests/cases/conformance/salsa/spellingCheckedJS2.ts +++ /dev/null @@ -1,48 +0,0 @@ -// @noEmit: true -// @allowJs: true -// @checkJs: true -// @filename: spellingCheckedJS2.js -export var inModule = 1 -inmodule.toFixed() - -function f() { - var locals = 2 - locale.toFixed() -} -class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - } -} -class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - } -} - - -var object = { - spaaace: 3 -} -object.spaaaace // error on read -object.spaace = 12 // error on write -object.fresh = 12 // OK -other.puuuce // OK, from another file -new Date().getGMTDate() // OK, from another file - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) -AudioBuffin // etc -Jimmy -Jon - -// @filename: other.js -var Jimmy = 1 -var John = 2 -Jon // error, it's from the same file -var other = { - puuce: 4 -} diff --git a/tests/cases/conformance/salsa/spellingNocheckJS.ts b/tests/cases/conformance/salsa/spellingNocheckJS.ts deleted file mode 100644 index 6b96c05686aaf..0000000000000 --- a/tests/cases/conformance/salsa/spellingNocheckJS.ts +++ /dev/null @@ -1,48 +0,0 @@ -// @noEmit: true -// @allowJs: true -// @filename: spellingNocheckJS.js -// @ts-nocheck -export var inModule = 1 -inmodule.toFixed() - -function f() { - var locals = 2 - locale.toFixed() -} -class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - } -} -class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - } -} - - -var object = { - spaaace: 3 -} -object.spaaaace // error on read -object.spaace = 12 // error on write -object.fresh = 12 // OK -other.puuuce // OK, from another file -new Date().getGMTDate() // OK, from another file - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) -AudioBuffin // etc -Jimmy -Jon - -// @filename: other.js -var Jimmy = 1 -var John = 2 -Jon // error, it's from the same file -var other = { - puuce: 4 -} diff --git a/tests/cases/conformance/salsa/spellingNocheckJS2.ts b/tests/cases/conformance/salsa/spellingNocheckJS2.ts deleted file mode 100644 index 538a0e599b5e2..0000000000000 --- a/tests/cases/conformance/salsa/spellingNocheckJS2.ts +++ /dev/null @@ -1,48 +0,0 @@ -// @noEmit: true -// @allowJs: true -// @checkJs: false -// @filename: spellingNocheckJS2.js -export var inModule = 1 -inmodule.toFixed() - -function f() { - var locals = 2 - locale.toFixed() -} -class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - } -} -class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - } -} - - -var object = { - spaaace: 3 -} -object.spaaaace // error on read -object.spaace = 12 // error on write -object.fresh = 12 // OK -other.puuuce // OK, from another file -new Date().getGMTDate() // OK, from another file - -// No suggestions for globals from other files -const atoc = setIntegral(() => console.log('ok'), 500) -AudioBuffin // etc -Jimmy -Jon - -// @filename: other.js -var Jimmy = 1 -var John = 2 -Jon // error, it's from the same file -var other = { - puuce: 4 -} diff --git a/tests/cases/fourslash/codeFixSpellingJs1.ts b/tests/cases/fourslash/codeFixSpellingJs1.ts new file mode 100644 index 0000000000000..01508f941aa18 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs1.ts @@ -0,0 +1,28 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// function f() { +//// var locals = 2 +//// [|locale|].toFixed() +//// return locals +//// } + +verify.codeFixAvailable([ + { description: "Change spelling to 'locals'" }, + { description: "Ignore this error message" }, + { description: "Disable checking for this file" }, +]); + +verify.codeFix({ + description: "Change spelling to 'locals'", + index: 0, + newFileContent: +`function f() { + var locals = 2 + locals.toFixed() + return locals +}`, +}); diff --git a/tests/cases/fourslash/codeFixSpellingJs2.ts b/tests/cases/fourslash/codeFixSpellingJs2.ts new file mode 100644 index 0000000000000..110737ade86c4 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs2.ts @@ -0,0 +1,16 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// export var inModule = 1 +//// [|inmodule|].toFixed() + +verify.codeFix({ + description: "Change spelling to 'inModule'", + index: 0, + newFileContent: +`export var inModule = 1 +inModule.toFixed()`, +}); diff --git a/tests/cases/fourslash/codeFixSpellingJs3.ts b/tests/cases/fourslash/codeFixSpellingJs3.ts new file mode 100644 index 0000000000000..6451ce662ae3c --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs3.ts @@ -0,0 +1,21 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// class Classe { +//// non = 'oui' +//// methode() { +//// // no error on 'this' references +//// return this.none +//// } +//// } +//// class Derivee extends Classe { +//// methode() { +//// // no error on 'super' references +//// return super.none +//// } +//// } +verify.noErrors() + diff --git a/tests/cases/fourslash/codeFixSpellingJs4.ts b/tests/cases/fourslash/codeFixSpellingJs4.ts new file mode 100644 index 0000000000000..97c6af2dce166 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs4.ts @@ -0,0 +1,23 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// var object = { +//// spaaace: 3 +//// } +//// object.spaaaace // error on read +//// object.spaace = 12 // error on write +//// object.fresh = 12 // OK +verify.codeFixAll({ + fixId: "fixSpelling", + fixAllDescription: "Fix all detected spelling errors", + newFileContent: +`var object = { + spaaace: 3 +} +object.spaaace // error on read +object.spaaace = 12 // error on write +object.fresh = 12 // OK`, +}); diff --git a/tests/cases/fourslash/codeFixSpellingJs5.ts b/tests/cases/fourslash/codeFixSpellingJs5.ts new file mode 100644 index 0000000000000..478cc9208e178 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs5.ts @@ -0,0 +1,26 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// var other = { +//// puuce: 4 +//// } +//// var Jimmy = 1 +//// var John = 2 + +// @filename: b.js +//// other.puuuce // OK, from another file +//// new Date().getGMTDate() // OK, from another file +//// window.argle // OK, from globalThis +//// self.blargle // OK, from globalThis +//// +//// // No suggestions for globals from other files +//// const atoc = setIntegral(() => console.log('ok'), 500) +//// AudioBuffin // etc +//// Jimmy +//// Jon + + +verify.noErrors() diff --git a/tests/cases/fourslash/codeFixSpellingJs6.ts b/tests/cases/fourslash/codeFixSpellingJs6.ts new file mode 100644 index 0000000000000..535a80d2d80c3 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs6.ts @@ -0,0 +1,56 @@ +/// + +// @allowjs: true +// @checkjs: false +// @noEmit: true +// @filename: spellingUncheckedJS.js +//// export var inModule = 1 +//// inmodule.toFixed() +//// +//// function f() { +//// var locals = 2 + true +//// locale.toFixed() +//// } +//// class Classe { +//// non = 'oui' +//// methode() { +//// // no error on 'this' references +//// return this.none +//// } +//// } +//// class Derivee extends Classe { +//// methode() { +//// // no error on 'super' references +//// return super.none +//// } +//// } +//// +//// +//// var object = { +//// spaaace: 3 +//// } +//// object.spaaaace // error on read +//// object.spaace = 12 // error on write +//// object.fresh = 12 // OK +//// other.puuuce // OK, from another file +//// new Date().getGMTDate() // OK, from another file +//// +//// // No suggestions for globals from other files +//// const atoc = setIntegral(() => console.log('ok'), 500) +//// AudioBuffin // etc +//// Jimmy +//// Jon +//// window.argle +//// self.blargle + +// @filename: other.js +//// var Jimmy = 1 +//// var John = 2 +//// Jon // error, it's from the same file +//// var other = { +//// puuce: 4 +//// } +//// window.argle +//// self.blargle + +verify.noErrors() diff --git a/tests/cases/fourslash/codeFixSpellingJs7.ts b/tests/cases/fourslash/codeFixSpellingJs7.ts new file mode 100644 index 0000000000000..b5c6df2c9d08a --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs7.ts @@ -0,0 +1,57 @@ +/// + +// @allowjs: true +// @noEmit: true +// @filename: spellingUncheckedJS.js +//// // @ts-nocheck +//// export var inModule = 1 +//// inmodule.toFixed() +//// +//// function f() { +//// var locals = 2 + true +//// locale.toFixed() +//// } +//// class Classe { +//// non = 'oui' +//// methode() { +//// // no error on 'this' references +//// return this.none +//// } +//// } +//// class Derivee extends Classe { +//// methode() { +//// // no error on 'super' references +//// return super.none +//// } +//// } +//// +//// +//// var object = { +//// spaaace: 3 +//// } +//// object.spaaaace // error on read +//// object.spaace = 12 // error on write +//// object.fresh = 12 // OK +//// other.puuuce // OK, from another file +//// new Date().getGMTDate() // OK, from another file +//// +//// // No suggestions for globals from other files +//// const atoc = setIntegral(() => console.log('ok'), 500) +//// AudioBuffin // etc +//// Jimmy +//// Jon +//// window.argle +//// self.blargle + +// @filename: other.js +//// // @ts-nocheck +//// var Jimmy = 1 +//// var John = 2 +//// Jon // error, it's from the same file +//// var other = { +//// puuce: 4 +//// } +//// window.argle +//// self.blargle + +verify.noErrors() From 70ce29392cca5b63c72adafe3e38b52f7f9aabcc Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 2 Jun 2021 10:02:57 -0700 Subject: [PATCH 08/16] add a few more tests --- src/compiler/program.ts | 1 - .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- .../reference/spellingUncheckedJS.errors.txt | 71 ------------------- .../reference/spellingUncheckedJS.symbols | 42 +++++------ .../reference/spellingUncheckedJS.types | 56 +++++++-------- .../conformance/salsa/spellingUncheckedJS.ts | 4 ++ tests/cases/fourslash/codeFixSpellingJs8.ts | 10 +++ tests/cases/fourslash/codeFixSpellingJs9.ts | 14 ++++ 9 files changed, 72 insertions(+), 130 deletions(-) delete mode 100644 tests/baselines/reference/spellingUncheckedJS.errors.txt create mode 100644 tests/cases/fourslash/codeFixSpellingJs8.ts create mode 100644 tests/cases/fourslash/codeFixSpellingJs9.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts index d2fd22e0a8227..1fb0e95db3c21 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1947,7 +1947,6 @@ namespace ts { const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics); for (const errorExpectation of directives.getUnusedExpectations()) { - // TODO: Need to test ts-expect-error (although it's not a good idea but w/e) diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive)); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 6f94fa1b356ed..b626ec649ca94 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2114,7 +2114,7 @@ declare namespace ts { getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; /** The first time this is called, it will return global diagnostics (no location). */ - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; getConfigFileParsingDiagnostics(): readonly Diagnostic[]; /** diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 9f58cc3bbab09..297e0038d3a28 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2114,7 +2114,7 @@ declare namespace ts { getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; /** The first time this is called, it will return global diagnostics (no location). */ - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; getConfigFileParsingDiagnostics(): readonly Diagnostic[]; /** diff --git a/tests/baselines/reference/spellingUncheckedJS.errors.txt b/tests/baselines/reference/spellingUncheckedJS.errors.txt deleted file mode 100644 index 7e2f6746f2568..0000000000000 --- a/tests/baselines/reference/spellingUncheckedJS.errors.txt +++ /dev/null @@ -1,71 +0,0 @@ -tests/cases/conformance/salsa/other.js(3,1): error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -tests/cases/conformance/salsa/spellingUncheckedJS.js(2,1): error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? -tests/cases/conformance/salsa/spellingUncheckedJS.js(6,5): error TS2552: Cannot find name 'locale'. Did you mean 'locals'? -tests/cases/conformance/salsa/spellingUncheckedJS.js(26,8): error TS2551: Property 'spaaaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? -tests/cases/conformance/salsa/spellingUncheckedJS.js(27,8): error TS2551: Property 'spaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? - - -==== tests/cases/conformance/salsa/spellingUncheckedJS.js (4 errors) ==== - export var inModule = 1 - inmodule.toFixed() - ~~~~~~~~ -!!! error TS2552: Cannot find name 'inmodule'. Did you mean 'inModule'? - - function f() { - var locals = 2 + true - locale.toFixed() - ~~~~~~ -!!! error TS2552: Cannot find name 'locale'. Did you mean 'locals'? -!!! related TS2728 tests/cases/conformance/salsa/spellingUncheckedJS.js:5:9: 'locals' is declared here. - } - class Classe { - non = 'oui' - methode() { - // no error on 'this' references - return this.none - } - } - class Derivee extends Classe { - methode() { - // no error on 'super' references - return super.none - } - } - - - var object = { - spaaace: 3 - } - object.spaaaace // error on read - ~~~~~~~~ -!!! error TS2551: Property 'spaaaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? -!!! related TS2728 tests/cases/conformance/salsa/spellingUncheckedJS.js:24:5: 'spaaace' is declared here. - object.spaace = 12 // error on write - ~~~~~~ -!!! error TS2551: Property 'spaace' does not exist on type '{ spaaace: number; }'. Did you mean 'spaaace'? -!!! related TS2728 tests/cases/conformance/salsa/spellingUncheckedJS.js:24:5: 'spaaace' is declared here. - object.fresh = 12 // OK - other.puuuce // OK, from another file - new Date().getGMTDate() // OK, from another file - - // No suggestions for globals from other files - const atoc = setIntegral(() => console.log('ok'), 500) - AudioBuffin // etc - Jimmy - Jon - window.argle - self.blargle - -==== tests/cases/conformance/salsa/other.js (1 errors) ==== - var Jimmy = 1 - var John = 2 - Jon // error, it's from the same file - ~~~ -!!! error TS2552: Cannot find name 'Jon'. Did you mean 'John'? -!!! related TS2728 tests/cases/conformance/salsa/other.js:2:5: 'John' is declared here. - var other = { - puuce: 4 - } - window.argle - self.blargle - \ No newline at end of file diff --git a/tests/baselines/reference/spellingUncheckedJS.symbols b/tests/baselines/reference/spellingUncheckedJS.symbols index e8925ea763c5f..59355de95cb92 100644 --- a/tests/baselines/reference/spellingUncheckedJS.symbols +++ b/tests/baselines/reference/spellingUncheckedJS.symbols @@ -11,49 +11,53 @@ function f() { >locals : Symbol(locals, Decl(spellingUncheckedJS.js, 4, 7)) locale.toFixed() + // @ts-expect-error + localf.toExponential() + // @ts-expect-error + "this is fine" } class Classe { ->Classe : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) +>Classe : Symbol(Classe, Decl(spellingUncheckedJS.js, 10, 1)) non = 'oui' ->non : Symbol(Classe.non, Decl(spellingUncheckedJS.js, 7, 14)) +>non : Symbol(Classe.non, Decl(spellingUncheckedJS.js, 11, 14)) methode() { ->methode : Symbol(Classe.methode, Decl(spellingUncheckedJS.js, 8, 15)) +>methode : Symbol(Classe.methode, Decl(spellingUncheckedJS.js, 12, 15)) // no error on 'this' references return this.none ->this : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) +>this : Symbol(Classe, Decl(spellingUncheckedJS.js, 10, 1)) } } class Derivee extends Classe { ->Derivee : Symbol(Derivee, Decl(spellingUncheckedJS.js, 13, 1)) ->Classe : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) +>Derivee : Symbol(Derivee, Decl(spellingUncheckedJS.js, 17, 1)) +>Classe : Symbol(Classe, Decl(spellingUncheckedJS.js, 10, 1)) methode() { ->methode : Symbol(Derivee.methode, Decl(spellingUncheckedJS.js, 14, 30)) +>methode : Symbol(Derivee.methode, Decl(spellingUncheckedJS.js, 18, 30)) // no error on 'super' references return super.none ->super : Symbol(Classe, Decl(spellingUncheckedJS.js, 6, 1)) +>super : Symbol(Classe, Decl(spellingUncheckedJS.js, 10, 1)) } } var object = { ->object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) +>object : Symbol(object, Decl(spellingUncheckedJS.js, 26, 3), Decl(spellingUncheckedJS.js, 29, 15), Decl(spellingUncheckedJS.js, 30, 18)) spaaace: 3 ->spaaace : Symbol(spaaace, Decl(spellingUncheckedJS.js, 22, 14)) +>spaaace : Symbol(spaaace, Decl(spellingUncheckedJS.js, 26, 14)) } object.spaaaace // error on read ->object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) +>object : Symbol(object, Decl(spellingUncheckedJS.js, 26, 3), Decl(spellingUncheckedJS.js, 29, 15), Decl(spellingUncheckedJS.js, 30, 18)) object.spaace = 12 // error on write ->object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) +>object : Symbol(object, Decl(spellingUncheckedJS.js, 26, 3), Decl(spellingUncheckedJS.js, 29, 15), Decl(spellingUncheckedJS.js, 30, 18)) object.fresh = 12 // OK ->object : Symbol(object, Decl(spellingUncheckedJS.js, 22, 3), Decl(spellingUncheckedJS.js, 25, 15), Decl(spellingUncheckedJS.js, 26, 18)) +>object : Symbol(object, Decl(spellingUncheckedJS.js, 26, 3), Decl(spellingUncheckedJS.js, 29, 15), Decl(spellingUncheckedJS.js, 30, 18)) other.puuuce // OK, from another file >other : Symbol(other, Decl(other.js, 3, 3)) @@ -63,7 +67,7 @@ new Date().getGMTDate() // OK, from another file // No suggestions for globals from other files const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : Symbol(atoc, Decl(spellingUncheckedJS.js, 32, 5)) +>atoc : Symbol(atoc, Decl(spellingUncheckedJS.js, 36, 5)) >console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) >console : Symbol(console, Decl(lib.dom.d.ts, --, --)) >log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) @@ -73,11 +77,6 @@ Jimmy >Jimmy : Symbol(Jimmy, Decl(other.js, 0, 3)) Jon -window.argle ->window : Symbol(window, Decl(lib.dom.d.ts, --, --)) - -self.blargle ->self : Symbol(self, Decl(lib.dom.d.ts, --, --)) === tests/cases/conformance/salsa/other.js === var Jimmy = 1 @@ -93,9 +92,4 @@ var other = { puuce: 4 >puuce : Symbol(puuce, Decl(other.js, 3, 13)) } -window.argle ->window : Symbol(window, Decl(lib.dom.d.ts, --, --)) - -self.blargle ->self : Symbol(self, Decl(lib.dom.d.ts, --, --)) diff --git a/tests/baselines/reference/spellingUncheckedJS.types b/tests/baselines/reference/spellingUncheckedJS.types index 0c9e28132b5bd..e686e246cc145 100644 --- a/tests/baselines/reference/spellingUncheckedJS.types +++ b/tests/baselines/reference/spellingUncheckedJS.types @@ -4,8 +4,8 @@ export var inModule = 1 >1 : 1 inmodule.toFixed() ->inmodule.toFixed() : any ->inmodule.toFixed : any +>inmodule.toFixed() : error +>inmodule.toFixed : error >inmodule : any >toFixed : any @@ -19,10 +19,21 @@ function f() { >true : true locale.toFixed() ->locale.toFixed() : any ->locale.toFixed : any +>locale.toFixed() : error +>locale.toFixed : error >locale : any >toFixed : any + + // @ts-expect-error + localf.toExponential() +>localf.toExponential() : error +>localf.toExponential : error +>localf : any +>toExponential : any + + // @ts-expect-error + "this is fine" +>"this is fine" : "this is fine" } class Classe { >Classe : Classe @@ -66,20 +77,20 @@ var object = { >3 : 3 } object.spaaaace // error on read ->object.spaaaace : any +>object.spaaaace : error >object : { spaaace: number; } >spaaaace : any object.spaace = 12 // error on write >object.spaace = 12 : 12 ->object.spaace : any +>object.spaace : error >object : { spaaace: number; } >spaace : any >12 : 12 object.fresh = 12 // OK >object.fresh = 12 : 12 ->object.fresh : any +>object.fresh : error >object : { spaaace: number; } >fresh : any >12 : 12 @@ -98,9 +109,9 @@ new Date().getGMTDate() // OK, from another file // No suggestions for globals from other files const atoc = setIntegral(() => console.log('ok'), 500) ->atoc : any ->setIntegral(() => console.log('ok'), 500) : any ->setIntegral : any +>atoc : error +>setIntegral(() => console.log('ok'), 500) : error +>setIntegral : error >() => console.log('ok') : () => void >console.log('ok') : void >console.log : (...data: any[]) => void @@ -110,23 +121,13 @@ const atoc = setIntegral(() => console.log('ok'), 500) >500 : 500 AudioBuffin // etc ->AudioBuffin : any +>AudioBuffin : error Jimmy >Jimmy : number Jon ->Jon : any - -window.argle ->window.argle : any ->window : Window & typeof globalThis ->argle : any - -self.blargle ->self.blargle : any ->self : Window & typeof globalThis ->blargle : any +>Jon : error === tests/cases/conformance/salsa/other.js === var Jimmy = 1 @@ -138,7 +139,7 @@ var John = 2 >2 : 2 Jon // error, it's from the same file ->Jon : any +>Jon : error var other = { >other : { puuce: number; } @@ -148,13 +149,4 @@ var other = { >puuce : number >4 : 4 } -window.argle ->window.argle : any ->window : Window & typeof globalThis ->argle : any - -self.blargle ->self.blargle : any ->self : Window & typeof globalThis ->blargle : any diff --git a/tests/cases/conformance/salsa/spellingUncheckedJS.ts b/tests/cases/conformance/salsa/spellingUncheckedJS.ts index f2b382df9b4f1..d5a1e8ac5602f 100644 --- a/tests/cases/conformance/salsa/spellingUncheckedJS.ts +++ b/tests/cases/conformance/salsa/spellingUncheckedJS.ts @@ -7,6 +7,10 @@ inmodule.toFixed() function f() { var locals = 2 + true locale.toFixed() + // @ts-expect-error + localf.toExponential() + // @ts-expect-error + "this is fine" } class Classe { non = 'oui' diff --git a/tests/cases/fourslash/codeFixSpellingJs8.ts b/tests/cases/fourslash/codeFixSpellingJs8.ts new file mode 100644 index 0000000000000..f43f9902592c0 --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs8.ts @@ -0,0 +1,10 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// var locals = {} +//// // @ts-expect-error +//// Object.keys(locale) +verify.noErrors() diff --git a/tests/cases/fourslash/codeFixSpellingJs9.ts b/tests/cases/fourslash/codeFixSpellingJs9.ts new file mode 100644 index 0000000000000..eb5763086786b --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingJs9.ts @@ -0,0 +1,14 @@ +/// + +// @allowjs: true +// @noEmit: true + +// @filename: a.js +//// var locals = {} +//// [|// @ts-expect-error|] +//// Object.keys(locals) +verify.codeFixAvailable([ + { description: "Ignore this error message" }, + { description: "Disable checking for this file" }, +]); + From 77667b7ce070988d0270fd320193a364535aa51b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 2 Jun 2021 10:53:42 -0700 Subject: [PATCH 09/16] fix incorrect parentheses --- src/compiler/program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 1fb0e95db3c21..662b86ed19b30 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1924,7 +1924,7 @@ namespace ts { (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX || isCheckJsTrue - || (isCheckJsUndefined && sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX) + || isCheckJsUndefined && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX) || sourceFile.scriptKind === ScriptKind.External || sourceFile.scriptKind === ScriptKind.Deferred); let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; From 38d806d7cfbebbbab5961154ba0ec734e04e77f1 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 2 Jun 2021 11:01:08 -0700 Subject: [PATCH 10/16] More cleanup in program.ts --- src/compiler/program.ts | 16 +++++++++------- src/compiler/utilities.ts | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 662b86ed19b30..d1567d33e1ea7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1917,22 +1917,24 @@ namespace ts { Debug.assert(!!sourceFile.bindDiagnostics); const isCheckJsTrue = isCheckJsEnabledForFile(sourceFile, options); - const isCheckJsUndefined = includeUncheckedJS && sourceFile.checkJsDirective === undefined && options.checkJs === undefined; + const isCheckJsUndefined = includeUncheckedJS + && sourceFile.checkJsDirective === undefined && options.checkJs === undefined + && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX); const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX || isCheckJsTrue - || isCheckJsUndefined && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX) + || isCheckJsUndefined || sourceFile.scriptKind === ScriptKind.External || sourceFile.scriptKind === ScriptKind.Deferred); - let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; + const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics && !isCheckJsUndefined ? sourceFile.bindDiagnostics : emptyArray; let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; - if (isCheckJsUndefined && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX)) { - // TODO: It can just be checkDiagnostics srsly - bindDiagnostics = bindDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code); - checkDiagnostics = checkDiagnostics.filter(d => d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code); + if (isCheckJsUndefined) { + checkDiagnostics = checkDiagnostics.filter(d => + d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code + || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code); } return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJsTrue ? sourceFile.jsDocDiagnostics : undefined); }); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1eac69172c93b..1970b9db104aa 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6797,7 +6797,7 @@ namespace ts { export function tryGetExtensionFromPath(path: string): Extension | undefined { return find(extensionsToRemove, e => fileExtensionIs(path, e)); } - // TODO: Callers of this function in services are probably what *actually* need to change + export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs; } From 1b44d728742be5ce7b7af37727472ec720759043 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 2 Jun 2021 12:55:31 -0700 Subject: [PATCH 11/16] Improve readability of isExcludedJSError --- src/compiler/checker.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e4c8e93bfdff6..a064a97601b87 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2063,7 +2063,7 @@ namespace ts { if (isGlobalScopeAugmentationDeclaration) { suggestion = undefined; } - if (originalLocation && isBadUncheckedJSSuggestion(lastLocation, /*parent*/ undefined, suggestion?.valueDeclaration ? [suggestion.valueDeclaration] : [])) { + if (originalLocation && isExcludedJSError(originalLocation, suggestion, /*forbidClasses*/ false)) { suggestion = undefined; } if (suggestion) { @@ -27455,8 +27455,8 @@ namespace ts { if (!prop) { const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; if (!(indexInfo && indexInfo.type)) { - const isBadJSSuggestion = isBadUncheckedJSSuggestion(node, leftType.symbol, leftType.symbol?.declarations); - if (isBadJSSuggestion === undefined ? isJSLiteralType(leftType) : isBadJSSuggestion) { + const isExcludedError = isExcludedJSError(node, leftType.symbol, /*forbidClasses*/ true); + if (isExcludedError === undefined ? isJSLiteralType(leftType) : isExcludedError) { return anyType; } if (leftType.symbol === globalThisSymbol) { @@ -27504,17 +27504,20 @@ namespace ts { /** * Only applies to unchecked JS files without checkJS, // @ts-check or // @ts-nocheck - * @returns undefined when not applicable, true for bad suggestions, false for good ones - * TODO: Should probably only pass one declaration + * @returns undefined when not applicable, true for excluded errors, false for included ones + * + * An error is excluded when it: + * - Is from a global file that is different from the reference file, or + * - (optionally) Is a class, or is a this.x property access expression */ - function isBadUncheckedJSSuggestion(node: Node | undefined, parent: Symbol | undefined, declarations: Node[] | undefined): boolean | undefined { + function isExcludedJSError(node: Node | undefined, suggestion: Symbol | undefined, excludeClasses: boolean): boolean | undefined { const file = getSourceFileOfNode(node); if (file) { if (compilerOptions.checkJs === undefined && !file.checkJsDirective && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { - const parentFile = forEach(declarations, getSourceFileOfNode); - return file !== parentFile && !!parentFile && isGlobalSourceFile(parentFile) - || !!(parent && parent.flags & SymbolFlags.Class) - || !!node && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword; + const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); + return file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile) + || !!(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class) + || !!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword; } } } From 73451cdd366688b9787b42023329807ef0219069 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 3 Jun 2021 09:40:59 -0700 Subject: [PATCH 12/16] make diff in program.ts smaller via closure --- src/compiler/program.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index d1567d33e1ea7..823a0fd9dd03b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1804,26 +1804,25 @@ namespace ts { function getDiagnosticsHelper( sourceFile: SourceFile | undefined, - getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined) => readonly T[], - cancellationToken: CancellationToken | undefined, - includeUncheckedJS: boolean | undefined): readonly T[] { + getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined) => readonly T[], + cancellationToken: CancellationToken | undefined): readonly T[] { if (sourceFile) { - return getDiagnostics(sourceFile, cancellationToken, includeUncheckedJS); + return getDiagnostics(sourceFile, cancellationToken); } return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => { if (cancellationToken) { cancellationToken.throwIfCancellationRequested(); } - return getDiagnostics(sourceFile, cancellationToken, includeUncheckedJS); + return getDiagnostics(sourceFile, cancellationToken); })); } function getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { - return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken, /*includeUncheckedJS*/ undefined); + return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken); } function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[] { - return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken, includeUncheckedJS); + return getDiagnosticsHelper(sourceFile, (file, token) => getSemanticDiagnosticsForFile(file, token, includeUncheckedJS), cancellationToken); } function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined { @@ -1856,7 +1855,7 @@ namespace ts { return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken); } else { - return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken, /*includeUncheckedJS*/ undefined); + return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken); } } From 3ceffd101072823d1c9bf9cd49c2ee0f0f838fca Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 8 Jun 2021 08:27:46 -0700 Subject: [PATCH 13/16] Switch unchecked JS did-you-mean to suggestion Instead of selectively letting errors through. --- src/compiler/checker.ts | 42 +++++++++------- src/compiler/program.ts | 49 +++++++------------ ...PropertyInitalizationInObjectLiteral.types | 2 +- .../reference/methodsReturningThis.types | 2 +- .../misspelledJsDocTypedefTags.types | 4 +- .../reference/multipleDeclarations.types | 4 +- .../reference/spellingUncheckedJS.types | 8 +-- tests/cases/fourslash/codeFixSpellingJs1.ts | 1 + tests/cases/fourslash/codeFixSpellingJs9.ts | 14 ------ 9 files changed, 52 insertions(+), 74 deletions(-) delete mode 100644 tests/cases/fourslash/codeFixSpellingJs9.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad2f3c3a90af2..913487237d3f1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1076,15 +1076,19 @@ namespace ts { return diagnostic; } - function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { - const diagnostic = location + function createError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { + return location ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3); + } + + function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { + const diagnostic = createError(location, message, arg0, arg1, arg2, arg3); diagnostics.add(diagnostic); return diagnostic; } - function addErrorOrSuggestion(isError: boolean, diagnostic: DiagnosticWithLocation) { + function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) { if (isError) { diagnostics.add(diagnostic); } @@ -2064,12 +2068,12 @@ namespace ts { if (isGlobalScopeAugmentationDeclaration) { suggestion = undefined; } - if (originalLocation && isExcludedJSError(originalLocation, suggestion, /*forbidClasses*/ false)) { - suggestion = undefined; - } if (suggestion) { const suggestionName = symbolToString(suggestion); - const diagnostic = error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName); + const isUncheckedJS = isUncheckedJSSuggestion(originalLocation, suggestion, /*excludeClasses*/ false); + const diagnostic = createError(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName); + addErrorOrSuggestion(!isUncheckedJS, diagnostic); + if (suggestion.valueDeclaration) { addRelatedInfo( diagnostic, @@ -27456,8 +27460,8 @@ namespace ts { if (!prop) { const indexInfo = !isPrivateIdentifier(right) && (assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType)) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; if (!(indexInfo && indexInfo.type)) { - const isExcludedError = isExcludedJSError(node, leftType.symbol, /*forbidClasses*/ true); - if (isExcludedError === undefined ? isJSLiteralType(leftType) : isExcludedError) { + const isUncheckedJS = isUncheckedJSSuggestion(node, leftType.symbol, /*excludeClasses*/ true); + if (!isUncheckedJS && isJSLiteralType(leftType)) { return anyType; } if (leftType.symbol === globalThisSymbol) { @@ -27470,7 +27474,7 @@ namespace ts { return anyType; } if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) { - reportNonexistentProperty(right, isThisTypeParameter(leftType) ? apparentType : leftType); + reportNonexistentProperty(right, isThisTypeParameter(leftType) ? apparentType : leftType, isUncheckedJS); } return errorType; } @@ -27504,23 +27508,23 @@ namespace ts { } /** + * Determines whether a did-you-mean error should be a suggestion in an unchecked JS file. * Only applies to unchecked JS files without checkJS, // @ts-check or // @ts-nocheck - * @returns undefined when not applicable, true for excluded errors, false for included ones - * - * An error is excluded when it: + * It does not suggest when the suggestion: * - Is from a global file that is different from the reference file, or * - (optionally) Is a class, or is a this.x property access expression */ - function isExcludedJSError(node: Node | undefined, suggestion: Symbol | undefined, excludeClasses: boolean): boolean | undefined { + function isUncheckedJSSuggestion(node: Node | undefined, suggestion: Symbol | undefined, excludeClasses: boolean): boolean { const file = getSourceFileOfNode(node); if (file) { if (compilerOptions.checkJs === undefined && !file.checkJsDirective && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); - return file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile) - || !!(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class) - || !!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword; + return !(file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile)) + && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class) + && !(!!node && excludeClasses && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword); } } + return false; } function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node, checkMode: CheckMode | undefined) { @@ -27654,7 +27658,7 @@ namespace ts { return getIntersectionType(x); } - function reportNonexistentProperty(propNode: Identifier | PrivateIdentifier, containingType: Type) { + function reportNonexistentProperty(propNode: Identifier | PrivateIdentifier, containingType: Type, isUncheckedJS: boolean) { let errorInfo: DiagnosticMessageChain | undefined; let relatedInfo: Diagnostic | undefined; if (!isPrivateIdentifier(propNode) && containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) { @@ -27703,7 +27707,7 @@ namespace ts { if (relatedInfo) { addRelatedInfo(resultDiagnostic, relatedInfo); } - diagnostics.add(resultDiagnostic); + addErrorOrSuggestion(!isUncheckedJS, resultDiagnostic); } function containerSeemsToBeEmptyDomElement(containingType: Type) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 823a0fd9dd03b..25a7631e4a431 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1821,8 +1821,8 @@ namespace ts { return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken); } - function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[] { - return getDiagnosticsHelper(sourceFile, (file, token) => getSemanticDiagnosticsForFile(file, token, includeUncheckedJS), cancellationToken); + function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { + return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken); } function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined { @@ -1832,7 +1832,7 @@ namespace ts { } function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { - return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken, /*includeUncheckedJS*/ undefined); + return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken); } function getProgramDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] { @@ -1894,18 +1894,18 @@ namespace ts { } } - function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined): readonly Diagnostic[] { + function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { return concatenate( - filterSemanticDiagnostics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken, includeUncheckedJS), options), + filterSemanticDiagnostics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken), options), getProgramDiagnostics(sourceFile) ); } - function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined): readonly Diagnostic[] { - return getAndCacheDiagnostics(sourceFile, cancellationToken, includeUncheckedJS, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache); + function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { + return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache); } - function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined): readonly Diagnostic[] { + function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { return runWithCancellationToken(() => { if (skipTypeChecking(sourceFile, options, program)) { return emptyArray; @@ -1915,27 +1915,15 @@ namespace ts { Debug.assert(!!sourceFile.bindDiagnostics); - const isCheckJsTrue = isCheckJsEnabledForFile(sourceFile, options); - const isCheckJsUndefined = includeUncheckedJS - && sourceFile.checkJsDirective === undefined && options.checkJs === undefined - && (sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX); + const isCheckJs = isCheckJsEnabledForFile(sourceFile, options); const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) - const includeBindAndCheckDiagnostics = !isTsNoCheck && - (sourceFile.scriptKind === ScriptKind.TS - || sourceFile.scriptKind === ScriptKind.TSX - || isCheckJsTrue - || isCheckJsUndefined - || sourceFile.scriptKind === ScriptKind.External - || sourceFile.scriptKind === ScriptKind.Deferred); - const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics && !isCheckJsUndefined ? sourceFile.bindDiagnostics : emptyArray; - let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; - if (isCheckJsUndefined) { - checkDiagnostics = checkDiagnostics.filter(d => - d.code === Diagnostics.Cannot_find_name_0_Did_you_mean_1.code - || d.code === Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code); - } - return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJsTrue ? sourceFile.jsDocDiagnostics : undefined); + const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX + || isCheckJs || sourceFile.scriptKind === ScriptKind.External || sourceFile.scriptKind === ScriptKind.Deferred); + const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; + const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; + + return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined); }); } @@ -2199,7 +2187,7 @@ namespace ts { } function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] { - return getAndCacheDiagnostics(sourceFile, cancellationToken, /*includeUncheckedJS*/ undefined, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache); + return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache); } function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] { @@ -2213,9 +2201,8 @@ namespace ts { function getAndCacheDiagnostics( sourceFile: T, cancellationToken: CancellationToken | undefined, - includeUncheckedJS: boolean | undefined, cache: DiagnosticCache, - getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined, includeUncheckedJS: boolean | undefined) => readonly U[], + getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[], ): readonly U[] { const cachedResult = sourceFile @@ -2225,7 +2212,7 @@ namespace ts { if (cachedResult) { return cachedResult; } - const result = getDiagnostics(sourceFile, cancellationToken, includeUncheckedJS); + const result = getDiagnostics(sourceFile, cancellationToken); if (sourceFile) { (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result); } diff --git a/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types b/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types index 184845e1e4bcc..52922bcefb6c0 100644 --- a/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types +++ b/tests/baselines/reference/jsFileClassPropertyInitalizationInObjectLiteral.types @@ -15,7 +15,7 @@ module.exports = function () { c: A.b = 1, >c : number >A.b = 1 : 1 ->A.b : any +>A.b : error >A : typeof A >b : any >1 : 1 diff --git a/tests/baselines/reference/methodsReturningThis.types b/tests/baselines/reference/methodsReturningThis.types index 856ea6925330f..544139497e088 100644 --- a/tests/baselines/reference/methodsReturningThis.types +++ b/tests/baselines/reference/methodsReturningThis.types @@ -13,7 +13,7 @@ Class.prototype.containsError = function () { return this.notPresent; }; >prototype : any >containsError : any >function () { return this.notPresent; } : () => any ->this.notPresent : any +>this.notPresent : error >this : this >notPresent : any diff --git a/tests/baselines/reference/misspelledJsDocTypedefTags.types b/tests/baselines/reference/misspelledJsDocTypedefTags.types index 5facd80a2675d..9a42e2d1cd8b0 100644 --- a/tests/baselines/reference/misspelledJsDocTypedefTags.types +++ b/tests/baselines/reference/misspelledJsDocTypedefTags.types @@ -1,7 +1,7 @@ === tests/cases/compiler/a.js === /** @typedef {{ endTime: number, screenshots: number}} A.*/ Animation.AnimationModel.ScreenshotCapture.Request; ->Animation.AnimationModel.ScreenshotCapture.Request : any +>Animation.AnimationModel.ScreenshotCapture.Request : error >Animation.AnimationModel.ScreenshotCapture : any >Animation.AnimationModel : any >Animation : { new (effect?: AnimationEffect, timeline?: AnimationTimeline): Animation; prototype: Animation; } @@ -11,7 +11,7 @@ Animation.AnimationModel.ScreenshotCapture.Request; /** @typedef {{ endTime: number, screenshots: !B.}} */ Animation.AnimationModel.ScreenshotCapture.Request; ->Animation.AnimationModel.ScreenshotCapture.Request : any +>Animation.AnimationModel.ScreenshotCapture.Request : error >Animation.AnimationModel.ScreenshotCapture : any >Animation.AnimationModel : any >Animation : { new (effect?: AnimationEffect, timeline?: AnimationTimeline): Animation; prototype: Animation; } diff --git a/tests/baselines/reference/multipleDeclarations.types b/tests/baselines/reference/multipleDeclarations.types index dc695bca44552..12aa74ecaf096 100644 --- a/tests/baselines/reference/multipleDeclarations.types +++ b/tests/baselines/reference/multipleDeclarations.types @@ -19,8 +19,8 @@ C.prototype.m = function() { >function() { this.nothing();} : () => void this.nothing(); ->this.nothing() : any ->this.nothing : any +>this.nothing() : error +>this.nothing : error >this : this >nothing : any } diff --git a/tests/baselines/reference/spellingUncheckedJS.types b/tests/baselines/reference/spellingUncheckedJS.types index e686e246cc145..61723e8da16ea 100644 --- a/tests/baselines/reference/spellingUncheckedJS.types +++ b/tests/baselines/reference/spellingUncheckedJS.types @@ -47,7 +47,7 @@ class Classe { // no error on 'this' references return this.none ->this.none : any +>this.none : error >this : this >none : any } @@ -61,7 +61,7 @@ class Derivee extends Classe { // no error on 'super' references return super.none ->super.none : any +>super.none : error >super : Classe >none : any } @@ -101,8 +101,8 @@ other.puuuce // OK, from another file >puuuce : any new Date().getGMTDate() // OK, from another file ->new Date().getGMTDate() : any ->new Date().getGMTDate : any +>new Date().getGMTDate() : error +>new Date().getGMTDate : error >new Date() : Date >Date : DateConstructor >getGMTDate : any diff --git a/tests/cases/fourslash/codeFixSpellingJs1.ts b/tests/cases/fourslash/codeFixSpellingJs1.ts index 01508f941aa18..a73e87a988caf 100644 --- a/tests/cases/fourslash/codeFixSpellingJs1.ts +++ b/tests/cases/fourslash/codeFixSpellingJs1.ts @@ -10,6 +10,7 @@ //// return locals //// } +verify.noErrors() verify.codeFixAvailable([ { description: "Change spelling to 'locals'" }, { description: "Ignore this error message" }, diff --git a/tests/cases/fourslash/codeFixSpellingJs9.ts b/tests/cases/fourslash/codeFixSpellingJs9.ts deleted file mode 100644 index eb5763086786b..0000000000000 --- a/tests/cases/fourslash/codeFixSpellingJs9.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// - -// @allowjs: true -// @noEmit: true - -// @filename: a.js -//// var locals = {} -//// [|// @ts-expect-error|] -//// Object.keys(locals) -verify.codeFixAvailable([ - { description: "Ignore this error message" }, - { description: "Disable checking for this file" }, -]); - From 1163b78b9f2c19691928a9a56addddf494949554 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 8 Jun 2021 08:32:23 -0700 Subject: [PATCH 14/16] undo more missed changes --- src/compiler/program.ts | 2 +- src/compiler/types.ts | 2 +- src/services/codeFixProvider.ts | 2 +- src/services/codefixes/addMissingAwait.ts | 2 +- src/services/services.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 25a7631e4a431..440a930983ebe 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1919,7 +1919,7 @@ namespace ts { const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins) const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX - || isCheckJs || sourceFile.scriptKind === ScriptKind.External || sourceFile.scriptKind === ScriptKind.Deferred); + || sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred); const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7d8deabcee3e7..e375b0a77acac 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3900,7 +3900,7 @@ namespace ts { getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; /** The first time this is called, it will return global diagnostics (no location). */ - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; getConfigFileParsingDiagnostics(): readonly Diagnostic[]; /* @internal */ getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index 2c6abcf703eea..5baf4734002fc 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -95,7 +95,7 @@ namespace ts.codefix { function getDiagnostics({ program, sourceFile, cancellationToken }: CodeFixContextBase) { return [ - ...program.getSemanticDiagnostics(sourceFile, cancellationToken, /*includeUncheckedJS*/ true), + ...program.getSemanticDiagnostics(sourceFile, cancellationToken), ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), ...computeSuggestionDiagnostics(sourceFile, program, cancellationToken) ]; diff --git a/src/services/codefixes/addMissingAwait.ts b/src/services/codefixes/addMissingAwait.ts index 08008dc2c150a..e3a15d8864144 100644 --- a/src/services/codefixes/addMissingAwait.ts +++ b/src/services/codefixes/addMissingAwait.ts @@ -156,7 +156,7 @@ namespace ts.codefix { continue; } - const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken, /*includeUncheckedJS*/ true); + const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); const isUsedElsewhere = FindAllReferences.Core.eachSymbolReferenceInFile(variableName, checker, sourceFile, reference => { return identifier !== reference && !symbolReferenceIsAlsoMissingAwait(reference, diagnostics, sourceFile, checker); }); diff --git a/src/services/services.ts b/src/services/services.ts index 1e9cf13f7a064..afe43e32a79c2 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1560,7 +1560,7 @@ namespace ts { // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file. // Therefore only get diagnostics for given file. - const semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken, /*includeUncheckedJS*/ true); + const semanticDiagnostics = program.getSemanticDiagnostics(targetSourceFile, cancellationToken); if (!getEmitDeclarations(program.getCompilerOptions())) { return semanticDiagnostics.slice(); } From 99ab9154b81e804f0807fcd58c7eed4b6e4a8a5a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 8 Jun 2021 10:11:37 -0700 Subject: [PATCH 15/16] disallow ignoring suggestions --- .../codefixes/disableJsDiagnostics.ts | 3 +- .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- .../codeFixDisableJsDiagnosticsInFile11.ts | 29 ------------------- .../codeFixDisableJsDiagnosticsInFile12.ts | 29 ------------------- tests/cases/fourslash/codeFixSpellingJs1.ts | 2 -- .../convertToEs6Class_emptyCatchClause.ts | 2 +- 7 files changed, 4 insertions(+), 65 deletions(-) delete mode 100644 tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts delete mode 100644 tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 1ce68c81f7c68..4469ffb6b716f 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -12,8 +12,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile, program, span, host, formatContext } = context; - const isCheckJsUndefined = sourceFile.checkJsDirective === undefined && program.getCompilerOptions().checkJs === undefined; - if (!isInJSFile(sourceFile) || !isCheckJsEnabledForFile(sourceFile, program.getCompilerOptions()) && !isCheckJsUndefined) { + if (!isInJSFile(sourceFile) || !isCheckJsEnabledForFile(sourceFile, program.getCompilerOptions())) { return undefined; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 0e395a9ed551c..376ebc377765f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2114,7 +2114,7 @@ declare namespace ts { getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; /** The first time this is called, it will return global diagnostics (no location). */ - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; getConfigFileParsingDiagnostics(): readonly Diagnostic[]; /** diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index e2ae500aae68a..ea5e5d3eeefd4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2114,7 +2114,7 @@ declare namespace ts { getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[]; getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; /** The first time this is called, it will return global diagnostics (no location). */ - getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken, includeUncheckedJS?: boolean): readonly Diagnostic[]; + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[]; getConfigFileParsingDiagnostics(): readonly Diagnostic[]; /** diff --git a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts deleted file mode 100644 index 283bdcee3ecfc..0000000000000 --- a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile11.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// - -// @allowjs: true -// @noEmit: true - -// @filename: a.js -//// function f() { -//// var locals = 2 -//// [|locale|].toFixed() -//// return locals -//// } - -verify.codeFixAvailable([ - { description: "Change spelling to 'locals'" }, - { description: "Ignore this error message" }, - { description: "Disable checking for this file" }, -]); - -verify.codeFix({ - description: ts.Diagnostics.Ignore_this_error_message.message, - index: 1, - newFileContent: -`function f() { - var locals = 2 - // @ts-ignore - locale.toFixed() - return locals -}`, -}); diff --git a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts deleted file mode 100644 index 5d706f7a7e104..0000000000000 --- a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile12.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// - -// @allowjs: true -// @noEmit: true - -// @filename: a.js -//// function f() { -//// var locals = 2 -//// [|locale|].toFixed() -//// return locals -//// } - -verify.codeFixAvailable([ - { description: "Change spelling to 'locals'" }, - { description: "Ignore this error message" }, - { description: "Disable checking for this file" }, -]); - -verify.codeFix({ - description: ts.Diagnostics.Disable_checking_for_this_file.message, - index: 2, - newFileContent: -`// @ts-nocheck -function f() { - var locals = 2 - locale.toFixed() - return locals -}`, -}); diff --git a/tests/cases/fourslash/codeFixSpellingJs1.ts b/tests/cases/fourslash/codeFixSpellingJs1.ts index a73e87a988caf..4ad4d000d45f5 100644 --- a/tests/cases/fourslash/codeFixSpellingJs1.ts +++ b/tests/cases/fourslash/codeFixSpellingJs1.ts @@ -13,8 +13,6 @@ verify.noErrors() verify.codeFixAvailable([ { description: "Change spelling to 'locals'" }, - { description: "Ignore this error message" }, - { description: "Disable checking for this file" }, ]); verify.codeFix({ diff --git a/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts b/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts index bc3f69ba78724..9c23d6c0378ed 100644 --- a/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts +++ b/tests/cases/fourslash/convertToEs6Class_emptyCatchClause.ts @@ -9,7 +9,7 @@ verify.codeFix({ description: "Convert function to an ES2015 class", - index: 2, + index: 0, newFileContent: `class MyClass { constructor() { } From c463a0055e1dac6396da2609f6cc0c03f8ecb880 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 14 Jun 2021 16:07:47 -0700 Subject: [PATCH 16/16] Issue different messages for plain JS than others Straw text for the messages, I just changed the modals to avoid name collisions. --- src/compiler/checker.ts | 21 +++++++++++---------- src/compiler/diagnosticMessages.json | 8 ++++++++ src/services/codefixes/fixSpelling.ts | 2 ++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 913487237d3f1..ea8526ba61464 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1712,8 +1712,8 @@ namespace ts { nameArg: __String | Identifier | undefined, isUse: boolean, excludeGlobals = false, - suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol | undefined { - return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSymbol, suggestedNameNotFoundMessage); + issueSuggestions?: boolean): Symbol | undefined { + return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSymbol, issueSuggestions); } function resolveNameHelper( @@ -1724,8 +1724,7 @@ namespace ts { nameArg: __String | Identifier | undefined, isUse: boolean, excludeGlobals: boolean, - lookup: typeof getSymbol, - suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol | undefined { + lookup: typeof getSymbol, issueSuggestions?: boolean): 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; @@ -2062,7 +2061,7 @@ namespace ts { !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) { let suggestion: Symbol | undefined; - if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) { + if (issueSuggestions && suggestionCount < maximumSuggestionCount) { suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning); const isGlobalScopeAugmentationDeclaration = suggestion?.valueDeclaration && isAmbientModule(suggestion.valueDeclaration) && isGlobalScopeAugmentation(suggestion.valueDeclaration); if (isGlobalScopeAugmentationDeclaration) { @@ -2071,7 +2070,8 @@ namespace ts { if (suggestion) { const suggestionName = symbolToString(suggestion); const isUncheckedJS = isUncheckedJSSuggestion(originalLocation, suggestion, /*excludeClasses*/ false); - const diagnostic = createError(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName); + const message = 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) { @@ -21946,7 +21946,7 @@ namespace ts { node, !isWriteOnlyAccess(node), /*excludeGlobals*/ false, - Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol; + /*issueSuggestions*/ true) || unknownSymbol; } return links.resolvedSymbol; } @@ -27517,7 +27517,7 @@ namespace ts { function isUncheckedJSSuggestion(node: Node | undefined, suggestion: Symbol | undefined, excludeClasses: boolean): boolean { const file = getSourceFileOfNode(node); if (file) { - if (compilerOptions.checkJs === undefined && !file.checkJsDirective && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { + if (compilerOptions.checkJs === undefined && file.checkJsDirective === undefined && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX)) { const declarationFile = forEach(suggestion?.declarations, getSourceFileOfNode); return !(file !== declarationFile && !!declarationFile && isGlobalSourceFile(declarationFile)) && !(excludeClasses && suggestion && suggestion.flags & SymbolFlags.Class) @@ -27691,7 +27691,8 @@ namespace ts { const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType); if (suggestion !== undefined) { const suggestedName = symbolName(suggestion); - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, missingProperty, container, suggestedName); + const message = isUncheckedJS ? Diagnostics.Property_0_may_not_exist_on_type_1_Did_you_mean_2 : Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2; + errorInfo = chainDiagnosticMessages(errorInfo, message, missingProperty, container, suggestedName); relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName); } else { @@ -34622,7 +34623,7 @@ namespace ts { const rootName = getFirstIdentifier(typeName); const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias; - const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isRefernce*/ true); + const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isReference*/ true); if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias && symbolIsValue(rootSymbol) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d2ddbc8b0a4ec..1e2a0329cb00b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2442,10 +2442,18 @@ "category": "Error", "code": 2567 }, + "Property '{0}' may not exist on type '{1}'. Did you mean '{2}'?": { + "category": "Error", + "code": 2568 + }, "Type '{0}' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.": { "category": "Error", "code": 2569 }, + "Could not find name '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 2570 + }, "Object is of type 'unknown'.": { "category": "Error", "code": 2571 diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 6d80e1f900899..2712976d7c6b4 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -3,7 +3,9 @@ namespace ts.codefix { const fixId = "fixSpelling"; const errorCodes = [ Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code, + 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_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,