diff --git a/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll b/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll index a118cf6b4720..0c4ce51f6192 100644 --- a/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll +++ b/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll @@ -4,6 +4,7 @@ */ private import rust +private import codeql.rust.elements.Call private import ControlFlowGraph private import internal.ControlFlowGraphImpl as CfgImpl private import internal.CfgNodes @@ -162,6 +163,30 @@ final class CallExprBaseCfgNode extends Nodes::CallExprBaseCfgNode { */ final class MethodCallExprCfgNode extends CallExprBaseCfgNode, Nodes::MethodCallExprCfgNode { } +/** + * A CFG node that calls a function. + * + * This class abstract over the different ways in which a function can be called in Rust. + */ +final class CallCfgNode extends ExprCfgNode { + private Call node; + + CallCfgNode() { node = this.getAstNode() } + + /** Gets the underlying `Call`. */ + Call getCall() { result = node } + + /** Gets the receiver of this call if it is a method call. */ + ExprCfgNode getReceiver() { + any(ChildMapping mapping).hasCfgChild(node, node.getReceiver(), this, result) + } + + /** Gets the `i`th argument of this call, if any. */ + ExprCfgNode getArgument(int i) { + any(ChildMapping mapping).hasCfgChild(node, node.getArgument(i), this, result) + } +} + /** * A function call expression. For example: * ```rust diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll index f0dc961a9f93..909d275dc11b 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll @@ -1,5 +1,8 @@ import codeql.rust.dataflow.DataFlow::DataFlow as DataFlow private import rust +private import codeql.rust.controlflow.ControlFlowGraph +private import codeql.rust.controlflow.internal.Splitting +private import codeql.rust.controlflow.CfgNodes as CfgNodes private import codeql.rust.dataflow.internal.DataFlowImpl private import codeql.rust.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import codeql.rust.dataflow.internal.Node as Node @@ -26,6 +29,17 @@ private module Input implements InputSig { } predicate missingLocationExclude(RustDataFlow::Node n) { not exists(n.asExpr().getLocation()) } + + predicate multipleArgumentCallExclude(Node::ArgumentNode arg, DataFlowCall call) { + // An argument such as `x` in `if !x { ... }` has two successors (and hence + // two calls); one for each Boolean outcome of `x`. + exists(CfgNodes::ExprCfgNode n | + arg.isArgumentOf(call, _) and + n = call.asCallCfgNode() and + arg.asExpr().getASuccessor(any(ConditionalSuccessor c)).getASuccessor*() = n and + n.getASplit() instanceof ConditionalCompletionSplitting::ConditionalCompletionSplit + ) + } } import MakeConsistency diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index d0f7378bd3a1..2cf9cc216681 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -8,6 +8,7 @@ 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 @@ -55,11 +56,7 @@ final class DataFlowCallable extends TDataFlowCallable { final class DataFlowCall extends TDataFlowCall { /** Gets the underlying call in the CFG, if any. */ - CallExprCfgNode asCallExprCfgNode() { result = this.asCallBaseExprCfgNode() } - - MethodCallExprCfgNode asMethodCallExprCfgNode() { result = this.asCallBaseExprCfgNode() } - - CallExprBaseCfgNode asCallBaseExprCfgNode() { this = TCall(result) } + CallCfgNode asCallCfgNode() { this = TCall(result) } predicate isSummaryCall( FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver @@ -68,7 +65,7 @@ final class DataFlowCall extends TDataFlowCall { } DataFlowCallable getEnclosingCallable() { - result = TCfgScope(this.asCallBaseExprCfgNode().getExpr().getEnclosingCfgScope()) + result = TCfgScope(this.asCallCfgNode().getExpr().getEnclosingCfgScope()) or exists(FlowSummaryImpl::Public::SummarizedCallable c | this.isSummaryCall(c, _) and @@ -77,7 +74,7 @@ final class DataFlowCall extends TDataFlowCall { } string toString() { - result = this.asCallBaseExprCfgNode().toString() + result = this.asCallCfgNode().toString() or exists( FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver @@ -87,7 +84,7 @@ final class DataFlowCall extends TDataFlowCall { ) } - Location getLocation() { result = this.asCallBaseExprCfgNode().getLocation() } + Location getLocation() { result = this.asCallCfgNode().getLocation() } } /** @@ -135,38 +132,23 @@ final class ParameterPosition extends TParameterPosition { */ final class ArgumentPosition extends ParameterPosition { /** Gets the argument of `call` at this position, if any. */ - Expr getArgument(CallExprBase call) { - result = call.getArgList().getArg(this.getPosition()) + Expr getArgument(Call call) { + result = call.getArgument(this.getPosition()) or - this.isSelf() and - result = call.(MethodCallExpr).getReceiver() + result = call.getReceiver() and this.isSelf() } } -/** Holds if `call` invokes a qualified path that resolves to a method. */ -private predicate callToMethod(CallExpr call) { - exists(Path path | - path = call.getFunction().(PathExpr).getPath() and - path.hasQualifier() and - resolvePath(path).(Function).getParamList().hasSelfParam() - ) -} - /** * Holds if `arg` is an argument of `call` at the position `pos`. * * Note that this does not hold for the receiever expression of a method call * as the synthetic `ReceiverNode` is the argument for the `self` parameter. */ -predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, ParameterPosition pos) { - if callToMethod(call.(CallExprCfgNode).getCallExpr()) - then - // The first argument is for the `self` parameter - arg = call.getArgument(0) and pos.isSelf() - or - // Succeeding arguments are shifted left - arg = call.getArgument(pos.getPosition() + 1) - else arg = call.getArgument(pos.getPosition()) +predicate isArgumentForCall(ExprCfgNode arg, CallCfgNode call, ParameterPosition pos) { + call.getArgument(pos.getPosition()) = arg + or + call.getReceiver() = arg and pos.isSelf() and not call.getCall().receiverImplicitlyBorrowed() } /** Provides logic related to SSA. */ @@ -419,9 +401,9 @@ module RustDataFlow implements InputSig { /** Gets a viable implementation of the target of the given `Call`. */ DataFlowCallable viableCallable(DataFlowCall call) { - result.asCfgScope() = call.asCallBaseExprCfgNode().getCallExprBase().getStaticTarget() + result.asCfgScope() = call.asCallCfgNode().getCall().getStaticTarget() or - result.asLibraryCallable().getACall() = call.asCallBaseExprCfgNode().getCallExprBase() + result.asLibraryCallable().getACall() = call.asCallCfgNode().getCall() } /** @@ -812,7 +794,7 @@ module RustDataFlow implements InputSig { */ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { ( - receiver.asExpr() = call.asCallExprCfgNode().getFunction() and + receiver.asExpr() = call.asCallCfgNode().(CallExprCfgNode).getFunction() and // All calls to complex expressions and local variable accesses are lambda call. exists(Expr f | f = receiver.asExpr().getExpr() | f instanceof PathExpr implies f = any(Variable v).getAnAccess() @@ -976,7 +958,7 @@ private module Cached { cached newtype TDataFlowCall = - TCall(CallExprBaseCfgNode c) { Stages::DataFlowStage::ref() } or + TCall(CallCfgNode c) { Stages::DataFlowStage::ref() } or TSummaryCall( FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver ) { diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll index 62cc47dfc0d3..ba91518eab68 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll @@ -153,7 +153,7 @@ private import Make as Impl private module StepsInput implements Impl::Private::StepsInputSig { DataFlowCall getACall(Public::SummarizedCallable sc) { - result.asCallBaseExprCfgNode().getCallExprBase() = sc.(LibraryCallable).getACall() + result.asCallCfgNode().getCall() = sc.(LibraryCallable).getACall() } RustDataFlow::Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll b/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll index 67782f0b7e00..939cb45a0ca5 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/Node.qll @@ -224,13 +224,13 @@ abstract class ArgumentNode extends Node { } final class ExprArgumentNode extends ArgumentNode, ExprNode { - private CallExprBaseCfgNode call_; + private CallCfgNode call_; private RustDataFlow::ArgumentPosition pos_; ExprArgumentNode() { isArgumentForCall(n, call_, pos_) } override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) { - call.asCallBaseExprCfgNode() = call_ and pos = pos_ + call.asCallCfgNode() = call_ and pos = pos_ } } @@ -239,7 +239,7 @@ final class ExprArgumentNode extends ArgumentNode, ExprNode { * has taken place. */ final class ReceiverNode extends ArgumentNode, TReceiverNode { - private MethodCallExprCfgNode n; + private CallCfgNode n; ReceiverNode() { this = TReceiverNode(n, false) } @@ -248,7 +248,7 @@ final class ReceiverNode extends ArgumentNode, TReceiverNode { MethodCallExprCfgNode getMethodCall() { result = n } override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) { - call.asMethodCallExprCfgNode() = n and pos = TSelfParameterPosition() + call.asCallCfgNode() = n and pos = TSelfParameterPosition() } override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() } @@ -281,7 +281,7 @@ final class ClosureArgumentNode extends ArgumentNode, ExprNode { ClosureArgumentNode() { lambdaCallExpr(call_, _, this.asExpr()) } override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) { - call.asCallExprCfgNode() = call_ and + call.asCallCfgNode() = call_ and pos.isClosureSelf() } } @@ -330,11 +330,11 @@ abstract class OutNode extends Node { } final private class ExprOutNode extends ExprNode, OutNode { - ExprOutNode() { this.asExpr() instanceof CallExprBaseCfgNode } + ExprOutNode() { this.asExpr() instanceof CallCfgNode } /** Gets the underlying call CFG node that includes this out node. */ override DataFlowCall getCall(ReturnKind kind) { - result.asCallBaseExprCfgNode() = this.getCfgNode() and + result.asCallCfgNode() = this.getCfgNode() and kind = TNormalReturnKind() } } @@ -404,7 +404,7 @@ final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode { } final class ReceiverPostUpdateNode extends PostUpdateNode, TReceiverNode { - private MethodCallExprCfgNode n; + private CallCfgNode n; ReceiverPostUpdateNode() { this = TReceiverNode(n, true) } @@ -467,11 +467,12 @@ newtype TNode = any(FieldExprCfgNode access).getContainer(), // any(TryExprCfgNode try).getExpr(), // any(PrefixExprCfgNode pe | pe.getOperatorName() = "*").getExpr(), // - any(AwaitExprCfgNode a).getExpr(), any(MethodCallExprCfgNode mc).getReceiver(), // + any(AwaitExprCfgNode a).getExpr(), // + any(MethodCallExprCfgNode mc).getReceiver(), // getPostUpdateReverseStep(any(PostUpdateNode n).getPreUpdateNode().asExpr(), _) ] } or - TReceiverNode(MethodCallExprCfgNode mc, Boolean isPost) or + TReceiverNode(CallCfgNode mc, Boolean isPost) { mc.getCall().receiverImplicitlyBorrowed() } or TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or TClosureSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c, _) } or diff --git a/rust/ql/lib/codeql/rust/elements/Call.qll b/rust/ql/lib/codeql/rust/elements/Call.qll new file mode 100644 index 000000000000..d09fe3350545 --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/Call.qll @@ -0,0 +1,7 @@ +/** + * Call. + */ + +private import internal.CallImpl + +final class Call = Impl::Call; diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallImpl.qll new file mode 100644 index 000000000000..dad65d2c824b --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/internal/CallImpl.qll @@ -0,0 +1,128 @@ +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 { + /** + * An expression that calls a function. + * + * This class abstract over the different ways in which a function can be called in Rust. + */ + abstract class Call extends ExprImpl::Expr { + Call() { this.fromSource() } + + /** Gets the number of arguments _excluding_ any `self` argument. */ + abstract int getNumberOfArguments(); + + /** Gets the receiver of this call if it is a method call. */ + abstract Expr getReceiver(); + + /** Holds if the call has a receiver that might be implicitly borrowed. */ + abstract predicate receiverImplicitlyBorrowed(); + + /** Gets the trait targeted by this call, if any. */ + abstract Trait getTrait(); + + /** Gets the name of the method called if this call is a method call. */ + abstract string getMethodName(); + + /** Gets the `i`th argument of this call, if any. */ + abstract Expr getArgument(int i); + + /** Gets the static target of this call, if any. */ + Function getStaticTarget() { + result = TypeInference::resolveMethodCallTarget(this) + or + not exists(TypeInference::resolveMethodCallTarget(this)) and + result = this.(CallExpr).getStaticTarget() + } + } + + /** Holds if the call expression dispatches to a trait method. */ + private predicate callIsMethodCall(CallExpr call, Path qualifier, string methodName) { + exists(Path path, Function f | + path = call.getFunction().(PathExpr).getPath() and + f = resolvePath(path) and + f.getParamList().hasSelfParam() and + qualifier = path.getQualifier() and + path.getSegment().getIdentifier().getText() = methodName + ) + } + + private class CallExprCall extends Call instanceof CallExpr { + CallExprCall() { not callIsMethodCall(this, _, _) } + + override string getMethodName() { none() } + + override Expr getReceiver() { none() } + + override Trait getTrait() { none() } + + override predicate receiverImplicitlyBorrowed() { none() } + + override int getNumberOfArguments() { result = super.getArgList().getNumberOfArgs() } + + override Expr getArgument(int i) { result = super.getArgList().getArg(i) } + } + + private class CallExprMethodCall extends Call instanceof CallExpr { + Path qualifier; + string methodName; + + CallExprMethodCall() { callIsMethodCall(this, qualifier, methodName) } + + override string getMethodName() { result = methodName } + + override Expr getReceiver() { result = super.getArgList().getArg(0) } + + override Trait getTrait() { + result = resolvePath(qualifier) 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. + qualifier.toString() != "Self" + } + + override predicate receiverImplicitlyBorrowed() { none() } + + override int getNumberOfArguments() { result = super.getArgList().getNumberOfArgs() - 1 } + + override Expr getArgument(int i) { result = super.getArgList().getArg(i + 1) } + } + + private class MethodCallExprCall extends Call instanceof MethodCallExpr { + override string getMethodName() { result = super.getIdentifier().getText() } + + override Expr getReceiver() { result = this.(MethodCallExpr).getReceiver() } + + override Trait getTrait() { none() } + + override predicate receiverImplicitlyBorrowed() { any() } + + override int getNumberOfArguments() { result = super.getArgList().getNumberOfArgs() } + + override Expr getArgument(int i) { result = super.getArgList().getArg(i) } + } + + private class OperatorCall extends Call instanceof Operation { + Trait trait; + string methodName; + + OperatorCall() { super.isOverloaded(trait, methodName) } + + override string getMethodName() { result = methodName } + + override Expr getReceiver() { result = super.getOperand(0) } + + override Trait getTrait() { result = trait } + + override predicate receiverImplicitlyBorrowed() { none() } + + override int getNumberOfArguments() { result = super.getNumberOfOperands() - 1 } + + override Expr getArgument(int i) { result = super.getOperand(1) and i = 0 } + } +} diff --git a/rust/ql/lib/codeql/rust/elements/internal/OperationImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/OperationImpl.qll index c1ba794e8e43..71f861b013dd 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/OperationImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/OperationImpl.qll @@ -8,75 +8,81 @@ private import rust private import codeql.rust.elements.internal.ExprImpl::Impl as ExprImpl /** - * Holds if the operator `op` is overloaded to a trait with the canonical path - * `path` and the method name `method`. + * Holds if the operator `op` with arity `arity` is overloaded to a trait with + * the canonical path `path` and the method name `method`. */ -private predicate isOverloaded(string op, string path, string method) { - // Negation - op = "-" and path = "core::ops::arith::Neg" and method = "neg" - or - // Not - op = "!" and path = "core::ops::bit::Not" and method = "not" - or - // Dereference - op = "*" and path = "core::ops::Deref" and method = "deref" - or - // Comparison operators - op = "==" and path = "core::cmp::PartialEq" and method = "eq" - or - op = "!=" and path = "core::cmp::PartialEq" and method = "ne" - or - op = "<" and path = "core::cmp::PartialOrd" and method = "lt" - or - op = "<=" and path = "core::cmp::PartialOrd" and method = "le" - or - op = ">" and path = "core::cmp::PartialOrd" and method = "gt" - or - op = ">=" and path = "core::cmp::PartialOrd" and method = "ge" - or - // Arithmetic operators - op = "+" and path = "core::ops::arith::Add" and method = "add" - or - op = "-" and path = "core::ops::arith::Sub" and method = "sub" - or - op = "*" and path = "core::ops::arith::Mul" and method = "mul" - or - op = "/" and path = "core::ops::arith::Div" and method = "div" - or - op = "%" and path = "core::ops::arith::Rem" and method = "rem" - or - // Arithmetic assignment expressions - op = "+=" and path = "core::ops::arith::AddAssign" and method = "add_assign" - or - op = "-=" and path = "core::ops::arith::SubAssign" and method = "sub_assign" - or - op = "*=" and path = "core::ops::arith::MulAssign" and method = "mul_assign" - or - op = "/=" and path = "core::ops::arith::DivAssign" and method = "div_assign" - or - op = "%=" and path = "core::ops::arith::RemAssign" and method = "rem_assign" - or - // Bitwise operators - op = "&" and path = "core::ops::bit::BitAnd" and method = "bitand" - or - op = "|" and path = "core::ops::bit::BitOr" and method = "bitor" - or - op = "^" and path = "core::ops::bit::BitXor" and method = "bitxor" - or - op = "<<" and path = "core::ops::bit::Shl" and method = "shl" - or - op = ">>" and path = "core::ops::bit::Shr" and method = "shr" - or - // Bitwise assignment operators - op = "&=" and path = "core::ops::bit::BitAndAssign" and method = "bitand_assign" - or - op = "|=" and path = "core::ops::bit::BitOrAssign" and method = "bitor_assign" - or - op = "^=" and path = "core::ops::bit::BitXorAssign" and method = "bitxor_assign" - or - op = "<<=" and path = "core::ops::bit::ShlAssign" and method = "shl_assign" - or - op = ">>=" and path = "core::ops::bit::ShrAssign" and method = "shr_assign" +private predicate isOverloaded(string op, int arity, string path, string method) { + arity = 1 and + ( + // Negation + op = "-" and path = "core::ops::arith::Neg" and method = "neg" + or + // Not + op = "!" and path = "core::ops::bit::Not" and method = "not" + or + // Dereference + op = "*" and path = "core::ops::deref::Deref" and method = "deref" + ) + or + arity = 2 and + ( + // Comparison operators + op = "==" and path = "core::cmp::PartialEq" and method = "eq" + or + op = "!=" and path = "core::cmp::PartialEq" and method = "ne" + or + op = "<" and path = "core::cmp::PartialOrd" and method = "lt" + or + op = "<=" and path = "core::cmp::PartialOrd" and method = "le" + or + op = ">" and path = "core::cmp::PartialOrd" and method = "gt" + or + op = ">=" and path = "core::cmp::PartialOrd" and method = "ge" + or + // Arithmetic operators + op = "+" and path = "core::ops::arith::Add" and method = "add" + or + op = "-" and path = "core::ops::arith::Sub" and method = "sub" + or + op = "*" and path = "core::ops::arith::Mul" and method = "mul" + or + op = "/" and path = "core::ops::arith::Div" and method = "div" + or + op = "%" and path = "core::ops::arith::Rem" and method = "rem" + or + // Arithmetic assignment expressions + op = "+=" and path = "core::ops::arith::AddAssign" and method = "add_assign" + or + op = "-=" and path = "core::ops::arith::SubAssign" and method = "sub_assign" + or + op = "*=" and path = "core::ops::arith::MulAssign" and method = "mul_assign" + or + op = "/=" and path = "core::ops::arith::DivAssign" and method = "div_assign" + or + op = "%=" and path = "core::ops::arith::RemAssign" and method = "rem_assign" + or + // Bitwise operators + op = "&" and path = "core::ops::bit::BitAnd" and method = "bitand" + or + op = "|" and path = "core::ops::bit::BitOr" and method = "bitor" + or + op = "^" and path = "core::ops::bit::BitXor" and method = "bitxor" + or + op = "<<" and path = "core::ops::bit::Shl" and method = "shl" + or + op = ">>" and path = "core::ops::bit::Shr" and method = "shr" + or + // Bitwise assignment operators + op = "&=" and path = "core::ops::bit::BitAndAssign" and method = "bitand_assign" + or + op = "|=" and path = "core::ops::bit::BitOrAssign" and method = "bitor_assign" + or + op = "^=" and path = "core::ops::bit::BitXorAssign" and method = "bitxor_assign" + or + op = "<<=" and path = "core::ops::bit::ShlAssign" and method = "shl_assign" + or + op = ">>=" and path = "core::ops::bit::ShrAssign" and method = "shr_assign" + ) } /** @@ -109,7 +115,8 @@ module Impl { * trait `trait`. */ predicate isOverloaded(Trait trait, string methodName) { - isOverloaded(this.getOperatorName(), trait.getCanonicalPath(), methodName) + isOverloaded(this.getOperatorName(), this.getNumberOfOperands(), trait.getCanonicalPath(), + methodName) } } } diff --git a/rust/ql/lib/codeql/rust/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/TypeInference.qll index 8399bde8aa80..408863be8e51 100644 --- a/rust/ql/lib/codeql/rust/internal/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/TypeInference.qll @@ -8,6 +8,7 @@ private import TypeMention private import codeql.typeinference.internal.TypeInference private import codeql.rust.frameworks.stdlib.Stdlib private import codeql.rust.frameworks.stdlib.Bultins as Builtins +private import codeql.rust.elements.Call class Type = T::Type; @@ -459,20 +460,17 @@ private Type inferPathExprType(PathExpr pe, TypePath path) { * like `foo::bar(baz)` and `foo.bar(baz)`. */ private module CallExprBaseMatchingInput implements MatchingInputSig { - private predicate paramPos(ParamList pl, Param p, int pos, boolean inMethod) { - p = pl.getParam(pos) and - if pl.hasSelfParam() then inMethod = true else inMethod = false - } + private predicate paramPos(ParamList pl, Param p, int pos) { p = pl.getParam(pos) } private newtype TDeclarationPosition = TSelfDeclarationPosition() or - TPositionalDeclarationPosition(int pos, boolean inMethod) { paramPos(_, _, pos, inMethod) } or + TPositionalDeclarationPosition(int pos) { paramPos(_, _, pos) } or TReturnDeclarationPosition() class DeclarationPosition extends TDeclarationPosition { predicate isSelf() { this = TSelfDeclarationPosition() } - int asPosition(boolean inMethod) { this = TPositionalDeclarationPosition(result, inMethod) } + int asPosition() { this = TPositionalDeclarationPosition(result) } predicate isReturn() { this = TReturnDeclarationPosition() } @@ -480,7 +478,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { this.isSelf() and result = "self" or - result = this.asPosition(_).toString() + result = this.asPosition().toString() or this.isReturn() and result = "(return)" @@ -513,7 +511,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { override Type getParameterType(DeclarationPosition dpos, TypePath path) { exists(int pos | result = this.getTupleField(pos).getTypeRepr().(TypeMention).resolveTypeAt(path) and - dpos = TPositionalDeclarationPosition(pos, false) + dpos = TPositionalDeclarationPosition(pos) ) } @@ -536,7 +534,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { override Type getParameterType(DeclarationPosition dpos, TypePath path) { exists(int p | result = this.getTupleField(p).getTypeRepr().(TypeMention).resolveTypeAt(path) and - dpos = TPositionalDeclarationPosition(p, false) + dpos = TPositionalDeclarationPosition(p) ) } @@ -566,9 +564,9 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { } override Type getParameterType(DeclarationPosition dpos, TypePath path) { - exists(Param p, int i, boolean inMethod | - paramPos(this.getParamList(), p, i, inMethod) and - dpos = TPositionalDeclarationPosition(i, inMethod) and + exists(Param p, int i | + paramPos(this.getParamList(), p, i) and + dpos = TPositionalDeclarationPosition(i) and result = inferAnnotatedType(p.getPat(), path) ) or @@ -587,108 +585,36 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { } } - private predicate argPos(CallExprBase call, Expr e, int pos, boolean isMethodCall) { - exists(ArgList al | - e = al.getArg(pos) and - call.getArgList() = al and - if call instanceof MethodCallExpr then isMethodCall = true else isMethodCall = false - ) - } - - private newtype TAccessPosition = - TSelfAccessPosition() or - TPositionalAccessPosition(int pos, boolean isMethodCall) { argPos(_, _, pos, isMethodCall) } or - TReturnAccessPosition() - - class AccessPosition extends TAccessPosition { - predicate isSelf() { this = TSelfAccessPosition() } - - int asPosition(boolean isMethodCall) { this = TPositionalAccessPosition(result, isMethodCall) } - - predicate isReturn() { this = TReturnAccessPosition() } - - string toString() { - this.isSelf() and - result = "self" - or - result = this.asPosition(_).toString() - or - this.isReturn() and - result = "(return)" - } - } + class AccessPosition = DeclarationPosition; private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl - abstract class Access extends Expr { - abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); - - abstract AstNode getNodeAt(AccessPosition apos); - - abstract Type getInferredType(AccessPosition apos, TypePath path); - - abstract Declaration getTarget(); - } - - private class CallExprBaseAccess extends Access instanceof CallExprBase { - private TypeMention getMethodTypeArg(int i) { - result = this.(MethodCallExpr).getGenericArgList().getTypeArg(i) - } - - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + final class Access extends Call { + Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { exists(TypeMention arg | result = arg.resolveTypeAt(path) | arg = getExplicitTypeArgMention(CallExprImpl::getFunctionPath(this), apos.asTypeParam()) or - arg = this.getMethodTypeArg(apos.asMethodTypeArgumentPosition()) + arg = + this.(MethodCallExpr).getGenericArgList().getTypeArg(apos.asMethodTypeArgumentPosition()) ) } - override AstNode getNodeAt(AccessPosition apos) { - exists(int p, boolean isMethodCall | - argPos(this, result, p, isMethodCall) and - apos = TPositionalAccessPosition(p, isMethodCall) - ) + AstNode getNodeAt(AccessPosition apos) { + result = this.getArgument(apos.asPosition()) or - result = this.(MethodCallExpr).getReceiver() and - apos = TSelfAccessPosition() + result = this.getReceiver() and apos.isSelf() or - result = this and - apos = TReturnAccessPosition() + result = this and apos.isReturn() } - override Type getInferredType(AccessPosition apos, TypePath path) { + Type getInferredType(AccessPosition apos, TypePath path) { result = inferType(this.getNodeAt(apos), path) } - override Declaration getTarget() { - result = CallExprImpl::getResolvedFunction(this) - or + Declaration getTarget() { result = inferMethodCallTarget(this) // mutual recursion; resolving method calls requires resolving types and vice versa - } - } - - private class OperationAccess extends Access instanceof Operation { - OperationAccess() { super.isOverloaded(_, _) } - - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - // The syntax for operators does not allow type arguments. - none() - } - - override AstNode getNodeAt(AccessPosition apos) { - result = super.getOperand(0) and apos = TSelfAccessPosition() - or - result = super.getOperand(1) and apos = TPositionalAccessPosition(0, true) or - result = this and apos = TReturnAccessPosition() - } - - override Type getInferredType(AccessPosition apos, TypePath path) { - result = inferType(this.getNodeAt(apos), path) - } - - override Declaration getTarget() { - result = inferMethodCallTarget(this) // mutual recursion; resolving method calls requires resolving types and vice versa + result = CallExprImpl::getResolvedFunction(this) } } @@ -696,16 +622,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { apos.isSelf() and dpos.isSelf() or - exists(int pos, boolean isMethodCall | pos = apos.asPosition(isMethodCall) | - pos = 0 and - isMethodCall = false and - dpos.isSelf() - or - isMethodCall = false and - pos = dpos.asPosition(true) + 1 - or - pos = dpos.asPosition(isMethodCall) - ) + apos.asPosition() = dpos.asPosition() or apos.isReturn() and dpos.isReturn() @@ -1010,31 +927,18 @@ private StructType inferLiteralType(LiteralExpr le) { ) } -private module MethodCall { - /** An expression that calls a method. */ - abstract private class MethodCallImpl extends Expr { - /** Gets the name of the method targeted. */ - abstract string getMethodName(); - - /** Gets the number of arguments _excluding_ the `self` argument. */ - abstract int getArity(); - - /** Gets the trait targeted by this method call, if any. */ - Trait getTrait() { none() } - - /** Gets the type of the receiver of the method call at `path`. */ - abstract Type getTypeAt(TypePath path); +final class MethodCall extends Call { + MethodCall() { + exists(this.getReceiver()) and + // We want the method calls that don't have a path to a concrete method in + // an impl block. We need to exclude calls like `MyType::my_method(..)`. + (this instanceof CallExpr implies exists(this.getTrait())) } - final class MethodCall = MethodCallImpl; - - private class MethodCallExprMethodCall extends MethodCallImpl instanceof MethodCallExpr { - override string getMethodName() { result = super.getIdentifier().getText() } - - override int getArity() { result = super.getArgList().getNumberOfArgs() } - - pragma[nomagic] - override Type getTypeAt(TypePath path) { + /** Gets the type of the receiver of the method call at `path`. */ + Type getTypeAt(TypePath path) { + if this.receiverImplicitlyBorrowed() + then exists(TypePath path0 | result = inferType(super.getReceiver(), path0) | path0.isCons(TRefTypeParameter(), path) or @@ -1042,59 +946,10 @@ private module MethodCall { not (path0.isEmpty() and result = TRefType()) and path = path0 ) - } - } - - private class CallExprMethodCall extends MethodCallImpl instanceof CallExpr { - TraitItemNode trait; - string methodName; - Expr receiver; - - CallExprMethodCall() { - receiver = this.getArgList().getArg(0) and - exists(Path path, Function f | - path = this.getFunction().(PathExpr).getPath() and - f = resolvePath(path) and - f.getParamList().hasSelfParam() and - trait = resolvePath(path.getQualifier()) and - trait.getAnAssocItem() = f and - path.getSegment().getIdentifier().getText() = methodName - ) - } - - override string getMethodName() { result = methodName } - - override int getArity() { result = super.getArgList().getNumberOfArgs() - 1 } - - override Trait getTrait() { result = trait } - - pragma[nomagic] - override Type getTypeAt(TypePath path) { result = inferType(receiver, path) } - } - - private class OperationMethodCall extends MethodCallImpl instanceof Operation { - TraitItemNode trait; - string methodName; - - OperationMethodCall() { super.isOverloaded(trait, methodName) } - - override string getMethodName() { result = methodName } - - override int getArity() { result = this.(Operation).getNumberOfOperands() - 1 } - - override Trait getTrait() { result = trait } - - pragma[nomagic] - override Type getTypeAt(TypePath path) { - result = inferType(this.(BinaryExpr).getLhs(), path) - or - result = inferType(this.(PrefixExpr).getExpr(), path) - } + else result = inferType(super.getReceiver(), path) } } -import MethodCall - /** * Holds if a method for `type` with the name `name` and the arity `arity` * exists in `impl`. @@ -1124,7 +979,7 @@ private module IsInstantiationOfInput implements IsInstantiationOfInputSig for MyInt { + fn mul_assign(&mut self, rhs: MyInt) { + (*self).value = rhs.value; // todo: implicit deref not yet supported + } +} + +impl Deref for MyInt { + type Target = i64; + + fn deref(&self) -> &Self::Target { + &(*self).value + } +} + fn test_operator_overloading() { + // Tests for simple binary operator. let a = MyInt { value: source(5) }; let b = MyInt { value: 2 }; let c = a + b; - sink(c.value); // $ MISSING: hasValueFlow=5 + sink(c.value); // $ hasValueFlow=5 let a = MyInt { value: 2 }; let b = MyInt { value: source(6) }; @@ -187,6 +206,28 @@ fn test_operator_overloading() { let b = MyInt { value: 2 }; let d = a.add(b); sink(d.value); // $ hasValueFlow=7 + + // Tests for assignment operator. + let mut a = MyInt { value: 0 }; + let b = MyInt { value: source(34) }; + // The line below is what `*=` desugars to. + MulAssign::mul_assign(&mut a, b); + sink(a.value); // $ MISSING: hasValueFlow=34 + + let mut a = MyInt { value: 0 }; + let b = MyInt { value: source(35) }; + a *= b; + sink(a.value); // $ MISSING: hasValueFlow=35 + + // Tests for deref operator. + let a = MyInt { value: source(27) }; + // The line below is what the prefix `*` desugars to. + let c = *Deref::deref(&a); + sink(c); // $ MISSING: hasValueFlow=27 + + let a = MyInt { value: source(28) }; + let c = *a; + sink(c); // $ MISSING: hasValueFlow=28 } trait MyTrait { diff --git a/rust/ql/test/library-tests/dataflow/global/viableCallable.expected b/rust/ql/test/library-tests/dataflow/global/viableCallable.expected index 63abc4c7f417..822ce4e0a323 100644 --- a/rust/ql/test/library-tests/dataflow/global/viableCallable.expected +++ b/rust/ql/test/library-tests/dataflow/global/viableCallable.expected @@ -7,75 +7,87 @@ | main.rs:38:23:38:31 | source(...) | main.rs:1:1:3:1 | fn source | | main.rs:39:5:39:22 | sink(...) | main.rs:5:1:7:1 | fn sink | | main.rs:39:10:39:21 | a.get_data() | main.rs:30:5:32:5 | fn get_data | -| main.rs:44:5:44:39 | ... .set_data(...) | main.rs:26:5:28:5 | fn set_data | -| main.rs:44:30:44:38 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:45:5:45:22 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:45:10:45:21 | a.get_data() | main.rs:30:5:32:5 | fn get_data | -| main.rs:49:5:49:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:53:13:53:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:54:5:54:14 | data_in(...) | main.rs:48:1:50:1 | fn data_in | -| main.rs:62:13:62:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:63:13:63:27 | pass_through(...) | main.rs:57:1:59:1 | fn pass_through | -| main.rs:64:5:64:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:68:13:71:6 | pass_through(...) | main.rs:57:1:59:1 | fn pass_through | -| main.rs:70:9:70:18 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:72:5:72:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:76:13:76:22 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:82:13:82:27 | pass_through(...) | main.rs:78:5:80:5 | fn pass_through | -| main.rs:83:5:83:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:95:9:95:15 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:102:13:102:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:117:13:117:25 | mn.get_data() | main.rs:98:5:104:5 | fn get_data | -| main.rs:118:5:118:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:123:13:123:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:124:5:124:17 | mn.data_in(...) | main.rs:94:5:96:5 | fn data_in | -| main.rs:129:13:129:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:130:13:130:30 | mn.data_through(...) | main.rs:106:5:112:5 | fn data_through | -| main.rs:131:5:131:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:136:13:136:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:137:5:137:26 | ...::data_in(...) | main.rs:94:5:96:5 | fn data_in | -| main.rs:142:13:142:22 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:143:13:143:39 | ...::data_through(...) | main.rs:106:5:112:5 | fn data_through | -| main.rs:144:5:144:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:161:13:161:34 | ...::new(...) | main.rs:154:5:157:5 | fn new | -| main.rs:161:24:161:33 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:163:5:163:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:176:28:176:36 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:179:5:179:17 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:182:28:182:36 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:184:5:184:17 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:186:28:186:36 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:188:13:188:20 | a.add(...) | main.rs:169:5:172:5 | fn add | -| main.rs:189:5:189:17 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:211:28:211:36 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:213:30:213:53 | ...::take_self(...) | main.rs:201:5:203:5 | fn take_self | -| main.rs:214:5:214:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:217:28:217:37 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:218:30:218:55 | ...::take_second(...) | main.rs:205:5:207:5 | fn take_second | -| main.rs:219:5:219:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:222:28:222:37 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:223:30:223:53 | ...::take_self(...) | main.rs:201:5:203:5 | fn take_self | -| main.rs:224:5:224:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:228:13:228:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:229:5:229:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:234:13:234:26 | async_source(...) | main.rs:227:1:231:1 | fn async_source | -| main.rs:235:5:235:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:238:17:238:25 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:239:9:239:15 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:242:5:242:17 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:246:13:246:55 | ...::block_on(...) | file://:0:0:0:0 | repo:https://github.com/rust-lang/futures-rs:futures-executor::_::crate::local_pool::block_on | -| main.rs:246:41:246:54 | async_source(...) | main.rs:227:1:231:1 | fn async_source | -| main.rs:247:5:247:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:249:5:249:62 | ...::block_on(...) | file://:0:0:0:0 | repo:https://github.com/rust-lang/futures-rs:futures-executor::_::crate::local_pool::block_on | -| main.rs:249:33:249:61 | test_async_await_async_part(...) | main.rs:233:1:243:1 | fn test_async_await_async_part | -| main.rs:253:5:253:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call | -| main.rs:254:5:254:35 | data_out_of_call_side_effect1(...) | main.rs:35:1:40:1 | fn data_out_of_call_side_effect1 | -| main.rs:255:5:255:35 | data_out_of_call_side_effect2(...) | main.rs:42:1:46:1 | fn data_out_of_call_side_effect2 | -| main.rs:256:5:256:21 | data_in_to_call(...) | main.rs:52:1:55:1 | fn data_in_to_call | -| main.rs:257:5:257:23 | data_through_call(...) | main.rs:61:1:65:1 | fn data_through_call | -| main.rs:258:5:258:34 | data_through_nested_function(...) | main.rs:75:1:84:1 | fn data_through_nested_function | -| main.rs:260:5:260:24 | data_out_of_method(...) | main.rs:115:1:119:1 | fn data_out_of_method | -| main.rs:261:5:261:28 | data_in_to_method_call(...) | main.rs:121:1:125:1 | fn data_in_to_method_call | -| main.rs:262:5:262:25 | data_through_method(...) | main.rs:127:1:132:1 | fn data_through_method | -| main.rs:264:5:264:31 | test_operator_overloading(...) | main.rs:175:1:190:1 | fn test_operator_overloading | -| main.rs:265:5:265:22 | test_async_await(...) | main.rs:245:1:250:1 | fn test_async_await | +| main.rs:44:5:48:24 | ... .set_data(...) | main.rs:26:5:28:5 | fn set_data | +| main.rs:48:15:48:23 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:49:5:49:22 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:49:10:49:21 | a.get_data() | main.rs:30:5:32:5 | fn get_data | +| main.rs:53:5:53:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:57:13:57:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:58:5:58:14 | data_in(...) | main.rs:52:1:54:1 | fn data_in | +| main.rs:66:13:66:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:67:13:67:27 | pass_through(...) | main.rs:61:1:63:1 | fn pass_through | +| main.rs:68:5:68:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:72:13:75:6 | pass_through(...) | main.rs:61:1:63:1 | fn pass_through | +| main.rs:74:9:74:18 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:76:5:76:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:80:13:80:22 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:86:13:86:27 | pass_through(...) | main.rs:82:5:84:5 | fn pass_through | +| main.rs:87:5:87:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:99:9:99:15 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:106:13:106:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:121:13:121:25 | mn.get_data() | main.rs:102:5:108:5 | fn get_data | +| main.rs:122:5:122:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:127:13:127:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:128:5:128:17 | mn.data_in(...) | main.rs:98:5:100:5 | fn data_in | +| main.rs:133:13:133:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:134:13:134:30 | mn.data_through(...) | main.rs:110:5:116:5 | fn data_through | +| main.rs:135:5:135:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:140:13:140:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:141:5:141:26 | ...::data_in(...) | main.rs:98:5:100:5 | fn data_in | +| main.rs:146:13:146:22 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:147:13:147:39 | ...::data_through(...) | main.rs:110:5:116:5 | fn data_through | +| main.rs:148:5:148:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:165:13:165:34 | ...::new(...) | main.rs:158:5:161:5 | fn new | +| main.rs:165:24:165:33 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:167:5:167:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:195:28:195:36 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:197:13:197:17 | ... + ... | main.rs:173:5:176:5 | fn add | +| main.rs:198:5:198:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:201:28:201:36 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:202:13:202:17 | ... + ... | main.rs:173:5:176:5 | fn add | +| main.rs:203:5:203:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:205:28:205:36 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:207:13:207:20 | a.add(...) | main.rs:173:5:176:5 | fn add | +| main.rs:208:5:208:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:212:28:212:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:215:5:215:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:218:28:218:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:219:5:219:10 | ... *= ... | main.rs:180:5:182:5 | fn mul_assign | +| main.rs:220:5:220:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:223:28:223:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:226:5:226:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:228:28:228:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:229:13:229:14 | * ... | main.rs:188:5:190:5 | fn deref | +| main.rs:230:5:230:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:252:28:252:36 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:254:30:254:53 | ...::take_self(...) | main.rs:242:5:244:5 | fn take_self | +| main.rs:255:5:255:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:258:28:258:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:259:30:259:55 | ...::take_second(...) | main.rs:246:5:248:5 | fn take_second | +| main.rs:260:5:260:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:263:28:263:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:264:30:264:53 | ...::take_self(...) | main.rs:242:5:244:5 | fn take_self | +| main.rs:265:5:265:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:269:13:269:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:270:5:270:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:275:13:275:26 | async_source(...) | main.rs:268:1:272:1 | fn async_source | +| main.rs:276:5:276:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:279:17:279:25 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:280:9:280:15 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:283:5:283:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:287:13:287:55 | ...::block_on(...) | file://:0:0:0:0 | repo:https://github.com/rust-lang/futures-rs:futures-executor::_::crate::local_pool::block_on | +| main.rs:287:41:287:54 | async_source(...) | main.rs:268:1:272:1 | fn async_source | +| main.rs:288:5:288:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:290:5:290:62 | ...::block_on(...) | file://:0:0:0:0 | repo:https://github.com/rust-lang/futures-rs:futures-executor::_::crate::local_pool::block_on | +| main.rs:290:33:290:61 | test_async_await_async_part(...) | main.rs:274:1:284:1 | fn test_async_await_async_part | +| main.rs:294:5:294:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call | +| main.rs:295:5:295:35 | data_out_of_call_side_effect1(...) | main.rs:35:1:40:1 | fn data_out_of_call_side_effect1 | +| main.rs:296:5:296:35 | data_out_of_call_side_effect2(...) | main.rs:42:1:50:1 | fn data_out_of_call_side_effect2 | +| main.rs:297:5:297:21 | data_in_to_call(...) | main.rs:56:1:59:1 | fn data_in_to_call | +| main.rs:298:5:298:23 | data_through_call(...) | main.rs:65:1:69:1 | fn data_through_call | +| main.rs:299:5:299:34 | data_through_nested_function(...) | main.rs:79:1:88:1 | fn data_through_nested_function | +| main.rs:301:5:301:24 | data_out_of_method(...) | main.rs:119:1:123:1 | fn data_out_of_method | +| main.rs:302:5:302:28 | data_in_to_method_call(...) | main.rs:125:1:129:1 | fn data_in_to_method_call | +| main.rs:303:5:303:25 | data_through_method(...) | main.rs:131:1:136:1 | fn data_through_method | +| main.rs:305:5:305:31 | test_operator_overloading(...) | main.rs:193:1:231:1 | fn test_operator_overloading | +| main.rs:306:5:306:22 | test_async_await(...) | main.rs:286:1:291:1 | fn test_async_await | diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs index 36d3f5a82ea8..becd76b5133a 100644 --- a/rust/ql/test/library-tests/type-inference/main.rs +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -1098,9 +1098,9 @@ mod method_call_type_conversion { println!("{:?}", x5.m1()); // $ method=m1 println!("{:?}", x5.0); // $ fieldof=S - let x6 = &S(S2); + let x6 = &S(S2); // $ SPURIOUS: type=x6:&T.&T.S // explicit dereference - println!("{:?}", (*x6).m1()); // $ method=m1 + println!("{:?}", (*x6).m1()); // $ method=m1 method=deref let x7 = S(&S2); // Non-implicit dereference with nested borrow in order to test that the diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index ff33ad89cb82..ead1c49948bd 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -1352,22 +1352,43 @@ inferType | main.rs:1099:26:1099:27 | x5 | &T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1099:26:1099:29 | x5.0 | | main.rs:1060:5:1061:14 | S2 | | main.rs:1101:13:1101:14 | x6 | | file://:0:0:0:0 | & | +| main.rs:1101:13:1101:14 | x6 | &T | file://:0:0:0:0 | & | | main.rs:1101:13:1101:14 | x6 | &T | main.rs:1057:5:1058:19 | S | +| main.rs:1101:13:1101:14 | x6 | &T.&T | main.rs:1057:5:1058:19 | S | +| main.rs:1101:13:1101:14 | x6 | &T.&T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1101:13:1101:14 | x6 | &T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1101:18:1101:23 | &... | | file://:0:0:0:0 | & | +| main.rs:1101:18:1101:23 | &... | &T | file://:0:0:0:0 | & | | main.rs:1101:18:1101:23 | &... | &T | main.rs:1057:5:1058:19 | S | +| main.rs:1101:18:1101:23 | &... | &T.&T | main.rs:1057:5:1058:19 | S | +| main.rs:1101:18:1101:23 | &... | &T.&T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1101:18:1101:23 | &... | &T.T | main.rs:1060:5:1061:14 | S2 | +| main.rs:1101:19:1101:23 | S(...) | | file://:0:0:0:0 | & | | main.rs:1101:19:1101:23 | S(...) | | main.rs:1057:5:1058:19 | S | +| main.rs:1101:19:1101:23 | S(...) | &T | file://:0:0:0:0 | & | +| main.rs:1101:19:1101:23 | S(...) | &T | main.rs:1057:5:1058:19 | S | +| main.rs:1101:19:1101:23 | S(...) | &T.&T | main.rs:1057:5:1058:19 | S | +| main.rs:1101:19:1101:23 | S(...) | &T.&T.T | main.rs:1060:5:1061:14 | S2 | +| main.rs:1101:19:1101:23 | S(...) | &T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1101:19:1101:23 | S(...) | T | main.rs:1060:5:1061:14 | S2 | | main.rs:1101:21:1101:22 | S2 | | main.rs:1060:5:1061:14 | S2 | | main.rs:1103:18:1103:23 | "{:?}\\n" | | {EXTERNAL LOCATION} | str | +| main.rs:1103:26:1103:30 | (...) | | file://:0:0:0:0 | & | | main.rs:1103:26:1103:30 | (...) | | main.rs:1057:5:1058:19 | S | +| main.rs:1103:26:1103:30 | (...) | &T | main.rs:1057:5:1058:19 | S | +| main.rs:1103:26:1103:30 | (...) | &T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1103:26:1103:30 | (...) | T | main.rs:1060:5:1061:14 | S2 | | main.rs:1103:26:1103:35 | ... .m1() | | main.rs:1060:5:1061:14 | S2 | +| main.rs:1103:27:1103:29 | * ... | | file://:0:0:0:0 | & | | main.rs:1103:27:1103:29 | * ... | | main.rs:1057:5:1058:19 | S | +| main.rs:1103:27:1103:29 | * ... | &T | main.rs:1057:5:1058:19 | S | +| main.rs:1103:27:1103:29 | * ... | &T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1103:27:1103:29 | * ... | T | main.rs:1060:5:1061:14 | S2 | | main.rs:1103:28:1103:29 | x6 | | file://:0:0:0:0 | & | +| main.rs:1103:28:1103:29 | x6 | &T | file://:0:0:0:0 | & | | main.rs:1103:28:1103:29 | x6 | &T | main.rs:1057:5:1058:19 | S | +| main.rs:1103:28:1103:29 | x6 | &T.&T | main.rs:1057:5:1058:19 | S | +| main.rs:1103:28:1103:29 | x6 | &T.&T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1103:28:1103:29 | x6 | &T.T | main.rs:1060:5:1061:14 | S2 | | main.rs:1105:13:1105:14 | x7 | | main.rs:1057:5:1058:19 | S | | main.rs:1105:13:1105:14 | x7 | T | file://:0:0:0:0 | & |