Skip to content

Commit a0a9666

Browse files
authored
Unused identifiers compiler code (#9200)
* Code changes to update references of the Identifiers * Added code for handling function, method and coonstructor level local variables and parameters * Rebased with origin master * Code changes to handle unused private variables, private methods and typed parameters * Code changes to handle namespace level elements * Code changes to handle unimplemented interfaces * Code to optimize the d.ts check * Correct Code change to handle the parameters for methods inside interfaces * Fix for lint error * Remove Trailing whitespace * Code changes to handle interface implementations * Changes to display the error position correctly * Compiler Test Cases * Adding condition to ignore constructor parameters * Removing unnecessary tests * Additional changes for compiler code * Additional changes to handle constructor scenario * Fixing the consolidated case * Changed logic to search for private instead of public * Response to PR Comments * Changed the error code in test cases as result of merge with master * Adding the missing file * Adding the missing file II * Response to PR comments * Code changes for checking unused imports * Test Cases for Unused Imports * Response to PR comments * Code change specific to position of Import Declaration * Code change for handling the position for unused import * New scenarios for handling parameters in lambda function, type parameters in methods, etc. * Additional scenarios based on PR comments * Removing a redundant check * Added ambient check to imports and typeparatmeter reporting * Added one more scenario to handle type parameters * Added new scenario for TypeParameter on Interface * Refactoring the code * Added scenario to handle private class elements declared in constructor. * Minor change to erro reporting
1 parent cca7000 commit a0a9666

File tree

324 files changed

+5641
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

324 files changed

+5641
-8
lines changed

src/compiler/checker.ts

+123-7
Original file line numberDiff line numberDiff line change
@@ -8310,8 +8310,21 @@ namespace ts {
83108310
return container === declarationContainer;
83118311
}
83128312

8313+
function updateReferencesForInterfaceHeritiageClauseTargets(node: InterfaceDeclaration): void {
8314+
const extendedTypeNode = getClassExtendsHeritageClauseElement(node);
8315+
if (extendedTypeNode) {
8316+
const t = getTypeFromTypeNode(extendedTypeNode);
8317+
if (t !== unknownType && t.symbol && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
8318+
t.symbol.hasReference = true;
8319+
}
8320+
}
8321+
}
8322+
83138323
function checkIdentifier(node: Identifier): Type {
83148324
const symbol = getResolvedSymbol(node);
8325+
if (symbol && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
8326+
symbol.hasReference = true;
8327+
}
83158328

83168329
// As noted in ECMAScript 6 language spec, arrow functions never have an arguments objects.
83178330
// Although in down-level emit of arrow function, we emit it using function expression which means that
@@ -10226,6 +10239,10 @@ namespace ts {
1022610239
return unknownType;
1022710240
}
1022810241

10242+
if ((compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
10243+
prop.hasReference = true;
10244+
}
10245+
1022910246
getNodeLinks(node).resolvedSymbol = prop;
1023010247

1023110248
if (prop.parent && prop.parent.flags & SymbolFlags.Class) {
@@ -12168,6 +12185,8 @@ namespace ts {
1216812185
}
1216912186
}
1217012187
}
12188+
checkUnusedIdentifiers(node);
12189+
checkUnusedTypeParameters(node);
1217112190
}
1217212191
}
1217312192

@@ -13238,6 +13257,9 @@ namespace ts {
1323813257
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
1323913258
}
1324013259
}
13260+
if (!(<FunctionDeclaration>node).body) {
13261+
checkUnusedTypeParameters(node);
13262+
}
1324113263
}
1324213264
}
1324313265

@@ -13390,6 +13412,8 @@ namespace ts {
1339013412
checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node);
1339113413

1339213414
checkSourceElement(node.body);
13415+
checkUnusedIdentifiers(node);
13416+
checkUnusedTypeParameters(node);
1339313417

1339413418
const symbol = getSymbolOfNode(node);
1339513419
const firstDeclaration = getDeclarationOfKind(symbol, node.kind);
@@ -13582,13 +13606,18 @@ namespace ts {
1358213606
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
1358313607
checkGrammarTypeArguments(node, node.typeArguments);
1358413608
const type = getTypeFromTypeReference(node);
13585-
if (type !== unknownType && node.typeArguments) {
13586-
// Do type argument local checks only if referenced type is successfully resolved
13587-
forEach(node.typeArguments, checkSourceElement);
13588-
if (produceDiagnostics) {
13589-
const symbol = getNodeLinks(node).resolvedSymbol;
13590-
const typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (<TypeReference>type).target.localTypeParameters;
13591-
checkTypeArgumentConstraints(typeParameters, node.typeArguments);
13609+
if (type !== unknownType) {
13610+
if (type.symbol && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
13611+
type.symbol.hasReference = true;
13612+
}
13613+
if (node.typeArguments) {
13614+
// Do type argument local checks only if referenced type is successfully resolved
13615+
forEach(node.typeArguments, checkSourceElement);
13616+
if (produceDiagnostics) {
13617+
const symbol = getNodeLinks(node).resolvedSymbol;
13618+
const typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (<TypeReference>type).target.localTypeParameters;
13619+
checkTypeArgumentConstraints(typeParameters, node.typeArguments);
13620+
}
1359213621
}
1359313622
}
1359413623
}
@@ -14431,6 +14460,8 @@ namespace ts {
1443114460
}
1443214461

1443314462
checkSourceElement(node.body);
14463+
checkUnusedIdentifiers(node);
14464+
checkUnusedTypeParameters(node);
1443414465
if (!node.asteriskToken) {
1443514466
const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type));
1443614467
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
@@ -14452,12 +14483,83 @@ namespace ts {
1445214483
}
1445314484
}
1445414485

14486+
function checkUnusedIdentifiers(node: FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction | ForInStatement | Block | CatchClause): void {
14487+
if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
14488+
for (const key in node.locals) {
14489+
if (hasProperty(node.locals, key)) {
14490+
const local = node.locals[key];
14491+
if (!local.hasReference && local.valueDeclaration) {
14492+
if (local.valueDeclaration.kind !== SyntaxKind.Parameter && compilerOptions.noUnusedLocals) {
14493+
error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name);
14494+
}
14495+
else if (local.valueDeclaration.kind === SyntaxKind.Parameter && compilerOptions.noUnusedParameters) {
14496+
if (local.valueDeclaration.flags === 0) {
14497+
error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name);
14498+
}
14499+
}
14500+
}
14501+
}
14502+
}
14503+
}
14504+
}
14505+
14506+
function checkUnusedClassLocals(node: ClassDeclaration): void {
14507+
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
14508+
if (node.members) {
14509+
for (const member of node.members) {
14510+
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) {
14511+
if (isPrivateNode(member) && !member.symbol.hasReference) {
14512+
error(member.name, Diagnostics._0_is_declared_but_never_used, member.symbol.name);
14513+
}
14514+
}
14515+
else if (member.kind === SyntaxKind.Constructor) {
14516+
for (const parameter of (<ConstructorDeclaration>member).parameters) {
14517+
if (isPrivateNode(parameter) && !parameter.symbol.hasReference) {
14518+
error(parameter.name, Diagnostics._0_is_declared_but_never_used, parameter.symbol.name);
14519+
}
14520+
}
14521+
}
14522+
}
14523+
}
14524+
}
14525+
}
14526+
14527+
function checkUnusedTypeParameters(node: ClassDeclaration | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration) {
14528+
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
14529+
if (node.typeParameters) {
14530+
for (const typeParameter of node.typeParameters) {
14531+
if (!typeParameter.symbol.hasReference) {
14532+
error(typeParameter.name, Diagnostics._0_is_declared_but_never_used, typeParameter.symbol.name);
14533+
}
14534+
}
14535+
}
14536+
}
14537+
}
14538+
14539+
function isPrivateNode(node: Node): boolean {
14540+
return (node.flags & NodeFlags.Private) !== 0;
14541+
}
14542+
14543+
function checkUnusedModuleLocals(node: ModuleDeclaration | SourceFile): void {
14544+
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
14545+
for (const key in node.locals) {
14546+
if (hasProperty(node.locals, key)) {
14547+
const local = node.locals[key];
14548+
if (!local.hasReference && !local.exportSymbol) {
14549+
forEach(local.declarations, d => error(d.name, Diagnostics._0_is_declared_but_never_used, local.name));
14550+
}
14551+
}
14552+
}
14553+
}
14554+
}
14555+
1445514556
function checkBlock(node: Block) {
1445614557
// Grammar checking for SyntaxKind.Block
1445714558
if (node.kind === SyntaxKind.Block) {
1445814559
checkGrammarStatementInAmbientContext(node);
1445914560
}
1446014561
forEach(node.statements, checkSourceElement);
14562+
checkUnusedIdentifiers(node);
1446114563
}
1446214564

1446314565
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
@@ -14962,6 +15064,7 @@ namespace ts {
1496215064
}
1496315065

1496415066
checkSourceElement(node.statement);
15067+
checkUnusedIdentifiers(node);
1496515068
}
1496615069

1496715070
function checkForInStatement(node: ForInStatement) {
@@ -15009,6 +15112,7 @@ namespace ts {
1500915112
}
1501015113

1501115114
checkSourceElement(node.statement);
15115+
checkUnusedIdentifiers(node);
1501215116
}
1501315117

1501415118
function checkForInOrForOfVariableDeclaration(iterationStatement: ForInStatement | ForOfStatement): void {
@@ -15448,6 +15552,7 @@ namespace ts {
1544815552
}
1544915553

1545015554
checkBlock(catchClause.block);
15555+
checkUnusedIdentifiers(catchClause);
1545115556
}
1545215557

1545315558
if (node.finallyBlock) {
@@ -15609,6 +15714,8 @@ namespace ts {
1560915714
}
1561015715
checkClassLikeDeclaration(node);
1561115716
forEach(node.members, checkSourceElement);
15717+
checkUnusedClassLocals(node);
15718+
checkUnusedTypeParameters(node);
1561215719
}
1561315720

1561415721
function checkClassLikeDeclaration(node: ClassLikeDeclaration) {
@@ -15918,6 +16025,8 @@ namespace ts {
1591816025

1591916026
if (produceDiagnostics) {
1592016027
checkTypeForDuplicateIndexSignatures(node);
16028+
updateReferencesForInterfaceHeritiageClauseTargets(node);
16029+
checkUnusedTypeParameters(node);
1592116030
}
1592216031
}
1592316032

@@ -16314,6 +16423,7 @@ namespace ts {
1631416423

1631516424
if (node.body) {
1631616425
checkSourceElement(node.body);
16426+
checkUnusedModuleLocals(node);
1631716427
}
1631816428
}
1631916429

@@ -16494,6 +16604,9 @@ namespace ts {
1649416604
if (target.flags & SymbolFlags.Type) {
1649516605
checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0);
1649616606
}
16607+
if ((compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
16608+
target.hasReference = true;
16609+
}
1649716610
}
1649816611
}
1649916612
else {
@@ -16836,6 +16949,9 @@ namespace ts {
1683616949

1683716950
deferredNodes = [];
1683816951
forEach(node.statements, checkSourceElement);
16952+
if (isExternalModule(node)) {
16953+
checkUnusedModuleLocals(node);
16954+
}
1683916955
checkDeferredNodes();
1684016956
deferredNodes = undefined;
1684116957

src/compiler/commandLineParser.ts

+10
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ namespace ts {
132132
type: "boolean",
133133
description: Diagnostics.Raise_error_on_this_expressions_with_an_implied_any_type,
134134
},
135+
{
136+
name: "noUnusedLocals",
137+
type: "boolean",
138+
description: Diagnostics.Report_Errors_on_Unused_Locals,
139+
},
140+
{
141+
name: "noUnusedParameters",
142+
type: "boolean",
143+
description: Diagnostics.Report_Errors_on_Unused_Parameters
144+
},
135145
{
136146
name: "noLib",
137147
type: "boolean",

src/compiler/diagnosticMessages.json

+12
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,18 @@
27922792
"category": "Message",
27932793
"code": 6132
27942794
},
2795+
"'{0}' is declared but never used.": {
2796+
"category": "Error",
2797+
"code": 6133
2798+
},
2799+
"Report Errors on Unused Locals.": {
2800+
"category": "Message",
2801+
"code": 6134
2802+
},
2803+
"Report Errors on Unused Parameters.": {
2804+
"category": "Message",
2805+
"code": 6135
2806+
},
27952807

27962808
"Variable '{0}' implicitly has an '{1}' type.": {
27972809
"category": "Error",

src/compiler/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,7 @@ namespace ts {
21272127
/* @internal */ parent?: Symbol; // Parent symbol
21282128
/* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
21292129
/* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
2130+
/* @internal */ hasReference?: boolean; // True if the symbol is referenced elsewhere
21302131
}
21312132

21322133
/* @internal */
@@ -2557,6 +2558,8 @@ namespace ts {
25572558
noImplicitAny?: boolean;
25582559
noImplicitReturns?: boolean;
25592560
noImplicitThis?: boolean;
2561+
noUnusedLocals?: boolean;
2562+
noUnusedParameters?: boolean;
25602563
noImplicitUseStrict?: boolean;
25612564
noLib?: boolean;
25622565
noResolve?: boolean;

src/compiler/utilities.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,7 @@ namespace ts {
17021702
node.kind === SyntaxKind.ExportAssignment && (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier;
17031703
}
17041704

1705-
export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) {
1705+
export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) {
17061706
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
17071707
return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined;
17081708
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/compiler/unusedClassesinModule1.ts(3,11): error TS6133: 'Calculator' is declared but never used.
2+
3+
4+
==== tests/cases/compiler/unusedClassesinModule1.ts (1 errors) ====
5+
6+
module A {
7+
class Calculator {
8+
~~~~~~~~~~
9+
!!! error TS6133: 'Calculator' is declared but never used.
10+
public handelChar() {
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [unusedClassesinModule1.ts]
2+
3+
module A {
4+
class Calculator {
5+
public handelChar() {
6+
}
7+
}
8+
}
9+
10+
//// [unusedClassesinModule1.js]
11+
var A;
12+
(function (A) {
13+
var Calculator = (function () {
14+
function Calculator() {
15+
}
16+
Calculator.prototype.handelChar = function () {
17+
};
18+
return Calculator;
19+
}());
20+
})(A || (A = {}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tests/cases/compiler/unusedClassesinNamespace1.ts(3,11): error TS6133: 'c1' is declared but never used.
2+
3+
4+
==== tests/cases/compiler/unusedClassesinNamespace1.ts (1 errors) ====
5+
6+
namespace Validation {
7+
class c1 {
8+
~~
9+
!!! error TS6133: 'c1' is declared but never used.
10+
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [unusedClassesinNamespace1.ts]
2+
3+
namespace Validation {
4+
class c1 {
5+
6+
}
7+
}
8+
9+
//// [unusedClassesinNamespace1.js]
10+
var Validation;
11+
(function (Validation) {
12+
var c1 = (function () {
13+
function c1() {
14+
}
15+
return c1;
16+
}());
17+
})(Validation || (Validation = {}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tests/cases/compiler/unusedClassesinNamespace2.ts(3,11): error TS6133: 'c1' is declared but never used.
2+
3+
4+
==== tests/cases/compiler/unusedClassesinNamespace2.ts (1 errors) ====
5+
6+
namespace Validation {
7+
class c1 {
8+
~~
9+
!!! error TS6133: 'c1' is declared but never used.
10+
11+
}
12+
13+
export class c2 {
14+
15+
}
16+
}

0 commit comments

Comments
 (0)