mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Rust: Restructure classes representing calls
This commit is contained in:
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
7
rust/ql/lib/codeql/rust/elements/ArgsExpr.qll
Normal file
7
rust/ql/lib/codeql/rust/elements/ArgsExpr.qll
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This module provides the public class `ArgsExpr`.
|
||||
*/
|
||||
|
||||
private import internal.ArgsExprImpl
|
||||
|
||||
final class ArgsExpr = Impl::ArgsExpr;
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
22
rust/ql/lib/codeql/rust/elements/Method.qll
Normal file
22
rust/ql/lib/codeql/rust/elements/Method.qll
Normal 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() }
|
||||
}
|
||||
7
rust/ql/lib/codeql/rust/elements/TupleStructExpr.qll
Normal file
7
rust/ql/lib/codeql/rust/elements/TupleStructExpr.qll
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This module provides the public class `TupleStructExpr`.
|
||||
*/
|
||||
|
||||
private import internal.CallExprImpl
|
||||
|
||||
final class TupleStructExpr = Impl::TupleStructExpr;
|
||||
7
rust/ql/lib/codeql/rust/elements/TupleVariantExpr.qll
Normal file
7
rust/ql/lib/codeql/rust/elements/TupleVariantExpr.qll
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This module provides the public class `TupleVariantExpr`.
|
||||
*/
|
||||
|
||||
private import internal.CallExprImpl
|
||||
|
||||
final class TupleVariantExpr = Impl::TupleVariantExpr;
|
||||
37
rust/ql/lib/codeql/rust/elements/internal/ArgsExprImpl.qll
Normal file
37
rust/ql/lib/codeql/rust/elements/internal/ArgsExprImpl.qll
Normal 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) }
|
||||
}
|
||||
}
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("% %")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)) }
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(...) |
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user