Skip to content

Commit 76c0b16

Browse files
authored
Merge pull request #19164 from yoff/ruby/refine-deadstore
ruby: remove some FPs from `rb/useless-assignment-to-local`
2 parents ca5cc8e + 6a8484f commit 76c0b16

File tree

9 files changed

+127
-2
lines changed

9 files changed

+127
-2
lines changed
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: majorAnalysis
3+
---
4+
* The query `rb/useless-assignment-to-local` now comes with query help and has been tweaked to produce fewer false positives.

Diff for: ruby/ql/src/queries/variables/DeadStoreOfLocal.qhelp

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
A value is assigned to a local variable, but either that variable is never read
8+
later on, or its value is always overwritten before being read. This means
9+
that the original assignment has no effect, and could indicate a logic error or
10+
incomplete code.
11+
</p>
12+
13+
</overview>
14+
<recommendation>
15+
16+
<p>
17+
Ensure that you check the control and data flow in the method carefully.
18+
If a value is really not needed, consider omitting the assignment. Be careful,
19+
though: if the right-hand side has a side-effect (like performing a method call),
20+
it is important to keep this to preserve the overall behavior.
21+
</p>
22+
23+
</recommendation>
24+
<example>
25+
26+
<p>
27+
In the following example, the return value of the call to <code>send</code> on line 2
28+
is assigned to the local variable <code>result</code>, but then never used.
29+
</p>
30+
31+
<sample src="examples/DeadStoreOfLocal.rb" />
32+
33+
<p>
34+
Assuming that <code>send</code> returns a status code indicating whether the operation
35+
succeeded or not, the value of <code>result</code> should be checked, perhaps like this:
36+
</p>
37+
38+
<sample src="examples/DeadStoreOfLocalGood.rb" />
39+
40+
</example>
41+
<references>
42+
43+
44+
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Dead_store">Dead store</a>.</li>
45+
46+
47+
48+
</references>
49+
</qhelp>

Diff for: ruby/ql/src/queries/variables/DeadStoreOfLocal.ql

+11-2
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,25 @@
77
* @id rb/useless-assignment-to-local
88
* @tags maintainability
99
* external/cwe/cwe-563
10-
* @precision low
10+
* @precision medium
1111
*/
1212

1313
import codeql.ruby.AST
1414
import codeql.ruby.dataflow.SSA
15+
import codeql.ruby.ApiGraphs
1516

1617
class RelevantLocalVariableWriteAccess extends LocalVariableWriteAccess {
1718
RelevantLocalVariableWriteAccess() {
1819
not this.getVariable().getName().charAt(0) = "_" and
19-
not this = any(Parameter p).getAVariable().getDefiningAccess()
20+
not this = any(Parameter p).getAVariable().getDefiningAccess() and
21+
not API::getTopLevelMember("ERB").getInstance().getAMethodCall("result").asExpr().getScope() =
22+
this.getCfgScope() and
23+
not exists(RetryStmt r | r.getCfgScope() = this.getCfgScope()) and
24+
not exists(MethodCall c |
25+
c.getReceiver() instanceof SelfVariableAccess and
26+
c.getMethodName() = "binding" and
27+
c.getCfgScope() = this.getCfgScope()
28+
)
2029
}
2130
}
2231

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def f(x)
2+
result = send(x)
3+
waitForResponse
4+
return getResponse
5+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
def f(x)
2+
result = send(x)
3+
# check for error
4+
if (result == -1)
5+
raise "Unable to send, check network."
6+
end
7+
waitForResponse
8+
return getResponse
9+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| DeadStoreOfLocal.rb:2:5:2:5 | y | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocal.rb:2:5:2:5 | y | y |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: queries/variables/DeadStoreOfLocal.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
def test_basic(x)
2+
y = x #$ Alert
3+
y = x + 2
4+
return y
5+
end
6+
7+
def test_retry
8+
x = 0
9+
begin
10+
if x == 0
11+
raise "error"
12+
end
13+
rescue
14+
x = 2 # OK - the retry will allow a later read
15+
retry
16+
end
17+
return 42
18+
end
19+
20+
def test_binding
21+
x = 4 # OK - the binding collects the value of x
22+
return binding
23+
end
24+
25+
class Sup
26+
def m(x)
27+
print(x + 1)
28+
end
29+
end
30+
31+
class Sub < Sup
32+
def m(y)
33+
y = 3 # OK - the call to `super` sees the value of `y``
34+
super
35+
end
36+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
require 'erb'
3+
4+
template = ERB.new <<EOF
5+
<%#-*- coding: Big5 -*-%>
6+
\_\_ENCODING\_\_ is <%= \_\_ENCODING\_\_ %>.
7+
x is <%= x %>.
8+
EOF
9+
x = 5 # OK - the template can see the value of x
10+
puts template.result

0 commit comments

Comments
 (0)