Skip to content

Commit 69e4a8f

Browse files
committed
Ruby: Fix ExprReturnNode to handle Rescue, Ensure, and Else.
1 parent 75cbcab commit 69e4a8f

3 files changed

Lines changed: 43 additions & 15 deletions

File tree

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,16 +1643,24 @@ private module ReturnNodes {
16431643
}
16441644
}
16451645

1646-
pragma[noinline]
1647-
private AstNode implicitReturn(Callable c, ExprNode n) {
1648-
exists(CfgNodes::ExprCfgNode en |
1649-
en = n.getExprNode() and
1650-
en.getASuccessor().(CfgNodes::AnnotatedExitNode).isNormal() and
1651-
n.(NodeImpl).getCfgScope() = c and
1652-
result = en.getExpr()
1653-
)
1646+
private AstNode desugar(AstNode n) {
1647+
result = n.getDesugared()
1648+
or
1649+
not exists(n.getDesugared()) and
1650+
result = n
1651+
}
1652+
1653+
private Expr getLast(StmtSequence s) {
1654+
result = getLast(s.(BodyStmt).getElse())
16541655
or
1655-
result = implicitReturn(c, n).getParent()
1656+
result = getLast(s.(BodyStmt).getARescue().getBody())
1657+
or
1658+
not exists(s.(BodyStmt).getElse()) and
1659+
exists(Stmt last | last = s.getLastStmt() |
1660+
result = getLast(last)
1661+
or
1662+
result = last and not last instanceof StmtSequence
1663+
)
16561664
}
16571665

16581666
/**
@@ -1661,7 +1669,7 @@ private module ReturnNodes {
16611669
* last thing that is evaluated in the body of the callable.
16621670
*/
16631671
class ExprReturnNode extends SourceReturnNode, ExprNode {
1664-
ExprReturnNode() { exists(Callable c | implicitReturn(c, this) = c.getBody().getAStmt()) }
1672+
ExprReturnNode() { this.getExprNode().getExpr() = desugar(getLast(any(Callable c).getBody())) }
16651673

16661674
override ReturnKind getKindSource() {
16671675
exists(CfgScope scope | scope = this.(NodeImpl).getCfgScope() |
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
models
22
edges
33
| implicit_return.rb:12:3:12:11 | call to source | implicit_return.rb:15:6:15:11 | call to m_body | provenance | |
4+
| implicit_return.rb:19:3:19:11 | call to source | implicit_return.rb:24:6:24:18 | call to m_body_ensure | provenance | |
5+
| implicit_return.rb:30:3:30:11 | call to source | implicit_return.rb:33:6:33:13 | call to m_rescue | provenance | |
6+
| implicit_return.rb:39:3:39:11 | call to source | implicit_return.rb:44:6:44:20 | call to m_rescue_ensure | provenance | |
7+
| implicit_return.rb:52:3:52:11 | call to source | implicit_return.rb:55:6:55:11 | call to m_else | provenance | |
8+
| implicit_return.rb:63:3:63:11 | call to source | implicit_return.rb:68:6:68:18 | call to m_else_ensure | provenance | |
49
nodes
510
| implicit_return.rb:12:3:12:11 | call to source | semmle.label | call to source |
611
| implicit_return.rb:15:6:15:11 | call to m_body | semmle.label | call to m_body |
12+
| implicit_return.rb:19:3:19:11 | call to source | semmle.label | call to source |
13+
| implicit_return.rb:24:6:24:18 | call to m_body_ensure | semmle.label | call to m_body_ensure |
14+
| implicit_return.rb:30:3:30:11 | call to source | semmle.label | call to source |
15+
| implicit_return.rb:33:6:33:13 | call to m_rescue | semmle.label | call to m_rescue |
16+
| implicit_return.rb:39:3:39:11 | call to source | semmle.label | call to source |
17+
| implicit_return.rb:44:6:44:20 | call to m_rescue_ensure | semmle.label | call to m_rescue_ensure |
18+
| implicit_return.rb:52:3:52:11 | call to source | semmle.label | call to source |
19+
| implicit_return.rb:55:6:55:11 | call to m_else | semmle.label | call to m_else |
20+
| implicit_return.rb:63:3:63:11 | call to source | semmle.label | call to source |
21+
| implicit_return.rb:68:6:68:18 | call to m_else_ensure | semmle.label | call to m_else_ensure |
722
subpaths
823
testFailures
924
#select
1025
| implicit_return.rb:15:6:15:11 | call to m_body | implicit_return.rb:12:3:12:11 | call to source | implicit_return.rb:15:6:15:11 | call to m_body | $@ | implicit_return.rb:12:3:12:11 | call to source | call to source |
26+
| implicit_return.rb:24:6:24:18 | call to m_body_ensure | implicit_return.rb:19:3:19:11 | call to source | implicit_return.rb:24:6:24:18 | call to m_body_ensure | $@ | implicit_return.rb:19:3:19:11 | call to source | call to source |
27+
| implicit_return.rb:33:6:33:13 | call to m_rescue | implicit_return.rb:30:3:30:11 | call to source | implicit_return.rb:33:6:33:13 | call to m_rescue | $@ | implicit_return.rb:30:3:30:11 | call to source | call to source |
28+
| implicit_return.rb:44:6:44:20 | call to m_rescue_ensure | implicit_return.rb:39:3:39:11 | call to source | implicit_return.rb:44:6:44:20 | call to m_rescue_ensure | $@ | implicit_return.rb:39:3:39:11 | call to source | call to source |
29+
| implicit_return.rb:55:6:55:11 | call to m_else | implicit_return.rb:52:3:52:11 | call to source | implicit_return.rb:55:6:55:11 | call to m_else | $@ | implicit_return.rb:52:3:52:11 | call to source | call to source |
30+
| implicit_return.rb:68:6:68:18 | call to m_else_ensure | implicit_return.rb:63:3:63:11 | call to source | implicit_return.rb:68:6:68:18 | call to m_else_ensure | $@ | implicit_return.rb:63:3:63:11 | call to source | call to source |

ruby/ql/test/library-tests/dataflow/implicit-return/implicit_return.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def m_body_ensure
2121
source(20)
2222
end
2323

24-
sink(m_body_ensure) # $ MISSING: hasValueFlow=2
24+
sink(m_body_ensure) # $ hasValueFlow=2
2525

2626
# Implicit return from a `rescue` clause.
2727
def m_rescue
@@ -30,7 +30,7 @@ def m_rescue
3030
source(3)
3131
end
3232

33-
sink(m_rescue) # $ MISSING: hasValueFlow=3
33+
sink(m_rescue) # $ hasValueFlow=3
3434

3535
# Implicit return from a `rescue` clause when an `ensure` clause is present.
3636
def m_rescue_ensure
@@ -41,7 +41,7 @@ def m_rescue_ensure
4141
source(40)
4242
end
4343

44-
sink(m_rescue_ensure) # $ MISSING: hasValueFlow=4
44+
sink(m_rescue_ensure) # $ hasValueFlow=4
4545

4646
# Implicit return from an `else` clause.
4747
def m_else
@@ -52,7 +52,7 @@ def m_else
5252
source(5)
5353
end
5454

55-
sink(m_else) # $ MISSING: hasValueFlow=5
55+
sink(m_else) # $ hasValueFlow=5
5656

5757
# Implicit return from an `else` clause when an `ensure` clause is present.
5858
def m_else_ensure
@@ -65,4 +65,4 @@ def m_else_ensure
6565
nil
6666
end
6767

68-
sink(m_else_ensure) # $ MISSING: hasValueFlow=6
68+
sink(m_else_ensure) # $ hasValueFlow=6

0 commit comments

Comments
 (0)