From 395901b8e01bba19e8950af3f88bb6e371651d80 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 27 Nov 2024 14:20:14 +0100 Subject: [PATCH 1/4] Rust: Adopt shared flow summaries library --- rust/ql/lib/codeql/rust/dataflow/DataFlow.qll | 9 +- .../lib/codeql/rust/dataflow/FlowSummary.qll | 58 ++++ .../rust/dataflow/internal/DataFlowImpl.qll | 300 +++++++++++++++--- .../dataflow/internal/FlowSummaryImpl.qll | 108 +++++++ .../library-tests/dataflow/models/main.rs | 89 ++++++ .../dataflow/models/models.expected | 57 ++++ .../library-tests/dataflow/models/models.ql | 78 +++++ 7 files changed, 658 insertions(+), 41 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll create mode 100644 rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll create mode 100644 rust/ql/test/library-tests/dataflow/models/main.rs create mode 100644 rust/ql/test/library-tests/dataflow/models/models.expected create mode 100644 rust/ql/test/library-tests/dataflow/models/models.ql diff --git a/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll b/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll index c0d47f8f6a77..310c6eceee1d 100644 --- a/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll +++ b/rust/ql/lib/codeql/rust/dataflow/DataFlow.qll @@ -15,7 +15,14 @@ private import DataFlowImpl::Node as Node module DataFlow { final class Node = Node::Node; - final class ParameterNode = Node::ParameterNode; + /** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ + final class ParameterNode extends Node instanceof Node::SourceParameterNode { + /** Gets the parameter that this node corresponds to. */ + ParamBase getParameter() { result = super.getParameter().getParamBase() } + } final class PostUpdateNode = Node::PostUpdateNode; diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll new file mode 100644 index 000000000000..13206803f028 --- /dev/null +++ b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll @@ -0,0 +1,58 @@ +/** Provides classes and predicates for defining flow summaries. */ + +private import rust +private import internal.FlowSummaryImpl as Impl +private import internal.DataFlowImpl + +// import all instances below +private module Summaries { + private import codeql.rust.Frameworks +} + +/** Provides the `Range` class used to define the extent of `LibraryCallable`. */ +module LibraryCallable { + /** A callable defined in library code, identified by a unique string. */ + abstract class Range extends string { + bindingset[this] + Range() { any() } + + /** Gets a call to this library callable. */ + CallExprBase getACall() { + exists(Resolvable r, string crate | + r = getCallResolvable(result) and + this = crate + r.getResolvedPath() + | + crate = r.getResolvedCrateOrigin() + "::_::" + or + not r.hasResolvedCrateOrigin() and + crate = "" + ) + } + } +} + +final class LibraryCallable = LibraryCallable::Range; + +/** Provides the `Range` class used to define the extent of `SummarizedCallable`. */ +module SummarizedCallable { + /** A callable with a flow summary, identified by a unique string. */ + abstract class Range extends LibraryCallable::Range, Impl::Public::SummarizedCallable { + bindingset[this] + Range() { any() } + + override predicate propagatesFlow( + string input, string output, boolean preservesValue, string model + ) { + this.propagatesFlow(input, output, preservesValue) and model = "" + } + + /** + * Holds if data may flow from `input` to `output` through this callable. + * + * `preservesValue` indicates whether this is a value-preserving step or a taint-step. + */ + abstract predicate propagatesFlow(string input, string output, boolean preservesValue); + } +} + +final class SummarizedCallable = SummarizedCallable::Range; diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index cedf9622ecff..2f49de555165 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -11,8 +11,8 @@ private import SsaImpl as SsaImpl private import codeql.rust.controlflow.ControlFlowGraph private import codeql.rust.controlflow.CfgNodes private import codeql.rust.dataflow.Ssa - -private newtype TReturnKind = TNormalReturnKind() +private import codeql.rust.dataflow.FlowSummary +private import FlowSummaryImpl as FlowSummaryImpl /** * A return kind. A return kind describes how a value can be returned from a @@ -35,8 +35,13 @@ final class DataFlowCallable extends TDataFlowCallable { */ CfgScope asCfgScope() { this = TCfgScope(result) } + /** + * Gets the underlying library callable, if any. + */ + LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) } + /** Gets a textual representation of this callable. */ - string toString() { result = this.asCfgScope().toString() } + string toString() { result = [this.asCfgScope().toString(), this.asLibraryCallable().toString()] } /** Gets the location of this callable. */ Location getLocation() { result = this.asCfgScope().getLocation() } @@ -54,11 +59,31 @@ final class DataFlowCall extends TDataFlowCall { CallExprBaseCfgNode asCallBaseExprCfgNode() { result = call } + predicate isSummaryCall( + FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver + ) { + this = TSummaryCall(c, receiver) + } + DataFlowCallable getEnclosingCallable() { result = TCfgScope(call.getExpr().getEnclosingCfgScope()) + or + exists(FlowSummaryImpl::Public::SummarizedCallable c | + this.isSummaryCall(c, _) and + result = TLibraryCallable(c) + ) } - string toString() { result = this.asCallBaseExprCfgNode().toString() } + string toString() { + result = this.asCallBaseExprCfgNode().toString() + or + exists( + FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver + | + this.isSummaryCall(c, receiver) and + result = "[summary] call to " + receiver + " in " + c + ) + } Location getLocation() { result = this.asCallBaseExprCfgNode().getLocation() } } @@ -148,6 +173,26 @@ module Node { override Location getLocation() { none() } } + /** A data-flow node used to model flow summaries. */ + class FlowSummaryNode extends Node, TFlowSummaryNode { + FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) } + + /** Gets the summarized callable that this node belongs to. */ + FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() { + result = this.getSummaryNode().getSummarizedCallable() + } + + override CfgScope getCfgScope() { none() } + + override DataFlowCallable getEnclosingCallable() { + result.asLibraryCallable() = this.getSummarizedCallable() + } + + override EmptyLocation getLocation() { any() } + + override string toString() { result = this.getSummaryNode().toString() } + } + /** A data flow node that corresponds directly to a CFG node for an AST node. */ abstract class AstCfgFlowNode extends Node { AstCfgNode n; @@ -189,17 +234,62 @@ module Node { * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ - final class ParameterNode extends AstCfgFlowNode, TParameterNode { + abstract class ParameterNode extends Node { + abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos); + } + + final class SourceParameterNode extends AstCfgFlowNode, ParameterNode, TSourceParameterNode { override ParamBaseCfgNode n; - ParameterNode() { this = TParameterNode(n) } + SourceParameterNode() { this = TSourceParameterNode(n) } + + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + n.getAstNode() = pos.getParameterIn(c.asCfgScope().(Callable).getParamList()) + } /** Gets the parameter in the CFG that this node corresponds to. */ ParamBaseCfgNode getParameter() { result = n } } - final class ArgumentNode extends ExprNode { - ArgumentNode() { isArgumentForCall(n, _, _) } + /** A parameter for a library callable with a flow summary. */ + final class SummaryParameterNode extends ParameterNode, FlowSummaryNode { + private ParameterPosition pos_; + + SummaryParameterNode() { + FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), pos_) + } + + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.getSummarizedCallable() = c.asLibraryCallable() and pos = pos_ + } + } + + abstract class ArgumentNode extends Node { + abstract predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos); + } + + final class ExprArgumentNode extends ArgumentNode, ExprNode { + private CallExprBaseCfgNode call_; + private RustDataFlow::ArgumentPosition pos_; + + ExprArgumentNode() { isArgumentForCall(n, call_, pos_) } + + override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) { + call.asCallBaseExprCfgNode() = call_ and pos = pos_ + } + } + + final class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode { + private FlowSummaryImpl::Private::SummaryNode receiver; + private RustDataFlow::ArgumentPosition pos_; + + SummaryArgumentNode() { + FlowSummaryImpl::Private::summaryArgumentNode(receiver, this.getSummaryNode(), pos_) + } + + override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) { + call.isSummaryCall(_, receiver) and pos = pos_ + } } /** An SSA node. */ @@ -222,23 +312,52 @@ module Node { } /** A data flow node that represents a value returned by a callable. */ - final class ReturnNode extends ExprNode { - ReturnNode() { this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode } + abstract class ReturnNode extends Node { + abstract ReturnKind getKind(); + } + + final class ExprReturnNode extends ExprNode, ReturnNode { + ExprReturnNode() { this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode } - ReturnKind getKind() { any() } + override ReturnKind getKind() { result = TNormalReturnKind() } + } + + final class SummaryReturnNode extends FlowSummaryNode, ReturnNode { + private ReturnKind rk; + + SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) } + + override ReturnKind getKind() { result = rk } } /** A data-flow node that represents the output of a call. */ - abstract class OutNode extends Node, ExprNode { + abstract class OutNode extends Node { /** Gets the underlying call for this node. */ - abstract DataFlowCall getCall(); + abstract DataFlowCall getCall(ReturnKind kind); } final private class ExprOutNode extends ExprNode, OutNode { ExprOutNode() { this.asExpr() instanceof CallExprBaseCfgNode } /** Gets the underlying call CFG node that includes this out node. */ - override DataFlowCall getCall() { result.asCallBaseExprCfgNode() = this.getCfgNode() } + override DataFlowCall getCall(ReturnKind kind) { + result.asCallBaseExprCfgNode() = this.getCfgNode() and + kind = TNormalReturnKind() + } + } + + final class SummaryOutNode extends FlowSummaryNode, OutNode { + private DataFlowCall call; + private ReturnKind kind_; + + SummaryOutNode() { + exists(FlowSummaryImpl::Private::SummaryNode receiver | + call.isSummaryCall(_, receiver) and + FlowSummaryImpl::Private::summaryOutNode(receiver, this.getSummaryNode(), kind_) + ) + } + + override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ } } /** @@ -252,19 +371,39 @@ module Node { * Nodes corresponding to AST elements, for example `ExprNode`, usually refer * to the value before the update. */ - final class PostUpdateNode extends Node, TExprPostUpdateNode { + abstract class PostUpdateNode extends Node { + /** Gets the node before the state update. */ + abstract Node getPreUpdateNode(); + + override CfgScope getCfgScope() { result = this.getPreUpdateNode().getCfgScope() } + + override Location getLocation() { result = this.getPreUpdateNode().getLocation() } + + override string toString() { result = "[post] " + this.getPreUpdateNode().toString() } + } + + final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode { private ExprCfgNode n; - PostUpdateNode() { this = TExprPostUpdateNode(n) } + ExprPostUpdateNode() { this = TExprPostUpdateNode(n) } - /** Gets the node before the state update. */ - Node getPreUpdateNode() { result = TExprNode(n) } + override Node getPreUpdateNode() { result = TExprNode(n) } + } - final override CfgScope getCfgScope() { result = n.getScope() } + final class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode { + private FlowSummaryNode pre; - final override Location getLocation() { result = n.getLocation() } + SummaryPostUpdateNode() { + FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), pre.getSummaryNode()) + } + + override Node getPreUpdateNode() { result = pre } + + override CfgScope getCfgScope() { result = PostUpdateNode.super.getCfgScope() } - final override string toString() { result = "[post] " + n.toString() } + override EmptyLocation getLocation() { result = PostUpdateNode.super.getLocation() } + + final override string toString() { result = PostUpdateNode.super.toString() } } final class CastNode = NaNode; @@ -277,7 +416,7 @@ module SsaFlow { private module SsaFlow = SsaImpl::DataFlowIntegration; private Node::ParameterNode toParameterNode(ParamCfgNode p) { - result.(Node::ParameterNode).getParameter() = p + result.(Node::SourceParameterNode).getParameter() = p } /** Converts a control flow node into an SSA control flow node. */ @@ -316,6 +455,15 @@ private ExprCfgNode getALastEvalNode(ExprCfgNode e) { } module LocalFlow { + predicate flowSummaryLocalStep( + Node::FlowSummaryNode nodeFrom, Node::FlowSummaryNode nodeTo, + FlowSummaryImpl::Public::SummarizedCallable c, string model + ) { + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.getSummaryNode(), + nodeTo.getSummaryNode(), true, model) and + c = nodeFrom.getSummarizedCallable() + } + pragma[nomagic] predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { nodeFrom.getCfgNode() = getALastEvalNode(nodeTo.getCfgNode()) @@ -329,9 +477,7 @@ module LocalFlow { nodeFrom.(Node::AstCfgFlowNode).getCfgNode() = nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode() or - nodeFrom.(Node::ParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat() - or - SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _) + nodeFrom.(Node::SourceParameterNode).getParameter().(ParamCfgNode).getPat() = nodeTo.asPat() or exists(AssignmentExprCfgNode a | a.getRhs() = nodeFrom.getCfgNode() and @@ -397,7 +543,7 @@ abstract class Content extends TContent { } /** A canonical path pointing to an enum variant. */ -private class VariantCanonicalPath extends MkVariantCanonicalPath { +class VariantCanonicalPath extends MkVariantCanonicalPath { CrateOriginOption crate; string path; string name; @@ -407,6 +553,8 @@ private class VariantCanonicalPath extends MkVariantCanonicalPath { /** Gets the underlying variant. */ Variant getVariant() { variantHasExtendedCanonicalPath(_, result, crate, path, name) } + string getExtendedCanonicalPath() { result = path + "::" + name } + string toString() { result = name } Location getLocation() { result = this.getVariant().getLocation() } @@ -541,6 +689,13 @@ private module Aliases { class ContentSetAlias = ContentSet; } +pragma[nomagic] +Resolvable getCallResolvable(CallExprBase call) { + result = call.(MethodCallExpr) + or + result = call.(CallExpr).getFunction().(PathExpr).getPath() +} + module RustDataFlow implements InputSig { private import Aliases @@ -564,19 +719,23 @@ module RustDataFlow implements InputSig { /** Holds if `p` is a parameter of `c` at the position `pos`. */ predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { - p.getCfgNode().getAstNode() = pos.getParameterIn(c.asCfgScope().(Callable).getParamList()) + p.isParameterOf(c, pos) } /** Holds if `n` is an argument of `c` at the position `pos`. */ predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) { - isArgumentForCall(n.getCfgNode(), call.asCallBaseExprCfgNode(), pos) + n.isArgumentOf(call, pos) } DataFlowCallable nodeGetEnclosingCallable(Node node) { result = node.getEnclosingCallable() } DataFlowType getNodeType(Node node) { any() } - predicate nodeIsHidden(Node node) { node instanceof Node::SsaNode } + predicate nodeIsHidden(Node node) { + node instanceof Node::SsaNode + or + node instanceof Node::FlowSummaryNode + } class DataFlowExpr = ExprCfgNode; @@ -592,15 +751,15 @@ 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() + or + result.asLibraryCallable().getACall() = call.asCallBaseExprCfgNode().getCallExprBase() } /** * Gets a node that can read the value returned from `call` with return kind * `kind`. */ - OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { - call = result.getCall() and exists(kind) - } + OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } // NOTE: For now we use the type `Unit` and do not benefit from type // information in the data flow analysis. @@ -637,8 +796,21 @@ module RustDataFlow implements InputSig { * are the value-preserving intra-callable flow steps. */ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) { - LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) and + ( + LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) + or + exists(SsaImpl::DefinitionExt def, boolean isUseStep | + SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) + | + isUseStep = false + or + isUseStep = true and + not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) + ) + ) and model = "" + or + LocalFlow::flowSummaryLocalStep(nodeFrom, nodeTo, _, model) } /** @@ -646,7 +818,10 @@ module RustDataFlow implements InputSig { * that does not follow a call edge. For example, a step through a global * variable. */ - predicate jumpStep(Node node1, Node node2) { none() } + predicate jumpStep(Node node1, Node node2) { + FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(Node::FlowSummaryNode).getSummaryNode(), + node2.(Node::FlowSummaryNode).getSummaryNode()) + } /** Holds if path `p` resolves to struct `s`. */ private predicate pathResolveToStructCanonicalPath(Path p, StructCanonicalPath s) { @@ -725,6 +900,9 @@ module RustDataFlow implements InputSig { node2.asExpr() = access ) ) + or + FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(Node::FlowSummaryNode).getSummaryNode(), + cs, node2.(Node::FlowSummaryNode).getSummaryNode()) } /** Holds if `ce` constructs an enum value of type `v`. */ @@ -789,6 +967,9 @@ module RustDataFlow implements InputSig { or tupleAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c) ) + or + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(Node::FlowSummaryNode).getSummaryNode(), + cs, node2.(Node::FlowSummaryNode).getSummaryNode()) } /** @@ -798,13 +979,19 @@ module RustDataFlow implements InputSig { */ predicate clearsContent(Node n, ContentSet cs) { tupleAssignment(_, n, cs.(SingletonContentSet).getContent()) + or + FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(Node::FlowSummaryNode).getSummaryNode(), + cs) } /** * Holds if the value that is being tracked is expected to be stored inside content `c` * at node `n`. */ - predicate expectsContent(Node n, ContentSet c) { none() } + predicate expectsContent(Node n, ContentSet cs) { + FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(Node::FlowSummaryNode) + .getSummaryNode(), cs) + } class NodeRegion instanceof Void { string toString() { result = "NodeRegion" } @@ -824,7 +1011,12 @@ module RustDataFlow implements InputSig { * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed * by default as a heuristic. */ - predicate allowParameterReturnInSelf(ParameterNode p) { none() } + predicate allowParameterReturnInSelf(ParameterNode p) { + exists(DataFlowCallable c, ParameterPosition pos | + p.isParameterOf(c, pos) and + FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asLibraryCallable(), pos) + ) + } /** * Holds if the value of `node2` is given by `node1`. @@ -837,6 +1029,12 @@ module RustDataFlow implements InputSig { */ predicate localMustFlowStep(Node node1, Node node2) { SsaFlow::localMustFlowStep(_, node1, node2) + or + node1 = + unique(Node::FlowSummaryNode n1 | + FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(), + node2.(Node::FlowSummaryNode).getSummaryNode(), true, _) + ) } class LambdaCallKind = Void; @@ -866,32 +1064,54 @@ private module Cached { cached newtype TNode = TExprNode(ExprCfgNode n) or - TParameterNode(ParamBaseCfgNode p) or + TSourceParameterNode(ParamBaseCfgNode p) or TPatNode(PatCfgNode p) or TExprPostUpdateNode(ExprCfgNode e) { isArgumentForCall(e, _, _) or e = any(FieldExprCfgNode access).getExpr() } or - TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) + TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or + TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) cached - newtype TDataFlowCall = TCall(CallExprBaseCfgNode c) + newtype TDataFlowCall = + TCall(CallExprBaseCfgNode c) or + TSummaryCall( + FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver + ) { + FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) + } cached - newtype TDataFlowCallable = TCfgScope(CfgScope scope) + newtype TDataFlowCallable = + TCfgScope(CfgScope scope) or + TLibraryCallable(LibraryCallable c) /** This is the local flow predicate that is exposed. */ cached predicate localFlowStepImpl(Node::Node nodeFrom, Node::Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) + or + SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _) + or + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _) } cached newtype TParameterPosition = TPositionalParameterPosition(int i) { i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1] + or + FlowSummaryImpl::ParsePositions::isParsedArgumentPosition(_, i) + or + FlowSummaryImpl::ParsePositions::isParsedParameterPosition(_, i) } or TSelfParameterPosition() + cached + newtype TReturnKind = TNormalReturnKind() + cached newtype TVariantCanonicalPath = MkVariantCanonicalPath(CrateOriginOption crate, string path, string name) { diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll new file mode 100644 index 000000000000..1292a936afdc --- /dev/null +++ b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll @@ -0,0 +1,108 @@ +/** + * Provides classes and predicates for defining flow summaries. + */ + +private import rust +private import codeql.dataflow.internal.FlowSummaryImpl +private import codeql.dataflow.internal.AccessPathSyntax as AccessPath +private import codeql.rust.dataflow.internal.DataFlowImpl +private import codeql.rust.dataflow.FlowSummary + +module Input implements InputSig { + class SummarizedCallableBase = string; + + RustDataFlow::ArgumentPosition callbackSelfParameterPosition() { none() } + + ReturnKind getStandardReturnValueKind() { result = TNormalReturnKind() } + + string encodeParameterPosition(ParameterPosition pos) { + result = pos.getPosition().toString() + or + pos.isSelf() and + result = "self" + } + + predicate encodeArgumentPosition = encodeParameterPosition/1; + + string encodeContent(ContentSet cs, string arg) { + exists(Content c | cs = TSingletonContentSet(c) | + exists(VariantCanonicalPath v | result = "Variant" | + exists(int pos | + c = TVariantPositionContent(v, pos) and + arg = v.getExtendedCanonicalPath() + "(" + pos + ")" + ) + or + exists(string field | + c = TVariantFieldContent(v, field) and + arg = v.getExtendedCanonicalPath() + "::" + field + ) + ) + ) + } + + string encodeReturn(ReturnKind rk, string arg) { none() } + + string encodeWithoutContent(ContentSet c, string arg) { + result = "Without" + encodeContent(c, arg) + } + + string encodeWithContent(ContentSet c, string arg) { result = "With" + encodeContent(c, arg) } + + bindingset[token] + ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) { + // needed to support `Argument[x..y]` ranges + token.getName() = "Argument" and + result.getPosition() = AccessPath::parseInt(token.getAnArgument()) + } + + bindingset[token] + RustDataFlow::ArgumentPosition decodeUnknownArgumentPosition(AccessPath::AccessPathTokenBase token) { + // needed to support `Parameter[x..y]` ranges + token.getName() = "Parameter" and + result.getPosition() = AccessPath::parseInt(token.getAnArgument()) + } + + bindingset[token] + ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) { none() } + + bindingset[token] + ContentSet decodeUnknownWithContent(AccessPath::AccessPathTokenBase token) { none() } +} + +private import Make as Impl + +private module StepsInput implements Impl::Private::StepsInputSig { + DataFlowCall getACall(Public::SummarizedCallable sc) { + result.asCallBaseExprCfgNode().getCallExprBase() = sc.(LibraryCallable).getACall() + } +} + +module Private { + import Impl::Private + + module Steps = Impl::Private::Steps; +} + +module Public = Impl::Public; + +module ParsePositions { + private import Private + + private predicate isParamBody(string body) { + body = any(AccessPathToken tok).getAnArgument("Parameter") + } + + private predicate isArgBody(string body) { + body = any(AccessPathToken tok).getAnArgument("Argument") + } + + predicate isParsedParameterPosition(string c, int i) { + isParamBody(c) and + i = AccessPath::parseInt(c) + } + + predicate isParsedArgumentPosition(string c, int i) { + isArgBody(c) and + i = AccessPath::parseInt(c) + } +} diff --git a/rust/ql/test/library-tests/dataflow/models/main.rs b/rust/ql/test/library-tests/dataflow/models/main.rs new file mode 100644 index 000000000000..ed49c6fd1eba --- /dev/null +++ b/rust/ql/test/library-tests/dataflow/models/main.rs @@ -0,0 +1,89 @@ +fn source(i: i64) -> i64 { + 1000 + i +} + +fn sink(s: i64) { + println!("{}", s); +} + +// has a flow model +fn identity(i: i64) -> i64 { + 0 +} + +fn test_identify() { + let s = source(1); + sink(identity(s)); // $ hasValueFlow=1 +} + +enum MyPosEnum { + A(i64), + B(i64), +} + +// has a flow model +fn get_var_pos(e: MyPosEnum) -> i64 { + 0 +} + +fn test_get_var_pos() { + let s = source(2); + let e1 = MyPosEnum::A(s); + sink(get_var_pos(e1)); // $ hasValueFlow=2 + let e2 = MyPosEnum::B(s); + sink(get_var_pos(e2)); +} + +// has a flow model +fn set_var_pos(i: i64) -> MyPosEnum { + MyPosEnum::A(0) +} + +fn test_set_var_pos() { + let s = source(3); + let e1 = set_var_pos(s); + match e1 { + MyPosEnum::A(i) => sink(i), + MyPosEnum::B(i) => sink(i), // $ hasValueFlow=3 + } +} + +enum MyFieldEnum { + C { field_c: i64 }, + D { field_d: i64 }, +} + +// has a flow model +fn get_var_field(e: MyFieldEnum) -> i64 { + 0 +} + +fn test_get_var_field() { + let s = source(4); + let e1 = MyFieldEnum::C { field_c: s }; + sink(get_var_field(e1)); // $ hasValueFlow=4 + let e2 = MyFieldEnum::D { field_d: s }; + sink(get_var_field(e2)); +} + +// has a flow model +fn set_var_field(i: i64) -> MyFieldEnum { + MyFieldEnum::C { field_c: 0 } +} + +fn test_set_var_field() { + let s = source(5); + let e1 = set_var_field(s); + match e1 { + MyFieldEnum::C { field_c: i } => sink(i), + MyFieldEnum::D { field_d: i } => sink(i), // $ hasValueFlow=5 + } +} + +fn main() { + test_identify(); + test_get_var_pos(); + test_set_var_pos(); + test_get_var_field(); + test_set_var_field(); +} diff --git a/rust/ql/test/library-tests/dataflow/models/models.expected b/rust/ql/test/library-tests/dataflow/models/models.expected new file mode 100644 index 000000000000..f525638282ba --- /dev/null +++ b/rust/ql/test/library-tests/dataflow/models/models.expected @@ -0,0 +1,57 @@ +models +edges +| main.rs:15:13:15:21 | source(...) | main.rs:16:19:16:19 | s | provenance | | +| main.rs:16:19:16:19 | s | main.rs:16:10:16:20 | identity(...) | provenance | | +| main.rs:30:13:30:21 | source(...) | main.rs:31:27:31:27 | s | provenance | | +| main.rs:31:14:31:28 | ...::A(...) [A] | main.rs:32:22:32:23 | e1 [A] | provenance | | +| main.rs:31:27:31:27 | s | main.rs:31:14:31:28 | ...::A(...) [A] | provenance | | +| main.rs:32:22:32:23 | e1 [A] | main.rs:32:10:32:24 | get_var_pos(...) | provenance | | +| main.rs:43:13:43:21 | source(...) | main.rs:44:26:44:26 | s | provenance | | +| main.rs:44:14:44:27 | set_var_pos(...) [B] | main.rs:47:9:47:23 | TupleStructPat [B] | provenance | | +| main.rs:44:26:44:26 | s | main.rs:44:14:44:27 | set_var_pos(...) [B] | provenance | | +| main.rs:47:9:47:23 | TupleStructPat [B] | main.rs:47:22:47:22 | i | provenance | | +| main.rs:47:22:47:22 | i | main.rs:47:33:47:33 | i | provenance | | +| main.rs:62:13:62:21 | source(...) | main.rs:63:40:63:40 | s | provenance | | +| main.rs:63:14:63:42 | ...::C {...} [C] | main.rs:64:24:64:25 | e1 [C] | provenance | | +| main.rs:63:40:63:40 | s | main.rs:63:14:63:42 | ...::C {...} [C] | provenance | | +| main.rs:64:24:64:25 | e1 [C] | main.rs:64:10:64:26 | get_var_field(...) | provenance | | +| main.rs:75:13:75:21 | source(...) | main.rs:76:28:76:28 | s | provenance | | +| main.rs:76:14:76:29 | set_var_field(...) [D] | main.rs:79:9:79:37 | ...::D {...} [D] | provenance | | +| main.rs:76:28:76:28 | s | main.rs:76:14:76:29 | set_var_field(...) [D] | provenance | | +| main.rs:79:9:79:37 | ...::D {...} [D] | main.rs:79:35:79:35 | i | provenance | | +| main.rs:79:35:79:35 | i | main.rs:79:47:79:47 | i | provenance | | +nodes +| main.rs:15:13:15:21 | source(...) | semmle.label | source(...) | +| main.rs:16:10:16:20 | identity(...) | semmle.label | identity(...) | +| main.rs:16:19:16:19 | s | semmle.label | s | +| main.rs:30:13:30:21 | source(...) | semmle.label | source(...) | +| main.rs:31:14:31:28 | ...::A(...) [A] | semmle.label | ...::A(...) [A] | +| main.rs:31:27:31:27 | s | semmle.label | s | +| main.rs:32:10:32:24 | get_var_pos(...) | semmle.label | get_var_pos(...) | +| main.rs:32:22:32:23 | e1 [A] | semmle.label | e1 [A] | +| main.rs:43:13:43:21 | source(...) | semmle.label | source(...) | +| main.rs:44:14:44:27 | set_var_pos(...) [B] | semmle.label | set_var_pos(...) [B] | +| main.rs:44:26:44:26 | s | semmle.label | s | +| main.rs:47:9:47:23 | TupleStructPat [B] | semmle.label | TupleStructPat [B] | +| main.rs:47:22:47:22 | i | semmle.label | i | +| main.rs:47:33:47:33 | i | semmle.label | i | +| main.rs:62:13:62:21 | source(...) | semmle.label | source(...) | +| main.rs:63:14:63:42 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:63:40:63:40 | s | semmle.label | s | +| main.rs:64:10:64:26 | get_var_field(...) | semmle.label | get_var_field(...) | +| main.rs:64:24:64:25 | e1 [C] | semmle.label | e1 [C] | +| main.rs:75:13:75:21 | source(...) | semmle.label | source(...) | +| main.rs:76:14:76:29 | set_var_field(...) [D] | semmle.label | set_var_field(...) [D] | +| main.rs:76:28:76:28 | s | semmle.label | s | +| main.rs:79:9:79:37 | ...::D {...} [D] | semmle.label | ...::D {...} [D] | +| main.rs:79:35:79:35 | i | semmle.label | i | +| main.rs:79:47:79:47 | i | semmle.label | i | +subpaths +testFailures +invalidSpecComponent +#select +| main.rs:16:10:16:20 | identity(...) | main.rs:15:13:15:21 | source(...) | main.rs:16:10:16:20 | identity(...) | $@ | main.rs:15:13:15:21 | source(...) | source(...) | +| main.rs:32:10:32:24 | get_var_pos(...) | main.rs:30:13:30:21 | source(...) | main.rs:32:10:32:24 | get_var_pos(...) | $@ | main.rs:30:13:30:21 | source(...) | source(...) | +| main.rs:47:33:47:33 | i | main.rs:43:13:43:21 | source(...) | main.rs:47:33:47:33 | i | $@ | main.rs:43:13:43:21 | source(...) | source(...) | +| main.rs:64:10:64:26 | get_var_field(...) | main.rs:62:13:62:21 | source(...) | main.rs:64:10:64:26 | get_var_field(...) | $@ | main.rs:62:13:62:21 | source(...) | source(...) | +| main.rs:79:47:79:47 | i | main.rs:75:13:75:21 | source(...) | main.rs:79:47:79:47 | i | $@ | main.rs:75:13:75:21 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/models/models.ql b/rust/ql/test/library-tests/dataflow/models/models.ql new file mode 100644 index 000000000000..080d6b58f5cd --- /dev/null +++ b/rust/ql/test/library-tests/dataflow/models/models.ql @@ -0,0 +1,78 @@ +/** + * @kind path-problem + */ + +import rust +import utils.InlineFlowTest +import codeql.rust.dataflow.DataFlow +import codeql.rust.dataflow.FlowSummary +import codeql.rust.dataflow.TaintTracking +import codeql.rust.dataflow.internal.FlowSummaryImpl +import PathGraph + +query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) { + (sc.propagatesFlow(s, _, _) or sc.propagatesFlow(_, s, _)) and + Private::External::invalidSpecComponent(s, c) +} + +private class SummarizedCallableIdentity extends SummarizedCallable::Range { + SummarizedCallableIdentity() { this = "repo::test::_::crate::identity" } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableGetVarPos extends SummarizedCallable::Range { + SummarizedCallableGetVarPos() { this = "repo::test::_::crate::get_var_pos" } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0].Variant[crate::MyPosEnum::A(0)]" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableSetVarPos extends SummarizedCallable::Range { + SummarizedCallableSetVarPos() { this = "repo::test::_::crate::set_var_pos" } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue.Variant[crate::MyPosEnum::B(0)]" and + preservesValue = true + } +} + +private class SummarizedCallableGetVarField extends SummarizedCallable::Range { + SummarizedCallableGetVarField() { this = "repo::test::_::crate::get_var_field" } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0].Variant[crate::MyFieldEnum::C::field_c]" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableSetVarField extends SummarizedCallable::Range { + SummarizedCallableSetVarField() { this = "repo::test::_::crate::set_var_field" } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue.Variant[crate::MyFieldEnum::D::field_d]" and + preservesValue = true + } +} + +module CustomConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { DefaultFlowConfig::isSource(source) } + + predicate isSink(DataFlow::Node sink) { DefaultFlowConfig::isSink(sink) } +} + +import ValueFlowTest + +from PathNode source, PathNode sink +where flowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() From fbeb6f39404c9d74f1775cd3b756e67fef0d87d6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 27 Nov 2024 14:50:46 +0100 Subject: [PATCH 2/4] Shared: Move shared logic into `FlowSummaryImpl.qll` --- .../code/csharp/dataflow/internal/DataFlowPrivate.qll | 8 +++----- .../semmle/code/java/dataflow/internal/DataFlowUtil.qll | 7 ++----- .../lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll | 8 +++----- .../ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll | 8 +++----- .../dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll | 5 +++++ 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index cab164846e2d..bf867ee1a832 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -731,11 +731,9 @@ module LocalFlow { or node2 = node1.(LocalFunctionCreationNode).getAnAccess(true) or - node1 = - unique(FlowSummaryNode n1 | - FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(), - node2.(FlowSummaryNode).getSummaryNode(), true, _) - ) + FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 + .(FlowSummaryNode) + .getSummaryNode(), node2.(FlowSummaryNode).getSummaryNode()) } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll index c1fc51bff049..bb71ed25c73a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -160,11 +160,8 @@ predicate localMustFlowStep(Node node1, Node node2) { or node2.asExpr().(AssignExpr).getSource() = node1.asExpr() or - node1 = - unique(FlowSummaryNode n1 | - FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(), - node2.(FlowSummaryNode).getSummaryNode(), true, _) - ) + FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1.(FlowSummaryNode).getSummaryNode(), + node2.(FlowSummaryNode).getSummaryNode()) } import Cached diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 36c71e04fefc..260fb3cab6f9 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -175,11 +175,9 @@ module LocalFlow { or node1 = node2.(ImplicitBlockArgumentNode).getParameterNode(true) or - node1 = - unique(FlowSummaryNode n1 | - FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(), - node2.(FlowSummaryNode).getSummaryNode(), true, _) - ) + FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 + .(FlowSummaryNode) + .getSummaryNode(), node2.(FlowSummaryNode).getSummaryNode()) } } diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 2f49de555165..8c15353b58b2 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -1030,11 +1030,9 @@ module RustDataFlow implements InputSig { predicate localMustFlowStep(Node node1, Node node2) { SsaFlow::localMustFlowStep(_, node1, node2) or - node1 = - unique(Node::FlowSummaryNode n1 | - FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(), - node2.(Node::FlowSummaryNode).getSummaryNode(), true, _) - ) + FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 + .(Node::FlowSummaryNode) + .getSummaryNode(), node2.(Node::FlowSummaryNode).getSummaryNode()) } class LambdaCallKind = Void; diff --git a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll index e7e588ff9ebe..e1770ca482a6 100644 --- a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll @@ -1207,6 +1207,11 @@ module Make< ) } + /** Holds if the value of `succ` is uniquely determined by the value of `pred`. */ + predicate summaryLocalMustFlowStep(SummaryNode pred, SummaryNode succ) { + pred = unique(SummaryNode n1 | summaryLocalStep(n1, succ, true, _)) + } + /** * Holds if there is a read step of content `c` from `pred` to `succ`, which * is synthesized from a flow summary. From 3e5f4b7f897c23a676d84391fd42de8408413636 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 27 Nov 2024 15:05:56 +0100 Subject: [PATCH 3/4] Rust: Add a model for `unwrap` --- .../lib/codeql/rust/dataflow/FlowSummary.qll | 11 + .../dataflow/local/DataFlowStep.expected | 319 +++++++++--------- .../dataflow/local/inline-flow.expected | 76 +++-- .../test/library-tests/dataflow/local/main.rs | 6 + 4 files changed, 221 insertions(+), 191 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll index 13206803f028..bbeb33ad13a6 100644 --- a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll +++ b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll @@ -7,6 +7,17 @@ private import internal.DataFlowImpl // import all instances below private module Summaries { private import codeql.rust.Frameworks + + // TODO: Used models-as-data when it's available + private class UnwrapSummary extends SummarizedCallable::Range { + UnwrapSummary() { this = "lang:core::_::::unwrap" } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[self].Variant[crate::std::option::Option::Some(0)]" and + output = "ReturnValue" and + preservesValue = true + } + } } /** Provides the `Range` class used to define the extent of `LibraryCallable`. */ diff --git a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected index 594728353c68..a02813c07865 100644 --- a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -201,146 +201,149 @@ localStep | main.rs:221:14:221:14 | n | main.rs:221:14:221:14 | [SSA] n | | main.rs:221:20:221:26 | sink(...) | main.rs:220:5:223:5 | match s2 { ... } | | main.rs:222:17:222:23 | sink(...) | main.rs:220:5:223:5 | match s2 { ... } | -| main.rs:232:9:232:10 | [SSA] s1 | main.rs:234:11:234:12 | s1 | -| main.rs:232:9:232:10 | s1 | main.rs:232:9:232:10 | [SSA] s1 | -| main.rs:232:14:232:39 | ...::A(...) | main.rs:232:9:232:10 | s1 | -| main.rs:233:9:233:10 | [SSA] s2 | main.rs:241:11:241:12 | s2 | -| main.rs:233:9:233:10 | s2 | main.rs:233:9:233:10 | [SSA] s2 | -| main.rs:233:14:233:30 | ...::B(...) | main.rs:233:9:233:10 | s2 | -| main.rs:234:11:234:12 | s1 | main.rs:235:9:235:25 | TupleStructPat | -| main.rs:234:11:234:12 | s1 | main.rs:236:9:236:25 | TupleStructPat | -| main.rs:234:11:234:12 | s1 | main.rs:238:11:238:12 | s1 | -| main.rs:235:24:235:24 | [SSA] n | main.rs:235:35:235:35 | n | -| main.rs:235:24:235:24 | n | main.rs:235:24:235:24 | [SSA] n | -| main.rs:235:30:235:36 | sink(...) | main.rs:234:5:237:5 | match s1 { ... } | -| main.rs:236:24:236:24 | [SSA] n | main.rs:236:35:236:35 | n | -| main.rs:236:24:236:24 | n | main.rs:236:24:236:24 | [SSA] n | -| main.rs:236:30:236:36 | sink(...) | main.rs:234:5:237:5 | match s1 { ... } | -| main.rs:238:11:238:12 | s1 | main.rs:239:9:239:45 | ... \| ... | -| main.rs:239:9:239:45 | ... \| ... | main.rs:239:9:239:25 | TupleStructPat | -| main.rs:239:9:239:45 | ... \| ... | main.rs:239:29:239:45 | TupleStructPat | -| main.rs:239:9:239:45 | [SSA] [match(true)] phi | main.rs:239:55:239:55 | n | -| main.rs:239:24:239:24 | [SSA] [input] [match(true)] phi | main.rs:239:9:239:45 | [SSA] [match(true)] phi | -| main.rs:239:24:239:24 | [SSA] n | main.rs:239:24:239:24 | [SSA] [input] [match(true)] phi | -| main.rs:239:24:239:24 | n | main.rs:239:24:239:24 | [SSA] n | -| main.rs:239:44:239:44 | [SSA] [input] [match(true)] phi | main.rs:239:9:239:45 | [SSA] [match(true)] phi | -| main.rs:239:44:239:44 | [SSA] n | main.rs:239:44:239:44 | [SSA] [input] [match(true)] phi | -| main.rs:239:44:239:44 | n | main.rs:239:44:239:44 | [SSA] n | -| main.rs:239:50:239:56 | sink(...) | main.rs:238:5:240:5 | match s1 { ... } | -| main.rs:241:5:244:5 | match s2 { ... } | main.rs:231:48:245:1 | { ... } | -| main.rs:241:11:241:12 | s2 | main.rs:242:9:242:25 | TupleStructPat | -| main.rs:241:11:241:12 | s2 | main.rs:243:9:243:25 | TupleStructPat | -| main.rs:242:24:242:24 | [SSA] n | main.rs:242:35:242:35 | n | -| main.rs:242:24:242:24 | n | main.rs:242:24:242:24 | [SSA] n | -| main.rs:242:30:242:36 | sink(...) | main.rs:241:5:244:5 | match s2 { ... } | -| main.rs:243:24:243:24 | [SSA] n | main.rs:243:35:243:35 | n | -| main.rs:243:24:243:24 | n | main.rs:243:24:243:24 | [SSA] n | -| main.rs:243:30:243:36 | sink(...) | main.rs:241:5:244:5 | match s2 { ... } | -| main.rs:250:9:250:10 | [SSA] s1 | main.rs:252:11:252:12 | s1 | -| main.rs:250:9:250:10 | s1 | main.rs:250:9:250:10 | [SSA] s1 | -| main.rs:250:14:250:26 | A(...) | main.rs:250:9:250:10 | s1 | -| main.rs:251:9:251:10 | [SSA] s2 | main.rs:259:11:259:12 | s2 | -| main.rs:251:9:251:10 | s2 | main.rs:251:9:251:10 | [SSA] s2 | -| main.rs:251:14:251:17 | B(...) | main.rs:251:9:251:10 | s2 | -| main.rs:252:11:252:12 | s1 | main.rs:253:9:253:12 | TupleStructPat | -| main.rs:252:11:252:12 | s1 | main.rs:254:9:254:12 | TupleStructPat | -| main.rs:252:11:252:12 | s1 | main.rs:256:11:256:12 | s1 | -| main.rs:253:11:253:11 | [SSA] n | main.rs:253:22:253:22 | n | -| main.rs:253:11:253:11 | n | main.rs:253:11:253:11 | [SSA] n | -| main.rs:253:17:253:23 | sink(...) | main.rs:252:5:255:5 | match s1 { ... } | -| main.rs:254:11:254:11 | [SSA] n | main.rs:254:22:254:22 | n | -| main.rs:254:11:254:11 | n | main.rs:254:11:254:11 | [SSA] n | -| main.rs:254:17:254:23 | sink(...) | main.rs:252:5:255:5 | match s1 { ... } | -| main.rs:256:11:256:12 | s1 | main.rs:257:9:257:19 | ... \| ... | -| main.rs:257:9:257:19 | ... \| ... | main.rs:257:9:257:12 | TupleStructPat | -| main.rs:257:9:257:19 | ... \| ... | main.rs:257:16:257:19 | TupleStructPat | -| main.rs:257:9:257:19 | [SSA] [match(true)] phi | main.rs:257:29:257:29 | n | -| main.rs:257:11:257:11 | [SSA] [input] [match(true)] phi | main.rs:257:9:257:19 | [SSA] [match(true)] phi | -| main.rs:257:11:257:11 | [SSA] n | main.rs:257:11:257:11 | [SSA] [input] [match(true)] phi | -| main.rs:257:11:257:11 | n | main.rs:257:11:257:11 | [SSA] n | -| main.rs:257:18:257:18 | [SSA] [input] [match(true)] phi | main.rs:257:9:257:19 | [SSA] [match(true)] phi | -| main.rs:257:18:257:18 | [SSA] n | main.rs:257:18:257:18 | [SSA] [input] [match(true)] phi | -| main.rs:257:18:257:18 | n | main.rs:257:18:257:18 | [SSA] n | -| main.rs:257:24:257:30 | sink(...) | main.rs:256:5:258:5 | match s1 { ... } | -| main.rs:259:5:262:5 | match s2 { ... } | main.rs:249:50:263:1 | { ... } | -| main.rs:259:11:259:12 | s2 | main.rs:260:9:260:12 | TupleStructPat | -| main.rs:259:11:259:12 | s2 | main.rs:261:9:261:12 | TupleStructPat | -| main.rs:260:11:260:11 | [SSA] n | main.rs:260:22:260:22 | n | -| main.rs:260:11:260:11 | n | main.rs:260:11:260:11 | [SSA] n | -| main.rs:260:17:260:23 | sink(...) | main.rs:259:5:262:5 | match s2 { ... } | -| main.rs:261:11:261:11 | [SSA] n | main.rs:261:22:261:22 | n | -| main.rs:261:11:261:11 | n | main.rs:261:11:261:11 | [SSA] n | -| main.rs:261:17:261:23 | sink(...) | main.rs:259:5:262:5 | match s2 { ... } | -| main.rs:271:9:271:10 | [SSA] s1 | main.rs:275:11:275:12 | s1 | -| main.rs:271:9:271:10 | s1 | main.rs:271:9:271:10 | [SSA] s1 | -| main.rs:271:14:273:5 | ...::C {...} | main.rs:271:9:271:10 | s1 | -| main.rs:274:9:274:10 | [SSA] s2 | main.rs:282:11:282:12 | s2 | -| main.rs:274:9:274:10 | s2 | main.rs:274:9:274:10 | [SSA] s2 | -| main.rs:274:14:274:43 | ...::D {...} | main.rs:274:9:274:10 | s2 | -| main.rs:275:11:275:12 | s1 | main.rs:276:9:276:38 | ...::C {...} | -| main.rs:275:11:275:12 | s1 | main.rs:277:9:277:38 | ...::D {...} | -| main.rs:275:11:275:12 | s1 | main.rs:279:11:279:12 | s1 | -| main.rs:276:36:276:36 | [SSA] n | main.rs:276:48:276:48 | n | -| main.rs:276:36:276:36 | n | main.rs:276:36:276:36 | [SSA] n | -| main.rs:276:43:276:49 | sink(...) | main.rs:275:5:278:5 | match s1 { ... } | -| main.rs:277:36:277:36 | [SSA] n | main.rs:277:48:277:48 | n | -| main.rs:277:36:277:36 | n | main.rs:277:36:277:36 | [SSA] n | -| main.rs:277:43:277:49 | sink(...) | main.rs:275:5:278:5 | match s1 { ... } | -| main.rs:279:11:279:12 | s1 | main.rs:280:9:280:71 | ... \| ... | -| main.rs:280:9:280:71 | ... \| ... | main.rs:280:9:280:38 | ...::C {...} | -| main.rs:280:9:280:71 | ... \| ... | main.rs:280:42:280:71 | ...::D {...} | -| main.rs:280:9:280:71 | [SSA] [match(true)] phi | main.rs:280:81:280:81 | n | -| main.rs:280:36:280:36 | [SSA] [input] [match(true)] phi | main.rs:280:9:280:71 | [SSA] [match(true)] phi | -| main.rs:280:36:280:36 | [SSA] n | main.rs:280:36:280:36 | [SSA] [input] [match(true)] phi | -| main.rs:280:36:280:36 | n | main.rs:280:36:280:36 | [SSA] n | -| main.rs:280:69:280:69 | [SSA] [input] [match(true)] phi | main.rs:280:9:280:71 | [SSA] [match(true)] phi | -| main.rs:280:69:280:69 | [SSA] n | main.rs:280:69:280:69 | [SSA] [input] [match(true)] phi | -| main.rs:280:69:280:69 | n | main.rs:280:69:280:69 | [SSA] n | -| main.rs:280:76:280:82 | sink(...) | main.rs:279:5:281:5 | match s1 { ... } | -| main.rs:282:5:285:5 | match s2 { ... } | main.rs:270:49:286:1 | { ... } | -| main.rs:282:11:282:12 | s2 | main.rs:283:9:283:38 | ...::C {...} | -| main.rs:282:11:282:12 | s2 | main.rs:284:9:284:38 | ...::D {...} | -| main.rs:283:36:283:36 | [SSA] n | main.rs:283:48:283:48 | n | -| main.rs:283:36:283:36 | n | main.rs:283:36:283:36 | [SSA] n | -| main.rs:283:43:283:49 | sink(...) | main.rs:282:5:285:5 | match s2 { ... } | -| main.rs:284:36:284:36 | [SSA] n | main.rs:284:48:284:48 | n | -| main.rs:284:36:284:36 | n | main.rs:284:36:284:36 | [SSA] n | -| main.rs:284:43:284:49 | sink(...) | main.rs:282:5:285:5 | match s2 { ... } | -| main.rs:291:9:291:10 | [SSA] s1 | main.rs:295:11:295:12 | s1 | -| main.rs:291:9:291:10 | s1 | main.rs:291:9:291:10 | [SSA] s1 | -| main.rs:291:14:293:5 | C {...} | main.rs:291:9:291:10 | s1 | -| main.rs:294:9:294:10 | [SSA] s2 | main.rs:302:11:302:12 | s2 | -| main.rs:294:9:294:10 | s2 | main.rs:294:9:294:10 | [SSA] s2 | -| main.rs:294:14:294:29 | D {...} | main.rs:294:9:294:10 | s2 | -| main.rs:295:11:295:12 | s1 | main.rs:296:9:296:24 | C {...} | -| main.rs:295:11:295:12 | s1 | main.rs:297:9:297:24 | D {...} | -| main.rs:295:11:295:12 | s1 | main.rs:299:11:299:12 | s1 | -| main.rs:296:22:296:22 | [SSA] n | main.rs:296:34:296:34 | n | -| main.rs:296:22:296:22 | n | main.rs:296:22:296:22 | [SSA] n | -| main.rs:296:29:296:35 | sink(...) | main.rs:295:5:298:5 | match s1 { ... } | -| main.rs:297:22:297:22 | [SSA] n | main.rs:297:34:297:34 | n | -| main.rs:297:22:297:22 | n | main.rs:297:22:297:22 | [SSA] n | -| main.rs:297:29:297:35 | sink(...) | main.rs:295:5:298:5 | match s1 { ... } | -| main.rs:299:11:299:12 | s1 | main.rs:300:9:300:43 | ... \| ... | -| main.rs:300:9:300:43 | ... \| ... | main.rs:300:9:300:24 | C {...} | -| main.rs:300:9:300:43 | ... \| ... | main.rs:300:28:300:43 | D {...} | -| main.rs:300:9:300:43 | [SSA] [match(true)] phi | main.rs:300:53:300:53 | n | -| main.rs:300:22:300:22 | [SSA] [input] [match(true)] phi | main.rs:300:9:300:43 | [SSA] [match(true)] phi | -| main.rs:300:22:300:22 | [SSA] n | main.rs:300:22:300:22 | [SSA] [input] [match(true)] phi | -| main.rs:300:22:300:22 | n | main.rs:300:22:300:22 | [SSA] n | -| main.rs:300:41:300:41 | [SSA] [input] [match(true)] phi | main.rs:300:9:300:43 | [SSA] [match(true)] phi | -| main.rs:300:41:300:41 | [SSA] n | main.rs:300:41:300:41 | [SSA] [input] [match(true)] phi | -| main.rs:300:41:300:41 | n | main.rs:300:41:300:41 | [SSA] n | -| main.rs:300:48:300:54 | sink(...) | main.rs:299:5:301:5 | match s1 { ... } | -| main.rs:302:5:305:5 | match s2 { ... } | main.rs:290:51:306:1 | { ... } | -| main.rs:302:11:302:12 | s2 | main.rs:303:9:303:24 | C {...} | -| main.rs:302:11:302:12 | s2 | main.rs:304:9:304:24 | D {...} | -| main.rs:303:22:303:22 | [SSA] n | main.rs:303:34:303:34 | n | -| main.rs:303:22:303:22 | n | main.rs:303:22:303:22 | [SSA] n | -| main.rs:303:29:303:35 | sink(...) | main.rs:302:5:305:5 | match s2 { ... } | -| main.rs:304:22:304:22 | [SSA] n | main.rs:304:34:304:34 | n | -| main.rs:304:22:304:22 | n | main.rs:304:22:304:22 | [SSA] n | -| main.rs:304:29:304:35 | sink(...) | main.rs:302:5:305:5 | match s2 { ... } | +| main.rs:227:9:227:10 | [SSA] s1 | main.rs:228:10:228:11 | s1 | +| main.rs:227:9:227:10 | s1 | main.rs:227:9:227:10 | [SSA] s1 | +| main.rs:227:14:227:29 | Some(...) | main.rs:227:9:227:10 | s1 | +| main.rs:237:9:237:10 | [SSA] s1 | main.rs:239:11:239:12 | s1 | +| main.rs:237:9:237:10 | s1 | main.rs:237:9:237:10 | [SSA] s1 | +| main.rs:237:14:237:39 | ...::A(...) | main.rs:237:9:237:10 | s1 | +| main.rs:238:9:238:10 | [SSA] s2 | main.rs:246:11:246:12 | s2 | +| main.rs:238:9:238:10 | s2 | main.rs:238:9:238:10 | [SSA] s2 | +| main.rs:238:14:238:30 | ...::B(...) | main.rs:238:9:238:10 | s2 | +| main.rs:239:11:239:12 | s1 | main.rs:240:9:240:25 | TupleStructPat | +| main.rs:239:11:239:12 | s1 | main.rs:241:9:241:25 | TupleStructPat | +| main.rs:239:11:239:12 | s1 | main.rs:243:11:243:12 | s1 | +| main.rs:240:24:240:24 | [SSA] n | main.rs:240:35:240:35 | n | +| main.rs:240:24:240:24 | n | main.rs:240:24:240:24 | [SSA] n | +| main.rs:240:30:240:36 | sink(...) | main.rs:239:5:242:5 | match s1 { ... } | +| main.rs:241:24:241:24 | [SSA] n | main.rs:241:35:241:35 | n | +| main.rs:241:24:241:24 | n | main.rs:241:24:241:24 | [SSA] n | +| main.rs:241:30:241:36 | sink(...) | main.rs:239:5:242:5 | match s1 { ... } | +| main.rs:243:11:243:12 | s1 | main.rs:244:9:244:45 | ... \| ... | +| main.rs:244:9:244:45 | ... \| ... | main.rs:244:9:244:25 | TupleStructPat | +| main.rs:244:9:244:45 | ... \| ... | main.rs:244:29:244:45 | TupleStructPat | +| main.rs:244:9:244:45 | [SSA] [match(true)] phi | main.rs:244:55:244:55 | n | +| main.rs:244:24:244:24 | [SSA] [input] [match(true)] phi | main.rs:244:9:244:45 | [SSA] [match(true)] phi | +| main.rs:244:24:244:24 | [SSA] n | main.rs:244:24:244:24 | [SSA] [input] [match(true)] phi | +| main.rs:244:24:244:24 | n | main.rs:244:24:244:24 | [SSA] n | +| main.rs:244:44:244:44 | [SSA] [input] [match(true)] phi | main.rs:244:9:244:45 | [SSA] [match(true)] phi | +| main.rs:244:44:244:44 | [SSA] n | main.rs:244:44:244:44 | [SSA] [input] [match(true)] phi | +| main.rs:244:44:244:44 | n | main.rs:244:44:244:44 | [SSA] n | +| main.rs:244:50:244:56 | sink(...) | main.rs:243:5:245:5 | match s1 { ... } | +| main.rs:246:5:249:5 | match s2 { ... } | main.rs:236:48:250:1 | { ... } | +| main.rs:246:11:246:12 | s2 | main.rs:247:9:247:25 | TupleStructPat | +| main.rs:246:11:246:12 | s2 | main.rs:248:9:248:25 | TupleStructPat | +| main.rs:247:24:247:24 | [SSA] n | main.rs:247:35:247:35 | n | +| main.rs:247:24:247:24 | n | main.rs:247:24:247:24 | [SSA] n | +| main.rs:247:30:247:36 | sink(...) | main.rs:246:5:249:5 | match s2 { ... } | +| main.rs:248:24:248:24 | [SSA] n | main.rs:248:35:248:35 | n | +| main.rs:248:24:248:24 | n | main.rs:248:24:248:24 | [SSA] n | +| main.rs:248:30:248:36 | sink(...) | main.rs:246:5:249:5 | match s2 { ... } | +| main.rs:255:9:255:10 | [SSA] s1 | main.rs:257:11:257:12 | s1 | +| main.rs:255:9:255:10 | s1 | main.rs:255:9:255:10 | [SSA] s1 | +| main.rs:255:14:255:26 | A(...) | main.rs:255:9:255:10 | s1 | +| main.rs:256:9:256:10 | [SSA] s2 | main.rs:264:11:264:12 | s2 | +| main.rs:256:9:256:10 | s2 | main.rs:256:9:256:10 | [SSA] s2 | +| main.rs:256:14:256:17 | B(...) | main.rs:256:9:256:10 | s2 | +| main.rs:257:11:257:12 | s1 | main.rs:258:9:258:12 | TupleStructPat | +| main.rs:257:11:257:12 | s1 | main.rs:259:9:259:12 | TupleStructPat | +| main.rs:257:11:257:12 | s1 | main.rs:261:11:261:12 | s1 | +| main.rs:258:11:258:11 | [SSA] n | main.rs:258:22:258:22 | n | +| main.rs:258:11:258:11 | n | main.rs:258:11:258:11 | [SSA] n | +| main.rs:258:17:258:23 | sink(...) | main.rs:257:5:260:5 | match s1 { ... } | +| main.rs:259:11:259:11 | [SSA] n | main.rs:259:22:259:22 | n | +| main.rs:259:11:259:11 | n | main.rs:259:11:259:11 | [SSA] n | +| main.rs:259:17:259:23 | sink(...) | main.rs:257:5:260:5 | match s1 { ... } | +| main.rs:261:11:261:12 | s1 | main.rs:262:9:262:19 | ... \| ... | +| main.rs:262:9:262:19 | ... \| ... | main.rs:262:9:262:12 | TupleStructPat | +| main.rs:262:9:262:19 | ... \| ... | main.rs:262:16:262:19 | TupleStructPat | +| main.rs:262:9:262:19 | [SSA] [match(true)] phi | main.rs:262:29:262:29 | n | +| main.rs:262:11:262:11 | [SSA] [input] [match(true)] phi | main.rs:262:9:262:19 | [SSA] [match(true)] phi | +| main.rs:262:11:262:11 | [SSA] n | main.rs:262:11:262:11 | [SSA] [input] [match(true)] phi | +| main.rs:262:11:262:11 | n | main.rs:262:11:262:11 | [SSA] n | +| main.rs:262:18:262:18 | [SSA] [input] [match(true)] phi | main.rs:262:9:262:19 | [SSA] [match(true)] phi | +| main.rs:262:18:262:18 | [SSA] n | main.rs:262:18:262:18 | [SSA] [input] [match(true)] phi | +| main.rs:262:18:262:18 | n | main.rs:262:18:262:18 | [SSA] n | +| main.rs:262:24:262:30 | sink(...) | main.rs:261:5:263:5 | match s1 { ... } | +| main.rs:264:5:267:5 | match s2 { ... } | main.rs:254:50:268:1 | { ... } | +| main.rs:264:11:264:12 | s2 | main.rs:265:9:265:12 | TupleStructPat | +| main.rs:264:11:264:12 | s2 | main.rs:266:9:266:12 | TupleStructPat | +| main.rs:265:11:265:11 | [SSA] n | main.rs:265:22:265:22 | n | +| main.rs:265:11:265:11 | n | main.rs:265:11:265:11 | [SSA] n | +| main.rs:265:17:265:23 | sink(...) | main.rs:264:5:267:5 | match s2 { ... } | +| main.rs:266:11:266:11 | [SSA] n | main.rs:266:22:266:22 | n | +| main.rs:266:11:266:11 | n | main.rs:266:11:266:11 | [SSA] n | +| main.rs:266:17:266:23 | sink(...) | main.rs:264:5:267:5 | match s2 { ... } | +| main.rs:276:9:276:10 | [SSA] s1 | main.rs:280:11:280:12 | s1 | +| main.rs:276:9:276:10 | s1 | main.rs:276:9:276:10 | [SSA] s1 | +| main.rs:276:14:278:5 | ...::C {...} | main.rs:276:9:276:10 | s1 | +| main.rs:279:9:279:10 | [SSA] s2 | main.rs:287:11:287:12 | s2 | +| main.rs:279:9:279:10 | s2 | main.rs:279:9:279:10 | [SSA] s2 | +| main.rs:279:14:279:43 | ...::D {...} | main.rs:279:9:279:10 | s2 | +| main.rs:280:11:280:12 | s1 | main.rs:281:9:281:38 | ...::C {...} | +| main.rs:280:11:280:12 | s1 | main.rs:282:9:282:38 | ...::D {...} | +| main.rs:280:11:280:12 | s1 | main.rs:284:11:284:12 | s1 | +| main.rs:281:36:281:36 | [SSA] n | main.rs:281:48:281:48 | n | +| main.rs:281:36:281:36 | n | main.rs:281:36:281:36 | [SSA] n | +| main.rs:281:43:281:49 | sink(...) | main.rs:280:5:283:5 | match s1 { ... } | +| main.rs:282:36:282:36 | [SSA] n | main.rs:282:48:282:48 | n | +| main.rs:282:36:282:36 | n | main.rs:282:36:282:36 | [SSA] n | +| main.rs:282:43:282:49 | sink(...) | main.rs:280:5:283:5 | match s1 { ... } | +| main.rs:284:11:284:12 | s1 | main.rs:285:9:285:71 | ... \| ... | +| main.rs:285:9:285:71 | ... \| ... | main.rs:285:9:285:38 | ...::C {...} | +| main.rs:285:9:285:71 | ... \| ... | main.rs:285:42:285:71 | ...::D {...} | +| main.rs:285:9:285:71 | [SSA] [match(true)] phi | main.rs:285:81:285:81 | n | +| main.rs:285:36:285:36 | [SSA] [input] [match(true)] phi | main.rs:285:9:285:71 | [SSA] [match(true)] phi | +| main.rs:285:36:285:36 | [SSA] n | main.rs:285:36:285:36 | [SSA] [input] [match(true)] phi | +| main.rs:285:36:285:36 | n | main.rs:285:36:285:36 | [SSA] n | +| main.rs:285:69:285:69 | [SSA] [input] [match(true)] phi | main.rs:285:9:285:71 | [SSA] [match(true)] phi | +| main.rs:285:69:285:69 | [SSA] n | main.rs:285:69:285:69 | [SSA] [input] [match(true)] phi | +| main.rs:285:69:285:69 | n | main.rs:285:69:285:69 | [SSA] n | +| main.rs:285:76:285:82 | sink(...) | main.rs:284:5:286:5 | match s1 { ... } | +| main.rs:287:5:290:5 | match s2 { ... } | main.rs:275:49:291:1 | { ... } | +| main.rs:287:11:287:12 | s2 | main.rs:288:9:288:38 | ...::C {...} | +| main.rs:287:11:287:12 | s2 | main.rs:289:9:289:38 | ...::D {...} | +| main.rs:288:36:288:36 | [SSA] n | main.rs:288:48:288:48 | n | +| main.rs:288:36:288:36 | n | main.rs:288:36:288:36 | [SSA] n | +| main.rs:288:43:288:49 | sink(...) | main.rs:287:5:290:5 | match s2 { ... } | +| main.rs:289:36:289:36 | [SSA] n | main.rs:289:48:289:48 | n | +| main.rs:289:36:289:36 | n | main.rs:289:36:289:36 | [SSA] n | +| main.rs:289:43:289:49 | sink(...) | main.rs:287:5:290:5 | match s2 { ... } | +| main.rs:296:9:296:10 | [SSA] s1 | main.rs:300:11:300:12 | s1 | +| main.rs:296:9:296:10 | s1 | main.rs:296:9:296:10 | [SSA] s1 | +| main.rs:296:14:298:5 | C {...} | main.rs:296:9:296:10 | s1 | +| main.rs:299:9:299:10 | [SSA] s2 | main.rs:307:11:307:12 | s2 | +| main.rs:299:9:299:10 | s2 | main.rs:299:9:299:10 | [SSA] s2 | +| main.rs:299:14:299:29 | D {...} | main.rs:299:9:299:10 | s2 | +| main.rs:300:11:300:12 | s1 | main.rs:301:9:301:24 | C {...} | +| main.rs:300:11:300:12 | s1 | main.rs:302:9:302:24 | D {...} | +| main.rs:300:11:300:12 | s1 | main.rs:304:11:304:12 | s1 | +| main.rs:301:22:301:22 | [SSA] n | main.rs:301:34:301:34 | n | +| main.rs:301:22:301:22 | n | main.rs:301:22:301:22 | [SSA] n | +| main.rs:301:29:301:35 | sink(...) | main.rs:300:5:303:5 | match s1 { ... } | +| main.rs:302:22:302:22 | [SSA] n | main.rs:302:34:302:34 | n | +| main.rs:302:22:302:22 | n | main.rs:302:22:302:22 | [SSA] n | +| main.rs:302:29:302:35 | sink(...) | main.rs:300:5:303:5 | match s1 { ... } | +| main.rs:304:11:304:12 | s1 | main.rs:305:9:305:43 | ... \| ... | +| main.rs:305:9:305:43 | ... \| ... | main.rs:305:9:305:24 | C {...} | +| main.rs:305:9:305:43 | ... \| ... | main.rs:305:28:305:43 | D {...} | +| main.rs:305:9:305:43 | [SSA] [match(true)] phi | main.rs:305:53:305:53 | n | +| main.rs:305:22:305:22 | [SSA] [input] [match(true)] phi | main.rs:305:9:305:43 | [SSA] [match(true)] phi | +| main.rs:305:22:305:22 | [SSA] n | main.rs:305:22:305:22 | [SSA] [input] [match(true)] phi | +| main.rs:305:22:305:22 | n | main.rs:305:22:305:22 | [SSA] n | +| main.rs:305:41:305:41 | [SSA] [input] [match(true)] phi | main.rs:305:9:305:43 | [SSA] [match(true)] phi | +| main.rs:305:41:305:41 | [SSA] n | main.rs:305:41:305:41 | [SSA] [input] [match(true)] phi | +| main.rs:305:41:305:41 | n | main.rs:305:41:305:41 | [SSA] n | +| main.rs:305:48:305:54 | sink(...) | main.rs:304:5:306:5 | match s1 { ... } | +| main.rs:307:5:310:5 | match s2 { ... } | main.rs:295:51:311:1 | { ... } | +| main.rs:307:11:307:12 | s2 | main.rs:308:9:308:24 | C {...} | +| main.rs:307:11:307:12 | s2 | main.rs:309:9:309:24 | D {...} | +| main.rs:308:22:308:22 | [SSA] n | main.rs:308:34:308:34 | n | +| main.rs:308:22:308:22 | n | main.rs:308:22:308:22 | [SSA] n | +| main.rs:308:29:308:35 | sink(...) | main.rs:307:5:310:5 | match s2 { ... } | +| main.rs:309:22:309:22 | [SSA] n | main.rs:309:34:309:34 | n | +| main.rs:309:22:309:22 | n | main.rs:309:22:309:22 | [SSA] n | +| main.rs:309:29:309:35 | sink(...) | main.rs:307:5:310:5 | match s2 { ... } | storeStep | main.rs:94:14:94:22 | source(...) | tuple.0 | main.rs:94:13:94:26 | TupleExpr | | main.rs:94:25:94:25 | 2 | tuple.1 | main.rs:94:13:94:26 | TupleExpr | @@ -371,12 +374,14 @@ storeStep | main.rs:186:12:186:12 | 4 | Point3D.z | main.rs:181:13:187:5 | Point3D {...} | | main.rs:214:19:214:28 | source(...) | Some | main.rs:214:14:214:29 | Some(...) | | main.rs:215:19:215:19 | 2 | Some | main.rs:215:14:215:20 | Some(...) | -| main.rs:232:29:232:38 | source(...) | A | main.rs:232:14:232:39 | ...::A(...) | -| main.rs:233:29:233:29 | 2 | B | main.rs:233:14:233:30 | ...::B(...) | -| main.rs:272:18:272:27 | source(...) | C | main.rs:271:14:273:5 | ...::C {...} | -| main.rs:274:41:274:41 | 2 | D | main.rs:274:14:274:43 | ...::D {...} | -| main.rs:312:27:312:27 | 0 | Some | main.rs:312:22:312:28 | Some(...) | +| main.rs:227:19:227:28 | source(...) | Some | main.rs:227:14:227:29 | Some(...) | +| main.rs:237:29:237:38 | source(...) | A | main.rs:237:14:237:39 | ...::A(...) | +| main.rs:238:29:238:29 | 2 | B | main.rs:238:14:238:30 | ...::B(...) | +| main.rs:277:18:277:27 | source(...) | C | main.rs:276:14:278:5 | ...::C {...} | +| main.rs:279:41:279:41 | 2 | D | main.rs:279:14:279:43 | ...::D {...} | +| main.rs:317:27:317:27 | 0 | Some | main.rs:317:22:317:28 | Some(...) | readStep +| file://:0:0:0:0 | [summary param] self in lang:core::_::::unwrap | Some | file://:0:0:0:0 | [summary] read: Argument[self].Variant[crate::std::option::Option::Some(0)] in lang:core::_::::unwrap | | main.rs:33:9:33:15 | TupleStructPat | Some | main.rs:33:14:33:14 | _ | | main.rs:95:10:95:10 | a | tuple.0 | main.rs:95:10:95:12 | a.0 | | main.rs:96:10:96:10 | a | tuple.1 | main.rs:96:10:96:12 | a.1 | @@ -396,15 +401,15 @@ readStep | main.rs:189:9:189:45 | Point3D {...} | Point3D.plane | main.rs:189:26:189:39 | Point {...} | | main.rs:217:9:217:15 | TupleStructPat | Some | main.rs:217:14:217:14 | n | | main.rs:221:9:221:15 | TupleStructPat | Some | main.rs:221:14:221:14 | n | -| main.rs:235:9:235:25 | TupleStructPat | A | main.rs:235:24:235:24 | n | -| main.rs:236:9:236:25 | TupleStructPat | B | main.rs:236:24:236:24 | n | -| main.rs:239:9:239:25 | TupleStructPat | A | main.rs:239:24:239:24 | n | -| main.rs:239:29:239:45 | TupleStructPat | B | main.rs:239:44:239:44 | n | -| main.rs:242:9:242:25 | TupleStructPat | A | main.rs:242:24:242:24 | n | -| main.rs:243:9:243:25 | TupleStructPat | B | main.rs:243:24:243:24 | n | -| main.rs:276:9:276:38 | ...::C {...} | C | main.rs:276:36:276:36 | n | -| main.rs:277:9:277:38 | ...::D {...} | D | main.rs:277:36:277:36 | n | -| main.rs:280:9:280:38 | ...::C {...} | C | main.rs:280:36:280:36 | n | -| main.rs:280:42:280:71 | ...::D {...} | D | main.rs:280:69:280:69 | n | -| main.rs:283:9:283:38 | ...::C {...} | C | main.rs:283:36:283:36 | n | -| main.rs:284:9:284:38 | ...::D {...} | D | main.rs:284:36:284:36 | n | +| main.rs:240:9:240:25 | TupleStructPat | A | main.rs:240:24:240:24 | n | +| main.rs:241:9:241:25 | TupleStructPat | B | main.rs:241:24:241:24 | n | +| main.rs:244:9:244:25 | TupleStructPat | A | main.rs:244:24:244:24 | n | +| main.rs:244:29:244:45 | TupleStructPat | B | main.rs:244:44:244:44 | n | +| main.rs:247:9:247:25 | TupleStructPat | A | main.rs:247:24:247:24 | n | +| main.rs:248:9:248:25 | TupleStructPat | B | main.rs:248:24:248:24 | n | +| main.rs:281:9:281:38 | ...::C {...} | C | main.rs:281:36:281:36 | n | +| main.rs:282:9:282:38 | ...::D {...} | D | main.rs:282:36:282:36 | n | +| main.rs:285:9:285:38 | ...::C {...} | C | main.rs:285:36:285:36 | n | +| main.rs:285:42:285:71 | ...::D {...} | D | main.rs:285:69:285:69 | n | +| main.rs:288:9:288:38 | ...::C {...} | C | main.rs:288:36:288:36 | n | +| main.rs:289:9:289:38 | ...::D {...} | D | main.rs:289:36:289:36 | n | diff --git a/rust/ql/test/library-tests/dataflow/local/inline-flow.expected b/rust/ql/test/library-tests/dataflow/local/inline-flow.expected index 6a40a2fc547b..0ed81dbc3e60 100644 --- a/rust/ql/test/library-tests/dataflow/local/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/local/inline-flow.expected @@ -29,20 +29,23 @@ edges | main.rs:214:19:214:28 | source(...) | main.rs:214:14:214:29 | Some(...) [Some] | provenance | | | main.rs:217:9:217:15 | TupleStructPat [Some] | main.rs:217:14:217:14 | n | provenance | | | main.rs:217:14:217:14 | n | main.rs:217:25:217:25 | n | provenance | | -| main.rs:232:14:232:39 | ...::A(...) [A] | main.rs:235:9:235:25 | TupleStructPat [A] | provenance | | -| main.rs:232:14:232:39 | ...::A(...) [A] | main.rs:239:9:239:25 | TupleStructPat [A] | provenance | | -| main.rs:232:29:232:38 | source(...) | main.rs:232:14:232:39 | ...::A(...) [A] | provenance | | -| main.rs:235:9:235:25 | TupleStructPat [A] | main.rs:235:24:235:24 | n | provenance | | -| main.rs:235:24:235:24 | n | main.rs:235:35:235:35 | n | provenance | | -| main.rs:239:9:239:25 | TupleStructPat [A] | main.rs:239:24:239:24 | n | provenance | | -| main.rs:239:24:239:24 | n | main.rs:239:55:239:55 | n | provenance | | -| main.rs:271:14:273:5 | ...::C {...} [C] | main.rs:276:9:276:38 | ...::C {...} [C] | provenance | | -| main.rs:271:14:273:5 | ...::C {...} [C] | main.rs:280:9:280:38 | ...::C {...} [C] | provenance | | -| main.rs:272:18:272:27 | source(...) | main.rs:271:14:273:5 | ...::C {...} [C] | provenance | | -| main.rs:276:9:276:38 | ...::C {...} [C] | main.rs:276:36:276:36 | n | provenance | | -| main.rs:276:36:276:36 | n | main.rs:276:48:276:48 | n | provenance | | -| main.rs:280:9:280:38 | ...::C {...} [C] | main.rs:280:36:280:36 | n | provenance | | -| main.rs:280:36:280:36 | n | main.rs:280:81:280:81 | n | provenance | | +| main.rs:227:14:227:29 | Some(...) [Some] | main.rs:228:10:228:11 | s1 [Some] | provenance | | +| main.rs:227:19:227:28 | source(...) | main.rs:227:14:227:29 | Some(...) [Some] | provenance | | +| main.rs:228:10:228:11 | s1 [Some] | main.rs:228:10:228:20 | ... .unwrap(...) | provenance | | +| main.rs:237:14:237:39 | ...::A(...) [A] | main.rs:240:9:240:25 | TupleStructPat [A] | provenance | | +| main.rs:237:14:237:39 | ...::A(...) [A] | main.rs:244:9:244:25 | TupleStructPat [A] | provenance | | +| main.rs:237:29:237:38 | source(...) | main.rs:237:14:237:39 | ...::A(...) [A] | provenance | | +| main.rs:240:9:240:25 | TupleStructPat [A] | main.rs:240:24:240:24 | n | provenance | | +| main.rs:240:24:240:24 | n | main.rs:240:35:240:35 | n | provenance | | +| main.rs:244:9:244:25 | TupleStructPat [A] | main.rs:244:24:244:24 | n | provenance | | +| main.rs:244:24:244:24 | n | main.rs:244:55:244:55 | n | provenance | | +| main.rs:276:14:278:5 | ...::C {...} [C] | main.rs:281:9:281:38 | ...::C {...} [C] | provenance | | +| main.rs:276:14:278:5 | ...::C {...} [C] | main.rs:285:9:285:38 | ...::C {...} [C] | provenance | | +| main.rs:277:18:277:27 | source(...) | main.rs:276:14:278:5 | ...::C {...} [C] | provenance | | +| main.rs:281:9:281:38 | ...::C {...} [C] | main.rs:281:36:281:36 | n | provenance | | +| main.rs:281:36:281:36 | n | main.rs:281:48:281:48 | n | provenance | | +| main.rs:285:9:285:38 | ...::C {...} [C] | main.rs:285:36:285:36 | n | provenance | | +| main.rs:285:36:285:36 | n | main.rs:285:81:285:81 | n | provenance | | nodes | main.rs:15:10:15:18 | source(...) | semmle.label | source(...) | | main.rs:19:13:19:21 | source(...) | semmle.label | source(...) | @@ -85,22 +88,26 @@ nodes | main.rs:217:9:217:15 | TupleStructPat [Some] | semmle.label | TupleStructPat [Some] | | main.rs:217:14:217:14 | n | semmle.label | n | | main.rs:217:25:217:25 | n | semmle.label | n | -| main.rs:232:14:232:39 | ...::A(...) [A] | semmle.label | ...::A(...) [A] | -| main.rs:232:29:232:38 | source(...) | semmle.label | source(...) | -| main.rs:235:9:235:25 | TupleStructPat [A] | semmle.label | TupleStructPat [A] | -| main.rs:235:24:235:24 | n | semmle.label | n | -| main.rs:235:35:235:35 | n | semmle.label | n | -| main.rs:239:9:239:25 | TupleStructPat [A] | semmle.label | TupleStructPat [A] | -| main.rs:239:24:239:24 | n | semmle.label | n | -| main.rs:239:55:239:55 | n | semmle.label | n | -| main.rs:271:14:273:5 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | -| main.rs:272:18:272:27 | source(...) | semmle.label | source(...) | -| main.rs:276:9:276:38 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | -| main.rs:276:36:276:36 | n | semmle.label | n | -| main.rs:276:48:276:48 | n | semmle.label | n | -| main.rs:280:9:280:38 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | -| main.rs:280:36:280:36 | n | semmle.label | n | -| main.rs:280:81:280:81 | n | semmle.label | n | +| main.rs:227:14:227:29 | Some(...) [Some] | semmle.label | Some(...) [Some] | +| main.rs:227:19:227:28 | source(...) | semmle.label | source(...) | +| main.rs:228:10:228:11 | s1 [Some] | semmle.label | s1 [Some] | +| main.rs:228:10:228:20 | ... .unwrap(...) | semmle.label | ... .unwrap(...) | +| main.rs:237:14:237:39 | ...::A(...) [A] | semmle.label | ...::A(...) [A] | +| main.rs:237:29:237:38 | source(...) | semmle.label | source(...) | +| main.rs:240:9:240:25 | TupleStructPat [A] | semmle.label | TupleStructPat [A] | +| main.rs:240:24:240:24 | n | semmle.label | n | +| main.rs:240:35:240:35 | n | semmle.label | n | +| main.rs:244:9:244:25 | TupleStructPat [A] | semmle.label | TupleStructPat [A] | +| main.rs:244:24:244:24 | n | semmle.label | n | +| main.rs:244:55:244:55 | n | semmle.label | n | +| main.rs:276:14:278:5 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:277:18:277:27 | source(...) | semmle.label | source(...) | +| main.rs:281:9:281:38 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:281:36:281:36 | n | semmle.label | n | +| main.rs:281:48:281:48 | n | semmle.label | n | +| main.rs:285:9:285:38 | ...::C {...} [C] | semmle.label | ...::C {...} [C] | +| main.rs:285:36:285:36 | n | semmle.label | n | +| main.rs:285:81:285:81 | n | semmle.label | n | subpaths testFailures #select @@ -116,7 +123,8 @@ testFailures | main.rs:121:10:121:14 | ... .1 | main.rs:118:17:118:26 | source(...) | main.rs:121:10:121:14 | ... .1 | $@ | main.rs:118:17:118:26 | source(...) | source(...) | | main.rs:158:10:158:10 | a | main.rs:154:12:154:21 | source(...) | main.rs:158:10:158:10 | a | $@ | main.rs:154:12:154:21 | source(...) | source(...) | | main.rs:217:25:217:25 | n | main.rs:214:19:214:28 | source(...) | main.rs:217:25:217:25 | n | $@ | main.rs:214:19:214:28 | source(...) | source(...) | -| main.rs:235:35:235:35 | n | main.rs:232:29:232:38 | source(...) | main.rs:235:35:235:35 | n | $@ | main.rs:232:29:232:38 | source(...) | source(...) | -| main.rs:239:55:239:55 | n | main.rs:232:29:232:38 | source(...) | main.rs:239:55:239:55 | n | $@ | main.rs:232:29:232:38 | source(...) | source(...) | -| main.rs:276:48:276:48 | n | main.rs:272:18:272:27 | source(...) | main.rs:276:48:276:48 | n | $@ | main.rs:272:18:272:27 | source(...) | source(...) | -| main.rs:280:81:280:81 | n | main.rs:272:18:272:27 | source(...) | main.rs:280:81:280:81 | n | $@ | main.rs:272:18:272:27 | source(...) | source(...) | +| main.rs:228:10:228:20 | ... .unwrap(...) | main.rs:227:19:227:28 | source(...) | main.rs:228:10:228:20 | ... .unwrap(...) | $@ | main.rs:227:19:227:28 | source(...) | source(...) | +| main.rs:240:35:240:35 | n | main.rs:237:29:237:38 | source(...) | main.rs:240:35:240:35 | n | $@ | main.rs:237:29:237:38 | source(...) | source(...) | +| main.rs:244:55:244:55 | n | main.rs:237:29:237:38 | source(...) | main.rs:244:55:244:55 | n | $@ | main.rs:237:29:237:38 | source(...) | source(...) | +| main.rs:281:48:281:48 | n | main.rs:277:18:277:27 | source(...) | main.rs:281:48:281:48 | n | $@ | main.rs:277:18:277:27 | source(...) | source(...) | +| main.rs:285:81:285:81 | n | main.rs:277:18:277:27 | source(...) | main.rs:285:81:285:81 | n | $@ | main.rs:277:18:277:27 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/local/main.rs b/rust/ql/test/library-tests/dataflow/local/main.rs index b4dc9799891d..5261c5b0b663 100644 --- a/rust/ql/test/library-tests/dataflow/local/main.rs +++ b/rust/ql/test/library-tests/dataflow/local/main.rs @@ -223,6 +223,11 @@ fn option_pattern_match_unqualified() { } } +fn option_unwrap() { + let s1 = Some(source(19)); + sink(s1.unwrap()); // $ hasValueFlow=19 +} + enum MyTupleEnum { A(i64), B(i64), @@ -324,6 +329,7 @@ fn main() { struct_nested_match(); option_pattern_match_qualified(); option_pattern_match_unqualified(); + option_unwrap(); custom_tuple_enum_pattern_match_qualified(); custom_tuple_enum_pattern_match_unqualified(); custom_record_enum_pattern_match_qualified(); From 52dc79eb7db7ece5134dd58aa39a8cd157d17a9e Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 3 Dec 2024 09:28:21 +0100 Subject: [PATCH 4/4] Address review comments --- .../lib/codeql/rust/dataflow/FlowSummary.qll | 6 +++--- .../rust/dataflow/internal/DataFlowImpl.qll | 21 +++++-------------- .../dataflow/internal/FlowSummaryImpl.qll | 7 +------ .../elements/internal/CallExprBaseImpl.qll | 2 +- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll index bbeb33ad13a6..3d238c05fd0b 100644 --- a/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll +++ b/rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll @@ -2,13 +2,13 @@ private import rust private import internal.FlowSummaryImpl as Impl -private import internal.DataFlowImpl +private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprBaseImpl // import all instances below private module Summaries { private import codeql.rust.Frameworks - // TODO: Used models-as-data when it's available + // TODO: Use models-as-data when it's available private class UnwrapSummary extends SummarizedCallable::Range { UnwrapSummary() { this = "lang:core::_::::unwrap" } @@ -30,7 +30,7 @@ module LibraryCallable { /** Gets a call to this library callable. */ CallExprBase getACall() { exists(Resolvable r, string crate | - r = getCallResolvable(result) and + r = CallExprBaseImpl::getCallResolvable(result) and this = crate + r.getResolvedPath() | crate = r.getResolvedCrateOrigin() + "::_::" diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 8c15353b58b2..9161d409da2b 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -173,7 +173,7 @@ module Node { override Location getLocation() { none() } } - /** A data-flow node used to model flow summaries. */ + /** A data flow node used to model flow summaries. */ class FlowSummaryNode extends Node, TFlowSummaryNode { FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) } @@ -375,10 +375,6 @@ module Node { /** Gets the node before the state update. */ abstract Node getPreUpdateNode(); - override CfgScope getCfgScope() { result = this.getPreUpdateNode().getCfgScope() } - - override Location getLocation() { result = this.getPreUpdateNode().getLocation() } - override string toString() { result = "[post] " + this.getPreUpdateNode().toString() } } @@ -388,6 +384,10 @@ module Node { ExprPostUpdateNode() { this = TExprPostUpdateNode(n) } override Node getPreUpdateNode() { result = TExprNode(n) } + + override CfgScope getCfgScope() { result = n.getScope() } + + override Location getLocation() { result = n.getLocation() } } final class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode { @@ -399,10 +399,6 @@ module Node { override Node getPreUpdateNode() { result = pre } - override CfgScope getCfgScope() { result = PostUpdateNode.super.getCfgScope() } - - override EmptyLocation getLocation() { result = PostUpdateNode.super.getLocation() } - final override string toString() { result = PostUpdateNode.super.toString() } } @@ -689,13 +685,6 @@ private module Aliases { class ContentSetAlias = ContentSet; } -pragma[nomagic] -Resolvable getCallResolvable(CallExprBase call) { - result = call.(MethodCallExpr) - or - result = call.(CallExpr).getFunction().(PathExpr).getPath() -} - module RustDataFlow implements InputSig { private import Aliases diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll index 1292a936afdc..3503ad073328 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll @@ -15,12 +15,7 @@ module Input implements InputSig { ReturnKind getStandardReturnValueKind() { result = TNormalReturnKind() } - string encodeParameterPosition(ParameterPosition pos) { - result = pos.getPosition().toString() - or - pos.isSelf() and - result = "self" - } + string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() } predicate encodeArgumentPosition = encodeParameterPosition/1; diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll index d0e4049a05d5..3729bb8eeb7f 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll @@ -17,7 +17,7 @@ module Impl { private import codeql.rust.elements.internal.PathExprImpl::Impl pragma[nomagic] - private Resolvable getCallResolvable(CallExprBase call) { + Resolvable getCallResolvable(CallExprBase call) { result = call.(MethodCallExpr) or result = call.(CallExpr).getFunction().(PathExpr).getPath()