Rust: Restructure classes representing calls

This commit is contained in:
Tom Hvitved
2025-11-24 09:45:00 +01:00
parent 3e5ea5664c
commit 7378fbc567
48 changed files with 716 additions and 548 deletions

View File

@@ -30,11 +30,11 @@ module ConstantPasswordConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) {
// `node` is an argument whose corresponding parameter name matches the pattern "pass%"
exists(CallExpr call, Function target, int argIndex, Variable v |
exists(Call call, Function target, int argIndex, Variable v |
call.getStaticTarget() = target and
v.getParameter() = target.getParam(argIndex) and
v.getText().matches("pass%") and
call.getArg(argIndex) = node.asExpr()
call.getPositionalArgument(argIndex) = node.asExpr()
)
}
}

View File

@@ -23,9 +23,9 @@ module SqlInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) {
// `node` is the first argument of a call to `sqlx_core::query::query`
exists(CallExpr call |
exists(Call call |
call.getStaticTarget().getCanonicalPath() = "sqlx_core::query::query" and
call.getArg(0) = node.asExpr()
call.getPositionalArgument(0) = node.asExpr()
)
}
}

View File

@@ -4,7 +4,6 @@
*/
private import rust
private import codeql.rust.elements.Call
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl as CfgImpl
private import internal.CfgNodes
@@ -200,20 +199,6 @@ final class BreakExprCfgNode extends Nodes::BreakExprCfgNode {
}
}
/**
* A function or method call expression. See `CallExpr` and `MethodCallExpr` for further details.
*/
final class CallExprBaseCfgNode extends Nodes::CallExprBaseCfgNode {
private CallExprBaseChildMapping node;
CallExprBaseCfgNode() { node = this.getAstNode() }
/** Gets the `i`th argument of this call. */
ExprCfgNode getArgument(int i) {
any(ChildMapping mapping).hasCfgChild(node, node.getArgList().getArg(i), this, result)
}
}
/**
* A method call expression. For example:
* ```rust
@@ -221,7 +206,16 @@ final class CallExprBaseCfgNode extends Nodes::CallExprBaseCfgNode {
* x.foo::<u32, u64>(42);
* ```
*/
final class MethodCallExprCfgNode extends CallExprBaseCfgNode, Nodes::MethodCallExprCfgNode { }
final class MethodCallExprCfgNode extends Nodes::MethodCallExprCfgNode {
private MethodCallExprChildMapping node;
MethodCallExprCfgNode() { node = this.getAstNode() }
/** Gets the `i`th argument of this call. */
ExprCfgNode getPositionalArgument(int i) {
any(ChildMapping mapping).hasCfgChild(node, node.getPositionalArgument(i), this, result)
}
}
/**
* A CFG node that calls a function.
@@ -238,7 +232,7 @@ final class CallCfgNode extends ExprCfgNode {
/** Gets the receiver of this call if it is a method call. */
ExprCfgNode getReceiver() {
any(ChildMapping mapping).hasCfgChild(node, node.getReceiver(), this, result)
any(ChildMapping mapping).hasCfgChild(node, node.(MethodCall).getReceiver(), this, result)
}
/** Gets the `i`th argument of this call, if any. */
@@ -248,15 +242,25 @@ final class CallCfgNode extends ExprCfgNode {
}
/**
* A function call expression. For example:
* An expression with parenthesized arguments. For example:
* ```rust
* foo(42);
* foo::<u32, u64>(42);
* foo[0](42);
* foo(1) = 4;
* Option::Some(42);
* ```
*/
final class CallExprCfgNode extends CallExprBaseCfgNode, Nodes::CallExprCfgNode { }
final class CallExprCfgNode extends Nodes::CallExprCfgNode {
private CallExprChildMapping node;
CallExprCfgNode() { node = this.getAstNode() }
/** Gets the `i`th argument of this call. */
ExprCfgNode getSyntacticArgument(int i) {
any(ChildMapping mapping).hasCfgChild(node, node.getSyntacticArgument(i), this, result)
}
}
/**
* A FormatArgsExpr. For example:

View File

@@ -57,8 +57,12 @@ class BreakExprTargetChildMapping extends ParentAstNode, Expr {
override predicate relevantChild(AstNode child) { child.(BreakExpr).getTarget() = this }
}
class CallExprBaseChildMapping extends ParentAstNode, CallExprBase {
override predicate relevantChild(AstNode child) { child = this.getAnArg() }
class CallExprChildMapping extends ParentAstNode, CallExpr {
override predicate relevantChild(AstNode child) { child = this.getArgList().getAnArg() }
}
class MethodCallExprChildMapping extends ParentAstNode, MethodCallExpr {
override predicate relevantChild(AstNode child) { child = this.getArgList().getAnArg() }
}
class StructExprChildMapping extends ParentAstNode, StructExpr {

View File

@@ -210,14 +210,13 @@ module ExprTrees {
override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() }
}
class BinaryOpExprTree extends StandardPostOrderTree instanceof BinaryExpr {
BinaryOpExprTree() { not this instanceof BinaryLogicalOperation }
override AstNode getChildNode(int i) {
i = 0 and result = super.getLhs()
or
i = 1 and result = super.getRhs()
class ArgsExprTree extends StandardPostOrderTree instanceof ArgsExpr {
ArgsExprTree() {
not this instanceof CallExpr and
not this instanceof BinaryLogicalOperation
}
override AstNode getChildNode(int i) { result = super.getSyntacticArgument(i) }
}
class LogicalOrExprTree extends PostOrderTree, LogicalOrExpr {
@@ -296,7 +295,7 @@ module ExprTrees {
override AstNode getChildNode(int i) {
i = 0 and result = super.getFunction()
or
result = super.getArgList().getArg(i - 1)
result = super.getSyntacticArgument(i - 1)
}
}
@@ -371,14 +370,6 @@ module ExprTrees {
}
}
class IndexExprTree extends StandardPostOrderTree instanceof IndexExpr {
override AstNode getChildNode(int i) {
i = 0 and result = super.getBase()
or
i = 1 and result = super.getIndex()
}
}
class LetExprTree extends StandardPostOrderTree, LetExpr {
override AstNode getChildNode(int i) {
i = 0 and
@@ -510,12 +501,6 @@ module ExprTrees {
}
}
class MethodCallExprTree extends StandardPostOrderTree, MethodCallExpr {
override AstNode getChildNode(int i) {
if i = 0 then result = this.getReceiver() else result = this.getArg(i - 1)
}
}
class OffsetOfExprTree extends LeafTree instanceof OffsetOfExpr { }
class ParenExprTree extends ControlFlowTree, ParenExpr {
@@ -534,10 +519,6 @@ module ExprTrees {
class PathExprTree extends LeafTree instanceof PathExpr { }
class PrefixExprTree extends StandardPostOrderTree instanceof PrefixExpr {
override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() }
}
class RangeExprTree extends StandardPostOrderTree instanceof RangeExpr {
override AstNode getChildNode(int i) {
i = 0 and result = super.getStart()

View File

@@ -163,15 +163,15 @@ final class TuplePositionContent extends FieldContent, TTuplePositionContent {
}
/**
* A content for the index of an argument to at function call.
* A content for the index of an argument to at closure call.
*
* Used by the model generator to create flow summaries for higher-order
* functions.
*/
final class FunctionCallArgumentContent extends Content, TFunctionCallArgumentContent {
final class ClosureCallArgumentContent extends Content, TClosureCallArgumentContent {
private int pos;
FunctionCallArgumentContent() { this = TFunctionCallArgumentContent(pos) }
ClosureCallArgumentContent() { this = TClosureCallArgumentContent(pos) }
int getPosition() { result = pos }
@@ -269,8 +269,8 @@ newtype TContent =
)]
} or
TFunctionCallReturnContent() or
TFunctionCallArgumentContent(int pos) {
pos in [0 .. any(CallExpr c).getArgList().getNumberOfArgs() - 1]
TClosureCallArgumentContent(int pos) {
pos in [0 .. any(ClosureCallExpr c).getNumberOfPositionalArguments()]
} or
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
TReferenceContent()

View File

@@ -8,7 +8,6 @@ private import codeql.util.Boolean
private import codeql.dataflow.DataFlow
private import codeql.dataflow.internal.DataFlowImpl
private import rust
private import codeql.rust.elements.Call
private import SsaImpl as SsaImpl
private import codeql.rust.controlflow.internal.Scope as Scope
private import codeql.rust.internal.PathResolution
@@ -57,7 +56,7 @@ final class DataFlowCallable extends TDataFlowCallable {
}
final class DataFlowCall extends TDataFlowCall {
/** Gets the underlying call in the CFG, if any. */
/** Gets the underlying call, if any. */
Call asCall() { this = TCall(result) }
predicate isSummaryCall(
@@ -134,7 +133,7 @@ final class ArgumentPosition extends ParameterPosition {
Expr getArgument(Call call) {
result = call.getPositionalArgument(this.getPosition())
or
result = call.getReceiver() and this.isSelf()
result = call.(MethodCall).getReceiver() and this.isSelf()
}
}
@@ -293,10 +292,8 @@ predicate lambdaCreationExpr(Expr creation) {
* Holds if `call` is a lambda call of kind `kind` where `receiver` is the
* invoked expression.
*/
predicate lambdaCallExpr(CallExpr call, LambdaCallKind kind, Expr receiver) {
receiver = call.getFunction() and
// All calls to complex expressions and local variable accesses are lambda call.
(receiver instanceof PathExpr implies receiver = any(Variable v).getAnAccess()) and
predicate lambdaCallExpr(ClosureCallExpr call, LambdaCallKind kind, Expr receiver) {
receiver = call.getClosureExpr() and
exists(kind)
}
@@ -666,10 +663,14 @@ module RustDataFlow implements InputSig<Location> {
pragma[nomagic]
additional predicate storeContentStep(Node node1, Content c, Node node2) {
exists(CallExpr call, int pos |
node1.asExpr() = call.getArg(pragma[only_bind_into](pos)) and
node2.asExpr() = call and
c = TTupleFieldContent(call.getTupleField(pragma[only_bind_into](pos)))
exists(CallExpr ce, TupleField tf, int pos |
node1.asExpr() = ce.getSyntacticArgument(pos) and
node2.asExpr() = ce and
c = TTupleFieldContent(tf)
|
tf = ce.(TupleStructExpr).getTupleField(pos)
or
tf = ce.(TupleVariantExpr).getTupleField(pos)
)
or
exists(StructExpr re, string field |
@@ -715,7 +716,7 @@ module RustDataFlow implements InputSig<Location> {
exists(DataFlowCall call, int i |
isArgumentNode(node1, call, TPositionalParameterPosition(i)) and
lambdaCall(call, _, node2.(PostUpdateNode).getPreUpdateNode()) and
c.(FunctionCallArgumentContent).getPosition() = i
c.(ClosureCallArgumentContent).getPosition() = i
)
or
VariableCapture::storeStep(node1, c, node2)
@@ -824,11 +825,7 @@ module RustDataFlow implements InputSig<Location> {
*/
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
receiver.asExpr() = call.asCall().(CallExpr).getFunction() and
// All calls to complex expressions and local variable accesses are lambda call.
exists(Expr f | f = receiver.asExpr() |
f instanceof PathExpr implies f = any(Variable v).getAnAccess()
)
receiver.asExpr() = call.asCall().(ClosureCallExpr).getClosureExpr()
or
call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode())
) and

View File

@@ -12,18 +12,17 @@ private import codeql.rust.dataflow.Ssa
private import Content
module Input implements InputSig<Location, RustDataFlow> {
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
private import codeql.rust.frameworks.stdlib.Stdlib
class SummarizedCallableBase = Function;
abstract private class SourceSinkBase extends AstNode {
/** Gets the associated call. */
abstract CallExprBase getCall();
abstract Call getCall();
/** Holds if the associated call resolves to `path`. */
final predicate callResolvesTo(string path) {
path = this.getCall().getStaticTarget().(Addressable).getCanonicalPath()
path = this.getCall().getResolvedTarget().getCanonicalPath()
}
}
@@ -36,7 +35,7 @@ module Input implements InputSig<Location, RustDataFlow> {
CallExprFunction() { this = call.getFunction() }
override CallExpr getCall() { result = call }
override Call getCall() { result = call }
}
private class MethodCallExprNameRef extends SourceBase, SinkBase {
@@ -53,9 +52,7 @@ module Input implements InputSig<Location, RustDataFlow> {
string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
string encodeArgumentPosition(RustDataFlow::ArgumentPosition pos) {
result = encodeParameterPosition(pos)
}
string encodeArgumentPosition(RustDataFlow::ArgumentPosition pos) { result = pos.toString() }
string encodeContent(ContentSet cs, string arg) {
exists(Content c | cs = TSingletonContentSet(c) |
@@ -173,7 +170,7 @@ private module StepsInput implements Impl::Private::StepsInputSig {
}
RustDataFlow::Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) {
exists(CallExprBase call, Expr arg, ArgumentPosition pos |
exists(ArgsExpr call, Expr arg, ArgumentPosition pos |
result.asExpr() = arg and
sc = Impl::Private::SummaryComponent::argument(pos) and
call = sink.getCall() and

View File

@@ -47,7 +47,6 @@ private import rust
private import codeql.rust.dataflow.FlowSummary
private import codeql.rust.dataflow.FlowSource
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl
/**
* Holds if in a call to the function with canonical path `path`, the value referred

View File

@@ -294,13 +294,12 @@ final class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
* passed into the closure body at an invocation.
*/
final class ClosureArgumentNode extends ArgumentNode, ExprNode {
private CallExpr call_;
private Call call_;
ClosureArgumentNode() { lambdaCallExpr(call_, _, this.asExpr()) }
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asCall() = call_ and
pos.isClosureSelf()
call.asCall() = call_ and pos.isClosureSelf()
}
}

View File

@@ -154,7 +154,7 @@ private predicate variableWriteInOuterScope(BasicBlock bb, int i, Variable v, Cf
}
/** Holds if evaluating `e` jumps to the evaluation of a different CFG scope. */
private predicate isControlFlowJump(Expr e) { e instanceof CallExprBase or e instanceof AwaitExpr }
private predicate isControlFlowJump(Expr e) { e instanceof Call or e instanceof AwaitExpr }
/**
* Holds if the call `call` at index `i` in basic block `bb` may reach
@@ -325,10 +325,8 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
predicate ssaDefHasSource(WriteDefinition def) { none() } // handled in `DataFlowImpl.qll` instead
private predicate isArg(CallExprBase call, Expr e) {
call.getAnArg() = e
or
call.(MethodCallExpr).getReceiver() = e
private predicate isArg(Call call, Expr e) {
call.getASyntacticArgument() = e
or
exists(Expr mid |
isArg(call, mid) and

View File

@@ -0,0 +1,7 @@
/**
* This module provides the public class `ArgsExpr`.
*/
private import internal.ArgsExprImpl
final class ArgsExpr = Impl::ArgsExpr;

View File

@@ -2,8 +2,28 @@
* This module provides the public class `Call`.
*/
private import rust
private import internal.CallImpl
final class ArgumentPosition = Impl::ArgumentPosition;
private import internal.CallExprImpl::Impl as CallExprImpl
final class Call = Impl::Call;
final class MethodCall = Impl::MethodCall;
/**
* A call expression that targets a closure.
*
* Closure calls never have a static target, and the set of potential
* run-time targets is only available internally to the data flow library.
*/
class ClosureCallExpr extends CallExprImpl::CallExprCall {
ClosureCallExpr() {
exists(Expr f | f = this.getFunction() |
// All calls to complex expressions and local variable accesses are lambda calls
f instanceof PathExpr implies f = any(Variable v).getAnAccess()
)
}
/** Gets the closure expression being called. */
Expr getClosureExpr() { result = super.getFunction() }
}

View File

@@ -0,0 +1,22 @@
/**
* This module provides the public class `Method`.
*/
private import rust
/**
* A method declaration. For example
* ```rust
* fn foo(self, x: u32) -> u64 { (x + 1).into() }
* ```
*
* A method declaration within a trait might not have a body:
* ```rust
* trait Trait {
* fn bar(self);
* }
* ```
*/
final class Method extends Function {
Method() { this.hasSelfParam() }
}

View File

@@ -0,0 +1,7 @@
/**
* This module provides the public class `TupleStructExpr`.
*/
private import internal.CallExprImpl
final class TupleStructExpr = Impl::TupleStructExpr;

View File

@@ -0,0 +1,7 @@
/**
* This module provides the public class `TupleVariantExpr`.
*/
private import internal.CallExprImpl
final class TupleVariantExpr = Impl::TupleVariantExpr;

View File

@@ -0,0 +1,37 @@
private import rust
module Impl {
private import codeql.rust.internal.TypeInference as TypeInference
private import codeql.rust.elements.internal.ExprImpl::Impl as ExprImpl
/**
* An expression with arguments.
*
* Either a `CallExpr`, a `MethodCallExpr`, an `Operation`, or an `IndexExpr`.
*/
abstract class ArgsExpr extends ExprImpl::Expr {
/**
* Gets the `i`th syntactic argument of this expression.
*
* Examples:
* ```rust
* foo(42, "bar"); // `42` is argument 0 and `"bar"` is argument 1
* foo.bar(42); // `foo` is argument 0 and `42` is argument 1
* x + y; // `x` is argument 0 and `y` is argument 1
* x[y]; // `x` is argument 0 and `y` is argument 1
* ```
*/
Expr getSyntacticArgument(int i) { none() }
/** Gets an argument of this expression. */
Expr getASyntacticArgument() { result = this.getSyntacticArgument(_) }
/** Gets the number of arguments of this expression. */
int getNumberOfSyntacticArguments() {
result = count(Expr arg | arg = this.getSyntacticArgument(_))
}
/** Gets the resolved target (function or tuple struct/variant), if any. */
Addressable getResolvedTarget() { result = TypeInference::resolveCallTarget(this) }
}
}

View File

@@ -5,7 +5,6 @@
*/
private import codeql.rust.elements.internal.generated.CallExpr
private import codeql.rust.elements.PathExpr
/**
* INTERNAL: This module contains the customizable definition of `CallExpr` and should not
@@ -13,7 +12,10 @@ private import codeql.rust.elements.PathExpr
*/
module Impl {
private import rust
private import codeql.rust.elements.internal.ArgsExprImpl::Impl as ArgsExprImpl
private import codeql.rust.elements.internal.CallImpl::Impl as CallImpl
private import codeql.rust.internal.PathResolution as PathResolution
private import codeql.rust.internal.TypeInference as TypeInference
pragma[nomagic]
Path getFunctionPath(CallExpr ce) { result = ce.getFunction().(PathExpr).getPath() }
@@ -37,33 +39,109 @@ module Impl {
* Option::Some(42); // tuple enum variant instantiation
* ```
*/
class CallExpr extends Generated::CallExpr {
class CallExpr extends Generated::CallExpr, ArgsExprImpl::ArgsExpr {
override string toStringImpl() { result = this.getFunction().toAbbreviatedString() + "(...)" }
/** Gets the struct that this call resolves to, if any. */
Struct getStruct() { result = getResolvedFunction(this) }
override Expr getSyntacticArgument(int i) { result = this.getArgList().getArg(i) }
/** Gets the variant that this call resolves to, if any. */
Variant getVariant() { result = getResolvedFunction(this) }
// todo: remove once internal query has been updated
Expr getArg(int i) { result = this.getSyntacticArgument(i) }
pragma[nomagic]
private PathResolution::ItemNode getResolvedFunctionAndPos(int pos) {
result = getResolvedFunction(this) and
exists(this.getArg(pos))
// todo: remove once internal query has been updated
int getNumberOfArgs() { result = this.getNumberOfSyntacticArguments() }
}
/**
* A call expression that is _not_ an instantiation of a tuple struct or a tuple enum variant.
*
* For example:
* ```rust
* foo(42);
* foo::<u32, u64>(42);
* foo[0](42);
* foo(1) = 4;
* ```
*/
class CallExprCall extends CallExpr, CallImpl::Call {
CallExprCall() {
not this instanceof TupleStructExpr and
not this instanceof TupleVariantExpr
}
/**
* Gets the tuple field that matches the `pos`th argument of this call, if any.
*
* For example, if this call is `Option::Some(42)`, then the tuple field matching
* `42` is the first field of `Option::Some`.
*/
pragma[nomagic]
TupleField getTupleField(int pos) {
exists(PathResolution::ItemNode i | i = this.getResolvedFunctionAndPos(pos) |
result.isStructField(i, pos) or
result.isVariantField(i, pos)
)
override Expr getPositionalArgument(int i) { result = super.getSyntacticArgument(i) }
}
/**
* A call expression that is a _potential_ method call.
*
* Note: In order to prevent the AST layer from relying on the type inference
* layer, we do not check that the resolved target is a method in the charpred,
* instead we check this in `getPositionalArgument` and `getReceiver`.
*/
class CallExprMethodCall extends CallExprCall, CallImpl::MethodCall {
CallExprMethodCall() { not this instanceof ClosureCallExpr }
private predicate isInFactMethodCall() { this.getResolvedTarget() instanceof Method }
override Expr getPositionalArgument(int i) {
if this.isInFactMethodCall()
then result = this.getSyntacticArgument(i + 1)
else result = this.getSyntacticArgument(i)
}
override Expr getReceiver() {
this.isInFactMethodCall() and
result = super.getSyntacticArgument(0)
}
}
/**
* A call expression that instantiates a tuple struct.
*
* For example:
* ```rust
* struct S(u32, u64);
* let s = S(42, 84);
* ```
*/
class TupleStructExpr extends CallExpr {
private Struct struct;
TupleStructExpr() { struct = getResolvedFunction(this) }
/** Gets the struct that is instantiated. */
Struct getStruct() { result = struct }
/** Gets the `i`th tuple field of the instantiated struct. */
pragma[nomagic]
TupleField getTupleField(int i) { result = this.getStruct().getTupleField(i) }
override string getAPrimaryQlClass() { result = "TupleStructExpr" }
}
/**
* A call expression that instantiates a tuple enum variant.
*
* For example:
* ```rust
* enum E {
* V(u32, u64),
* }
* let e = E::V(42, 84);
* ```
*/
class TupleVariantExpr extends CallExpr {
private Variant variant;
TupleVariantExpr() { variant = getResolvedFunction(this) }
/** Gets the variant that is instantiated. */
Variant getVariant() { result = variant }
/** Gets the `i`th tuple field of the instantiated variant. */
pragma[nomagic]
TupleField getTupleField(int i) { result = this.getVariant().getTupleField(i) }
override string getAPrimaryQlClass() { result = "TupleVariantExpr" }
}
}

View File

@@ -1,69 +1,53 @@
private import rust
private import codeql.rust.internal.PathResolution
private import codeql.rust.internal.TypeInference as TypeInference
private import codeql.rust.elements.internal.ExprImpl::Impl as ExprImpl
private import codeql.rust.elements.Operation
module Impl {
newtype TArgumentPosition =
TPositionalArgumentPosition(int i) {
i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1]
} or
TSelfArgumentPosition()
/** An argument position in a call. */
class ArgumentPosition extends TArgumentPosition {
/** Gets the index of the argument in the call, if this is a positional argument. */
int asPosition() { this = TPositionalArgumentPosition(result) }
/** Holds if this call position is a self argument. */
predicate isSelf() { this instanceof TSelfArgumentPosition }
/** Gets a string representation of this argument position. */
string toString() {
result = this.asPosition().toString()
or
this.isSelf() and result = "self"
}
}
private import codeql.rust.internal.TypeInference as TypeInference
private import codeql.rust.elements.internal.ExprImpl::Impl as ExprImpl
private import codeql.rust.elements.internal.ArgsExprImpl::Impl as ArgsExprImpl
/**
* An expression that calls a function.
* A call.
*
* This class abstracts over the different ways in which a function can be
* called in Rust.
* Either
*
* - a `CallExpr` that is _not_ an instantiation of a tuple struct or a tuple enum variant,
* - a `MethodCallExpr`,
* - an `Operation` that targets an overloadable operator, or
* - an `IndexExpr`.
*/
abstract class Call extends ExprImpl::Expr {
/** Holds if the receiver of this call is implicitly borrowed. */
predicate receiverImplicitlyBorrowed() { this.implicitBorrowAt(TSelfArgumentPosition(), _) }
abstract class Call extends ArgsExprImpl::ArgsExpr {
// todo: remove once internal query has been updated
Expr getReceiver() { none() }
/** Gets the trait targeted by this call, if any. */
abstract Trait getTrait();
/**
* Gets the `i`th positional argument of this call, if any.
*
* Examples:
* ```rust
* foo(42, "bar"); // `42` is argument 0 and `"bar"` is argument 1
* foo.bar(42); // `42` is argument 0
* x + y; // `y` is argument 0
* x[y]; // `y` is argument 0
* ```
*/
Expr getPositionalArgument(int i) { none() }
/** Holds if this call targets a trait. */
predicate hasTrait() { exists(this.getTrait()) }
/** Gets a positional argument of this expression. */
Expr getAPositionalArgument() { result = this.getPositionalArgument(_) }
/** Gets the name of the method called if this call is a method call. */
abstract string getMethodName();
/** Gets the number of positional arguments of this expression. */
int getNumberOfPositionalArguments() {
result = count(Expr arg | arg = this.getPositionalArgument(_))
}
/** Gets the argument at the given position, if any. */
abstract Expr getArgument(ArgumentPosition pos);
/** Holds if the argument at `pos` might be implicitly borrowed. */
abstract predicate implicitBorrowAt(ArgumentPosition pos, boolean certain);
/** Gets the number of arguments _excluding_ any `self` argument. */
int getNumberOfArguments() { result = count(this.getArgument(TPositionalArgumentPosition(_))) }
/** Gets the `i`th argument of this call, if any. */
Expr getPositionalArgument(int i) { result = this.getArgument(TPositionalArgumentPosition(i)) }
/** Gets the receiver of this call if it is a method call. */
Expr getReceiver() { result = this.getArgument(TSelfArgumentPosition()) }
/** Gets the static target of this call, if any. */
/** Gets the resolved target of this call, if any. */
Function getStaticTarget() { result = TypeInference::resolveCallTarget(this) }
/** Gets the name of the method called, if any. */
string getMethodName() {
result = any(Function m | m = this.getStaticTarget() and m.hasSelfParam()).getName().getText()
}
/** Gets a runtime target of this call, if any. */
pragma[nomagic]
Function getARuntimeTarget() {
@@ -76,134 +60,28 @@ module Impl {
}
}
private predicate callHasQualifier(CallExpr call, Path path, Path qualifier) {
path = call.getFunction().(PathExpr).getPath() and
qualifier = path.getQualifier()
}
private predicate callHasTraitQualifier(CallExpr call, Trait qualifier) {
exists(PathExt qualifierPath |
callHasQualifier(call, _, qualifierPath) and
qualifier = resolvePath(qualifierPath) and
// When the qualifier is `Self` and resolves to a trait, it's inside a
// trait method's default implementation. This is not a dispatch whose
// target is inferred from the type of the receiver, but should always
// resolve to the function in the trait block as path resolution does.
not qualifierPath.isUnqualified("Self")
)
}
/** Holds if the call expression dispatches to a method. */
private predicate callIsMethodCall(
CallExpr call, Path qualifier, string methodName, boolean selfIsRef
) {
exists(Path path, Function f |
callHasQualifier(call, path, qualifier) and
f = resolvePath(path) and
path.getSegment().getIdentifier().getText() = methodName and
exists(SelfParam self |
self = f.getSelfParam() and
if self.isRef() then selfIsRef = true else selfIsRef = false
)
)
}
class CallExprCall extends Call instanceof CallExpr {
CallExprCall() { not callIsMethodCall(this, _, _, _) }
override string getMethodName() { none() }
override Trait getTrait() { callHasTraitQualifier(this, result) }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) { none() }
override Expr getArgument(ArgumentPosition pos) {
result = super.getArgList().getArg(pos.asPosition())
}
}
class CallExprMethodCall extends Call instanceof CallExpr {
string methodName;
boolean selfIsRef;
CallExprMethodCall() { callIsMethodCall(this, _, methodName, selfIsRef) }
/**
* A method call.
*
* Either
*
* - a `CallExpr` where we can resolve the target as a method,
* - a `MethodCallExpr`,
* - an `Operation` that targets an overloadable operator, or
* - an `IndexExpr`.
*/
abstract class MethodCall extends Call {
/**
* Holds if this call must have an explicit borrow for the `self` argument,
* because the corresponding parameter is `&self`. Explicit borrows are not
* needed when using method call syntax.
* Gets the receiver of this method call.
*
* Examples:
* ```rust
* foo(42, "bar"); // no receiver
* foo.bar(42); // `foo` is receiver
* x + y; // `x` is receiver
* x[y]; // `x` is receiver
* ```
*/
predicate hasExplicitSelfBorrow() { selfIsRef = true }
override string getMethodName() { result = methodName }
override Trait getTrait() { callHasTraitQualifier(this, result) }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) { none() }
override Expr getArgument(ArgumentPosition pos) {
pos.isSelf() and result = super.getArgList().getArg(0)
or
result = super.getArgList().getArg(pos.asPosition() + 1)
}
}
private class MethodCallExprCall extends Call instanceof MethodCallExpr {
override string getMethodName() { result = super.getIdentifier().getText() }
override Trait getTrait() { none() }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) {
pos.isSelf() and certain = false
}
override Expr getArgument(ArgumentPosition pos) {
pos.isSelf() and result = this.(MethodCallExpr).getReceiver()
or
result = super.getArgList().getArg(pos.asPosition())
}
}
private class OperatorCall extends Call instanceof Operation {
Trait trait;
string methodName;
int borrows;
OperatorCall() { super.isOverloaded(trait, methodName, borrows) }
override string getMethodName() { result = methodName }
override Trait getTrait() { result = trait }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) {
(
pos.isSelf() and borrows >= 1
or
pos.asPosition() = 0 and borrows = 2
) and
certain = true
}
override Expr getArgument(ArgumentPosition pos) {
pos.isSelf() and result = super.getOperand(0)
or
pos.asPosition() = 0 and result = super.getOperand(1)
}
}
private class IndexCall extends Call instanceof IndexExpr {
override string getMethodName() { result = "index" }
override Trait getTrait() { result.getCanonicalPath() = "core::ops::index::Index" }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) {
pos.isSelf() and certain = true
}
override Expr getArgument(ArgumentPosition pos) {
pos.isSelf() and result = super.getBase()
or
pos.asPosition() = 0 and result = super.getIndex()
}
override Expr getReceiver() { none() }
}
}

View File

@@ -11,6 +11,9 @@ private import codeql.rust.elements.internal.generated.IndexExpr
* be referenced directly.
*/
module Impl {
private import codeql.rust.elements.internal.CallImpl::Impl as CallImpl
private import codeql.rust.elements.internal.ArgsExprImpl::Impl as ArgsExprImpl
// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* An index expression. For example:
@@ -19,10 +22,20 @@ module Impl {
* list[42] = 1;
* ```
*/
class IndexExpr extends Generated::IndexExpr {
class IndexExpr extends Generated::IndexExpr, CallImpl::MethodCall {
override string toStringImpl() {
result =
this.getBase().toAbbreviatedString() + "[" + this.getIndex().toAbbreviatedString() + "]"
}
override Expr getSyntacticArgument(int i) {
i = 0 and result = this.getBase()
or
i = 1 and result = this.getIndex()
}
override Expr getPositionalArgument(int i) { i = 0 and result = this.getIndex() }
override Expr getReceiver() { result = this.getBase() }
}
}

View File

@@ -12,6 +12,9 @@ private import codeql.rust.elements.internal.generated.MethodCallExpr
* be referenced directly.
*/
module Impl {
private import codeql.rust.elements.internal.CallImpl::Impl as CallImpl
private import codeql.rust.elements.internal.ArgsExprImpl::Impl as ArgsExprImpl
// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* NOTE: Consider using `MethodCall` instead, as that also includes calls to methods using
@@ -24,7 +27,7 @@ module Impl {
* x.foo::<u32, u64>(42);
* ```
*/
class MethodCallExpr extends Generated::MethodCallExpr {
class MethodCallExpr extends Generated::MethodCallExpr, CallImpl::MethodCall {
private string toStringPart(int index) {
index = 0 and
result = this.getReceiver().toAbbreviatedString()
@@ -43,7 +46,14 @@ module Impl {
result = strictconcat(int i | | this.toStringPart(i) order by i)
}
/** Gets the static target of this method call, if any. */
final Function getStaticTarget() { result = super.getStaticTarget() }
override Expr getSyntacticArgument(int i) {
i = 0 and result = this.getReceiver()
or
result = this.getPositionalArgument(i - 1)
}
override Expr getPositionalArgument(int i) { result = this.getArgList().getArg(i) }
override Expr getReceiver() { result = Generated::MethodCallExpr.super.getReceiver() }
}
}

View File

@@ -7,100 +7,116 @@
private import rust
private import codeql.rust.elements.internal.ExprImpl::Impl as ExprImpl
/**
* Holds if the operator `op` with arity `arity` is overloaded to a trait with
* the canonical path `path` and the method name `method`, and if it borrows its
* first `borrows` arguments.
*/
predicate isOverloaded(string op, int arity, string path, string method, int borrows) {
arity = 1 and
(
// Negation
op = "-" and path = "core::ops::arith::Neg" and method = "neg" and borrows = 0
or
// Not
op = "!" and path = "core::ops::bit::Not" and method = "not" and borrows = 0
or
// Dereference
op = "*" and path = "core::ops::deref::Deref" and method = "deref" and borrows = 1
)
or
arity = 2 and
(
// Comparison operators
op = "==" and path = "core::cmp::PartialEq" and method = "eq" and borrows = 2
or
op = "!=" and path = "core::cmp::PartialEq" and method = "ne" and borrows = 2
or
op = "<" and path = "core::cmp::PartialOrd" and method = "lt" and borrows = 2
or
op = "<=" and path = "core::cmp::PartialOrd" and method = "le" and borrows = 2
or
op = ">" and path = "core::cmp::PartialOrd" and method = "gt" and borrows = 2
or
op = ">=" and path = "core::cmp::PartialOrd" and method = "ge" and borrows = 2
or
// Arithmetic operators
op = "+" and path = "core::ops::arith::Add" and method = "add" and borrows = 0
or
op = "-" and path = "core::ops::arith::Sub" and method = "sub" and borrows = 0
or
op = "*" and path = "core::ops::arith::Mul" and method = "mul" and borrows = 0
or
op = "/" and path = "core::ops::arith::Div" and method = "div" and borrows = 0
or
op = "%" and path = "core::ops::arith::Rem" and method = "rem" and borrows = 0
or
// Arithmetic assignment expressions
op = "+=" and path = "core::ops::arith::AddAssign" and method = "add_assign" and borrows = 1
or
op = "-=" and path = "core::ops::arith::SubAssign" and method = "sub_assign" and borrows = 1
or
op = "*=" and path = "core::ops::arith::MulAssign" and method = "mul_assign" and borrows = 1
or
op = "/=" and path = "core::ops::arith::DivAssign" and method = "div_assign" and borrows = 1
or
op = "%=" and path = "core::ops::arith::RemAssign" and method = "rem_assign" and borrows = 1
or
// Bitwise operators
op = "&" and path = "core::ops::bit::BitAnd" and method = "bitand" and borrows = 0
or
op = "|" and path = "core::ops::bit::BitOr" and method = "bitor" and borrows = 0
or
op = "^" and path = "core::ops::bit::BitXor" and method = "bitxor" and borrows = 0
or
op = "<<" and path = "core::ops::bit::Shl" and method = "shl" and borrows = 0
or
op = ">>" and path = "core::ops::bit::Shr" and method = "shr" and borrows = 0
or
// Bitwise assignment operators
op = "&=" and path = "core::ops::bit::BitAndAssign" and method = "bitand_assign" and borrows = 1
or
op = "|=" and path = "core::ops::bit::BitOrAssign" and method = "bitor_assign" and borrows = 1
or
op = "^=" and path = "core::ops::bit::BitXorAssign" and method = "bitxor_assign" and borrows = 1
or
op = "<<=" and path = "core::ops::bit::ShlAssign" and method = "shl_assign" and borrows = 1
or
op = ">>=" and path = "core::ops::bit::ShrAssign" and method = "shr_assign" and borrows = 1
)
}
/**
* INTERNAL: This module contains the customizable definition of `Operation` and should not
* be referenced directly.
*/
module Impl {
private import codeql.rust.elements.internal.CallImpl::Impl as CallImpl
private import codeql.rust.elements.internal.ArgsExprImpl::Impl as ArgsExprImpl
/**
* Holds if the operator `op` with arity `arity` is overloaded to a trait with
* the canonical path `path` and the method name `method`, and if it borrows its
* first `borrows` arguments.
*/
predicate isOverloaded(string op, int arity, string path, string method, int borrows) {
arity = 1 and
(
// Negation
op = "-" and path = "core::ops::arith::Neg" and method = "neg" and borrows = 0
or
// Not
op = "!" and path = "core::ops::bit::Not" and method = "not" and borrows = 0
or
// Dereference
op = "*" and path = "core::ops::deref::Deref" and method = "deref" and borrows = 1
)
or
arity = 2 and
(
// Comparison operators
op = "==" and path = "core::cmp::PartialEq" and method = "eq" and borrows = 2
or
op = "!=" and path = "core::cmp::PartialEq" and method = "ne" and borrows = 2
or
op = "<" and path = "core::cmp::PartialOrd" and method = "lt" and borrows = 2
or
op = "<=" and path = "core::cmp::PartialOrd" and method = "le" and borrows = 2
or
op = ">" and path = "core::cmp::PartialOrd" and method = "gt" and borrows = 2
or
op = ">=" and path = "core::cmp::PartialOrd" and method = "ge" and borrows = 2
or
// Arithmetic operators
op = "+" and path = "core::ops::arith::Add" and method = "add" and borrows = 0
or
op = "-" and path = "core::ops::arith::Sub" and method = "sub" and borrows = 0
or
op = "*" and path = "core::ops::arith::Mul" and method = "mul" and borrows = 0
or
op = "/" and path = "core::ops::arith::Div" and method = "div" and borrows = 0
or
op = "%" and path = "core::ops::arith::Rem" and method = "rem" and borrows = 0
or
// Arithmetic assignment expressions
op = "+=" and path = "core::ops::arith::AddAssign" and method = "add_assign" and borrows = 1
or
op = "-=" and path = "core::ops::arith::SubAssign" and method = "sub_assign" and borrows = 1
or
op = "*=" and path = "core::ops::arith::MulAssign" and method = "mul_assign" and borrows = 1
or
op = "/=" and path = "core::ops::arith::DivAssign" and method = "div_assign" and borrows = 1
or
op = "%=" and path = "core::ops::arith::RemAssign" and method = "rem_assign" and borrows = 1
or
// Bitwise operators
op = "&" and path = "core::ops::bit::BitAnd" and method = "bitand" and borrows = 0
or
op = "|" and path = "core::ops::bit::BitOr" and method = "bitor" and borrows = 0
or
op = "^" and path = "core::ops::bit::BitXor" and method = "bitxor" and borrows = 0
or
op = "<<" and path = "core::ops::bit::Shl" and method = "shl" and borrows = 0
or
op = ">>" and path = "core::ops::bit::Shr" and method = "shr" and borrows = 0
or
// Bitwise assignment operators
op = "&=" and
path = "core::ops::bit::BitAndAssign" and
method = "bitand_assign" and
borrows = 1
or
op = "|=" and path = "core::ops::bit::BitOrAssign" and method = "bitor_assign" and borrows = 1
or
op = "^=" and
path = "core::ops::bit::BitXorAssign" and
method = "bitxor_assign" and
borrows = 1
or
op = "<<=" and path = "core::ops::bit::ShlAssign" and method = "shl_assign" and borrows = 1
or
op = ">>=" and path = "core::ops::bit::ShrAssign" and method = "shr_assign" and borrows = 1
)
}
/**
* An operation, for example `&&`, `+=`, `!` or `*`.
*
* Overloadable operations are syntatic sugar for method calls, where the
* first operand is the receiver. For example, `x + y` is syntactic sugar
* for `Add::add(x, y)`, and `x += y` is syntactic sugar for
* `AddAssign::add_assign(&mut x, y)`.
*/
abstract class Operation extends ExprImpl::Expr {
abstract class Operation extends ArgsExprImpl::ArgsExpr {
/** Gets the operator name of this operation, if it exists. */
abstract string getOperatorName();
/** Gets the `n`th operand of this operation, if any. */
abstract Expr getOperand(int n);
override Expr getSyntacticArgument(int i) { result = this.getOperand(i) }
/**
* Gets the number of operands of this operation.
*
@@ -120,4 +136,12 @@ module Impl {
methodName, borrows)
}
}
private class OperationMethodCall extends CallImpl::MethodCall instanceof Operation {
OperationMethodCall() { super.isOverloaded(_, _, _) }
override Expr getPositionalArgument(int i) { result = super.getOperand(i + 1) and i >= 0 }
override Expr getReceiver() { result = super.getOperand(0) }
}
}

View File

@@ -27,6 +27,16 @@ module Impl {
class StructExpr extends Generated::StructExpr {
override string toStringImpl() { result = this.getPath().toStringImpl() + " {...}" }
private PathResolution::ItemNode getResolvedPath() {
result = PathResolution::resolvePath(this.getPath())
}
/** Gets the struct that is instantiated, if any. */
Struct getStruct() { result = this.getResolvedPath() }
/** Gets the variant that is instantiated, if any. */
Variant getVariant() { result = this.getResolvedPath() }
/** Gets the record expression for the field `name`. */
pragma[nomagic]
StructExprField getFieldExpr(string name) {
@@ -34,19 +44,11 @@ module Impl {
name = result.getFieldName()
}
pragma[nomagic]
private PathResolution::ItemNode getResolvedPath(string name) {
result = PathResolution::resolvePath(this.getPath()) and
exists(this.getFieldExpr(name))
}
/** Gets the record field that matches the `name` field of this record expression. */
pragma[nomagic]
/** Gets the record field named `name` of the instantiated struct or variant. */
StructField getStructField(string name) {
exists(PathResolution::ItemNode i | i = this.getResolvedPath(name) |
result.isStructField(i, name) or
result.isVariantField(i, name)
)
result = this.getStruct().getStructField(name)
or
result = this.getVariant().getStructField(name)
}
}
}

View File

@@ -24,13 +24,13 @@ class StreamCipherInit extends Cryptography::CryptographicOperation::Range {
StreamCipherInit() {
// a call to `cipher::KeyInit::new`, `cipher::KeyInit::new_from_slice`,
// `cipher::KeyIvInit::new`, `cipher::KeyIvInit::new_from_slices`, `rc2::Rc2::new_with_eff_key_len` or similar.
exists(CallExprBase ce, string rawAlgorithmName |
ce = this.asExpr() and
ce.getStaticTarget().(Function).getName().getText() =
exists(Call call, string rawAlgorithmName |
call = this.asExpr() and
call.getStaticTarget().getName().getText() =
["new", "new_from_slice", "new_with_eff_key_len", "new_from_slices"] and
// extract the algorithm name from the type of `ce` or its receiver.
exists(Type t, TypePath tp |
t = inferType([ce, ce.(MethodCallExpr).getReceiver()], tp) and
t = inferType([call, call.(MethodCall).getReceiver()], tp) and
rawAlgorithmName = t.(StructType).getStruct().(Addressable).getCanonicalPath().splitAt("::")
) and
algorithmName = simplifyAlgorithmName(rawAlgorithmName) and

View File

@@ -10,7 +10,7 @@ private import codeql.rust.internal.PathResolution
/**
* A call to the `starts_with` method on a `Path`.
*/
private class StartswithCall extends Path::SafeAccessCheck::Range, MethodCallExpr {
private class StartswithCall extends Path::SafeAccessCheck::Range, MethodCall {
StartswithCall() { this.getStaticTarget().getCanonicalPath() = "<std::path::Path>::starts_with" }
override predicate checks(Expr e, boolean branch) {

View File

@@ -147,7 +147,7 @@ private class PathUse extends Use instanceof NameRef {
override Definition getDefinition() {
// Our call resolution logic may disambiguate some calls, so only use those
result.asItemNode() = this.getCall().getStaticTarget()
result.asItemNode() = this.getCall().getResolvedTarget()
or
not exists(this.getCall()) and
result.asItemNode() = resolvePath(path)

View File

@@ -2046,7 +2046,7 @@ private ItemNode resolvePathCand(PathExt path) {
or
exists(CallExpr ce |
path = CallExprImpl::getFunctionPath(ce) and
result.(ParameterizableItemNode).getArity() = ce.getNumberOfArgs()
result.(ParameterizableItemNode).getArity() = ce.getArgList().getNumberOfArgs()
)
)
}

View File

@@ -24,10 +24,17 @@ query predicate multiplePathResolutions(Path p, ItemNode i) {
strictcount(ItemNode i0 | i0 = resolvePath(p) and not i0 instanceof Crate) > 1
}
// TODO: Take other calls into account
abstract private class CallExprBase extends ArgsExpr { }
private class CallExprCallExprBase extends CallExpr, CallExprBase { }
private class MethodCallExprCallExprBase extends MethodCallExpr, CallExprBase { }
/** Holds if `call` has multiple static call targets including `target`. */
query predicate multipleCallTargets(CallExprBase call, Callable target) {
target = call.getStaticTarget() and
strictcount(call.getStaticTarget()) > 1
target = call.getResolvedTarget() and
strictcount(call.getResolvedTarget()) > 1
}
/** Holds if `fe` resolves to multiple record fields including `field`. */

View File

@@ -13,8 +13,6 @@ private import codeql.rust.internal.CachedStages
private import codeql.typeinference.internal.TypeInference
private import codeql.rust.frameworks.stdlib.Stdlib
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
private import codeql.rust.elements.Call
private import codeql.rust.elements.internal.CallImpl::Impl as CallImpl
private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl
class Type = T::Type;
@@ -230,11 +228,6 @@ module Consistency {
}
}
/** A method, that is, a function with a `self` parameter. */
private class Method extends Function {
Method() { this.hasSelfParam() }
}
/** A function without a `self` parameter. */
private class NonMethodFunction extends Function {
NonMethodFunction() { not this.hasSelfParam() }
@@ -961,6 +954,24 @@ private Type getCallExprTypeQualifier(CallExpr ce, TypePath path) {
)
}
/**
* Gets the trait qualifier of function call `ce`, if any.
*
* For example, the trait qualifier of `Default::<i32>::default()` is `Default`.
*/
pragma[nomagic]
private Trait getCallExprTraitQualifier(CallExpr ce) {
exists(PathExt qualifierPath |
qualifierPath = getCallExprPathQualifier(ce) and
result = resolvePath(qualifierPath) and
// When the qualifier is `Self` and resolves to a trait, it's inside a
// trait method's default implementation. This is not a dispatch whose
// target is inferred from the type of the receiver, but should always
// resolve to the function in the trait block as path resolution does.
not qualifierPath.isUnqualified("Self")
)
}
/**
* Provides functionality related to context-based typing of calls.
*/
@@ -1244,14 +1255,17 @@ private module MethodResolution {
pragma[nomagic]
private predicate methodCallTraitCandidate(Element mc, Trait trait) {
exists(string name, int arity |
mc.(MethodCall).hasNameAndArity(name, arity) and
methodTraitInfo(name, arity, trait)
|
not mc.(Call).hasTrait()
or
trait = mc.(Call).getTrait()
)
mc =
any(MethodCall mc0 |
exists(string name, int arity |
mc0.hasNameAndArity(name, arity) and
methodTraitInfo(name, arity, trait)
|
not mc0.hasTrait()
or
trait = mc0.getTrait()
)
)
}
private module MethodTraitIsVisible = TraitIsVisible<methodCallTraitCandidate/2>;
@@ -1296,7 +1310,7 @@ private module MethodResolution {
or
methodCallVisibleTraitCandidate(mc, i)
or
i.(ImplItemNode).resolveTraitTy() = mc.(Call).getTrait()
i.(ImplItemNode).resolveTraitTy() = mc.getTrait()
)
}
@@ -1323,7 +1337,7 @@ private module MethodResolution {
|
methodCallVisibleImplTraitCandidate(mc, impl)
or
impl.resolveTraitTy() = mc.(Call).getTrait()
impl.resolveTraitTy() = mc.getTrait()
)
}
@@ -1350,18 +1364,24 @@ private module MethodResolution {
abstract class MethodCall extends Expr {
abstract predicate hasNameAndArity(string name, int arity);
abstract Expr getArgument(ArgumentPosition pos);
abstract Expr getArg(ArgumentPosition pos);
abstract predicate supportsAutoDerefAndBorrow();
/** Gets the trait targeted by this call, if any. */
abstract Trait getTrait();
/** Holds if this call targets a trait. */
predicate hasTrait() { exists(this.getTrait()) }
AstNode getNodeAt(FunctionPosition apos) {
result = this.getArgument(apos.asArgumentPosition())
result = this.getArg(apos.asArgumentPosition())
or
result = this and apos.isReturn()
}
Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) {
result = inferType(this.getArgument(pos), path)
result = inferType(this.getArg(pos), path)
}
private Type getReceiverTypeAt(TypePath path) {
@@ -1587,12 +1607,12 @@ private module MethodResolution {
predicate receiverHasImplicitDeref(AstNode receiver) {
exists(this.resolveCallTarget(_, ".ref", false)) and
receiver = this.getArgument(CallImpl::TSelfArgumentPosition())
receiver = this.getArg(any(ArgumentPosition pos | pos.isSelf()))
}
predicate receiverHasImplicitBorrow(AstNode receiver) {
predicate argumentHasImplicitBorrow(AstNode arg) {
exists(this.resolveCallTarget(_, "", true)) and
receiver = this.getArgument(CallImpl::TSelfArgumentPosition())
arg = this.getArg(any(ArgumentPosition pos | pos.isSelf()))
}
}
@@ -1603,7 +1623,7 @@ private module MethodResolution {
arity = super.getArgList().getNumberOfArgs()
}
override Expr getArgument(ArgumentPosition pos) {
override Expr getArg(ArgumentPosition pos) {
pos.isSelf() and
result = MethodCallExpr.super.getReceiver()
or
@@ -1611,27 +1631,31 @@ private module MethodResolution {
}
override predicate supportsAutoDerefAndBorrow() { any() }
override Trait getTrait() { none() }
}
private class MethodCallIndexExpr extends MethodCall, IndexExpr {
private class MethodCallIndexExpr extends MethodCall instanceof IndexExpr {
pragma[nomagic]
override predicate hasNameAndArity(string name, int arity) {
name = "index" and
arity = 1
}
override Expr getArgument(ArgumentPosition pos) {
override Expr getArg(ArgumentPosition pos) {
pos.isSelf() and
result = this.getBase()
result = super.getBase()
or
pos.asPosition() = 0 and
result = this.getIndex()
result = super.getIndex()
}
override predicate supportsAutoDerefAndBorrow() { any() }
override Trait getTrait() { result.getCanonicalPath() = "core::ops::index::Index" }
}
private class MethodCallCallExpr extends MethodCall, CallExpr {
private class MethodCallCallExpr extends MethodCall instanceof CallExpr {
MethodCallCallExpr() {
exists(getCallExprPathQualifier(this)) and
// even if a method cannot be resolved by path resolution, it may still
@@ -1656,14 +1680,14 @@ private module MethodResolution {
pragma[nomagic]
override predicate hasNameAndArity(string name, int arity) {
name = CallExprImpl::getFunctionPath(this).getText() and
arity = this.getArgList().getNumberOfArgs() - 1
arity = super.getArgList().getNumberOfArgs() - 1
}
override Expr getArgument(ArgumentPosition pos) {
override Expr getArg(ArgumentPosition pos) {
pos.isSelf() and
result = this.getArg(0)
result = super.getSyntacticArgument(0)
or
result = this.getArgList().getArg(pos.asPosition() + 1)
result = super.getSyntacticArgument(pos.asPosition() + 1)
}
// needed for `TypeQualifierIsInstantiationOfImplSelfInput`
@@ -1672,38 +1696,55 @@ private module MethodResolution {
}
override predicate supportsAutoDerefAndBorrow() { none() }
override Trait getTrait() { result = getCallExprTraitQualifier(this) }
}
final class MethodCallOperation extends MethodCall, Operation {
final class MethodCallOperation extends MethodCall instanceof Operation {
pragma[nomagic]
override predicate hasNameAndArity(string name, int arity) {
name = this.(Call).getMethodName() and
arity = this.getNumberOfOperands() - 1
super.isOverloaded(_, name, _) and
arity = super.getNumberOfOperands() - 1
}
override Expr getArgument(ArgumentPosition pos) { result = this.(Call).getArgument(pos) }
override Expr getArg(ArgumentPosition pos) {
pos.isSelf() and
result = super.getOperand(0)
or
result = super.getOperand(pos.asPosition() + 1)
}
private predicate implicitBorrowAt(ArgumentPosition pos) {
exists(int borrows | super.isOverloaded(_, _, borrows) |
pos.isSelf() and borrows >= 1
or
pos.asPosition() = 0 and borrows = 2
)
}
override Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) {
if this.(Call).implicitBorrowAt(pos, true)
if this.implicitBorrowAt(pos)
then
result instanceof RefType and
path.isEmpty()
or
exists(TypePath path0 |
result = inferType(this.getArgument(pos), path0) and
result = inferType(this.getArg(pos), path0) and
path = TypePath::cons(getRefTypeParameter(), path0)
)
else result = inferType(this.getArgument(pos), path)
else result = inferType(this.getArg(pos), path)
}
override predicate receiverHasImplicitBorrow(AstNode receiver) {
override predicate argumentHasImplicitBorrow(AstNode arg) {
exists(ArgumentPosition pos |
this.(Call).implicitBorrowAt(pos, true) and
receiver = this.getArgument(pos)
this.implicitBorrowAt(pos) and
arg = this.getArg(pos)
)
}
override predicate supportsAutoDerefAndBorrow() { none() }
override Trait getTrait() { super.isOverloaded(result, _, _) }
}
pragma[nomagic]
@@ -2274,14 +2315,17 @@ private module NonMethodResolution {
pragma[nomagic]
private predicate blanketLikeCallTraitCandidate(Element fc, Trait trait) {
exists(string name, int arity |
fc.(NonMethodCall).hasNameAndArity(name, arity) and
functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _)
|
not fc.(Call).hasTrait()
or
trait = fc.(Call).getTrait()
)
fc =
any(NonMethodCall nmc |
exists(string name, int arity |
nmc.hasNameAndArity(name, arity) and
functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _)
|
not nmc.hasTrait()
or
trait = nmc.getTrait()
)
)
}
private module BlanketTraitIsVisible = TraitIsVisible<blanketLikeCallTraitCandidate/2>;
@@ -2323,9 +2367,15 @@ private module NonMethodResolution {
)
}
/** Gets the trait targeted by this call, if any. */
Trait getTrait() { result = getCallExprTraitQualifier(this) }
/** Holds if this call targets a trait. */
predicate hasTrait() { exists(this.getTrait()) }
pragma[nomagic]
NonMethodFunction resolveAssocCallTargetCand(ImplItemNode i) {
not this.(Call).hasTrait() and
not this.hasTrait() and
result = this.getPathResolutionResolved() and
result = i.getASuccessor(_)
or
@@ -2333,7 +2383,7 @@ private module NonMethodResolution {
}
AstNode getNodeAt(FunctionPosition pos) {
result = this.getArg(pos.asPosition())
result = this.getSyntacticArgument(pos.asPosition())
or
result = this and pos.isReturn()
}
@@ -2358,7 +2408,7 @@ private module NonMethodResolution {
pragma[nomagic]
predicate hasTraitResolved(TraitItemNode trait, NonMethodFunction resolved) {
resolved = this.getPathResolutionResolved() and
trait = this.(Call).getTrait()
trait = this.getTrait()
}
/**
@@ -2366,7 +2416,7 @@ private module NonMethodResolution {
*/
pragma[nomagic]
ItemNode resolveCallTargetViaPathResolution() {
not this.(Call).hasTrait() and
not this.hasTrait() and
result = this.getPathResolutionResolved() and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
}
@@ -2391,7 +2441,7 @@ private module NonMethodResolution {
pragma[nomagic]
NonMethodFunction resolveTraitFunctionViaPathResolution(TraitItemNode trait) {
this.(Call).hasTrait() and
this.hasTrait() and
result = this.getPathResolutionResolved() and
result = trait.getASuccessor(_)
}
@@ -2703,7 +2753,7 @@ private predicate inferNonMethodCallType =
* A matching configuration for resolving types of operations like `a + b`.
*/
private module OperationMatchingInput implements MatchingInputSig {
private import codeql.rust.elements.internal.OperationImpl as OperationImpl
private import codeql.rust.elements.internal.OperationImpl::Impl as OperationImpl
import FunctionPositionMatchingInput
class Declaration extends MethodCallMatchingInput::Declaration {
@@ -3365,8 +3415,9 @@ private Type inferDynamicCallExprType(Expr n, TypePath path) {
or
// Propagate the function's parameter type to the arguments
exists(int index |
n = ce.getCall().getArgList().getArg(index) and
path = path0.stripPrefix(fnParameterPath(ce.getCall().getNumberOfArgs(), index))
n = ce.getCall().getSyntacticArgument(index) and
path =
path0.stripPrefix(fnParameterPath(ce.getCall().getArgList().getNumberOfArgs(), index))
)
)
or
@@ -3376,16 +3427,17 @@ private Type inferDynamicCallExprType(Expr n, TypePath path) {
ce.getTypeAt(TypePath::nil()).(DynTraitType).getTrait() instanceof FnOnceTrait
|
// Propagate the type of arguments to the parameter types of closure
exists(int index |
exists(int index, ArgList args |
n = ce and
arity = ce.getCall().getNumberOfArgs() and
result = inferType(ce.getCall().getArg(index), path0) and
args = ce.getCall().getArgList() and
arity = args.getNumberOfArgs() and
result = inferType(args.getArg(index), path0) and
path = closureParameterPath(arity, index).append(path0)
)
or
// Propagate the type of the call expression to the return type of the closure
n = ce and
arity = ce.getCall().getNumberOfArgs() and
arity = ce.getCall().getArgList().getNumberOfArgs() and
result = inferType(ce.getCall(), path0) and
path = closureReturnPath().append(path0)
)
@@ -3432,12 +3484,12 @@ private module Cached {
/** Holds if `n` is implicitly borrowed. */
cached
predicate implicitBorrow(AstNode n) {
any(MethodResolution::MethodCall mc).receiverHasImplicitBorrow(n)
any(MethodResolution::MethodCall mc).argumentHasImplicitBorrow(n)
}
/** Gets an item (function or tuple struct/variant) that `call` resolves to, if any. */
cached
Addressable resolveCallTarget(Call call) {
Addressable resolveCallTarget(Expr call) {
result = call.(MethodResolution::MethodCall).resolveCallTarget(_, _, _)
or
result = call.(NonMethodResolution::NonMethodCall).resolveCallTarget()
@@ -3579,7 +3631,7 @@ private module Debug {
result = inferType(n, path)
}
Addressable debugResolveCallTarget(Call c) {
Addressable debugResolveCallTarget(ArgsExpr c) {
c = getRelevantLocatable() and
result = resolveCallTarget(c)
}

View File

@@ -3,7 +3,28 @@ private import codeql.rust.internal.TypeInference
private import codeql.rust.internal.PathResolution
private import codeql.rust.internal.Type
private import codeql.rust.internal.TypeMention
private import codeql.rust.elements.Call
private newtype TArgumentPosition =
TPositionalArgumentPosition(int i) {
i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1]
} or
TSelfArgumentPosition()
/** An argument position in a call. */
class ArgumentPosition extends TArgumentPosition {
/** Gets the index of the argument in the call, if this is a positional argument. */
int asPosition() { this = TPositionalArgumentPosition(result) }
/** Holds if this call position is a self argument. */
predicate isSelf() { this instanceof TSelfArgumentPosition }
/** Gets a string representation of this argument position. */
string toString() {
result = this.asPosition().toString()
or
this.isSelf() and result = "self"
}
}
private newtype TFunctionPosition =
TArgumentFunctionPosition(ArgumentPosition pos) or

View File

@@ -100,9 +100,9 @@ module AccessAfterLifetime {
a = b.getEnclosingBlock*()
or
// propagate through function calls
exists(CallExprBase ce |
mayEncloseOnStack(a, ce.getEnclosingBlock()) and
ce.getStaticTarget() = b.getEnclosingCallable()
exists(Call call |
mayEncloseOnStack(a, call.getEnclosingBlock()) and
call.getStaticTarget() = b.getEnclosingCallable()
)
}

View File

@@ -33,12 +33,12 @@ module DisabledCertificateCheckExtensions {
*/
private class HeuristicSink extends Sink {
HeuristicSink() {
exists(CallExprBase fc |
fc.getStaticTarget().(Function).getName().getText() =
exists(Call call |
call.getStaticTarget().getName().getText() =
["danger_accept_invalid_certs", "danger_accept_invalid_hostnames"] and
fc.getArg(0) = this.asExpr() and
call.getPositionalArgument(0) = this.asExpr() and
// don't duplicate modeled sinks
not exists(ModelsAsDataSink s | s.(Node::FlowSummaryNode).getSinkElement().getCall() = fc)
not exists(ModelsAsDataSink s | s.(Node::FlowSummaryNode).getSinkElement().getCall() = call)
)
}
}

View File

@@ -103,10 +103,9 @@ module HardcodedCryptographicValue {
*/
private class GetRandomBarrier extends Barrier {
GetRandomBarrier() {
exists(CallExprBase ce |
ce.getStaticTarget().(Addressable).getCanonicalPath() =
["getrandom::fill", "getrandom::getrandom"] and
this.asExpr().getParentNode*() = ce.getArgList().getArg(0)
exists(Call call |
call.getStaticTarget().getCanonicalPath() = ["getrandom::fill", "getrandom::getrandom"] and
this.asExpr().getParentNode*() = call.getPositionalArgument(0)
)
}
}

View File

@@ -80,12 +80,12 @@ module InsecureCookie {
* as `false`.
*/
predicate cookieSetNode(DataFlow::Node node, string attrib, boolean value) {
exists(FlowSummaryNode summaryNode, CallExprBase ce, int arg, DataFlow::Node argNode |
exists(FlowSummaryNode summaryNode, MethodCall call, int arg, DataFlow::Node argNode |
// decode the models-as-data `OptionalBarrier`
cookieOptionalBarrier(summaryNode, attrib, arg) and
// find a call and arg referenced by this optional barrier
ce.getStaticTarget() = summaryNode.getSummarizedCallable() and
ce.getArg(arg) = argNode.asExpr() and
call.getStaticTarget() = summaryNode.getSummarizedCallable() and
call.getPositionalArgument(arg) = argNode.asExpr() and
// check if the argument is always `true`
(
if
@@ -101,12 +101,12 @@ module InsecureCookie {
// and find the node where this happens (we can't just use the flow summary node, since its
// shared across all calls to the modeled function, we need a node specific to this call)
(
node.asExpr() = ce.(MethodCallExpr).getReceiver() // e.g. `a` in `a.set_secure(true)`
node.asExpr() = call.getReceiver() // e.g. `a` in `a.set_secure(true)`
or
exists(BasicBlock bb, int i |
// associated SSA node
node.(SsaNode).asDefinition().definesAt(_, bb, i) and
ce.(MethodCallExpr).getReceiver() = bb.getNode(i).getAstNode()
call.getReceiver() = bb.getNode(i).getAstNode()
)
)
)

View File

@@ -28,12 +28,13 @@ private class SensitiveDataCall extends SensitiveData {
SensitiveDataClassification classification;
SensitiveDataCall() {
exists(CallExprBase call, string name |
exists(ArgsExpr call, Addressable target, string name |
call = this.asExpr() and
target = call.getResolvedTarget() and
name =
[
call.getStaticTarget().(Function).getName().getText(),
call.(CallExpr).getVariant().getName().getText(),
target.(Function).getName().getText(),
target.(Variant).getName().getText(),
] and
HeuristicNames::nameIndicatesSensitiveData(name, classification)
)

View File

@@ -66,11 +66,11 @@ module SanitizerGuard {
* A check of the form `!strings.Contains(nd, "..")`, considered as a sanitizer guard for
* path traversal.
*/
private class DotDotCheck extends SanitizerGuard::Range, MethodCallExpr {
private class DotDotCheck extends SanitizerGuard::Range, MethodCall {
DotDotCheck() {
this.getStaticTarget().(Addressable).getCanonicalPath() =
this.getStaticTarget().getCanonicalPath() =
["<alloc::string::String>::contains", "<core::str>::contains"] and
this.getArg(0).(LiteralExpr).getTextValue() = ["\"..\"", "\"../\"", "\"..\\\""]
this.getPositionalArgument(0).(LiteralExpr).getTextValue() = ["\"..\"", "\"../\"", "\"..\\\""]
}
override predicate checks(Expr e, boolean branch) {

View File

@@ -55,7 +55,7 @@ module Xss {
HeuristicHtmlEncodingBarrier() {
exists(Call fc |
fc.getStaticTarget().getName().getText().regexpMatch(".*(escape|encode).*") and
fc.getArgument(_) = this.asExpr()
fc.getAPositionalArgument() = this.asExpr()
)
}
}

View File

@@ -53,10 +53,10 @@ module RegexInjection {
*/
private class NewSink extends Sink {
NewSink() {
exists(CallExprBase call, Addressable a |
call.getStaticTarget() = a and
a.getCanonicalPath() = "<regex::regex::string::Regex>::new" and
this.asExpr() = call.getArg(0) and
exists(Call call, Function f |
call.getStaticTarget() = f and
f.getCanonicalPath() = "<regex::regex::string::Regex>::new" and
this.asExpr() = call.getPositionalArgument(0) and
not this.asExpr() instanceof LiteralExpr
)
}

View File

@@ -6,6 +6,7 @@ import codeql.Locations
import codeql.files.FileSystem
import codeql.rust.elements.Operation
import codeql.rust.elements.ArithmeticOperation
import codeql.rust.elements.ArgsExpr
import codeql.rust.elements.AssignmentOperation
import codeql.rust.elements.BitwiseOperation
import codeql.rust.elements.ComparisonOperation
@@ -18,6 +19,7 @@ import codeql.rust.elements.Variable
import codeql.rust.elements.NamedFormatArgument
import codeql.rust.elements.PositionalFormatArgument
import codeql.rust.elements.RangeExprExt
private import codeql.rust.elements.Call as Call
class Call = Call::Call;
import codeql.rust.elements.Call
import codeql.rust.elements.TupleStructExpr
import codeql.rust.elements.TupleVariantExpr
import codeql.rust.elements.Method

View File

@@ -26,15 +26,16 @@ private module FlowTestImpl implements InputSig<Location, RustDataFlow> {
predicate defaultSource(DataFlow::Node source) { callTargetName(source.asExpr(), "source") }
predicate defaultSink(DataFlow::Node sink) {
any(CallExpr call | callTargetName(call, "sink")).getAnArg() = sink.asExpr()
any(CallExpr call | callTargetName(call, "sink")).getASyntacticArgument() = sink.asExpr()
}
private string getSourceArgString(DataFlow::Node src) {
defaultSource(src) and
result = src.asExpr().(CallExpr).getArg(0).toString()
result = src.asExpr().(CallExpr).getSyntacticArgument(0).toString()
or
sourceNode(src, _) and
result = src.(Node::FlowSummaryNode).getSourceElement().getCall().getArg(0).toString() and
result =
src.(Node::FlowSummaryNode).getSourceElement().getCall().getPositionalArgument(0).toString() and
// Don't use the result if it contains spaces
not result.matches("% %")
}

View File

@@ -30,10 +30,8 @@ class CtorAttr extends Attr {
/**
* A call into the Rust standard library, that is, a sink for this query.
*/
class StdCall extends Expr {
StdCall() {
this.(CallExprBase).getStaticTarget().getCanonicalPath().matches(["std::%", "<std::%"])
}
class StdCall extends Call {
StdCall() { this.getStaticTarget().getCanonicalPath().matches(["std::%", "<std::%"]) }
}
class PathElement = AstNode;
@@ -56,11 +54,11 @@ predicate edgesFwd(PathElement pred, PathElement succ) {
or
// [forwards reachable] callable -> enclosed call
edgesFwd(_, pred) and
pred = succ.(CallExprBase).getEnclosingCallable()
pred = succ.(Call).getEnclosingCallable()
or
// [forwards reachable] call -> target callable
edgesFwd(_, pred) and
pred.(CallExprBase).getStaticTarget() = succ
pred.(Call).getStaticTarget() = succ
}
/**

View File

@@ -19,22 +19,22 @@ private class RelevantFile extends File {
}
module CallTargetStats implements StatsSig {
// TODO: Take other calls into account
abstract private class CallExprBase extends ArgsExpr { }
private class CallExprCallExprBase extends CallExpr, CallExprBase { }
private class MethodCallExprCallExprBase extends MethodCallExpr, CallExprBase { }
int getNumberOfOk() {
result =
count(CallExprBase c | c.getFile() instanceof RelevantFile and exists(c.getStaticTarget()))
}
private predicate isLambdaCall(CallExpr call) {
exists(Expr receiver | receiver = call.getFunction() |
// All calls to complex expressions and local variable accesses are lambda calls
receiver instanceof PathExpr implies receiver = any(Variable v).getAnAccess()
)
count(CallExprBase c | c.getFile() instanceof RelevantFile and exists(c.getResolvedTarget()))
}
additional predicate isNotOkCall(CallExprBase c) {
c.getFile() instanceof RelevantFile and
not exists(c.getStaticTarget()) and
not isLambdaCall(c)
not exists(c.getResolvedTarget()) and
not c instanceof ClosureCallExpr
}
int getNumberOfNotOk() { result = count(CallExprBase c | isNotOkCall(c)) }

View File

@@ -134,7 +134,7 @@ private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputS
predicate isCallback(DataFlow::ContentSet cs) {
exists(Content c | c = cs.(SingletonContentSet).getContent() |
c instanceof FunctionCallReturnContent or
c instanceof FunctionCallArgumentContent
c instanceof ClosureCallArgumentContent
)
}
@@ -145,7 +145,7 @@ private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputS
or
exists(Content c | cs = DataFlowImpl::TSingletonContentSet(c) |
exists(int pos |
pos = c.(FunctionCallArgumentContent).getPosition() and
pos = c.(ClosureCallArgumentContent).getPosition() and
result = "Parameter" and
arg = pos.toString()
)

View File

@@ -1,21 +1,19 @@
instances
| gen_call_expr.rs:5:5:5:11 | foo(...) |
| gen_call_expr.rs:6:5:6:23 | foo::<...>(...) |
| gen_call_expr.rs:7:5:7:14 | ...(...) |
| gen_call_expr.rs:8:5:8:10 | foo(...) |
| gen_call_expr.rs:8:5:8:11 | foo(...) |
| gen_call_expr.rs:9:5:9:23 | foo::<...>(...) |
| gen_call_expr.rs:10:5:10:14 | ...(...) |
| gen_call_expr.rs:11:5:11:10 | foo(...) |
| gen_call_expr.rs:12:5:12:20 | ...::Some(...) |
getArgList
| gen_call_expr.rs:5:5:5:11 | foo(...) | gen_call_expr.rs:5:8:5:11 | ArgList |
| gen_call_expr.rs:6:5:6:23 | foo::<...>(...) | gen_call_expr.rs:6:20:6:23 | ArgList |
| gen_call_expr.rs:7:5:7:14 | ...(...) | gen_call_expr.rs:7:11:7:14 | ArgList |
| gen_call_expr.rs:8:5:8:10 | foo(...) | gen_call_expr.rs:8:8:8:10 | ArgList |
| gen_call_expr.rs:8:5:8:11 | foo(...) | gen_call_expr.rs:8:8:8:11 | ArgList |
| gen_call_expr.rs:9:5:9:23 | foo::<...>(...) | gen_call_expr.rs:9:20:9:23 | ArgList |
| gen_call_expr.rs:10:5:10:14 | ...(...) | gen_call_expr.rs:10:11:10:14 | ArgList |
| gen_call_expr.rs:11:5:11:10 | foo(...) | gen_call_expr.rs:11:8:11:10 | ArgList |
| gen_call_expr.rs:12:5:12:20 | ...::Some(...) | gen_call_expr.rs:12:17:12:20 | ArgList |
getAttr
getArg
| gen_call_expr.rs:5:5:5:11 | foo(...) | 0 | gen_call_expr.rs:5:9:5:10 | 42 |
| gen_call_expr.rs:6:5:6:23 | foo::<...>(...) | 0 | gen_call_expr.rs:6:21:6:22 | 42 |
| gen_call_expr.rs:7:5:7:14 | ...(...) | 0 | gen_call_expr.rs:7:12:7:13 | 42 |
| gen_call_expr.rs:8:5:8:10 | foo(...) | 0 | gen_call_expr.rs:8:9:8:9 | 1 |
getFunction
| gen_call_expr.rs:5:5:5:11 | foo(...) | gen_call_expr.rs:5:5:5:7 | foo |
| gen_call_expr.rs:6:5:6:23 | foo::<...>(...) | gen_call_expr.rs:6:5:6:19 | foo::<...> |
| gen_call_expr.rs:7:5:7:14 | ...(...) | gen_call_expr.rs:7:5:7:10 | foo[0] |
| gen_call_expr.rs:8:5:8:10 | foo(...) | gen_call_expr.rs:8:5:8:7 | foo |
| gen_call_expr.rs:8:5:8:11 | foo(...) | gen_call_expr.rs:8:5:8:7 | foo |
| gen_call_expr.rs:9:5:9:23 | foo::<...>(...) | gen_call_expr.rs:9:5:9:19 | foo::<...> |
| gen_call_expr.rs:10:5:10:14 | ...(...) | gen_call_expr.rs:10:5:10:10 | foo[0] |
| gen_call_expr.rs:11:5:11:10 | foo(...) | gen_call_expr.rs:11:5:11:7 | foo |
| gen_call_expr.rs:12:5:12:20 | ...::Some(...) | gen_call_expr.rs:12:5:12:16 | ...::Some |

View File

@@ -1,18 +1,15 @@
instances
| gen_method_call_expr.rs:5:5:5:13 | x.foo(...) |
| gen_method_call_expr.rs:6:5:6:25 | x.foo(...) |
| gen_method_call_expr.rs:9:5:9:13 | x.foo(...) |
| gen_method_call_expr.rs:10:5:10:25 | x.foo(...) |
getArgList
| gen_method_call_expr.rs:5:5:5:13 | x.foo(...) | gen_method_call_expr.rs:5:10:5:13 | ArgList |
| gen_method_call_expr.rs:6:5:6:25 | x.foo(...) | gen_method_call_expr.rs:6:22:6:25 | ArgList |
| gen_method_call_expr.rs:9:5:9:13 | x.foo(...) | gen_method_call_expr.rs:9:10:9:13 | ArgList |
| gen_method_call_expr.rs:10:5:10:25 | x.foo(...) | gen_method_call_expr.rs:10:22:10:25 | ArgList |
getAttr
getArg
| gen_method_call_expr.rs:5:5:5:13 | x.foo(...) | 0 | gen_method_call_expr.rs:5:11:5:12 | 42 |
| gen_method_call_expr.rs:6:5:6:25 | x.foo(...) | 0 | gen_method_call_expr.rs:6:23:6:24 | 42 |
getGenericArgList
| gen_method_call_expr.rs:6:5:6:25 | x.foo(...) | gen_method_call_expr.rs:6:10:6:21 | <...> |
| gen_method_call_expr.rs:10:5:10:25 | x.foo(...) | gen_method_call_expr.rs:10:10:10:21 | <...> |
getIdentifier
| gen_method_call_expr.rs:5:5:5:13 | x.foo(...) | gen_method_call_expr.rs:5:7:5:9 | foo |
| gen_method_call_expr.rs:6:5:6:25 | x.foo(...) | gen_method_call_expr.rs:6:7:6:9 | foo |
| gen_method_call_expr.rs:9:5:9:13 | x.foo(...) | gen_method_call_expr.rs:9:7:9:9 | foo |
| gen_method_call_expr.rs:10:5:10:25 | x.foo(...) | gen_method_call_expr.rs:10:7:10:9 | foo |
getReceiver
| gen_method_call_expr.rs:5:5:5:13 | x.foo(...) | gen_method_call_expr.rs:5:5:5:5 | x |
| gen_method_call_expr.rs:6:5:6:25 | x.foo(...) | gen_method_call_expr.rs:6:5:6:5 | x |
| gen_method_call_expr.rs:9:5:9:13 | x.foo(...) | gen_method_call_expr.rs:9:5:9:5 | x |
| gen_method_call_expr.rs:10:5:10:25 | x.foo(...) | gen_method_call_expr.rs:10:5:10:5 | x |

View File

@@ -1770,7 +1770,7 @@ proc_macro.rs:
# 16| getIdentifier(): [NameRef] items
# 15| getMatchArmList(): [MatchArmList] MatchArmList
# 15| getArm(0): [MatchArm] ... => ...
# 15| getExpr(): [CallExpr] ...::RepInterp(...)
# 15| getExpr(): [TupleStructExpr] ...::RepInterp(...)
# 15| getArgList(): [ArgList] ArgList
# 15| getArg(0): [VariableAccess] _x
# 15| getPath(): [Path] _x

View File

@@ -41,6 +41,9 @@ edges
| main.rs:73:10:73:10 | [post] b [&ref] | main.rs:74:15:74:15 | b [&ref] | provenance | |
| main.rs:73:14:73:23 | source(...) | main.rs:73:10:73:10 | [post] b [&ref] | provenance | |
| main.rs:74:15:74:15 | b [&ref] | main.rs:74:14:74:15 | * ... | provenance | MaD:1 |
| main.rs:90:11:90:16 | [post] &mut a [&ref] | main.rs:90:16:90:16 | [post] a | provenance | |
| main.rs:90:16:90:16 | [post] a | main.rs:91:14:91:14 | a | provenance | |
| main.rs:90:21:90:30 | source(...) | main.rs:90:11:90:16 | [post] &mut a [&ref] | provenance | |
| main.rs:105:10:105:10 | [post] c [&ref] | main.rs:106:15:106:15 | c [&ref] | provenance | |
| main.rs:105:14:105:23 | source(...) | main.rs:105:10:105:10 | [post] c [&ref] | provenance | |
| main.rs:106:15:106:15 | c [&ref] | main.rs:106:14:106:15 | * ... | provenance | MaD:1 |
@@ -175,6 +178,10 @@ nodes
| main.rs:73:14:73:23 | source(...) | semmle.label | source(...) |
| main.rs:74:14:74:15 | * ... | semmle.label | * ... |
| main.rs:74:15:74:15 | b [&ref] | semmle.label | b [&ref] |
| main.rs:90:11:90:16 | [post] &mut a [&ref] | semmle.label | [post] &mut a [&ref] |
| main.rs:90:16:90:16 | [post] a | semmle.label | [post] a |
| main.rs:90:21:90:30 | source(...) | semmle.label | source(...) |
| main.rs:91:14:91:14 | a | semmle.label | a |
| main.rs:105:10:105:10 | [post] c [&ref] | semmle.label | [post] c [&ref] |
| main.rs:105:14:105:23 | source(...) | semmle.label | source(...) |
| main.rs:106:14:106:15 | * ... | semmle.label | * ... |
@@ -288,6 +295,7 @@ testFailures
| main.rs:53:14:53:15 | * ... | main.rs:51:17:51:26 | source(...) | main.rs:53:14:53:15 | * ... | $@ | main.rs:51:17:51:26 | source(...) | source(...) |
| main.rs:59:33:59:34 | * ... | main.rs:57:22:57:31 | source(...) | main.rs:59:33:59:34 | * ... | $@ | main.rs:57:22:57:31 | source(...) | source(...) |
| main.rs:74:14:74:15 | * ... | main.rs:73:14:73:23 | source(...) | main.rs:74:14:74:15 | * ... | $@ | main.rs:73:14:73:23 | source(...) | source(...) |
| main.rs:91:14:91:14 | a | main.rs:90:21:90:30 | source(...) | main.rs:91:14:91:14 | a | $@ | main.rs:90:21:90:30 | source(...) | source(...) |
| main.rs:106:14:106:15 | * ... | main.rs:105:14:105:23 | source(...) | main.rs:106:14:106:15 | * ... | $@ | main.rs:105:14:105:23 | source(...) | source(...) |
| main.rs:113:14:113:15 | * ... | main.rs:112:25:112:34 | source(...) | main.rs:113:14:113:15 | * ... | $@ | main.rs:112:25:112:34 | source(...) | source(...) |
| main.rs:175:14:175:34 | my_number.to_number() | main.rs:174:44:174:53 | source(...) | main.rs:175:14:175:34 | my_number.to_number() | $@ | main.rs:174:44:174:53 | source(...) | source(...) |

View File

@@ -88,7 +88,7 @@ mod intraprocedural_mutable_borrows {
let mut a = 1;
sink(a);
*(&mut a) = source(87);
sink(a); // $ MISSING: hasValueFlow=87
sink(a); // $ hasValueFlow=87
}
pub fn clear_through_borrow() {