Skip to content

Commit

Permalink
Unused identifiers compiler code (#9200)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
sarangan12 authored Jun 24, 2016
1 parent cca7000 commit a0a9666
Show file tree
Hide file tree
Showing 324 changed files with 5,641 additions and 8 deletions.
130 changes: 123 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8310,8 +8310,21 @@ namespace ts {
return container === declarationContainer;
}

function updateReferencesForInterfaceHeritiageClauseTargets(node: InterfaceDeclaration): void {
const extendedTypeNode = getClassExtendsHeritageClauseElement(node);
if (extendedTypeNode) {
const t = getTypeFromTypeNode(extendedTypeNode);
if (t !== unknownType && t.symbol && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
t.symbol.hasReference = true;
}
}
}

function checkIdentifier(node: Identifier): Type {
const symbol = getResolvedSymbol(node);
if (symbol && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
symbol.hasReference = true;
}

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

if ((compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
prop.hasReference = true;
}

getNodeLinks(node).resolvedSymbol = prop;

if (prop.parent && prop.parent.flags & SymbolFlags.Class) {
Expand Down Expand Up @@ -12168,6 +12185,8 @@ namespace ts {
}
}
}
checkUnusedIdentifiers(node);
checkUnusedTypeParameters(node);
}
}

Expand Down Expand Up @@ -13238,6 +13257,9 @@ namespace ts {
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
}
}
if (!(<FunctionDeclaration>node).body) {
checkUnusedTypeParameters(node);
}
}
}

Expand Down Expand Up @@ -13390,6 +13412,8 @@ namespace ts {
checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node);

checkSourceElement(node.body);
checkUnusedIdentifiers(node);
checkUnusedTypeParameters(node);

const symbol = getSymbolOfNode(node);
const firstDeclaration = getDeclarationOfKind(symbol, node.kind);
Expand Down Expand Up @@ -13582,13 +13606,18 @@ namespace ts {
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
checkGrammarTypeArguments(node, node.typeArguments);
const type = getTypeFromTypeReference(node);
if (type !== unknownType && node.typeArguments) {
// Do type argument local checks only if referenced type is successfully resolved
forEach(node.typeArguments, checkSourceElement);
if (produceDiagnostics) {
const symbol = getNodeLinks(node).resolvedSymbol;
const typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (<TypeReference>type).target.localTypeParameters;
checkTypeArgumentConstraints(typeParameters, node.typeArguments);
if (type !== unknownType) {
if (type.symbol && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
type.symbol.hasReference = true;
}
if (node.typeArguments) {
// Do type argument local checks only if referenced type is successfully resolved
forEach(node.typeArguments, checkSourceElement);
if (produceDiagnostics) {
const symbol = getNodeLinks(node).resolvedSymbol;
const typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (<TypeReference>type).target.localTypeParameters;
checkTypeArgumentConstraints(typeParameters, node.typeArguments);
}
}
}
}
Expand Down Expand Up @@ -14431,6 +14460,8 @@ namespace ts {
}

checkSourceElement(node.body);
checkUnusedIdentifiers(node);
checkUnusedTypeParameters(node);
if (!node.asteriskToken) {
const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type));
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
Expand All @@ -14452,12 +14483,83 @@ namespace ts {
}
}

function checkUnusedIdentifiers(node: FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction | ForInStatement | Block | CatchClause): void {
if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && (compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
for (const key in node.locals) {
if (hasProperty(node.locals, key)) {
const local = node.locals[key];
if (!local.hasReference && local.valueDeclaration) {
if (local.valueDeclaration.kind !== SyntaxKind.Parameter && compilerOptions.noUnusedLocals) {
error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name);
}
else if (local.valueDeclaration.kind === SyntaxKind.Parameter && compilerOptions.noUnusedParameters) {
if (local.valueDeclaration.flags === 0) {
error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name);
}
}
}
}
}
}
}

function checkUnusedClassLocals(node: ClassDeclaration): void {
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
if (node.members) {
for (const member of node.members) {
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) {
if (isPrivateNode(member) && !member.symbol.hasReference) {
error(member.name, Diagnostics._0_is_declared_but_never_used, member.symbol.name);
}
}
else if (member.kind === SyntaxKind.Constructor) {
for (const parameter of (<ConstructorDeclaration>member).parameters) {
if (isPrivateNode(parameter) && !parameter.symbol.hasReference) {
error(parameter.name, Diagnostics._0_is_declared_but_never_used, parameter.symbol.name);
}
}
}
}
}
}
}

function checkUnusedTypeParameters(node: ClassDeclaration | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration | InterfaceDeclaration) {
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
if (node.typeParameters) {
for (const typeParameter of node.typeParameters) {
if (!typeParameter.symbol.hasReference) {
error(typeParameter.name, Diagnostics._0_is_declared_but_never_used, typeParameter.symbol.name);
}
}
}
}
}

function isPrivateNode(node: Node): boolean {
return (node.flags & NodeFlags.Private) !== 0;
}

function checkUnusedModuleLocals(node: ModuleDeclaration | SourceFile): void {
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) {
for (const key in node.locals) {
if (hasProperty(node.locals, key)) {
const local = node.locals[key];
if (!local.hasReference && !local.exportSymbol) {
forEach(local.declarations, d => error(d.name, Diagnostics._0_is_declared_but_never_used, local.name));
}
}
}
}
}

function checkBlock(node: Block) {
// Grammar checking for SyntaxKind.Block
if (node.kind === SyntaxKind.Block) {
checkGrammarStatementInAmbientContext(node);
}
forEach(node.statements, checkSourceElement);
checkUnusedIdentifiers(node);
}

function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
Expand Down Expand Up @@ -14962,6 +15064,7 @@ namespace ts {
}

checkSourceElement(node.statement);
checkUnusedIdentifiers(node);
}

function checkForInStatement(node: ForInStatement) {
Expand Down Expand Up @@ -15009,6 +15112,7 @@ namespace ts {
}

checkSourceElement(node.statement);
checkUnusedIdentifiers(node);
}

function checkForInOrForOfVariableDeclaration(iterationStatement: ForInStatement | ForOfStatement): void {
Expand Down Expand Up @@ -15448,6 +15552,7 @@ namespace ts {
}

checkBlock(catchClause.block);
checkUnusedIdentifiers(catchClause);
}

if (node.finallyBlock) {
Expand Down Expand Up @@ -15609,6 +15714,8 @@ namespace ts {
}
checkClassLikeDeclaration(node);
forEach(node.members, checkSourceElement);
checkUnusedClassLocals(node);
checkUnusedTypeParameters(node);
}

function checkClassLikeDeclaration(node: ClassLikeDeclaration) {
Expand Down Expand Up @@ -15918,6 +16025,8 @@ namespace ts {

if (produceDiagnostics) {
checkTypeForDuplicateIndexSignatures(node);
updateReferencesForInterfaceHeritiageClauseTargets(node);
checkUnusedTypeParameters(node);
}
}

Expand Down Expand Up @@ -16314,6 +16423,7 @@ namespace ts {

if (node.body) {
checkSourceElement(node.body);
checkUnusedModuleLocals(node);
}
}

Expand Down Expand Up @@ -16494,6 +16604,9 @@ namespace ts {
if (target.flags & SymbolFlags.Type) {
checkTypeNameIsReserved(node.name, Diagnostics.Import_name_cannot_be_0);
}
if ((compilerOptions.noUnusedLocals || compilerOptions.noUnusedParameters) && !isInAmbientContext(node)) {
target.hasReference = true;
}
}
}
else {
Expand Down Expand Up @@ -16836,6 +16949,9 @@ namespace ts {

deferredNodes = [];
forEach(node.statements, checkSourceElement);
if (isExternalModule(node)) {
checkUnusedModuleLocals(node);
}
checkDeferredNodes();
deferredNodes = undefined;

Expand Down
10 changes: 10 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ namespace ts {
type: "boolean",
description: Diagnostics.Raise_error_on_this_expressions_with_an_implied_any_type,
},
{
name: "noUnusedLocals",
type: "boolean",
description: Diagnostics.Report_Errors_on_Unused_Locals,
},
{
name: "noUnusedParameters",
type: "boolean",
description: Diagnostics.Report_Errors_on_Unused_Parameters
},
{
name: "noLib",
type: "boolean",
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2792,6 +2792,18 @@
"category": "Message",
"code": 6132
},
"'{0}' is declared but never used.": {
"category": "Error",
"code": 6133
},
"Report Errors on Unused Locals.": {
"category": "Message",
"code": 6134
},
"Report Errors on Unused Parameters.": {
"category": "Message",
"code": 6135
},

"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2127,6 +2127,7 @@ namespace ts {
/* @internal */ parent?: Symbol; // Parent symbol
/* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
/* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
/* @internal */ hasReference?: boolean; // True if the symbol is referenced elsewhere
}

/* @internal */
Expand Down Expand Up @@ -2557,6 +2558,8 @@ namespace ts {
noImplicitAny?: boolean;
noImplicitReturns?: boolean;
noImplicitThis?: boolean;
noUnusedLocals?: boolean;
noUnusedParameters?: boolean;
noImplicitUseStrict?: boolean;
noLib?: boolean;
noResolve?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1702,7 +1702,7 @@ namespace ts {
node.kind === SyntaxKind.ExportAssignment && (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier;
}

export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) {
export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) {
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined;
}
Expand Down
13 changes: 13 additions & 0 deletions tests/baselines/reference/unusedClassesinModule1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
tests/cases/compiler/unusedClassesinModule1.ts(3,11): error TS6133: 'Calculator' is declared but never used.


==== tests/cases/compiler/unusedClassesinModule1.ts (1 errors) ====

module A {
class Calculator {
~~~~~~~~~~
!!! error TS6133: 'Calculator' is declared but never used.
public handelChar() {
}
}
}
20 changes: 20 additions & 0 deletions tests/baselines/reference/unusedClassesinModule1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//// [unusedClassesinModule1.ts]

module A {
class Calculator {
public handelChar() {
}
}
}

//// [unusedClassesinModule1.js]
var A;
(function (A) {
var Calculator = (function () {
function Calculator() {
}
Calculator.prototype.handelChar = function () {
};
return Calculator;
}());
})(A || (A = {}));
12 changes: 12 additions & 0 deletions tests/baselines/reference/unusedClassesinNamespace1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
tests/cases/compiler/unusedClassesinNamespace1.ts(3,11): error TS6133: 'c1' is declared but never used.


==== tests/cases/compiler/unusedClassesinNamespace1.ts (1 errors) ====

namespace Validation {
class c1 {
~~
!!! error TS6133: 'c1' is declared but never used.

}
}
17 changes: 17 additions & 0 deletions tests/baselines/reference/unusedClassesinNamespace1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// [unusedClassesinNamespace1.ts]

namespace Validation {
class c1 {

}
}

//// [unusedClassesinNamespace1.js]
var Validation;
(function (Validation) {
var c1 = (function () {
function c1() {
}
return c1;
}());
})(Validation || (Validation = {}));
16 changes: 16 additions & 0 deletions tests/baselines/reference/unusedClassesinNamespace2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
tests/cases/compiler/unusedClassesinNamespace2.ts(3,11): error TS6133: 'c1' is declared but never used.


==== tests/cases/compiler/unusedClassesinNamespace2.ts (1 errors) ====

namespace Validation {
class c1 {
~~
!!! error TS6133: 'c1' is declared but never used.

}

export class c2 {

}
}
Loading

0 comments on commit a0a9666

Please sign in to comment.