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

Skip to content

Commit 33a297c

Browse files
author
Esben Sparre Andreasen
committed
JS: add query: js/useless-assignment-to-property
1 parent 6ee47c4 commit 33a297c

10 files changed

Lines changed: 367 additions & 0 deletions

File tree

javascript/config/suites/javascript/maintainability-more

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
+ semmlecode-javascript-queries/Comments/TodoComments.ql: /Maintainability/Comments
33
+ semmlecode-javascript-queries/Declarations/DeadStoreOfGlobal.ql: /Maintainability/Declarations
44
+ semmlecode-javascript-queries/Declarations/DeadStoreOfLocal.ql: /Maintainability/Declarations
5+
+ semmlecode-javascript-queries/Declarations/DeadStoreOfProperty.ql: /Maintainability/Declarations
56
+ semmlecode-javascript-queries/Declarations/DuplicateVarDecl.ql: /Maintainability/Declarations
67
+ semmlecode-javascript-queries/Declarations/UnusedParameter.ql: /Maintainability/Declarations
78
+ semmlecode-javascript-queries/Declarations/UnusedVariable.ql: /Maintainability/Declarations
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
5+
<qhelp>
6+
7+
<include src="DeadStore.qhelp" />
8+
9+
</qhelp>
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* @name Useless assignment to property
3+
* @description An assignment to a property whose value is always overwritten, has no effect.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @id js/useless-assignment-to-property
7+
* @tags maintainability
8+
* @precision high
9+
*/
10+
11+
import javascript
12+
import Expressions.DOMProperties
13+
import DeadStore
14+
15+
/**
16+
* Holds if `assign` definitely assigns property `name` of `base`.
17+
*/
18+
predicate unambigiousPropWrite(DataFlow::SourceNode base, string name, Assignment assign) {
19+
exists(DataFlow::PropWrite lhs |
20+
assign.getLhs().flow() = lhs and
21+
base.getAPropertyWrite(name) = lhs and
22+
not exists (DataFlow::SourceNode otherBase |
23+
not otherBase = base and
24+
lhs = otherBase.getAPropertyWrite(name)
25+
)
26+
)
27+
}
28+
29+
/**
30+
* Holds if `assign1` and `assign2` both assign property `name` of the same object, and `assign2` post-dominates `assign1`.
31+
*/
32+
predicate postDominatedPropWrite(string name, Assignment assign1, Assignment assign2) {
33+
exists (DataFlow::SourceNode base, ReachableBasicBlock block1, ReachableBasicBlock block2 |
34+
block1 = assign1.getBasicBlock() and
35+
block2 = assign2.getBasicBlock() and
36+
unambigiousPropWrite(base, name, assign1) and
37+
unambigiousPropWrite(base, name, assign2) and
38+
block2.postDominates(block1) and
39+
(block1 = block2 implies
40+
exists (int i1, int i2 |
41+
assign1 = block1.getNode(i1) and
42+
assign2 = block2.getNode(i2) and
43+
i1 < i2
44+
)
45+
)
46+
)
47+
}
48+
49+
/**
50+
* Holds if `e` may access a property named `name`.
51+
*/
52+
bindingset[name]
53+
predicate maybeAccessesProperty(Expr e, string name) {
54+
(e.(PropAccess).getPropertyName() = name and e instanceof RValue) or
55+
// conservatively reject all side-effects
56+
e.isImpure()
57+
}
58+
59+
/**
60+
* Holds if `assign1` and `assign2` both assign property `name`, but `assign1` is dead because of `assign2`.
61+
*/
62+
predicate isDeadAssignment(string name, Assignment assign1, Assignment assign2) {
63+
postDominatedPropWrite(name, assign1, assign2) and
64+
maybeHasPropAccessBetween(name, assign1, assign2) and
65+
not isDOMProperty(name)
66+
}
67+
68+
/**
69+
* Holds if `assign` assigns a property `name` that may be accessed somewhere else in the same block,
70+
* `after` indicates if the if the access happens before or after the node for `assign`.
71+
*/
72+
bindingset[name]
73+
predicate maybeAccessesAssignedPropInBlock(string name, Assignment assign, boolean after) {
74+
exists (ReachableBasicBlock block, int i, int j, Expr e |
75+
block = assign.getBasicBlock() and
76+
assign = block.getNode(i) and
77+
e = block.getNode(j) and
78+
maybeAccessesProperty(e, name) |
79+
after = true and i < j
80+
or
81+
after = false and j < i
82+
)
83+
}
84+
85+
/**
86+
* Holds if `assign1` and `assign2` both assign property `name`, and the assigned property may be accessed between the two assignments.
87+
*/
88+
bindingset[name]
89+
predicate maybeHasPropAccessBetween(string name, Assignment assign1, Assignment assign2) {
90+
exists (ReachableBasicBlock block1, ReachableBasicBlock block2 |
91+
assign1.getBasicBlock() = block1 and
92+
assign2.getBasicBlock() = block2 and
93+
if block1 = block2 then
94+
// same block: check for read between
95+
not exists (int i1, int iMid, Expr mid, int i2 |
96+
assign1 = block1.getNode(i1) and
97+
assign2 = block2.getNode(i2) and
98+
i1 < iMid and iMid < i2 and
99+
mid = block1.getNode(iMid) and
100+
maybeAccessesProperty(mid, name)
101+
)
102+
else
103+
// other block:
104+
not (
105+
// check for read after in start block
106+
maybeAccessesAssignedPropInBlock(name, assign1, true) or
107+
// check for read after in other block
108+
exists (ReachableBasicBlock mid |
109+
block1.getASuccessor+() = mid and
110+
mid.getASuccessor+() = block2 |
111+
maybeAccessesProperty(mid.getANode(), name)
112+
) or
113+
// check for read before in end block
114+
maybeAccessesAssignedPropInBlock(name, assign2, false)
115+
)
116+
)
117+
}
118+
119+
from string name, Assignment assign1, Assignment assign2
120+
where isDeadAssignment(name, assign1, assign2) and
121+
// whitelist
122+
not (
123+
// Google Closure Compiler pattern: `o.p = o['p'] = v`
124+
exists (PropAccess p1, PropAccess p2 |
125+
p1 = assign1.getLhs() and
126+
p2 = assign2.getLhs() |
127+
p1 instanceof DotExpr and p2 instanceof IndexExpr
128+
or
129+
p2 instanceof DotExpr and p1 instanceof IndexExpr
130+
)
131+
or
132+
// don't flag overwrites for default values
133+
isDefaultInit(assign1.getRhs().getUnderlyingValue())
134+
or
135+
// don't flag assignments in externs
136+
assign1.inExternsFile()
137+
)
138+
select assign1, "This write to property '" + name + "' is useless, since $@ always overrides it.", assign2, "another property write"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
| real-world-examples.js:5:4:5:11 | o.p = 42 | This write to property 'p' is useless, since $@ always overrides it. | real-world-examples.js:10:2:10:9 | o.p = 42 | another property write |
2+
| real-world-examples.js:15:9:15:18 | o.p1 += 42 | This write to property 'p1' is useless, since $@ always overrides it. | real-world-examples.js:15:2:15:18 | o.p1 = o.p1 += 42 | another property write |
3+
| real-world-examples.js:16:11:16:20 | o.p2 *= 42 | This write to property 'p2' is useless, since $@ always overrides it. | real-world-examples.js:16:2:16:21 | o.p2 -= (o.p2 *= 42) | another property write |
4+
| real-world-examples.js:29:5:29:12 | o.p = 42 | This write to property 'p' is useless, since $@ always overrides it. | real-world-examples.js:32:3:32:10 | o.p = 42 | another property write |
5+
| real-world-examples.js:38:15:38:24 | o.p = f3() | This write to property 'p' is useless, since $@ always overrides it. | real-world-examples.js:38:2:38:31 | o.p = f ... : f4() | another property write |
6+
| tst.js:3:5:3:16 | o.pure1 = 42 | This write to property 'pure1' is useless, since $@ always overrides it. | tst.js:4:5:4:16 | o.pure1 = 42 | another property write |
7+
| tst.js:6:5:6:16 | o.pure2 = 42 | This write to property 'pure2' is useless, since $@ always overrides it. | tst.js:7:5:7:16 | o.pure2 = 43 | another property write |
8+
| tst.js:13:5:13:16 | o.pure4 = 42 | This write to property 'pure4' is useless, since $@ always overrides it. | tst.js:15:5:15:16 | o.pure4 = 42 | another property write |
9+
| tst.js:20:5:20:17 | o.pure6 = f() | This write to property 'pure6' is useless, since $@ always overrides it. | tst.js:21:5:21:16 | o.pure6 = 42 | another property write |
10+
| tst.js:23:5:23:16 | o.pure7 = 42 | This write to property 'pure7' is useless, since $@ always overrides it. | tst.js:25:5:25:16 | o.pure7 = 42 | another property write |
11+
| tst.js:76:5:76:34 | o.pure1 ... te = 42 | This write to property 'pure16_simpleAliasWrite' is useless, since $@ always overrides it. | tst.js:77:5:77:36 | o16.pur ... te = 42 | another property write |
12+
| tst.js:95:5:95:17 | o.pure18 = 42 | This write to property 'pure18' is useless, since $@ always overrides it. | tst.js:96:5:96:17 | o.pure18 = 42 | another property write |
13+
| tst.js:96:5:96:17 | o.pure18 = 42 | This write to property 'pure18' is useless, since $@ always overrides it. | tst.js:97:5:97:17 | o.pure18 = 42 | another property write |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Declarations/DeadStoreOfProperty.ql
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(function(){
2+
var o = {};
3+
o.prop1 = o['prop1'] = x;
4+
5+
o['prop2'] = o.prop2 = x;
6+
7+
o.prop3 = x
8+
o['prop3'] = x;
9+
10+
o['prop4'] = x;
11+
o.prop4 = x
12+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Adapted from the Google Closure externs; original copyright header included below.
2+
/*
3+
* Copyright 2008 The Closure Compiler Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* @interface
20+
*/
21+
function EventTarget() {}
22+
23+
/**
24+
* @constructor
25+
* @implements {EventTarget}
26+
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247
27+
*/
28+
function Node() {}
29+
30+
/**
31+
* @constructor
32+
* @extends {Node}
33+
* @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-745549614
34+
*/
35+
function Element() {}
36+
37+
/**
38+
* @type {number}
39+
* @see http://www.w3.org/TR/cssom-view/#dom-element-clienttop
40+
*/
41+
Element.prototype.clientTop;
42+
43+
//semmle-extractor-options: --externs
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
(function(){
2+
var first = f();
3+
4+
function flush() {
5+
var flushed = first;
6+
7+
var next = g();
8+
first = next;
9+
next.prev = 42;
10+
11+
flushed.prev = 42;
12+
}
13+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
(function(){
2+
var o = f1();
3+
while (f2()) {
4+
if (f4()) {
5+
o.p = 42;
6+
break;
7+
}
8+
f5();
9+
}
10+
o.p = 42;
11+
});
12+
13+
(function(){
14+
var o = f1();
15+
o.p1 = o.p1 += 42;
16+
o.p2 -= (o.p2 *= 42);
17+
});
18+
19+
(function(){
20+
var o = f1();
21+
o.m(function () {
22+
if (f2()) {
23+
24+
} else {
25+
try {
26+
f3();
27+
} catch (e) {
28+
f4();
29+
o.p = 42;
30+
}
31+
}
32+
o.p = 42;
33+
});
34+
});
35+
36+
(function(){
37+
var o = f1();
38+
o.p = f2() ? o.p = f3() : f4();
39+
});
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
(function(){
2+
var o = {};
3+
o.pure1 = 42;
4+
o.pure1 = 42;
5+
6+
o.pure2 = 42;
7+
o.pure2 = 43;
8+
9+
o.impure3 = 42;
10+
f();
11+
o.impure3 = 42;
12+
13+
o.pure4 = 42;
14+
43;
15+
o.pure4 = 42;
16+
17+
o.impure5 = 42;
18+
o.impure5 = f();
19+
20+
o.pure6 = f();
21+
o.pure6 = 42;
22+
23+
o.pure7 = 42;
24+
if(x){}
25+
o.pure7 = 42;
26+
27+
o.pure8_cond = 42;
28+
if(x){
29+
o.pure8_cond = 42;
30+
}
31+
32+
o.impure9 = 42;
33+
f();
34+
if(x){
35+
}
36+
o.impure9 = 42;
37+
38+
o.impure10 = 42;
39+
if(x){
40+
f();
41+
}
42+
o.impure10 = 42;
43+
44+
o.impure11 = 42;
45+
if(x){
46+
47+
}
48+
f();
49+
o.impure11 = 42;
50+
51+
o.pure12_read = 42;
52+
o.pure12_read;
53+
o.pure12_read = 42;
54+
55+
var o2;
56+
o.pure13_otherRead = 42;
57+
o2.pure13_otherRead;
58+
o.pure13_otherRead = 42;
59+
60+
function id14(e) {
61+
return e;
62+
}
63+
var o14 = id14(o);
64+
o.pure14_aliasRead = 42;
65+
o14.pure14_aliasRead;
66+
o.pure14_aliasRead = 42;
67+
68+
function id15(e) {
69+
return e;
70+
}
71+
var o15 = id15(o);
72+
o.pure15_aliasWrite = 42;
73+
o15.pure15_aliasWrite = 42;
74+
75+
var o16 = x? o: null;
76+
o.pure16_simpleAliasWrite = 42;
77+
o16.pure16_simpleAliasWrite = 42;
78+
79+
var o17 = {
80+
duplicate17: 42,
81+
duplicate17: 42
82+
}
83+
84+
// DOM
85+
o.clientTop = 42;
86+
o.clientTop = 42;
87+
88+
o.defaulted1 = null;
89+
o.defaulted1 = 42;
90+
91+
o.defaulted2 = -1;
92+
o.defaulted2 = 42;
93+
94+
var o = {};
95+
o.pure18 = 42;
96+
o.pure18 = 42;
97+
o.pure18 = 42;
98+
});

0 commit comments

Comments
 (0)