-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Unused identifiers compiler code #9200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 38 commits
617b819
a8bc8b4
01966ba
1e5ca92
890d178
69dbeea
c78d0e2
107b369
481baa3
cd1ce0f
a38149d
0571c19
e17ed58
2b7f3a7
5a34352
93b7490
7aba626
ed5052d
f5cdc9c
8c3c7b1
dfad7cc
c325625
6a711bc
d62a43f
043a625
8f9d4ae
f15448a
c82453f
bcd6fc4
49385f4
5993015
f464f92
3b5f8d2
ed282d7
e502ba0
0e2e43d
d6c2bcd
f93c6c8
4521058
5eb7153
1401506
5361e5f
7fc4616
9753d09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8290,8 +8290,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 | ||
|
@@ -10181,6 +10194,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) { | ||
|
@@ -12122,6 +12139,8 @@ namespace ts { | |
} | ||
} | ||
} | ||
checkUnusedIdentifiers(node); | ||
checkUnusedTypeParameters(node); | ||
} | ||
} | ||
|
||
|
@@ -13192,6 +13211,9 @@ namespace ts { | |
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node); | ||
} | ||
} | ||
if (node.kind === SyntaxKind.ConstructSignature || node.kind === SyntaxKind.ConstructorType) { | ||
checkUnusedTypeParameters(node); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -13344,6 +13366,8 @@ namespace ts { | |
checkGrammarConstructorTypeParameters(node) || checkGrammarConstructorTypeAnnotation(node); | ||
|
||
checkSourceElement(node.body); | ||
checkUnusedIdentifiers(node); | ||
checkUnusedTypeParameters(node); | ||
|
||
const symbol = getSymbolOfNode(node); | ||
const firstDeclaration = getDeclarationOfKind(symbol, node.kind); | ||
|
@@ -13536,13 +13560,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); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -14385,6 +14414,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); | ||
|
@@ -14406,12 +14437,113 @@ namespace ts { | |
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should check unsued type parameters in functions as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done (And added test cases also) |
||
} | ||
|
||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed it |
||
if (local.valueDeclaration.kind !== SyntaxKind.Parameter && compilerOptions.noUnusedLocals) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would just inline this in previous condition. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
error(local.valueDeclaration, Diagnostics._0_is_declared_but_never_used, key); | ||
} | ||
else if (local.valueDeclaration.kind === SyntaxKind.Parameter && compilerOptions.noUnusedParameters) { | ||
if (local.valueDeclaration.modifiers) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do not think you need this condition, the getCombinedNodeFlags should be sufficient. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Removed the condition |
||
if (getCombinedNodeFlags(local.valueDeclaration) & NodeFlags.Private) { | ||
error(local.valueDeclaration, Diagnostics._0_is_declared_but_never_used, key); | ||
} | ||
} | ||
else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we combine these two functions in one. seems like we are looping over the same set of symbols twice. just loop once, and do the check on option before you report the error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
error(local.valueDeclaration, Diagnostics._0_is_declared_but_never_used, key); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
function checkUnusedModuleLocals(node: ModuleDeclaration): 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) { | ||
if ((local.valueDeclaration && local.valueDeclaration.kind)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to check kind in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed it |
||
error(local.valueDeclaration, Diagnostics._0_is_declared_but_never_used, key); | ||
} | ||
else if (local.declarations && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need these checks? why not just forEach(local.declarations, d => error(d, Diagnostics._0_is_declared_but_never_used, key)); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have already tried this one. But I feel this might not work. The reason is that ImportEqualsDeclaration is a special case in which we need to use local.declarations[0].name instead of local.declarations[0]. Scenario: module B { Here 'a' must be indicated as error instead of "import a = A". But, I have optimized it a little so the condition does not look long. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would say all errors should be on the declaration.name if it exists. |
||
(local.declarations[0].kind === SyntaxKind.InterfaceDeclaration || local.declarations[0].kind === SyntaxKind.ModuleDeclaration)) { | ||
error(local.declarations[0], Diagnostics._0_is_declared_but_never_used, key); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, can we rename this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if (local.declarations) {
const declaration = local.declarations[0];
error(declaration.name? declaration.name : declaation, Diagnostics._0_is_declared_but_never_used, key);
} |
||
else if (local.declarations && local.declarations[0].kind === SyntaxKind.ImportEqualsDeclaration) { | ||
error(local.declarations[0].name, Diagnostics._0_is_declared_but_never_used, key); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
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 (isPrivateClassElement(member) && !member.symbol.hasReference) { | ||
error(member, Diagnostics._0_is_declared_but_never_used, member.symbol.name); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
function checkUnusedTypeParameters(node: ClassDeclaration | FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction | ConstructorDeclaration | SignatureDeclaration) { | ||
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { | ||
if (node.typeParameters) { | ||
for (const typeParameter of node.typeParameters) { | ||
if (!typeParameter.symbol.hasReference) { | ||
error(typeParameter, Diagnostics._0_is_declared_but_never_used, typeParameter.symbol.name); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would extract this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
function isPrivateClassElement(node: ClassElement): boolean { | ||
return (node.flags & NodeFlags.Private) !== 0; | ||
} | ||
|
||
function checkUnusedImports(node: SourceFile) { | ||
if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { | ||
for (const local in node.locals) { | ||
if (hasProperty(node.locals, local)) { | ||
const localValue = node.locals[local]; | ||
if (localValue.declarations && !localValue.exportSymbol) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if (declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause || declaration.kind === SyntaxKind.NamespaceImport || declaration.kind === SyntaxKind.ImportEqualsDeclaration) {
error(declaration.name, Diagnostics._0_is_declared_but_never_used, localValue.name);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am still not sure i understand why checkUnusedImports is not part of the checkUnusedModuleLocals? |
||
for (const declaration of localValue.declarations) { | ||
const symbol = declaration.symbol; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i do not think this is correct. you already have the symbol, and the symbol is not exported, so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed the code. |
||
if (!symbol.hasReference) { | ||
if (declaration.kind === SyntaxKind.ImportSpecifier || declaration.kind === SyntaxKind.ImportClause || declaration.kind === SyntaxKind.NamespaceImport) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to check the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we merge this function with the checkUnusedModulePrivates ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the ambient context check. I prefer to keep them separate for sake of clarity, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am not sure i see the point here. just merge them togather, and get rid of all the checks on the kinds. |
||
error(declaration, Diagnostics._0_is_declared_but_never_used, symbol.name); | ||
} | ||
else if (declaration.kind === SyntaxKind.ImportEqualsDeclaration) { | ||
error(declaration.name, Diagnostics._0_is_declared_but_never_used, symbol.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) { | ||
|
@@ -14916,6 +15048,7 @@ namespace ts { | |
} | ||
|
||
checkSourceElement(node.statement); | ||
checkUnusedIdentifiers(node); | ||
} | ||
|
||
function checkForInStatement(node: ForInStatement) { | ||
|
@@ -14963,6 +15096,7 @@ namespace ts { | |
} | ||
|
||
checkSourceElement(node.statement); | ||
checkUnusedIdentifiers(node); | ||
} | ||
|
||
function checkForInOrForOfVariableDeclaration(iterationStatement: ForInStatement | ForOfStatement): void { | ||
|
@@ -15402,6 +15536,7 @@ namespace ts { | |
} | ||
|
||
checkBlock(catchClause.block); | ||
checkUnusedIdentifiers(catchClause); | ||
} | ||
|
||
if (node.finallyBlock) { | ||
|
@@ -15563,6 +15698,8 @@ namespace ts { | |
} | ||
checkClassLikeDeclaration(node); | ||
forEach(node.members, checkSourceElement); | ||
checkUnusedClassLocals(node); | ||
checkUnusedTypeParameters(node); | ||
} | ||
|
||
function checkClassLikeDeclaration(node: ClassLikeDeclaration) { | ||
|
@@ -15845,6 +15982,7 @@ namespace ts { | |
checkExportsOnMergedDeclarations(node); | ||
const symbol = getSymbolOfNode(node); | ||
checkTypeParameterListsIdentical(node, symbol); | ||
updateReferencesForInterfaceHeritiageClauseTargets(node); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed that checkUnusedTypeParameters(node) has to be added here. But, I am adding it a little down after "forEach(node.members, checkSourceElement);" So that the body will be completely processed before checking the hasReference of the type parameter |
||
|
||
// Only check this symbol once | ||
const firstInterfaceDecl = <InterfaceDeclaration>getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); | ||
|
@@ -16268,6 +16406,7 @@ namespace ts { | |
|
||
if (node.body) { | ||
checkSourceElement(node.body); | ||
checkUnusedModuleLocals(node); | ||
} | ||
} | ||
|
||
|
@@ -16448,6 +16587,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 { | ||
|
@@ -16790,6 +16932,9 @@ namespace ts { | |
|
||
deferredNodes = []; | ||
forEach(node.statements, checkSourceElement); | ||
if (isExternalModule(node)) { | ||
checkUnusedImports(node); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
export {}; // makes the file a module
var x; // should be reported as unused
export var y; // should not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the code and test code |
||
} | ||
checkDeferredNodes(); | ||
deferredNodes = undefined; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2792,6 +2792,18 @@ | |
"category": "Message", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be an "Error" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
"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", | ||
|
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() { | ||
} | ||
} | ||
} |
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 = {})); |
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. | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not all signatures?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have already tried this. But checkSignatureDeclaration is also called from CheckConstructorDeclartion and checkFunctionOrMethodDeclaration (before their body is processed). So, we should put them in the condition