diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 10400a3853..1113d131a0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1086,7 +1086,10 @@ namespace ts { location = location.parent; } - if (result && nameNotFoundMessage && noUnusedIdentifiers) { + // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. + // If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` looking up a name, and resolving to `lastLocation` itself. + // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. + if (result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) { result.isReferenced = true; } @@ -10800,17 +10803,6 @@ namespace ts { return undefined; } - function getLeftmostIdentifierOrThis(node: Node): Node { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.ThisKeyword: - return node; - case SyntaxKind.PropertyAccessExpression: - return getLeftmostIdentifierOrThis((node).expression); - } - return undefined; - } - function getBindingElementNameText(element: BindingElement): string | undefined { if (element.parent.kind === SyntaxKind.ObjectBindingPattern) { const name = element.propertyName || element.name; @@ -18520,15 +18512,6 @@ namespace ts { return forEachChild(n, containsSuperCall); } - function markThisReferencesAsErrors(n: Node): void { - if (n.kind === SyntaxKind.ThisKeyword) { - error(n, Diagnostics.this_cannot_be_referenced_in_current_location); - } - else if (n.kind !== SyntaxKind.FunctionExpression && n.kind !== SyntaxKind.FunctionDeclaration) { - forEachChild(n, markThisReferencesAsErrors); - } - } - function isInstancePropertyWithInitializer(n: Node): boolean { return n.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(n) & ModifierFlags.Static) && diff --git a/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt b/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt new file mode 100644 index 0000000000..af40081e71 --- /dev/null +++ b/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt @@ -0,0 +1,28 @@ +tests/cases/compiler/noUnusedLocals_selfReference.ts(3,10): error TS6133: 'f' is declared but never used. +tests/cases/compiler/noUnusedLocals_selfReference.ts(4,7): error TS6133: 'C' is declared but never used. +tests/cases/compiler/noUnusedLocals_selfReference.ts(7,6): error TS6133: 'E' is declared but never used. + + +==== tests/cases/compiler/noUnusedLocals_selfReference.ts (3 errors) ==== + export {}; // Make this a module scope, so these are local variables. + + function f() { f; } + ~ +!!! error TS6133: 'f' is declared but never used. + class C { + ~ +!!! error TS6133: 'C' is declared but never used. + m() { C; } + } + enum E { A = 0, B = E.A } + ~ +!!! error TS6133: 'E' is declared but never used. + + // Does not detect mutual recursion. + function g() { D; } + class D { m() { g; } } + + // Does not work on private methods. + class P { private m() { this.m; } } + P; + \ No newline at end of file diff --git a/tests/baselines/reference/noUnusedLocals_selfReference.js b/tests/baselines/reference/noUnusedLocals_selfReference.js new file mode 100644 index 0000000000..74a39923d5 --- /dev/null +++ b/tests/baselines/reference/noUnusedLocals_selfReference.js @@ -0,0 +1,49 @@ +//// [noUnusedLocals_selfReference.ts] +export {}; // Make this a module scope, so these are local variables. + +function f() { f; } +class C { + m() { C; } +} +enum E { A = 0, B = E.A } + +// Does not detect mutual recursion. +function g() { D; } +class D { m() { g; } } + +// Does not work on private methods. +class P { private m() { this.m; } } +P; + + +//// [noUnusedLocals_selfReference.js] +"use strict"; +exports.__esModule = true; +function f() { f; } +var C = (function () { + function C() { + } + C.prototype.m = function () { C; }; + return C; +}()); +var E; +(function (E) { + E[E["A"] = 0] = "A"; + E[E["B"] = 0] = "B"; +})(E || (E = {})); +// Does not detect mutual recursion. +function g() { D; } +var D = (function () { + function D() { + } + D.prototype.m = function () { g; }; + return D; +}()); +// Does not work on private methods. +var P = (function () { + function P() { + } + P.prototype.m = function () { this.m; }; + return P; +}()); +P; diff --git a/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt b/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt index 528ca9f9e7..e5c6f47648 100644 --- a/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt @@ -1,8 +1,9 @@ tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(2,6): error TS6133: 'handler1' is declared but never used. +tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(5,10): error TS6133: 'foo' is declared but never used. tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(6,10): error TS6133: 'handler2' is declared but never used. -==== tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts (2 errors) ==== +==== tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts (3 errors) ==== // unused type handler1 = () => void; ~~~~~~~~ @@ -10,6 +11,8 @@ tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(6,10): error TS613 function foo() { + ~~~ +!!! error TS6133: 'foo' is declared but never used. type handler2 = () => void; ~~~~~~~~ !!! error TS6133: 'handler2' is declared but never used. diff --git a/tests/cases/compiler/noUnusedLocals_selfReference.ts b/tests/cases/compiler/noUnusedLocals_selfReference.ts new file mode 100644 index 0000000000..8eb528743c --- /dev/null +++ b/tests/cases/compiler/noUnusedLocals_selfReference.ts @@ -0,0 +1,17 @@ +// @noUnusedLocals: true + +export {}; // Make this a module scope, so these are local variables. + +function f() { f; } +class C { + m() { C; } +} +enum E { A = 0, B = E.A } + +// Does not detect mutual recursion. +function g() { D; } +class D { m() { g; } } + +// Does not work on private methods. +class P { private m() { this.m; } } +P; diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index abfd71a8ff..20228f9a74 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -125,22 +125,6 @@ function dir(dirPath: string, spec?: string, options?: any) { } } -// fs.rmdirSync won't delete directories with files in it -function deleteFolderRecursive(dirPath: string) { - if (fs.existsSync(dirPath)) { - fs.readdirSync(dirPath).forEach((file) => { - const curPath = path.join(dirPath, file); - if (fs.statSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath); - } - else { // delete file - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(dirPath); - } -}; - function writeFile(path: string, data: any) { ensureDirectoriesExist(getDirectoryPath(path)); fs.writeFileSync(path, data);