-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathDeadStoreOfLocal.ql
More file actions
87 lines (83 loc) · 3.39 KB
/
DeadStoreOfLocal.ql
File metadata and controls
87 lines (83 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* @name Useless assignment to local variable
* @description An assignment to a local variable that is not used later on, or whose value is always
* overwritten, has no effect.
* @kind problem
* @problem.severity warning
* @id js/useless-assignment-to-local
* @tags maintainability
* external/cwe/cwe-563
* @precision very-high
*/
import javascript
import DeadStore
/**
* Holds if `vd` is a definition of variable `v` that is dead, that is,
* the value it assigns to `v` is not read.
*
* Captured variables may be read by closures, so we restrict this to
* purely local variables.
*/
predicate deadStoreOfLocal(VarDef vd, PurelyLocalVariable v) {
v = vd.getAVariable() and
(exists(vd.getSource()) or exists(vd.getDestructuringSource())) and
// the definition is not in dead code
exists(ReachableBasicBlock rbb | vd = rbb.getANode()) and
// but it has no associated SSA definition, that is, it is dead
not exists(SsaExplicitDefinition ssa | ssa.defines(vd, v))
}
/**
* Holds if there exists another definition of the variable `v` that dominates `dead`.
*/
predicate hasDominatingDef(VarDef dead, PurelyLocalVariable v) {
exists(VarDef otherDef | not otherDef = dead and otherDef.getAVariable() = v |
dead.getBasicBlock().getASuccessor+() = otherDef.getBasicBlock()
or
exists(ReachableBasicBlock bb, int i, int j |
bb = otherDef.getBasicBlock() and bb = dead.getBasicBlock()
|
bb.defAt(i, v, dead) and
bb.defAt(j, v, otherDef) and
j > i
)
)
}
from VarDef dead, PurelyLocalVariable v, string msg
where
deadStoreOfLocal(dead, v) and
// the variable should be accessed somewhere; otherwise it will be flagged by UnusedVariable
exists(v.getAnAccess()) and
// don't flag ambient variable definitions
not dead.(ASTNode).isAmbient() and
// don't flag variables with ambient uses
not exists(LexicalAccess access |
access.getALexicalName() = v.getADeclaration().getALexicalName() and
access.isAmbient()
) and
// don't flag function expressions
not exists(FunctionExpr fe | dead = fe.getId()) and
// don't flag function declarations nested inside blocks or other compound statements;
// their meaning is only partially specified by the standard
not exists(FunctionDeclStmt fd, StmtContainer outer |
dead = fd.getId() and outer = fd.getEnclosingContainer()
|
not fd = outer.getBody().(BlockStmt).getAStmt()
) and
// don't flag overwrites with `null` or `undefined`
not SyntacticConstants::isNullOrUndefined(dead.getSource()) and
// don't flag default inits that are later overwritten
not (isDefaultInit(dead.getSource().(Expr).getUnderlyingValue()) and dead.isOverwritten(v)) and
// don't flag assignments in externs
not dead.(ASTNode).inExternsFile() and
// don't flag exported variables
not any(ES2015Module m).exportsAs(v, _) and
// don't flag 'exports' assignments in closure modules
not any(Closure::ClosureModule mod).getExportsVariable() = v and
(
// To avoid confusion about the meaning of "definition" and "declaration" we avoid
// the term "definition" when the alert location is a variable declaration.
if dead instanceof VariableDeclarator and hasDominatingDef(dead, v)
then msg = "The initial value of " + v.getName() + " is unused, since it is always overwritten."
else msg = "This definition of " + v.getName() + " is useless, since its value is never read."
)
select dead, msg