Skip to content

Commit 4786478

Browse files
committed
Rust: Add abstraction over all kinds of calls
1 parent ce1c9fb commit 4786478

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
private import rust
7+
private import codeql.rust.elements.Call
78
private import ControlFlowGraph
89
private import internal.ControlFlowGraphImpl as CfgImpl
910
private import internal.CfgNodes
@@ -162,6 +163,30 @@ final class CallExprBaseCfgNode extends Nodes::CallExprBaseCfgNode {
162163
*/
163164
final class MethodCallExprCfgNode extends CallExprBaseCfgNode, Nodes::MethodCallExprCfgNode { }
164165

166+
/**
167+
* A CFG node that calls a function.
168+
*
169+
* This class abstract over the different ways in which a function can be called in Rust.
170+
*/
171+
final class CallCfgNode extends ExprCfgNode {
172+
private Call node;
173+
174+
CallCfgNode() { node = this.getAstNode() }
175+
176+
/** Gets the underlying `Call`. */
177+
Call getCall() { result = node }
178+
179+
/** Gets the receiver of this call if it is a method call. */
180+
ExprCfgNode getReceiver() {
181+
any(ChildMapping mapping).hasCfgChild(node, node.getReceiver(), this, result)
182+
}
183+
184+
/** Gets the `i`th argument of this call, if any. */
185+
ExprCfgNode getArgument(int i) {
186+
any(ChildMapping mapping).hasCfgChild(node, node.getArgument(i), this, result)
187+
}
188+
}
189+
165190
/**
166191
* A function call expression. For example:
167192
* ```rust
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Call.
3+
*/
4+
5+
private import internal.CallImpl
6+
7+
final class Call = Impl::Call;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
private import rust
2+
private import codeql.rust.internal.PathResolution
3+
private import codeql.rust.internal.TypeInference as TypeInference
4+
private import codeql.rust.elements.internal.ExprImpl::Impl as ExprImpl
5+
private import codeql.rust.elements.Operation
6+
7+
module Impl {
8+
/**
9+
* An expression that calls a function.
10+
*
11+
* This class abstract over the different ways in which a function can be called in Rust.
12+
*/
13+
abstract class Call extends ExprImpl::Expr {
14+
Call() { this.fromSource() }
15+
16+
/** Gets the number of arguments _excluding_ any `self` argument. */
17+
abstract int getNumberOfArguments();
18+
19+
/** Gets the receiver of this call if it is a method call. */
20+
abstract Expr getReceiver();
21+
22+
/** Holds if the call has a receiver that might be implicitly borrowed. */
23+
abstract predicate receiverImplicitlyBorrowed();
24+
25+
/** Gets the trait targeted by this call, if any. */
26+
abstract Trait getTrait();
27+
28+
/** Gets the name of the method called if this call is a method call. */
29+
abstract string getMethodName();
30+
31+
/** Gets the `i`th argument of this call, if any. */
32+
abstract Expr getArgument(int i);
33+
34+
/** Gets the static target of this call, if any. */
35+
Function getStaticTarget() {
36+
result = TypeInference::resolveMethodCallTarget(this)
37+
or
38+
not exists(TypeInference::resolveMethodCallTarget(this)) and
39+
result = this.(CallExpr).getStaticTarget()
40+
}
41+
}
42+
43+
/** Holds if the call expression dispatches to a trait method. */
44+
private predicate callIsMethodCall(CallExpr call, Path qualifier, string methodName) {
45+
exists(Path path, Function f |
46+
path = call.getFunction().(PathExpr).getPath() and
47+
f = resolvePath(path) and
48+
f.getParamList().hasSelfParam() and
49+
qualifier = path.getQualifier() and
50+
path.getSegment().getIdentifier().getText() = methodName
51+
)
52+
}
53+
54+
private class CallExprCall extends Call instanceof CallExpr {
55+
CallExprCall() { not callIsMethodCall(this, _, _) }
56+
57+
override string getMethodName() { none() }
58+
59+
override Expr getReceiver() { none() }
60+
61+
override Trait getTrait() { none() }
62+
63+
override predicate receiverImplicitlyBorrowed() { none() }
64+
65+
override int getNumberOfArguments() { result = super.getArgList().getNumberOfArgs() }
66+
67+
override Expr getArgument(int i) { result = super.getArgList().getArg(i) }
68+
}
69+
70+
private class CallExprMethodCall extends Call instanceof CallExpr {
71+
Path qualifier;
72+
string methodName;
73+
74+
CallExprMethodCall() { callIsMethodCall(this, qualifier, methodName) }
75+
76+
override string getMethodName() { result = methodName }
77+
78+
override Expr getReceiver() { result = super.getArgList().getArg(0) }
79+
80+
override Trait getTrait() {
81+
result = resolvePath(qualifier) and
82+
// When the qualifier is `Self` and resolves to a trait, it's inside a
83+
// trait method's default implementation. This is not a dispatch whose
84+
// target is inferred from the type of the receiver, but should always
85+
// resolve to the function in the trait block as path resolution does.
86+
qualifier.toString() != "Self"
87+
}
88+
89+
override predicate receiverImplicitlyBorrowed() { none() }
90+
91+
override int getNumberOfArguments() { result = super.getArgList().getNumberOfArgs() - 1 }
92+
93+
override Expr getArgument(int i) { result = super.getArgList().getArg(i + 1) }
94+
}
95+
96+
private class MethodCallExprCall extends Call instanceof MethodCallExpr {
97+
override string getMethodName() { result = super.getIdentifier().getText() }
98+
99+
override Expr getReceiver() { result = this.(MethodCallExpr).getReceiver() }
100+
101+
override Trait getTrait() { none() }
102+
103+
override predicate receiverImplicitlyBorrowed() { any() }
104+
105+
override int getNumberOfArguments() { result = super.getArgList().getNumberOfArgs() }
106+
107+
override Expr getArgument(int i) { result = super.getArgList().getArg(i) }
108+
}
109+
110+
private class OperatorCall extends Call instanceof Operation {
111+
Trait trait;
112+
string methodName;
113+
114+
OperatorCall() { super.isOverloaded(trait, methodName) }
115+
116+
override string getMethodName() { result = methodName }
117+
118+
override Expr getReceiver() { result = super.getOperand(0) }
119+
120+
override Trait getTrait() { result = trait }
121+
122+
override predicate receiverImplicitlyBorrowed() { none() }
123+
124+
override int getNumberOfArguments() { result = super.getNumberOfOperands() - 1 }
125+
126+
override Expr getArgument(int i) { result = super.getOperand(1) and i = 0 }
127+
}
128+
}

0 commit comments

Comments
 (0)