Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44409,21 +44409,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return tryCast(getRootDeclaration(node), isParameter);
}

function isValidUnusedLocalDeclaration(declaration: Declaration): boolean {
if (isBindingElement(declaration)) {
if (isObjectBindingPattern(declaration.parent)) {
/**
* ignore starts with underscore names _
* const { a: _a } = { a: 1 }
*/
return !!(declaration.propertyName && isIdentifierThatStartsWithUnderscore(declaration.name));
}
return isIdentifierThatStartsWithUnderscore(declaration.name);
}
return isAmbientModule(declaration) ||
(isVariableDeclaration(declaration) && isForInOrOfStatement(declaration.parent.parent) || isImportedDeclaration(declaration)) && isIdentifierThatStartsWithUnderscore(declaration.name!);
}

function checkUnusedLocalsAndParameters(nodeWithLocals: HasLocals, addDiagnostic: AddUnusedDiagnostic): void {
// Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value.
const unusedImports = new Map<string, [ImportClause, ImportedDeclaration[]]>();
Expand All @@ -44438,7 +44423,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

if (local.declarations) {
for (const declaration of local.declarations) {
if (isValidUnusedLocalDeclaration(declaration)) {
const name = getNameOfDeclaration(declaration);
if (isAmbientModule(declaration) || name && isIdentifierThatStartsWithUnderscore(name)) {
Copy link
Author

Choose a reason for hiding this comment

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

If I do not include this isAmbientModule(declaration) guard, then I get failures in two fourslash tests (moduleDeclarationDeprecated_suggestion1.ts and moduleDeclarationDeprecated_suggestion1.ts), which both start unexpectedly surfacing "'__global' is declared but its value is never read.",. I was not able to figure out how to fix those tests.

But I also was not able to figure out any way to exercise this guard in tests/case/compiler tests. That is, I was unable to come up with a test case that would raise an unused declaration or declared but its value is never read, even when I did not include this isAmbientModule(declaration). Here’s an example of what I tried.

```ts
// @Filename: node_modules/library/index.d.ts
declare function get(): string;
export { get };

// @Filename: a.ts
declare module "library" { // ok - ambient modules are not reported as unused
    export function init(): void;
}

declare global { // ok - global augmentation is not reported as unused
    interface Window {
        customProperty: number;
    }
}

Probably this doesn’t matter. After all, there was no test validating that we don’t error on unused ambient modules even before this change, but I still thought I’d call it out.

continue;
}

Expand Down
29 changes: 29 additions & 0 deletions tests/baselines/reference/noUnusedLocals_types.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
noUnusedLocals_types.ts(4,6): error TS6196: 'UnusedType1' is declared but never used.
noUnusedLocals_types.ts(5,11): error TS6196: 'UnusedInterface1' is declared but never used.
noUnusedLocals_types.ts(6,7): error TS6196: 'UnusedClass1' is declared but never used.


==== noUnusedLocals_types.ts (3 errors) ====
// Test specifically for type declarations with underscore prefix

// These should all produce errors (no underscore)
type UnusedType1 = string;
~~~~~~~~~~~
!!! error TS6196: 'UnusedType1' is declared but never used.
interface UnusedInterface1 { x: number; }
~~~~~~~~~~~~~~~~
!!! error TS6196: 'UnusedInterface1' is declared but never used.
class UnusedClass1 { }
~~~~~~~~~~~~
!!! error TS6196: 'UnusedClass1' is declared but never used.

// These should NOT produce errors (underscore prefix)
type _UnusedType2 = string;
interface _UnusedInterface2 { x: number; }
class _UnusedClass2 { }

// Mixed usage - only the one without underscore should error
type UsedInOther = number;
type _Helper = UsedInOther; // _Helper is not an error, but it uses UsedInOther

export {};
35 changes: 35 additions & 0 deletions tests/baselines/reference/noUnusedLocals_types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [tests/cases/compiler/noUnusedLocals_types.ts] ////

//// [noUnusedLocals_types.ts]
// Test specifically for type declarations with underscore prefix

// These should all produce errors (no underscore)
type UnusedType1 = string;
interface UnusedInterface1 { x: number; }
class UnusedClass1 { }

// These should NOT produce errors (underscore prefix)
type _UnusedType2 = string;
interface _UnusedInterface2 { x: number; }
class _UnusedClass2 { }

// Mixed usage - only the one without underscore should error
type UsedInOther = number;
type _Helper = UsedInOther; // _Helper is not an error, but it uses UsedInOther

export {};

//// [noUnusedLocals_types.js]
"use strict";
// Test specifically for type declarations with underscore prefix
Object.defineProperty(exports, "__esModule", { value: true });
var UnusedClass1 = /** @class */ (function () {
function UnusedClass1() {
}
return UnusedClass1;
}());
var _UnusedClass2 = /** @class */ (function () {
function _UnusedClass2() {
}
return _UnusedClass2;
}());
36 changes: 36 additions & 0 deletions tests/baselines/reference/noUnusedLocals_types.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//// [tests/cases/compiler/noUnusedLocals_types.ts] ////

=== noUnusedLocals_types.ts ===
// Test specifically for type declarations with underscore prefix

// These should all produce errors (no underscore)
type UnusedType1 = string;
>UnusedType1 : Symbol(UnusedType1, Decl(noUnusedLocals_types.ts, 0, 0))

interface UnusedInterface1 { x: number; }
>UnusedInterface1 : Symbol(UnusedInterface1, Decl(noUnusedLocals_types.ts, 3, 26))
>x : Symbol(UnusedInterface1.x, Decl(noUnusedLocals_types.ts, 4, 28))

class UnusedClass1 { }
>UnusedClass1 : Symbol(UnusedClass1, Decl(noUnusedLocals_types.ts, 4, 41))

// These should NOT produce errors (underscore prefix)
type _UnusedType2 = string;
>_UnusedType2 : Symbol(_UnusedType2, Decl(noUnusedLocals_types.ts, 5, 22))

interface _UnusedInterface2 { x: number; }
>_UnusedInterface2 : Symbol(_UnusedInterface2, Decl(noUnusedLocals_types.ts, 8, 27))
>x : Symbol(_UnusedInterface2.x, Decl(noUnusedLocals_types.ts, 9, 29))

class _UnusedClass2 { }
>_UnusedClass2 : Symbol(_UnusedClass2, Decl(noUnusedLocals_types.ts, 9, 42))

// Mixed usage - only the one without underscore should error
type UsedInOther = number;
>UsedInOther : Symbol(UsedInOther, Decl(noUnusedLocals_types.ts, 10, 23))

type _Helper = UsedInOther; // _Helper is not an error, but it uses UsedInOther
>_Helper : Symbol(_Helper, Decl(noUnusedLocals_types.ts, 13, 26))
>UsedInOther : Symbol(UsedInOther, Decl(noUnusedLocals_types.ts, 10, 23))

export {};
41 changes: 41 additions & 0 deletions tests/baselines/reference/noUnusedLocals_types.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//// [tests/cases/compiler/noUnusedLocals_types.ts] ////

=== noUnusedLocals_types.ts ===
// Test specifically for type declarations with underscore prefix

// These should all produce errors (no underscore)
type UnusedType1 = string;
>UnusedType1 : string
> : ^^^^^^

interface UnusedInterface1 { x: number; }
>x : number
> : ^^^^^^

class UnusedClass1 { }
>UnusedClass1 : UnusedClass1
> : ^^^^^^^^^^^^

// These should NOT produce errors (underscore prefix)
type _UnusedType2 = string;
>_UnusedType2 : string
> : ^^^^^^

interface _UnusedInterface2 { x: number; }
>x : number
> : ^^^^^^

class _UnusedClass2 { }
>_UnusedClass2 : _UnusedClass2
> : ^^^^^^^^^^^^^

// Mixed usage - only the one without underscore should error
type UsedInOther = number;
>UsedInOther : number
> : ^^^^^^

type _Helper = UsedInOther; // _Helper is not an error, but it uses UsedInOther
>_Helper : number
> : ^^^^^^

export {};
164 changes: 149 additions & 15 deletions tests/baselines/reference/unusedLocalsStartingWithUnderscore.errors.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,156 @@
/a.ts(7,11): error TS6133: '_ns' is declared but its value is never read.
/a.ts(8,9): error TS6133: '_' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(2,7): error TS6133: 'unusedVar' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(5,5): error TS6133: 'unusedLet' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(8,5): error TS6133: 'unusedVar2' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(11,9): error TS6133: 'a1' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(15,10): error TS6133: 'unusedFunc' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(18,7): error TS6133: 'unusedArrow' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(22,7): error TS6196: 'UnusedClass' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(26,11): error TS6196: 'UnusedInterface' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(30,6): error TS6196: 'UnusedType' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(34,6): error TS6196: 'UnusedEnum' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(39,12): error TS6133: 'x' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(41,12): error TS6133: 'x' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(44,11): error TS6133: 'UnusedNamespace' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(52,7): error TS6133: 'unusedA' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(54,8): error TS6133: 'unusedC' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(61,7): error TS6196: 'TestClass' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(62,13): error TS6133: 'unusedMember' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(63,13): error TS6133: '_unusedMember' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(65,13): error TS6133: 'unusedMethod' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(66,13): error TS6133: '_unusedMethod' is declared but its value is never read.
unusedLocalsStartingWithUnderscore.ts(69,6): error TS6196: 'TestEnum' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(74,11): error TS6196: 'TestInterface' is declared but never used.
unusedLocalsStartingWithUnderscore.ts(79,7): error TS6133: 'obj' is declared but its value is never read.


==== /a.ts (2 errors) ====
import * as _ from "./a";
==== unusedLocalsStartingWithUnderscore.ts (23 errors) ====
// Variables
const unusedVar = 1; // error
~~~~~~~~~
!!! error TS6133: 'unusedVar' is declared but its value is never read.
const _unusedVar = 2; // ok

for (const _ of []) { }
let unusedLet = 3; // error
~~~~~~~~~
!!! error TS6133: 'unusedLet' is declared but its value is never read.
let _unusedLet = 4; // ok

for (const _ in []) { }
var unusedVar2 = 5; // error
~~~~~~~~~~
!!! error TS6133: 'unusedVar2' is declared but its value is never read.
var _unusedVar2 = 6; // ok

namespace _ns {
~~~
!!! error TS6133: '_ns' is declared but its value is never read.
let _;
~
!!! error TS6133: '_' is declared but its value is never read.
for (const _ of []) { }
const { a1, _b1 } = { a1: 1, _b1: 2 }; // error on a1
~~
!!! error TS6133: 'a1' is declared but its value is never read.
const { _a2, _b2 } = { _a2: 1, _b2: 2 }; // ok

for (const _ in []) { }
// Functions
function unusedFunc() { } // error
~~~~~~~~~~
!!! error TS6133: 'unusedFunc' is declared but its value is never read.
function _unusedFunc() { } // ok

const unusedArrow = () => { }; // error
~~~~~~~~~~~
!!! error TS6133: 'unusedArrow' is declared but its value is never read.
const _unusedArrow = () => { }; // ok

// Classes
class UnusedClass { } // error
~~~~~~~~~~~
!!! error TS6196: 'UnusedClass' is declared but never used.
class _UnusedClass { } // ok

// Interfaces
interface UnusedInterface { } // error
~~~~~~~~~~~~~~~
!!! error TS6196: 'UnusedInterface' is declared but never used.
interface _UnusedInterface { } // ok

// Type aliases
type UnusedType = string; // error
~~~~~~~~~~
!!! error TS6196: 'UnusedType' is declared but never used.
type _UnusedType = string; // ok

// Enums
enum UnusedEnum { A } // error
~~~~~~~~~~
!!! error TS6196: 'UnusedEnum' is declared but never used.
enum _UnusedEnum { A } // ok

// Declarations in for loops
for (const _x of []) { } // ok
for (const x of []) { } // error
~
!!! error TS6133: 'x' is declared but its value is never read.
for (const _x in []) { } // ok
for (const x in []) { } // error
~
!!! error TS6133: 'x' is declared but its value is never read.

// Namespaces
namespace UnusedNamespace { // error
~~~~~~~~~~~~~~~
!!! error TS6133: 'UnusedNamespace' is declared but its value is never read.
export const x = 1;
}
namespace _UnusedNamespace { // ok
export const x = 1;
}


// Destructuring
const { a: unusedA } = { a: 1 }; // error
~~~~~~~~~~~~~~
!!! error TS6133: 'unusedA' is declared but its value is never read.
const { b: _unusedB } = { b: 2 }; // ok
const [unusedC] = [3]; // error
~~~~~~~
!!! error TS6133: 'unusedC' is declared but its value is never read.
const [_unusedD] = [4]; // ok

//
// The following declarations may _not_ use an underscore to bypass @noUnusedLocals
//

class TestClass {
~~~~~~~~~
!!! error TS6196: 'TestClass' is declared but never used.
private unusedMember = 1; // error
~~~~~~~~~~~~
!!! error TS6133: 'unusedMember' is declared but its value is never read.
private _unusedMember = 2; // still error
~~~~~~~~~~~~~
!!! error TS6133: '_unusedMember' is declared but its value is never read.

private unusedMethod() { } // error
~~~~~~~~~~~~
!!! error TS6133: 'unusedMethod' is declared but its value is never read.
private _unusedMethod() { } // still error
~~~~~~~~~~~~~
!!! error TS6133: '_unusedMethod' is declared but its value is never read.
}

enum TestEnum {
~~~~~~~~
!!! error TS6196: 'TestEnum' is declared but never used.
UnusedMember = 1, // error
_UnusedMember = 2, // still error
}

interface TestInterface {
~~~~~~~~~~~~~
!!! error TS6196: 'TestInterface' is declared but never used.
unusedProp: number; // error
_unusedProp: number; // still error
}

const obj = {
~~~
!!! error TS6133: 'obj' is declared but its value is never read.
unusedProp: 1, // error
_unusedProp: 2, // still error
};

export { };
Loading
Loading