mirror of
https://github.com/github/codeql.git
synced 2026-03-28 10:18:17 +01:00
The query also flags unused imports, functions and classes (which, of course, are just unused variables at the end of the day). This is now made more explicit in the description.
130 lines
4.4 KiB
Plaintext
130 lines
4.4 KiB
Plaintext
/**
|
|
* @name Unused variable, import, function or class
|
|
* @description Unused variables, imports, functions or classes may be a symptom of a bug
|
|
* and should be examined carefully.
|
|
* @kind problem
|
|
* @problem.severity recommendation
|
|
* @id js/unused-local-variable
|
|
* @tags maintainability
|
|
* @precision very-high
|
|
*/
|
|
|
|
import javascript
|
|
|
|
/**
|
|
* A local variable that is neither used nor exported, and is not a parameter
|
|
* or a function name.
|
|
*/
|
|
class UnusedLocal extends LocalVariable {
|
|
UnusedLocal() {
|
|
not exists(getAnAccess()) and
|
|
not exists(Parameter p | this = p.getAVariable()) and
|
|
not exists(FunctionExpr fe | this = fe.getVariable()) and
|
|
not exists(ClassExpr ce | this = ce.getVariable()) and
|
|
not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and
|
|
not exists(LocalVarTypeAccess type | type.getVariable() = this)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holds if `v` is mentioned in a JSDoc comment in the same file, and that file
|
|
* contains externs declarations.
|
|
*/
|
|
predicate mentionedInJSDocComment(UnusedLocal v) {
|
|
exists (Externs ext, JSDoc jsdoc |
|
|
ext = v.getADeclaration().getTopLevel() and jsdoc.getComment().getTopLevel() = ext |
|
|
jsdoc.getComment().getText().regexpMatch("(?s).*\\b" + v.getName() + "\\b.*")
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `v` is declared in an object pattern that also contains a rest pattern.
|
|
*
|
|
* This is often used to filter out properties; for example, `var { x: v, ...props } = o`
|
|
* copies all properties of `o` into `props`, except for `x` which is copied into `v`.
|
|
*/
|
|
predicate isPropertyFilter(UnusedLocal v) {
|
|
exists (ObjectPattern op | exists(op.getRest()) |
|
|
op.getAPropertyPattern().getValuePattern() = v.getADeclaration()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `v` is an import of React, and there is a JSX element that implicitly
|
|
* references it.
|
|
*/
|
|
predicate isReactImportForJSX(UnusedLocal v) {
|
|
exists (ImportSpecifier is |
|
|
is.getLocal() = v.getADeclaration() and
|
|
exists (JSXNode jsx | jsx.getTopLevel() = is.getTopLevel()) |
|
|
v.getName() = "React" or
|
|
// also accept legacy `@jsx` pragmas
|
|
exists (JSXPragma p | p.getTopLevel() = is.getTopLevel() | p.getDOMName() = v.getName())
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `decl` is both a variable declaration and a type declaration,
|
|
* and the declared type has a use.
|
|
*/
|
|
predicate isUsedAsType(VarDecl decl) {
|
|
exists (decl.(TypeDecl).getLocalTypeName().getAnAccess())
|
|
}
|
|
|
|
/**
|
|
* Holds if `decl` declares a local alias for a namespace that is used from inside a type.
|
|
*/
|
|
predicate isUsedAsNamespace(VarDecl decl) {
|
|
exists (decl.(LocalNamespaceDecl).getLocalNamespaceName().getAnAccess())
|
|
}
|
|
|
|
/**
|
|
* Holds if the given identifier belongs to a decorated class or enum.
|
|
*/
|
|
predicate isDecorated(VarDecl decl) {
|
|
exists (ClassDefinition cd | cd.getIdentifier() = decl | exists(cd.getDecorator(_))) or
|
|
exists (EnumDeclaration cd | cd.getIdentifier() = decl | exists(cd.getDecorator(_)))
|
|
}
|
|
|
|
/**
|
|
* Holds if this is the name of an enum member.
|
|
*/
|
|
predicate isEnumMember(VarDecl decl) {
|
|
decl = any(EnumMember member).getIdentifier()
|
|
}
|
|
|
|
/**
|
|
* Gets a description of the declaration `vd`, which is either of the form "function f" if
|
|
* it is a function name, or "variable v" if it is not.
|
|
*/
|
|
string describe(VarDecl vd) {
|
|
if vd = any(Function f).getId() then
|
|
result = "function " + vd.getName()
|
|
else if vd = any(ClassDefinition c).getIdentifier() then
|
|
result = "class " + vd.getName()
|
|
else if (vd = any(ImportSpecifier im).getLocal() or vd = any(ImportEqualsDeclaration im).getId()) then
|
|
result = "import " + vd.getName()
|
|
else
|
|
result = "variable " + vd.getName()
|
|
}
|
|
|
|
from VarDecl vd, UnusedLocal v
|
|
where v = vd.getVariable() and
|
|
// exclude variables mentioned in JSDoc comments in externs
|
|
not mentionedInJSDocComment(v) and
|
|
// exclude variables used to filter out unwanted properties
|
|
not isPropertyFilter(v) and
|
|
// exclude imports of React that are implicitly referenced by JSX
|
|
not isReactImportForJSX(v) and
|
|
// exclude names that are used as types
|
|
not isUsedAsType(vd) and
|
|
// exclude names that are used as namespaces from inside a type
|
|
not isUsedAsNamespace(vd) and
|
|
// exclude decorated functions and classes
|
|
not isDecorated(vd) and
|
|
// exclude names of enum members; they also define property names
|
|
not isEnumMember(vd) and
|
|
// ignore ambient declarations - too noisy
|
|
not vd.isAmbient()
|
|
select vd, "Unused " + describe(vd) + "."
|