Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 91dccc3

Browse files
author
Esben Sparre Andreasen
committed
JS: add query js/unused-property
1 parent 0cf2eae commit 91dccc3

12 files changed

Lines changed: 301 additions & 51 deletions

File tree

javascript/config/suites/javascript/maintainability-more

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
+ semmlecode-javascript-queries/Declarations/DeadStoreOfProperty.ql: /Maintainability/Declarations
66
+ semmlecode-javascript-queries/Declarations/DuplicateVarDecl.ql: /Maintainability/Declarations
77
+ semmlecode-javascript-queries/Declarations/UnusedParameter.ql: /Maintainability/Declarations
8+
+ semmlecode-javascript-queries/Declarations/UnusedProperty.ql: /Maintainability/Declarations
89
+ semmlecode-javascript-queries/Declarations/UnusedVariable.ql: /Maintainability/Declarations
910
+ semmlecode-javascript-queries/Expressions/UnneededDefensiveProgramming.ql: /Maintainability/Expressions
1011
+ semmlecode-javascript-queries/LanguageFeatures/ArgumentsCallerCallee.ql: /Maintainability/Language Features
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
Unused object properties make code harder to maintain and use. Clients that are unaware that a
8+
property is unused may perform nontrivial computations to compute a value that is ultimately
9+
unused.
10+
</p>
11+
12+
</overview>
13+
<recommendation>
14+
<p>Remove the unused property.</p>
15+
16+
</recommendation>
17+
<example>
18+
19+
<p>
20+
In this code, the function <code>f</code> initializes a property <code>prop_a</code> with a
21+
call to the function <code>expensiveComputation</code>, but later on this property is never read.
22+
Removing <code>prop</code> would improve code quality and performance.
23+
</p>
24+
25+
<sample src="examples/UnusedProperty.js" />
26+
27+
</example>
28+
<references>
29+
30+
<li>Coding Horror: <a href="http://blog.codinghorror.com/code-smells/">Code Smells</a>.</li>
31+
32+
33+
</references>
34+
</qhelp>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @name Unused property
3+
* @description Unused properties may be a symptom of a bug and should be examined carefully.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id js/unused-property
7+
* @tags maintainability
8+
* @precision high
9+
*/
10+
11+
import javascript
12+
import semmle.javascript.dataflow.CapturedNodes
13+
import UnusedVariable
14+
import UnusedParameter
15+
import Expressions.ExprHasNoEffect
16+
17+
predicate hasUnknownPropertyRead(CapturedSource obj) {
18+
// dynamic reads
19+
exists(DataFlow::PropRead r | obj.getAPropertyRead() = r | not exists(r.getPropertyName()))
20+
or
21+
// reflective reads
22+
obj.flowsToExpr(any(EnhancedForLoop l).getIterationDomain())
23+
or
24+
obj.flowsToExpr(any(InExpr l).getRightOperand())
25+
or
26+
obj.flowsToExpr(any(SpreadElement e).getOperand())
27+
or
28+
exists(obj.getAPropertyRead("hasOwnProperty"))
29+
or
30+
exists(obj.getAPropertyRead("propertyIsEnumerable"))
31+
}
32+
33+
predicate flowsToTypeRestrictedExpression(CapturedSource n) {
34+
exists (Expr restricted, TypeExpr type |
35+
n.flowsToExpr(restricted) and
36+
not type.isAny() |
37+
exists (TypeAssertion assertion |
38+
type = assertion.getTypeAnnotation() and
39+
restricted = assertion.getExpression()
40+
)
41+
or
42+
exists (BindingPattern v |
43+
type = v.getTypeAnnotation() and
44+
restricted = v.getAVariable().getAnAssignedExpr()
45+
)
46+
// no need to reason about writes to typed fields, captured nodes do not reach them
47+
)
48+
}
49+
50+
from DataFlow::PropWrite w, CapturedSource n, string name
51+
where
52+
w = n.getAPropertyWrite(name) and
53+
not exists(n.getAPropertyRead(name)) and
54+
not w.getBase().analyze().getAValue() != n.analyze().getAValue() and
55+
not hasUnknownPropertyRead(n) and
56+
// avoid reporting if the definition is unreachable
57+
w.getAstNode().getFirstControlFlowNode().getBasicBlock() instanceof ReachableBasicBlock and
58+
// avoid implicitly read properties
59+
not (
60+
name = "toString" or
61+
name = "valueOf" or
62+
name.matches("@@%") // @@iterator, for example
63+
) and
64+
// avoid flagging properties that a type system requires
65+
not flowsToTypeRestrictedExpression(n) and
66+
// flagged by js/unused-local-variable
67+
not exists(UnusedLocal l | l.getAnAssignedExpr().getUnderlyingValue().flow() = n) and
68+
// flagged by js/unused-parameter
69+
not exists(Parameter p | isAnAccidentallyUnusedParameter(p) |
70+
p.getDefault().getUnderlyingValue().flow() = n
71+
) and
72+
// flagged by js/useless-expression
73+
not inVoidContext(n.asExpr())
74+
select w, "Unused property " + name + "."

javascript/ql/src/Declarations/UnusedVariable.ql

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,7 @@
1010
*/
1111

1212
import javascript
13-
14-
/**
15-
* A local variable that is neither used nor exported, and is not a parameter
16-
* or a function name.
17-
*/
18-
class UnusedLocal extends LocalVariable {
19-
UnusedLocal() {
20-
not exists(getAnAccess()) and
21-
not exists(Parameter p | this = p.getAVariable()) and
22-
not exists(FunctionExpr fe | this = fe.getVariable()) and
23-
not exists(ClassExpr ce | this = ce.getVariable()) and
24-
not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and
25-
not exists(LocalVarTypeAccess type | type.getVariable() = this) and
26-
// common convention: variables with leading underscore are intentionally unused
27-
getName().charAt(0) != "_"
28-
}
29-
}
13+
import UnusedVariable
3014

3115
/**
3216
* Holds if `v` is mentioned in a JSDoc comment in the same file, and that file
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* This library contains parts of the 'js/unused-local-variable' query implementation.
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* A local variable that is neither used nor exported, and is not a parameter
9+
* or a function name.
10+
*/
11+
class UnusedLocal extends LocalVariable {
12+
UnusedLocal() {
13+
not exists(getAnAccess()) and
14+
not exists(Parameter p | this = p.getAVariable()) and
15+
not exists(FunctionExpr fe | this = fe.getVariable()) and
16+
not exists(ClassExpr ce | this = ce.getVariable()) and
17+
not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and
18+
not exists(LocalVarTypeAccess type | type.getVariable() = this) and
19+
// common convention: variables with leading underscore are intentionally unused
20+
getName().charAt(0) != "_"
21+
}
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
function f() {
2+
var o = {
3+
prop_a: expensiveComputation(),
4+
prop_b: anotherComputation()
5+
};
6+
7+
return o.prop_b;
8+
}

javascript/ql/src/Expressions/ExprHasNoEffect.ql

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,7 @@ import javascript
1616
import DOMProperties
1717
import semmle.javascript.frameworks.xUnit
1818
import semmle.javascript.RestrictedLocations
19-
20-
/**
21-
* Holds if `e` appears in a syntactic context where its value is discarded.
22-
*/
23-
predicate inVoidContext(Expr e) {
24-
exists(ExprStmt parent |
25-
// e is a toplevel expression in an expression statement
26-
parent = e.getParent() and
27-
// but it isn't an HTML attribute or a configuration object
28-
not exists(TopLevel tl | tl = parent.getParent() |
29-
tl instanceof CodeInAttribute
30-
or
31-
// if the toplevel in its entirety is of the form `({ ... })`,
32-
// it is probably a configuration object (e.g., a require.js build configuration)
33-
tl.getNumChildStmt() = 1 and e.stripParens() instanceof ObjectExpr
34-
)
35-
)
36-
or
37-
exists(SeqExpr seq, int i, int n |
38-
e = seq.getOperand(i) and
39-
n = seq.getNumOperands()
40-
|
41-
i < n - 1 or inVoidContext(seq)
42-
)
43-
or
44-
exists(ForStmt stmt | e = stmt.getUpdate())
45-
or
46-
exists(ForStmt stmt | e = stmt.getInit() |
47-
// Allow the pattern `for(i; i < 10; i++)`
48-
not e instanceof VarAccess
49-
)
50-
or
51-
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
52-
}
19+
import ExprHasNoEffect
5320

5421
/**
5522
* Holds if `e` is of the form `x;` or `e.p;` and has a JSDoc comment containing a tag.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* This library contains parts of the 'js/useless-expression' query implementation.
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* Holds if `e` appears in a syntactic context where its value is discarded.
9+
*/
10+
predicate inVoidContext(Expr e) {
11+
exists(ExprStmt parent |
12+
// e is a toplevel expression in an expression statement
13+
parent = e.getParent() and
14+
// but it isn't an HTML attribute or a configuration object
15+
not exists(TopLevel tl | tl = parent.getParent() |
16+
tl instanceof CodeInAttribute
17+
or
18+
// if the toplevel in its entirety is of the form `({ ... })`,
19+
// it is probably a configuration object (e.g., a require.js build configuration)
20+
tl.getNumChildStmt() = 1 and e.stripParens() instanceof ObjectExpr
21+
)
22+
)
23+
or
24+
exists(SeqExpr seq, int i, int n |
25+
e = seq.getOperand(i) and
26+
n = seq.getNumOperands()
27+
|
28+
i < n - 1 or inVoidContext(seq)
29+
)
30+
or
31+
exists(ForStmt stmt | e = stmt.getUpdate())
32+
or
33+
exists(ForStmt stmt | e = stmt.getInit() |
34+
// Allow the pattern `for(i; i < 10; i++)`
35+
not e instanceof VarAccess
36+
)
37+
or
38+
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
39+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| tst.js:4:9:4:19 | unused1: 42 | Unused property unused1. |
2+
| tst.js:19:5:19:15 | unused9: 42 | Unused property unused9. |
3+
| tst.js:26:13:26:24 | unused11: 42 | Unused property unused11. |
4+
| tst.js:31:13:31:35 | used12_ ... lly: 42 | Unused property used12_butNotReally. |
5+
| tst.js:32:13:32:24 | unused12: 42 | Unused property unused12. |
6+
| tst.js:52:3:52:14 | unused14: 42 | Unused property unused14. |
7+
| tst.js:54:2:54:20 | captured14.unused14 | Unused property unused14. |
8+
| tst.js:55:2:55:20 | captured14.unused14 | Unused property unused14. |
9+
| tst.ts:24:21:24:25 | p: 42 | Unused property p. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Declarations/UnusedProperty.ql

0 commit comments

Comments
 (0)