From 189c16095d978cdcbaa6bc97feaaae445f50e6e8 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 4 Mar 2026 08:54:58 +0100 Subject: [PATCH 1/5] Data flow: Add `FeatureEscapesSourceCallContext(OrEqualSourceSinkCallContext)` flow feature --- .../codeql/dataflow/internal/DataFlowImpl.qll | 181 ++++++++++++++---- .../dataflow/internal/DataFlowImplCommon.qll | 34 +++- .../dataflow/internal/DataFlowImplStage1.qll | 6 + 3 files changed, 182 insertions(+), 39 deletions(-) diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index aa74e44a8e80..d0c48d8148c1 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -567,7 +567,7 @@ module MakeImpl Lang> { ) { sourceNode(node) and (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - summaryCtx = TSummaryCtxNone() and + summaryCtx.isSourceCtx() and t = getNodeTyp(node) and ap instanceof ApNil and apa = getApprox(ap) and @@ -602,18 +602,19 @@ module MakeImpl Lang> { apa = getApprox(ap) or // flow into a callable without summary context - fwdFlowInNoFlowThrough(node, cc, t, ap, stored) and + fwdFlowInNoFlowThrough(node, cc, summaryCtx, t, ap, stored) and apa = getApprox(ap) and - summaryCtx = TSummaryCtxNone() and // When the call contexts of source and sink needs to match then there's // never any reason to enter a callable except to find a summary. See also // the comment in `PathNodeMid::isAtSink`. not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext or // flow into a callable with summary context (non-linear recursion) - fwdFlowInFlowThrough(node, cc, t, ap, stored) and - apa = getApprox(ap) and - summaryCtx = TSummaryCtxSome(node, t, ap, stored) + exists(boolean mustReturn | + fwdFlowInFlowThrough(node, cc, t, ap, stored, mustReturn) and + apa = getApprox(ap) and + summaryCtx = TSummaryCtxSome(node, t, ap, stored, mustReturn) + ) or // flow out of a callable fwdFlowOut(_, _, node, cc, summaryCtx, t, ap, stored) and @@ -630,9 +631,10 @@ module MakeImpl Lang> { private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) { - fwdFlowInFlowThrough(p, _, t, ap, stored) - } + TSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored, Boolean mustReturn) { + fwdFlowInFlowThrough(p, _, t, ap, stored, mustReturn) + } or + TSummaryCtxSource(Boolean mustEscape) /** * A context for generating flow summaries. This represents flow entry through @@ -644,6 +646,69 @@ module MakeImpl Lang> { abstract string toString(); abstract Location getLocation(); + + /** + * Holds if this context is the unique context used at flow sources. + */ + predicate isSourceCtx() { + exists(boolean strict | + Stage1::hasFeatureEscapesSourceCallContext(strict) and + this = TSummaryCtxSource(strict) + ) + or + not Stage1::hasFeatureEscapesSourceCallContext(_) and + this = TSummaryCtxNone() + } + + pragma[nomagic] + private predicate isSome(boolean mustReturn) { + this = TSummaryCtxSome(_, _, _, _, mustReturn) + } + + /** + * Holds if this context is valid as a flow-in context when no flow-through is possible, + * in which case `innerSummaryCtx` is the summary context to be used when entering the + * callable. + */ + pragma[nomagic] + predicate isValidForFlowInNoThrough(SummaryCtx innerSummaryCtx) { + this = TSummaryCtxNone() and + innerSummaryCtx = TSummaryCtxNone() + or + this.isSome(false) and + innerSummaryCtx = TSummaryCtxNone() + or + // Even if we must escape the source call context, we can still allow for flow-in + // without flow-through, as long as the inner context is escaped (which must then + // necessarily be via a jump-step -- possibly after even more flow-in + // without-flow-through steps). + this = TSummaryCtxSource(_) and + innerSummaryCtx = TSummaryCtxSource(true) + } + + /** + * Holds if this context is valid as a flow-in context when flow-through is possible. + * + * The boolean `mustReturn` indicates whether flow must return. + */ + predicate isValidForFlowThrough(boolean mustReturn) { + this = TSummaryCtxSource(_) and + mustReturn = true + or + this = TSummaryCtxNone() and + mustReturn = false + or + this.isSome(mustReturn) + } + + /** Holds if this context is valid as a sink context. */ + predicate isASinkCtx() { + this = TSummaryCtxNone() + or + this.isSome(false) + or + this = TSummaryCtxSource(false) + } } /** A summary context from which no flow summary can be generated. */ @@ -659,20 +724,43 @@ module MakeImpl Lang> { private Typ t; private Ap ap; private TypOption stored; + private boolean mustReturn; - SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored) } + SummaryCtxSome() { this = TSummaryCtxSome(p, t, ap, stored, mustReturn) } ParamNd getParamNode() { result = p } private string ppTyp() { result = t.toString() and result != "" } + private string ppMustReturn() { + if mustReturn = true then result = " " else result = "" + } + override string toString() { - result = p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored) + result = + p + concat(" : " + this.ppTyp()) + " " + ap + ppStored(stored) + this.ppMustReturn() } override Location getLocation() { result = p.getLocation() } } + /** + * A special summary context that is used when the flow feature + * `FeatureEscapesSourceCallContext(OrEqualSourceSinkCallContext)` + * is enabled. + */ + private class SummaryCtxSource extends SummaryCtx, TSummaryCtxSource { + private boolean mustEscape; + + SummaryCtxSource() { this = TSummaryCtxSource(mustEscape) } + + override string toString() { + if mustEscape = true then result = "" else result = "" + } + + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } + } + private predicate fwdFlowJump(Nd node, Typ t, Ap ap, TypOption stored) { exists(Nd mid | fwdFlow(mid, _, _, t, ap, stored) and @@ -915,9 +1003,12 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInNoFlowThrough( - ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored + ParamNd p, CcCall innercc, SummaryCtx innerSummaryCtx, Typ t, Ap ap, TypOption stored ) { - FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _) + exists(SummaryCtx summaryCtx | + FwdFlowInNoThrough::fwdFlowIn(_, _, _, p, _, innercc, summaryCtx, t, ap, stored, _) and + summaryCtx.isValidForFlowInNoThrough(innerSummaryCtx) + ) } private predicate top() { any() } @@ -926,9 +1017,12 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowInFlowThrough( - ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored + ParamNd p, CcCall innercc, Typ t, Ap ap, TypOption stored, boolean mustReturn ) { - FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, _, t, ap, stored, _) + exists(SummaryCtx outerSummaryCtx | + FwdFlowInThrough::fwdFlowIn(_, _, _, p, _, innercc, outerSummaryCtx, t, ap, stored, _) and + outerSummaryCtx.isValidForFlowThrough(mustReturn) + ) } pragma[nomagic] @@ -999,7 +1093,8 @@ module MakeImpl Lang> { TypOption stored ) { exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow | - fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and + fwdFlowIntoRet(ret, innercc, _, t, ap, stored) and + summaryCtx = TSummaryCtxNone() and fwdFlowOutValidEdge(call, ret, innercc, inner, out, outercc, allowsFieldFlow) and if allowsFieldFlow = false then ap instanceof ApNil else any() ) @@ -1090,7 +1185,7 @@ module MakeImpl Lang> { instanceofCcCall(ccc) and fwdFlow(pragma[only_bind_into](ret), ccc, summaryCtx, t, ap, stored) and summaryCtx = - TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _) and + TSummaryCtxSome(pragma[only_bind_into](p), _, pragma[only_bind_into](argAp), _, _) and kind = ret.getKind() and Stage1::parameterFlowThroughAllowed(p, kind) and PrevStage::returnMayFlowThrough(ret, kind) @@ -1116,9 +1211,10 @@ module MakeImpl Lang> { pragma[nomagic] private predicate fwdFlowIsEntered0( Call call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, ParamNd p, Typ t, - Ap ap, TypOption stored + Ap ap, TypOption stored, boolean mustReturn ) { - FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _) + FwdFlowInThrough::fwdFlowIn(call, arg, _, p, cc, innerCc, summaryCtx, t, ap, stored, _) and + summaryCtx.isValidForFlowThrough(mustReturn) } /** @@ -1130,9 +1226,9 @@ module MakeImpl Lang> { Call call, ArgNd arg, Cc cc, CcCall innerCc, SummaryCtx summaryCtx, SummaryCtxSome innerSummaryCtx ) { - exists(ParamNd p, Typ t, Ap ap, TypOption stored | - fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored) and - innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored) + exists(ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn | + fwdFlowIsEntered0(call, arg, cc, innerCc, summaryCtx, p, t, ap, stored, mustReturn) and + innerSummaryCtx = TSummaryCtxSome(p, t, ap, stored, mustReturn) ) } @@ -1160,7 +1256,7 @@ module MakeImpl Lang> { TypOption argStored, Ap ap ) { exists(Call call, boolean allowsFieldFlow | - returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored)) and + returnFlowsThrough0(call, ccc, ap, ret, TSummaryCtxSome(p, argT, argAp, argStored, _)) and flowThroughOutOfCall(call, ret, _, allowsFieldFlow) and pos = ret.getReturnPosition() and if allowsFieldFlow = false then ap instanceof ApNil else any() @@ -1216,7 +1312,7 @@ module MakeImpl Lang> { pragma[nomagic] private predicate revFlow0(Nd node, ReturnCtx returnCtx, ApOption returnAp, Ap ap) { - fwdFlow(node, _, _, _, ap, _) and + fwdFlow(node, _, any(SummaryCtx sinkCtx | sinkCtx.isASinkCtx()), _, ap, _) and sinkNode(node) and ( if hasSinkCallCtx() @@ -1490,7 +1586,7 @@ module MakeImpl Lang> { exists(Ap ap0 | parameterMayFlowThrough(p, _) and revFlow(n, TReturnCtxMaybeFlowThrough(_), _, ap0) and - fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _), _, ap0, _) + fwdFlow(n, any(CcCall ccc), TSummaryCtxSome(p, _, ap, _, _), _, ap0, _) ) } @@ -1962,6 +2058,8 @@ module MakeImpl Lang> { private string ppSummaryCtx() { summaryCtx instanceof SummaryCtxNone and result = "" or + result = " " + summaryCtx.(SummaryCtxSource) + or summaryCtx instanceof SummaryCtxSome and result = " <" + summaryCtx + ">" } @@ -1983,7 +2081,7 @@ module MakeImpl Lang> { override predicate isSource() { sourceNode(node) and (if hasSourceCallCtx() then cc = ccSomeCall() else cc = ccNone()) and - summaryCtx = TSummaryCtxNone() and + summaryCtx.isSourceCtx() and t = getNodeTyp(node) and ap instanceof ApNil } @@ -1991,6 +2089,7 @@ module MakeImpl Lang> { predicate isAtSink() { sinkNode(node) and ap instanceof ApNil and + summaryCtx.isASinkCtx() and // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` // is exactly what we need to check. // For `FeatureEqualSourceSinkCallContext` the initial call @@ -2042,10 +2141,12 @@ module MakeImpl Lang> { override predicate isSource() { sourceNode(node) } } - bindingset[p, t, ap, stored] + bindingset[p, t, ap, stored, mustReturn] pragma[inline_late] - private SummaryCtxSome mkSummaryCtxSome(ParamNd p, Typ t, Ap ap, TypOption stored) { - result = TSummaryCtxSome(p, t, ap, stored) + private SummaryCtxSome mkSummaryCtxSome( + ParamNd p, Typ t, Ap ap, TypOption stored, boolean mustReturn + ) { + result = TSummaryCtxSome(p, t, ap, stored, mustReturn) } pragma[nomagic] @@ -2055,11 +2156,14 @@ module MakeImpl Lang> { ) { FwdFlowInNoThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap, stored, _) and - innerSummaryCtx = TSummaryCtxNone() + outerSummaryCtx.isValidForFlowInNoThrough(innerSummaryCtx) or - FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap, - stored, _) and - innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored) + exists(boolean mustReturn | + FwdFlowInThrough::fwdFlowIn(_, arg, _, p, outercc, innercc, outerSummaryCtx, t, ap, + stored, _) and + outerSummaryCtx.isValidForFlowThrough(mustReturn) and + innerSummaryCtx = mkSummaryCtxSome(p, t, ap, stored, mustReturn) + ) } pragma[nomagic] @@ -2098,7 +2202,7 @@ module MakeImpl Lang> { | fwdFlowThroughStep0(call, arg, cc, ccc, summaryCtx, t, ap, stored, ret, innerSummaryCtx) and - innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored) and + innerSummaryCtx = TSummaryCtxSome(p, innerArgT, innerArgAp, innerArgStored, _) and pn1 = mkPathNode(arg, cc, summaryCtx, innerArgT, innerArgAp, innerArgStored) and pn2 = typeStrengthenToPathNode(p, ccc, innerSummaryCtx, innerArgT, innerArgAp, @@ -2212,11 +2316,14 @@ module MakeImpl Lang> { ) or // flow out of a callable - exists(RetNd ret, CcNoCall innercc, boolean allowsFieldFlow | - pn1 = TPathNodeMid(ret, innercc, summaryCtx, t, ap, stored) and - fwdFlowIntoRet(ret, innercc, summaryCtx, t, ap, stored) and + exists( + RetNd ret, CcNoCall innercc, SummaryCtx innerSummaryCtx, boolean allowsFieldFlow + | + pn1 = TPathNodeMid(ret, innercc, innerSummaryCtx, t, ap, stored) and + fwdFlowIntoRet(ret, innercc, innerSummaryCtx, t, ap, stored) and fwdFlowOutValidEdge(_, ret, innercc, _, node, cc, allowsFieldFlow) and label = "" and + summaryCtx = TSummaryCtxNone() and if allowsFieldFlow = false then ap instanceof ApNil else any() ) } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index 288814c4c511..17bb1be28e60 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -3,6 +3,7 @@ module; private import codeql.dataflow.DataFlow private import codeql.typetracking.TypeTracking as Tt +private import codeql.util.Boolean private import codeql.util.Location private import codeql.util.Option private import codeql.util.Unit @@ -46,10 +47,11 @@ module MakeImplCommon Lang> { } } - private newtype TFlowFeature = + newtype TFlowFeature = TFeatureHasSourceCallContext() or TFeatureHasSinkCallContext() or - TFeatureEqualSourceSinkCallContext() + TFeatureEqualSourceSinkCallContext() or + TFeatureEscapesSourceCallContext(Boolean strict) /** A flow configuration feature for use in `Configuration::getAFeature()`. */ class FlowFeature extends TFlowFeature { @@ -80,6 +82,34 @@ module MakeImplCommon Lang> { override string toString() { result = "FeatureEqualSourceSinkCallContext" } } + /** + * A flow configuration feature that implies that the sink must be reached from + * the source by escaping the source call context, that is, flow must either + * return from the callable containing the source or use a jump-step before reaching + * the sink. + */ + class FeatureEscapesSourceCallContext extends FlowFeature, TFeatureEscapesSourceCallContext { + FeatureEscapesSourceCallContext() { this = TFeatureEscapesSourceCallContext(true) } + + override string toString() { result = "FeatureEscapesSourceCallContext" } + } + + /** + * A flow configuration feature that is the disjuction of `FeatureEscapesSourceCallContext` + * and `FeatureEqualSourceSinkCallContext`. + */ + class FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext extends FlowFeature, + TFeatureEscapesSourceCallContext + { + FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext() { + this = TFeatureEscapesSourceCallContext(false) + } + + override string toString() { + result = "FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext" + } + } + /** * Holds if `source` is a relevant data flow source. */ diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll index bb79ff62f5be..0f50507ea68b 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplStage1.qll @@ -64,6 +64,8 @@ module MakeImplStage1 Lang> { predicate hasSourceCallCtx(); + predicate hasFeatureEscapesSourceCallContext(boolean nonEmpty); + predicate hasSinkCallCtx(); predicate jumpStepEx(Nd node1, Nd node2); @@ -1016,6 +1018,10 @@ module MakeImplStage1 Lang> { ) } + predicate hasFeatureEscapesSourceCallContext(boolean strict) { + Config::getAFeature() = TFeatureEscapesSourceCallContext(strict) + } + predicate hasSinkCallCtx() { exists(FlowFeature feature | feature = Config::getAFeature() | feature instanceof FeatureHasSinkCallContext or From 18d2f586b35971cdf4a8313a2ec923c0bcab0fef Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 4 Mar 2026 09:08:05 +0100 Subject: [PATCH 2/5] Rust: Update `AccessAfterLifetime` query to use `FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext` --- .../security/CWE-825/AccessAfterLifetime.ql | 59 +++++-------------- .../CWE-825/AccessAfterLifetime.expected | 34 +---------- .../query-tests/security/CWE-825/lifetime.rs | 2 +- 3 files changed, 18 insertions(+), 77 deletions(-) diff --git a/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql index 2f2991678930..b27251c397cb 100644 --- a/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql +++ b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql @@ -50,6 +50,10 @@ module AccessAfterLifetimeConfig implements DataFlow::ConfigSig { result = [target.getLocation(), source.getLocation()] ) } + + DataFlow::FlowFeature getAFeature() { + result instanceof DataFlow::FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext + } } module AccessAfterLifetimeFlow = TaintTracking::Global; @@ -64,53 +68,22 @@ predicate sinkBlock(Sink s, BlockExpr be) { be = s.asExpr().getEnclosingBlock() } -private predicate tcStep(BlockExpr a, BlockExpr b) { - // propagate through function calls - exists(Call call | - a = call.getEnclosingBlock() and - call.getARuntimeTarget() = b.getEnclosingCallable() - ) -} - -private predicate isTcSource(BlockExpr be) { sourceBlock(_, _, be) } - -private predicate isTcSink(BlockExpr be) { sinkBlock(_, be) } - -/** - * Holds if block `a` contains block `b`, in the sense that a stack allocated variable in - * `a` may still be on the stack during execution of `b`. This is interprocedural, - * but is an overapproximation that doesn't accurately track call contexts - * (for example if `f` and `g` both call `b`, then depending on the - * caller a variable in `f` or `g` may or may-not be on the stack during `b`). - */ -private predicate mayEncloseOnStack(BlockExpr a, BlockExpr b) = - doublyBoundedFastTC(tcStep/2, isTcSource/1, isTcSink/1)(a, b) - -/** - * Holds if the pair `(source, sink)`, that represents a flow from a - * pointer or reference to a dereference, has its dereference outside the - * lifetime of the target variable `target`. - */ -predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) { - AccessAfterLifetimeFlow::flow(source, sink) and - sourceValueScope(source, target, _) and - not exists(BlockExpr beSource, BlockExpr beSink | - sourceBlock(source, target, beSource) and - sinkBlock(sink, beSink) - | - beSource = beSink - or - mayEncloseOnStack(beSource, beSink) - ) -} - from AccessAfterLifetimeFlow::PathNode sourceNode, AccessAfterLifetimeFlow::PathNode sinkNode, - Variable target + Source source, Sink sink, Variable target where // flow from a pointer or reference to the dereference AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and - // check that the dereference is outside the lifetime of the target - dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target) + source = sourceNode.getNode() and + sink = sinkNode.getNode() and + sourceValueScope(source, target, _) and + // check that the dereference is outside the lifetime of the target, when the source + // and the sink are in the same callable + // (`FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext` handles the case when + // they are not) + not exists(BlockExpr be | + sourceBlock(source, target, be) and + sinkBlock(sink, be) + ) select sinkNode.getNode(), sourceNode, sinkNode, "Access of a pointer to $@ after its lifetime has ended.", target, target.toString() diff --git a/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected index 92e11e895cdc..a301fad6560a 100644 --- a/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected +++ b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected @@ -9,6 +9,7 @@ | lifetime.rs:76:4:76:5 | p2 | lifetime.rs:27:9:27:22 | &mut my_local2 | lifetime.rs:76:4:76:5 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:25:10:25:18 | my_local2 | my_local2 | | lifetime.rs:77:4:77:5 | p4 | lifetime.rs:39:9:39:26 | &raw mut my_local4 | lifetime.rs:77:4:77:5 | p4 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:37:10:37:18 | my_local4 | my_local4 | | lifetime.rs:172:13:172:15 | ptr | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:172:13:172:15 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:186:6:186:14 | my_local1 | my_local1 | +| lifetime.rs:180:13:180:15 | ptr | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:180:13:180:15 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:186:6:186:14 | my_local1 | my_local1 | | lifetime.rs:255:14:255:17 | prev | lifetime.rs:251:10:251:19 | &my_local2 | lifetime.rs:255:14:255:17 | prev | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:242:7:242:15 | my_local2 | my_local2 | | lifetime.rs:310:31:310:32 | e1 | lifetime.rs:272:30:272:32 | &e1 | lifetime.rs:310:31:310:32 | e1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:271:6:271:7 | e1 | e1 | | lifetime.rs:314:23:314:24 | p2 | lifetime.rs:279:28:279:30 | &v2 | lifetime.rs:314:23:314:24 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:278:6:278:7 | v2 | v2 | @@ -55,39 +56,23 @@ edges | lifetime.rs:59:11:59:36 | get_local_field_dangling(...) | lifetime.rs:59:6:59:7 | p6 | provenance | | | lifetime.rs:63:3:63:4 | p7 | lifetime.rs:75:13:75:14 | p7 | provenance | | | lifetime.rs:63:8:63:27 | &raw const my_local7 | lifetime.rs:63:3:63:4 | p7 | provenance | | -| lifetime.rs:91:17:91:30 | ...: ... | lifetime.rs:101:14:101:15 | p1 | provenance | | -| lifetime.rs:91:33:91:44 | ...: ... | lifetime.rs:102:14:102:15 | p2 | provenance | | -| lifetime.rs:91:33:91:44 | ...: ... | lifetime.rs:110:5:110:6 | p2 | provenance | | | lifetime.rs:94:2:94:3 | p3 | lifetime.rs:103:14:103:15 | p3 | provenance | | | lifetime.rs:94:7:94:16 | &my_local1 | lifetime.rs:94:2:94:3 | p3 | provenance | | -| lifetime.rs:119:15:119:24 | &my_local3 | lifetime.rs:91:17:91:30 | ...: ... | provenance | | -| lifetime.rs:119:27:119:44 | &mut my_local_mut4 | lifetime.rs:91:33:91:44 | ...: ... | provenance | | -| lifetime.rs:161:17:161:31 | ...: ... | lifetime.rs:164:13:164:15 | ptr | provenance | | | lifetime.rs:169:17:169:31 | ...: ... | lifetime.rs:172:13:172:15 | ptr | provenance | | | lifetime.rs:177:17:177:31 | ...: ... | lifetime.rs:180:13:180:15 | ptr | provenance | | -| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:189:15:189:17 | ptr | provenance | | -| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:190:15:190:17 | ptr | provenance | | | lifetime.rs:187:6:187:8 | ptr | lifetime.rs:192:2:192:11 | return ptr | provenance | | | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:187:6:187:8 | ptr | provenance | | -| lifetime.rs:189:15:189:17 | ptr | lifetime.rs:161:17:161:31 | ...: ... | provenance | | -| lifetime.rs:190:15:190:17 | ptr | lifetime.rs:177:17:177:31 | ...: ... | provenance | | | lifetime.rs:192:2:192:11 | return ptr | lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | provenance | | | lifetime.rs:196:6:196:8 | ptr | lifetime.rs:200:15:200:17 | ptr | provenance | | | lifetime.rs:196:6:196:8 | ptr | lifetime.rs:201:15:201:17 | ptr | provenance | | | lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | lifetime.rs:196:6:196:8 | ptr | provenance | | | lifetime.rs:200:15:200:17 | ptr | lifetime.rs:169:17:169:31 | ...: ... | provenance | | | lifetime.rs:201:15:201:17 | ptr | lifetime.rs:177:17:177:31 | ...: ... | provenance | | -| lifetime.rs:206:19:206:36 | ...: ... | lifetime.rs:216:16:216:21 | ptr_up | provenance | | -| lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:211:33:211:40 | ptr_ours | provenance | | | lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:225:2:225:16 | return ptr_ours | provenance | | | lifetime.rs:208:17:208:29 | &my_local_rec | lifetime.rs:208:6:208:13 | ptr_ours | provenance | | | lifetime.rs:211:7:211:14 | ptr_down | lifetime.rs:218:18:218:25 | ptr_down | provenance | | | lifetime.rs:211:18:211:52 | access_ptr_rec(...) | lifetime.rs:211:7:211:14 | ptr_down | provenance | | -| lifetime.rs:211:33:211:40 | ptr_ours | lifetime.rs:206:19:206:36 | ...: ... | provenance | | | lifetime.rs:225:2:225:16 | return ptr_ours | lifetime.rs:211:18:211:52 | access_ptr_rec(...) | provenance | | -| lifetime.rs:230:6:230:14 | ptr_start | lifetime.rs:232:21:232:29 | ptr_start | provenance | | -| lifetime.rs:230:18:230:31 | &my_local_rec2 | lifetime.rs:230:6:230:14 | ptr_start | provenance | | -| lifetime.rs:232:21:232:29 | ptr_start | lifetime.rs:206:19:206:36 | ...: ... | provenance | | | lifetime.rs:239:6:239:13 | mut prev | lifetime.rs:247:15:247:18 | prev | provenance | | | lifetime.rs:239:6:239:13 | mut prev | lifetime.rs:255:14:255:17 | prev | provenance | | | lifetime.rs:239:34:239:43 | &my_local1 | lifetime.rs:239:6:239:13 | mut prev | provenance | | @@ -215,43 +200,26 @@ nodes | lifetime.rs:75:13:75:14 | p7 | semmle.label | p7 | | lifetime.rs:76:4:76:5 | p2 | semmle.label | p2 | | lifetime.rs:77:4:77:5 | p4 | semmle.label | p4 | -| lifetime.rs:91:17:91:30 | ...: ... | semmle.label | ...: ... | -| lifetime.rs:91:33:91:44 | ...: ... | semmle.label | ...: ... | | lifetime.rs:94:2:94:3 | p3 | semmle.label | p3 | | lifetime.rs:94:7:94:16 | &my_local1 | semmle.label | &my_local1 | -| lifetime.rs:101:14:101:15 | p1 | semmle.label | p1 | -| lifetime.rs:102:14:102:15 | p2 | semmle.label | p2 | | lifetime.rs:103:14:103:15 | p3 | semmle.label | p3 | -| lifetime.rs:110:5:110:6 | p2 | semmle.label | p2 | -| lifetime.rs:119:15:119:24 | &my_local3 | semmle.label | &my_local3 | -| lifetime.rs:119:27:119:44 | &mut my_local_mut4 | semmle.label | &mut my_local_mut4 | -| lifetime.rs:161:17:161:31 | ...: ... | semmle.label | ...: ... | -| lifetime.rs:164:13:164:15 | ptr | semmle.label | ptr | | lifetime.rs:169:17:169:31 | ...: ... | semmle.label | ...: ... | | lifetime.rs:172:13:172:15 | ptr | semmle.label | ptr | | lifetime.rs:177:17:177:31 | ...: ... | semmle.label | ...: ... | | lifetime.rs:180:13:180:15 | ptr | semmle.label | ptr | | lifetime.rs:187:6:187:8 | ptr | semmle.label | ptr | | lifetime.rs:187:12:187:21 | &my_local1 | semmle.label | &my_local1 | -| lifetime.rs:189:15:189:17 | ptr | semmle.label | ptr | -| lifetime.rs:190:15:190:17 | ptr | semmle.label | ptr | | lifetime.rs:192:2:192:11 | return ptr | semmle.label | return ptr | | lifetime.rs:196:6:196:8 | ptr | semmle.label | ptr | | lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | semmle.label | access_and_get_dangling(...) | | lifetime.rs:200:15:200:17 | ptr | semmle.label | ptr | | lifetime.rs:201:15:201:17 | ptr | semmle.label | ptr | -| lifetime.rs:206:19:206:36 | ...: ... | semmle.label | ...: ... | | lifetime.rs:208:6:208:13 | ptr_ours | semmle.label | ptr_ours | | lifetime.rs:208:17:208:29 | &my_local_rec | semmle.label | &my_local_rec | | lifetime.rs:211:7:211:14 | ptr_down | semmle.label | ptr_down | | lifetime.rs:211:18:211:52 | access_ptr_rec(...) | semmle.label | access_ptr_rec(...) | -| lifetime.rs:211:33:211:40 | ptr_ours | semmle.label | ptr_ours | -| lifetime.rs:216:16:216:21 | ptr_up | semmle.label | ptr_up | | lifetime.rs:218:18:218:25 | ptr_down | semmle.label | ptr_down | | lifetime.rs:225:2:225:16 | return ptr_ours | semmle.label | return ptr_ours | -| lifetime.rs:230:6:230:14 | ptr_start | semmle.label | ptr_start | -| lifetime.rs:230:18:230:31 | &my_local_rec2 | semmle.label | &my_local_rec2 | -| lifetime.rs:232:21:232:29 | ptr_start | semmle.label | ptr_start | | lifetime.rs:239:6:239:13 | mut prev | semmle.label | mut prev | | lifetime.rs:239:34:239:43 | &my_local1 | semmle.label | &my_local1 | | lifetime.rs:247:15:247:18 | prev | semmle.label | prev | diff --git a/rust/ql/test/query-tests/security/CWE-825/lifetime.rs b/rust/ql/test/query-tests/security/CWE-825/lifetime.rs index 05a099e903fb..51042b56ecc0 100644 --- a/rust/ql/test/query-tests/security/CWE-825/lifetime.rs +++ b/rust/ql/test/query-tests/security/CWE-825/lifetime.rs @@ -177,7 +177,7 @@ fn access_ptr_2(ptr: *const i64) { fn access_ptr_3(ptr: *const i64) { // called from contexts with `ptr` safe and dangling unsafe { - let v3 = *ptr; // $ MISSING: Alert + let v3 = *ptr; // $ Alert[rust/access-after-lifetime-ended]=local1 println!(" v3 = {v3} (!)"); // corrupt in practice (in one context) } } From 4474e252fe985390f63ee4a21043c61735119612 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 4 Mar 2026 10:01:09 +0100 Subject: [PATCH 3/5] Add change note --- .../2026-03-04-flow-feature-escapes-source-call-context.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 shared/dataflow/change-notes/2026-03-04-flow-feature-escapes-source-call-context.md diff --git a/shared/dataflow/change-notes/2026-03-04-flow-feature-escapes-source-call-context.md b/shared/dataflow/change-notes/2026-03-04-flow-feature-escapes-source-call-context.md new file mode 100644 index 000000000000..b7b61b754115 --- /dev/null +++ b/shared/dataflow/change-notes/2026-03-04-flow-feature-escapes-source-call-context.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Two new flow features `FeatureEscapesSourceCallContext` and `FeatureEscapesSourceCallContextOrEqualSourceSinkCallContext` have been added. The former implies that the sink must be reached from the source by escaping the source call context, that is, flow must either return from the callable containing the source or use a jump-step before reaching the sink. The latter is the disjunction of the former and the existing `FeatureEqualSourceSinkCallContext` flow feature. \ No newline at end of file From d1003c469f9f292a42ff08e1bf3540afb309c6ed Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 18 Feb 2026 08:30:40 +0100 Subject: [PATCH 4/5] Rust: Add type inference regression test --- .../type-inference/regressions.rs | 32 +++ .../type-inference/type-inference.expected | 222 ++++++++++++++++++ 2 files changed, 254 insertions(+) diff --git a/rust/ql/test/library-tests/type-inference/regressions.rs b/rust/ql/test/library-tests/type-inference/regressions.rs index 17475d50166b..b4562714756d 100644 --- a/rust/ql/test/library-tests/type-inference/regressions.rs +++ b/rust/ql/test/library-tests/type-inference/regressions.rs @@ -32,3 +32,35 @@ mod regression1 { opt_e.unwrap() // $ target=unwrap } } + +mod regression2 { + trait SomeTrait {} + + trait MyFrom { + fn my_from(value: T) -> Self; + } + + impl MyFrom for T { + fn my_from(s: T) -> Self { + s + } + } + + impl MyFrom for Option { + fn my_from(val: T) -> Option { + Some(val) + } + } + + pub struct S(Ts); + + pub fn f(x: T2) -> T2 + where + T2: SomeTrait + MyFrom>, + Option: MyFrom, + { + let y = MyFrom::my_from(x); // $ target=my_from + let z = MyFrom::my_from(y); // $ target=my_from + z + } +} 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 5d0b167074ae..7732523ecd3a 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -4947,6 +4947,17 @@ inferCertainType | regressions.rs:27:37:27:41 | vec_e | | {EXTERNAL LOCATION} | Vec | | regressions.rs:27:37:27:41 | vec_e | A | {EXTERNAL LOCATION} | Global | | regressions.rs:28:9:30:9 | { ... } | | {EXTERNAL LOCATION} | () | +| regressions.rs:40:20:40:24 | value | | regressions.rs:39:18:39:18 | T | +| regressions.rs:44:20:44:20 | s | | regressions.rs:43:10:43:10 | T | +| regressions.rs:44:34:46:9 | { ... } | | regressions.rs:43:10:43:10 | T | +| regressions.rs:45:13:45:13 | s | | regressions.rs:43:10:43:10 | T | +| regressions.rs:50:20:50:22 | val | | regressions.rs:49:10:49:10 | T | +| regressions.rs:50:41:52:9 | { ... } | | {EXTERNAL LOCATION} | Option | +| regressions.rs:50:41:52:9 | { ... } | T | regressions.rs:49:10:49:10 | T | +| regressions.rs:51:18:51:20 | val | | regressions.rs:49:10:49:10 | T | +| regressions.rs:57:22:57:22 | x | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:61:5:65:5 | { ... } | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:33:62:33 | x | | regressions.rs:57:18:57:19 | T2 | inferType | associated_types.rs:5:15:5:18 | SelfParam | | associated_types.rs:1:1:2:21 | Wrapper | | associated_types.rs:5:15:5:18 | SelfParam | A | associated_types.rs:4:6:4:6 | A | @@ -14789,4 +14800,215 @@ inferType | regressions.rs:32:9:32:13 | opt_e | | {EXTERNAL LOCATION} | Option | | regressions.rs:32:9:32:13 | opt_e | T | regressions.rs:5:5:7:5 | E | | regressions.rs:32:9:32:22 | opt_e.unwrap() | | regressions.rs:5:5:7:5 | E | +| regressions.rs:40:20:40:24 | value | | regressions.rs:39:18:39:18 | T | +| regressions.rs:44:20:44:20 | s | | regressions.rs:43:10:43:10 | T | +| regressions.rs:44:34:46:9 | { ... } | | regressions.rs:43:10:43:10 | T | +| regressions.rs:45:13:45:13 | s | | regressions.rs:43:10:43:10 | T | +| regressions.rs:50:20:50:22 | val | | regressions.rs:49:10:49:10 | T | +| regressions.rs:50:41:52:9 | { ... } | | {EXTERNAL LOCATION} | Option | +| regressions.rs:50:41:52:9 | { ... } | T | regressions.rs:49:10:49:10 | T | +| regressions.rs:51:13:51:21 | Some(...) | | {EXTERNAL LOCATION} | Option | +| regressions.rs:51:13:51:21 | Some(...) | T | regressions.rs:49:10:49:10 | T | +| regressions.rs:51:18:51:20 | val | | regressions.rs:49:10:49:10 | T | +| regressions.rs:57:22:57:22 | x | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:61:5:65:5 | { ... } | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:62:33:62:33 | x | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | +| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | testFailures From eeb8264eb8a35d5063f557524f3194ed60219952 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 23 Feb 2026 10:41:43 +0100 Subject: [PATCH 5/5] Rust: Unify type inference logic for associated functions --- .../rust/elements/internal/ImplImpl.qll | 6 + .../elements/internal/InvocationExprImpl.qll | 2 +- .../typeinference/BlanketImplementation.qll | 23 +- .../internal/typeinference/FunctionType.qll | 114 +- .../internal/typeinference/TypeInference.qll | 2075 ++++++++--------- .../PathResolutionConsistency.expected | 6 - .../dataflow/taint/inline-taint-flow.expected | 8 + .../test/library-tests/dataflow/taint/main.rs | 4 +- .../type-inference/type-inference.expected | 186 -- .../BrokenCryptoAlgorithm.expected | 6 + .../PathResolutionConsistency.expected | 2 - .../BrokenCryptoAlgorithm/test_cipher.rs | 12 +- 12 files changed, 1068 insertions(+), 1376 deletions(-) delete mode 100644 rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/CONSISTENCY/PathResolutionConsistency.expected diff --git a/rust/ql/lib/codeql/rust/elements/internal/ImplImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/ImplImpl.qll index 298e07f4b3e9..3ff04276c638 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/ImplImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/ImplImpl.qll @@ -33,5 +33,11 @@ module Impl { result = "impl " + trait + this.getSelfTy().toAbbreviatedString() + " { ... }" ) } + + /** + * Holds if this is an inherent `impl` block, that is, one that does not implement a trait. + */ + pragma[nomagic] + predicate isInherent() { not this.hasTrait() } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll index e5dd4cdaee67..412a3b51ae82 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll @@ -6,7 +6,7 @@ module Impl { private newtype TArgumentPosition = TPositionalArgumentPosition(int i) { - i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1] + i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()])] } or TSelfArgumentPosition() or TTypeQualifierArgumentPosition() diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll b/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll index 51781a473057..db1402280d4d 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll @@ -41,16 +41,19 @@ private predicate hasFirstNonTrivialTraitBound(TypeParamItemNode tp, Trait trait */ pragma[nomagic] predicate isBlanketLike(ImplItemNode i, TypePath blanketSelfPath, TypeParam blanketTypeParam) { - blanketTypeParam = i.getBlanketImplementationTypeParam() and - blanketSelfPath.isEmpty() - or - exists(TypeMention tm, Type root, TypeParameter tp | - tm = i.(Impl).getSelfTy() and - complexSelfRoot(root, tp) and - tm.getType() = root and - tm.getTypeAt(blanketSelfPath) = TTypeParamTypeParameter(blanketTypeParam) and - blanketSelfPath = TypePath::singleton(tp) and - hasFirstNonTrivialTraitBound(blanketTypeParam, _) + i.(Impl).hasTrait() and + ( + blanketTypeParam = i.getBlanketImplementationTypeParam() and + blanketSelfPath.isEmpty() + or + exists(TypeMention tm, Type root, TypeParameter tp | + tm = i.(Impl).getSelfTy() and + complexSelfRoot(root, tp) and + tm.getType() = root and + tm.getTypeAt(blanketSelfPath) = TTypeParamTypeParameter(blanketTypeParam) and + blanketSelfPath = TypePath::singleton(tp) and + hasFirstNonTrivialTraitBound(blanketTypeParam, _) + ) ) } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll index f8611ce2a3c0..42094d6ce293 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll @@ -29,18 +29,20 @@ class FunctionPosition extends TFunctionPosition { predicate isReturn() { this = TReturnFunctionPosition() } + /** Gets the corresponding position when function call syntax is used. */ + FunctionPosition getFunctionCallAdjusted() { + (this.isReturn() or this.isTypeQualifier()) and + result = this + or + this.isSelf() and result.asPosition() = 0 + or + result.asPosition() = this.asPosition() + 1 + } + /** Gets the corresponding position when `f` is invoked via a function call. */ bindingset[f] FunctionPosition getFunctionCallAdjusted(Function f) { - this.isReturn() and - result = this - or - if f.hasSelfParam() - then - this.isSelf() and result.asPosition() = 0 - or - result.asPosition() = this.asPosition() + 1 - else result = this + if f.hasSelfParam() then result = this.getFunctionCallAdjusted() else result = this } TypeMention getTypeMention(Function f) { @@ -197,8 +199,7 @@ class AssocFunctionType extends MkAssocFunctionType { exists(Function f, ImplOrTraitItemNode i, FunctionPosition pos | this.appliesTo(f, i, pos) | result = pos.getTypeMention(f) or - pos.isSelf() and - not f.hasSelfParam() and + pos.isTypeQualifier() and result = [i.(Impl).getSelfTy().(AstNode), i.(Trait).getName()] ) } @@ -209,7 +210,7 @@ class AssocFunctionType extends MkAssocFunctionType { } pragma[nomagic] -private Trait getALookupTrait(Type t) { +Trait getALookupTrait(Type t) { result = t.(TypeParamTypeParameter).getTypeParam().(TypeParamItemNode).resolveABound() or result = t.(SelfTypeParameter).getTrait() @@ -313,9 +314,9 @@ signature module ArgsAreInstantiationsOfInputSig { * If `i` is an inherent implementation, `tp` is a type parameter of the type being * implemented, otherwise `tp` is a type parameter of the trait (being implemented). * - * `pos` is one of the positions in `f` in which the relevant type occours. + * `posAdj` is one of the adjusted positions in `f` in which the relevant type occurs. */ - predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos); + predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition posAdj); /** A call whose argument types are to be checked. */ class Call { @@ -323,7 +324,7 @@ signature module ArgsAreInstantiationsOfInputSig { Location getLocation(); - Type getArgType(FunctionPosition pos, TypePath path); + Type getArgType(FunctionPosition posAdj, TypePath path); predicate hasTargetCand(ImplOrTraitItemNode i, Function f); } @@ -337,9 +338,9 @@ signature module ArgsAreInstantiationsOfInputSig { module ArgsAreInstantiationsOf { pragma[nomagic] private predicate toCheckRanked( - ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos, int rnk + ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition posAdj, int rnk ) { - Input::toCheck(i, f, tp, pos) and + Input::toCheck(i, f, tp, posAdj) and tp = rank[rnk + 1](TypeParameter tp0, int j | Input::toCheck(i, f, tp0, _) and @@ -351,53 +352,59 @@ module ArgsAreInstantiationsOf { pragma[nomagic] private predicate toCheck( - ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos, AssocFunctionType t + ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition posAdj, + AssocFunctionType t ) { - Input::toCheck(i, f, tp, pos) and - t.appliesTo(f, i, pos) + exists(FunctionPosition pos | + Input::toCheck(i, f, tp, posAdj) and + t.appliesTo(f, i, pos) and + posAdj = pos.getFunctionCallAdjusted(f) + ) } - private newtype TCallAndPos = - MkCallAndPos(Input::Call call, FunctionPosition pos) { exists(call.getArgType(pos, _)) } + private newtype TCallAndPosAdj = + MkCallAndPosAdj(Input::Call call, FunctionPosition posAdj) { + exists(call.getArgType(posAdj, _)) + } - /** A call tagged with a position. */ - private class CallAndPos extends MkCallAndPos { + /** A call tagged with an adjusted position. */ + private class CallAndPosAdj extends MkCallAndPosAdj { Input::Call call; - FunctionPosition pos; + FunctionPosition posAdj; - CallAndPos() { this = MkCallAndPos(call, pos) } + CallAndPosAdj() { this = MkCallAndPosAdj(call, posAdj) } Input::Call getCall() { result = call } - FunctionPosition getPos() { result = pos } + FunctionPosition getPosAdj() { result = posAdj } Location getLocation() { result = call.getLocation() } - Type getTypeAt(TypePath path) { result = call.getArgType(pos, path) } + Type getTypeAt(TypePath path) { result = call.getArgType(posAdj, path) } - string toString() { result = call.toString() + " [arg " + pos + "]" } + string toString() { result = call.toString() + " [arg " + posAdj + "]" } } pragma[nomagic] private predicate potentialInstantiationOf0( - CallAndPos cp, Input::Call call, TypeParameter tp, FunctionPosition pos, Function f, + CallAndPosAdj cp, Input::Call call, TypeParameter tp, FunctionPosition posAdj, Function f, TypeAbstraction abs, AssocFunctionType constraint ) { - cp = MkCallAndPos(call, pragma[only_bind_into](pos)) and + cp = MkCallAndPosAdj(call, pragma[only_bind_into](posAdj)) and call.hasTargetCand(abs, f) and - toCheck(abs, f, tp, pragma[only_bind_into](pos), constraint) + toCheck(abs, f, tp, pragma[only_bind_into](posAdj), constraint) } private module ArgIsInstantiationOfToIndexInput implements - IsInstantiationOfInputSig + IsInstantiationOfInputSig { pragma[nomagic] predicate potentialInstantiationOf( - CallAndPos cp, TypeAbstraction abs, AssocFunctionType constraint + CallAndPosAdj cp, TypeAbstraction abs, AssocFunctionType constraint ) { - exists(Input::Call call, TypeParameter tp, FunctionPosition pos, int rnk, Function f | - potentialInstantiationOf0(cp, call, tp, pos, f, abs, constraint) and - toCheckRanked(abs, f, tp, pos, rnk) + exists(Input::Call call, TypeParameter tp, FunctionPosition posAdj, int rnk, Function f | + potentialInstantiationOf0(cp, call, tp, posAdj, f, abs, constraint) and + toCheckRanked(abs, f, tp, posAdj, rnk) | rnk = 0 or @@ -409,24 +416,25 @@ module ArgsAreInstantiationsOf { } private module ArgIsInstantiationOfToIndex = - ArgIsInstantiationOf; + ArgIsInstantiationOf; pragma[nomagic] private predicate argIsInstantiationOf( - Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i, Function f, int rnk + Input::Call call, ImplOrTraitItemNode i, Function f, int rnk ) { - ArgIsInstantiationOfToIndex::argIsInstantiationOf(MkCallAndPos(call, pos), i, _) and - toCheckRanked(i, f, _, pos, rnk) + exists(FunctionPosition posAdj | + ArgIsInstantiationOfToIndex::argIsInstantiationOf(MkCallAndPosAdj(call, posAdj), i, _) and + toCheckRanked(i, f, _, posAdj, rnk) + ) } pragma[nomagic] private predicate argsAreInstantiationsOfToIndex( Input::Call call, ImplOrTraitItemNode i, Function f, int rnk ) { - exists(FunctionPosition pos | - argIsInstantiationOf(call, pos, i, f, rnk) and - call.hasTargetCand(i, f) - | + argIsInstantiationOf(call, i, f, rnk) and + call.hasTargetCand(i, f) and + ( rnk = 0 or argsAreInstantiationsOfToIndex(call, i, f, rnk - 1) @@ -448,11 +456,11 @@ module ArgsAreInstantiationsOf { } private module ArgsAreNotInstantiationOfInput implements - IsInstantiationOfInputSig + IsInstantiationOfInputSig { pragma[nomagic] predicate potentialInstantiationOf( - CallAndPos cp, TypeAbstraction abs, AssocFunctionType constraint + CallAndPosAdj cp, TypeAbstraction abs, AssocFunctionType constraint ) { potentialInstantiationOf0(cp, _, _, _, _, abs, constraint) } @@ -461,13 +469,13 @@ module ArgsAreInstantiationsOf { } private module ArgsAreNotInstantiationOf = - ArgIsInstantiationOf; + ArgIsInstantiationOf; pragma[nomagic] private predicate argsAreNotInstantiationsOf0( - Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i + Input::Call call, FunctionPosition posAdj, ImplOrTraitItemNode i ) { - ArgsAreNotInstantiationOf::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _) + ArgsAreNotInstantiationOf::argIsNotInstantiationOf(MkCallAndPosAdj(call, posAdj), i, _, _) } /** @@ -478,10 +486,10 @@ module ArgsAreInstantiationsOf { */ pragma[nomagic] predicate argsAreNotInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) { - exists(FunctionPosition pos | - argsAreNotInstantiationsOf0(call, pos, i) and + exists(FunctionPosition posAdj | + argsAreNotInstantiationsOf0(call, posAdj, i) and call.hasTargetCand(i, f) and - Input::toCheck(i, f, _, pos) + Input::toCheck(i, f, _, posAdj) ) } } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 70dfe9e90056..5002700df326 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -298,14 +298,17 @@ private class FunctionDeclaration extends Function { } pragma[nomagic] - Type getParameterType(ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path) { + Type getParameterType(ImplOrTraitItemNodeOption i, FunctionPosition posAdj, TypePath path) { i = parent and ( - not pos.isReturn() and - result = getAssocFunctionTypeAt(this, i.asSome(), pos, path) + exists(FunctionPosition pos | + not pos.isReturn() and + result = getAssocFunctionTypeAt(this, i.asSome(), pos, path) and + posAdj = pos.getFunctionCallAdjusted(this) + ) or i.isNone() and - result = this.getParam(pos.asPosition()).getTypeRepr().(TypeMention).getTypeAt(path) + result = this.getParam(posAdj.asPosition()).getTypeRepr().(TypeMention).getTypeAt(path) ) } @@ -343,6 +346,10 @@ private class FunctionDeclaration extends Function { } } +private class AssocFunction extends FunctionDeclaration { + AssocFunction() { this.isAssoc(_) } +} + pragma[nomagic] private TypeMention getCallExprTypeMentionArgument(CallExpr ce, TypeArgumentPosition apos) { exists(Path p, int i | p = CallExprImpl::getFunctionPath(ce) | @@ -1106,6 +1113,24 @@ private Trait getCallExprTraitQualifier(CallExpr ce) { ) } +pragma[nomagic] +private predicate nonAssocFunction(ItemNode i) { not i instanceof AssocFunction } + +/** + * A call expression that can resolve to something that is not an associated + * function, and hence does not need type inference for resolution. + */ +private class NonAssocCallExpr extends CallExpr { + NonAssocCallExpr() { + forex(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | nonAssocFunction(i)) + } + + /** + * Gets the target of this call, which can be resolved using only path resolution. + */ + ItemNode resolveCallTargetViaPathResolution() { result = CallExprImpl::getResolvedFunction(this) } +} + /** * Provides functionality related to context-based typing of calls. */ @@ -1236,42 +1261,6 @@ private module ContextTyping { } } -/** - * Holds if function `f` with the name `name` and the arity `arity` exists in - * `i`, and the type at position `pos` is `t`. - */ -pragma[nomagic] -private predicate assocFunctionInfo( - Function f, string name, int arity, ImplOrTraitItemNode i, FunctionPosition pos, - AssocFunctionType t -) { - f = i.getASuccessor(name) and - arity = f.getParamList().getNumberOfParams() and - t.appliesTo(f, i, pos) -} - -/** - * Holds if function `f` with the name `name` and the arity `arity` exists in - * blanket (like) implementation `impl` of `trait`, and the type at position - * `pos` is `t`. - * - * `blanketPath` points to the type `blanketTypeParam` inside `t`, which - * is the type parameter used in the blanket implementation. - */ -pragma[nomagic] -private predicate functionInfoBlanketLike( - Function f, string name, int arity, ImplItemNode impl, Trait trait, FunctionPosition pos, - AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam -) { - exists(TypePath blanketSelfPath | - assocFunctionInfo(f, name, arity, impl, pos, t) and - TTypeParamTypeParameter(blanketTypeParam) = t.getTypeAt(blanketPath) and - blanketPath = any(string s) + blanketSelfPath and - BlanketImplementation::isBlanketLike(impl, blanketSelfPath, blanketTypeParam) and - trait = impl.resolveTraitTy() - ) -} - /** * Holds if the type path `path` pointing to `type` is stripped of any leading * complex root type allowed for `self` parameters, such as `&`, `Box`, `Rc`, @@ -1327,7 +1316,7 @@ private class BorrowKind extends TBorrowKind { } /** - * Provides logic for resolving calls to methods. + * Provides logic for resolving calls to associated functions. * * When resolving a method call, a list of [candidate receiver types][1] is constructed * @@ -1361,190 +1350,365 @@ private class BorrowKind extends TBorrowKind { * * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers */ -private module MethodResolution { +private module AssocFunctionResolution { + /** + * Holds if function `f` with the name `name` and the arity `arity` exists in + * `i`, and the type at adjusted position `posAdj` is `t`. + */ + pragma[nomagic] + private predicate assocFunctionInfo( + Function f, string name, int arity, ImplOrTraitItemNode i, FunctionPosition posAdj, + AssocFunctionType t + ) { + exists(FunctionPosition pos | + f = i.getASuccessor(name) and + arity = f.getNumberOfParamsInclSelf() and + t.appliesTo(f, i, pos) and + posAdj = pos.getFunctionCallAdjusted(f) + ) + } + /** - * Holds if method `m` with the name `name` and the arity `arity` exists in - * `i`, and the type of the `self` parameter is `selfType`. + * Holds if function `f` with the name `name` and the arity `arity` exists in + * blanket (like) implementation `impl` of `trait`, and the type at adjusted + * position `posAdj` is `t`. * - * `strippedTypePath` points to the type `strippedType` inside `selfType`, - * which is the (possibly complex-stripped) root type of `selfType`. For example, - * if `m` has a `&self` parameter, then `strippedTypePath` is `getRefSharedTypeParameter()` - * and `strippedType` is the type inside the reference. + * `blanketPath` points to the type `blanketTypeParam` inside `t`, which + * is the type parameter used in the blanket implementation. */ pragma[nomagic] - private predicate methodInfo( - Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType + private predicate assocFunctionInfoBlanketLike( + Function f, string name, int arity, ImplItemNode impl, TypeOption implType, TypeOption trait, + FunctionPosition posAdj, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam, + boolean isMethod ) { - assocFunctionInfo(m, name, arity, i, selfPos, selfType) and - strippedType = selfType.getTypeAt(strippedTypePath) and - isComplexRootStripped(strippedTypePath, strippedType) and - selfPos.isSelfOrTypeQualifier() + exists(TypePath blanketSelfPath | + assocFunctionInfo(f, name, arity, posAdj, impl, t, _, _, implType, trait, isMethod) and + TTypeParamTypeParameter(blanketTypeParam) = t.getTypeAt(blanketPath) and + blanketPath = any(string s) + blanketSelfPath and + BlanketImplementation::isBlanketLike(impl, blanketSelfPath, blanketTypeParam) + ) } + /** + * Holds if the non-method trait function `f` mentions the implicit `Self` type + * parameter at adjusted position `posAdj`. + */ pragma[nomagic] - private predicate methodInfoTypeParam( - Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType selfType, TypePath strippedTypePath, TypeParam tp + private predicate traitSelfTypeParameterOccurrence( + TraitItemNode trait, NonMethodFunction f, FunctionPosition posAdj ) { - methodInfo(m, name, arity, selfPos, i, selfType, strippedTypePath, TTypeParamTypeParameter(tp)) + exists(FunctionPosition pos | + FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _, + TSelfTypeParameter(trait)) and + posAdj = pos.getFunctionCallAdjusted(f) + ) } /** - * Same as `methodInfo`, but restricted to non-blanket implementations, and - * allowing for any `strippedType` when the corresponding type inside `m` is - * a type parameter. + * Holds if the non-method function `f` implements a trait function that mentions + * the implicit `Self` type parameter at adjusted position `posAdj`. */ - pragma[inline] - private predicate methodInfoNonBlanket( - Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType + pragma[nomagic] + private predicate traitImplSelfTypeParameterOccurrence( + ImplItemNode impl, NonMethodFunction f, FunctionPosition posAdj ) { - ( - methodInfo(m, name, arity, selfPos, i, selfType, strippedTypePath, strippedType) or - methodInfoTypeParam(m, name, arity, selfPos, i, selfType, strippedTypePath, _) - ) and - not BlanketImplementation::isBlanketLike(i, _, _) + exists(NonMethodFunction traitFunction | + f = impl.getAnAssocItem() and + f.implements(traitFunction) and + traitSelfTypeParameterOccurrence(_, traitFunction, posAdj) + ) } + private module TypeOption = Option; + + private class TypeOption = TypeOption::Option; + /** - * Holds if method `m` with the name `name` and the arity `arity` exists in - * blanket (like) implementation `impl` of `trait`, and the type of the `self` - * parameter is `selfType`. + * Holds if function `f` with the name `name` and the arity `arity` exists in + * `i`, and the type at `selfPosAdj` is `selfType`. * - * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which - * is the type parameter used in the blanket implementation. + * `selfPosAdj` is an adjusted position relevant for call resolution: either a position + * corresponding to the `self` parameter of `f` (if present); a type qualifier position; + * or a position where the implicit `Self` type parameter of some trait is mentioned in + * some non-method function `f_trait`, and either `f = f_trait` or `f` implements `f_trait`. + * + * `strippedTypePath` points to the type `strippedType` inside `selfType`, + * which is the (possibly complex-stripped) root type of `selfType`. For example, + * if `f` has a `&self` parameter, then `strippedTypePath` is `getRefSharedTypeParameter()` + * and `strippedType` is the type inside the reference. + * + * `implType` is the type being implemented by `i` (`None` when `i` is a trait). + * + * `trait` is the trait being implemented by `i` or `i` itself (`None` when `i` is inherent). + * + * `isMethod` indicates whether `f` is a method. */ pragma[nomagic] - private predicate methodInfoBlanketLike( - Method m, string name, int arity, FunctionPosition selfPos, ImplItemNode impl, Trait trait, - AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam + private predicate assocFunctionInfo( + Function f, string name, int arity, FunctionPosition selfPosAdj, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, TypeOption implType, + TypeOption trait, boolean isMethod ) { - functionInfoBlanketLike(m, name, arity, impl, trait, selfPos, selfType, blanketPath, - blanketTypeParam) and - selfPos.isSelfOrTypeQualifier() + assocFunctionInfo(f, name, arity, i, selfPosAdj, selfType) and + strippedType = selfType.getTypeAt(strippedTypePath) and + ( + isComplexRootStripped(strippedTypePath, strippedType) + or + selfPosAdj.isTypeQualifier() and strippedTypePath.isEmpty() + ) and + ( + f instanceof Method and + selfPosAdj.asPosition() = 0 + or + selfPosAdj.isTypeQualifier() + or + traitSelfTypeParameterOccurrence(i, f, selfPosAdj) + or + traitImplSelfTypeParameterOccurrence(i, f, selfPosAdj) + ) and + ( + implType.asSome() = resolveImplSelfTypeAt(i, TypePath::nil()) + or + i instanceof Trait and + implType.isNone() + ) and + ( + trait.asSome() = + [ + TTrait(i).(Type), + TTrait(i.(ImplItemNode).resolveTraitTy()).(Type) + ] + or + i.(Impl).isInherent() and trait.isNone() + ) and + if f instanceof Method then isMethod = true else isMethod = false } pragma[nomagic] - private predicate methodTraitInfo(string name, int arity, Trait trait) { + private predicate assocFunctionTraitInfo(string name, int arity, Trait trait) { exists(ImplItemNode i | - methodInfo(_, name, arity, _, i, _, _, _) and + assocFunctionInfo(_, name, arity, _, i, _, _, _, _, _, _) and trait = i.resolveTraitTy() ) or - methodInfo(_, name, arity, _, trait, _, _, _) + assocFunctionInfo(_, name, arity, _, trait, _, _, _, _, _, _) } pragma[nomagic] - private predicate methodCallTraitCandidate(Element mc, Trait trait) { - mc = - any(MethodCall mc0 | + private predicate assocFunctionCallTraitCandidate(Element afc, Trait trait) { + afc = + any(AssocFunctionCall afc0 | exists(string name, int arity | - mc0.hasNameAndArity(name, arity) and - methodTraitInfo(name, arity, trait) - | - not mc0.hasTrait() - or - trait = mc0.getTrait() + afc0.hasNameAndArity(name, arity) and + assocFunctionTraitInfo(name, arity, trait) and + // we only need to check visibility of traits that are not mentioned explicitly + not afc0.hasATrait() ) ) } - private module MethodTraitIsVisible = TraitIsVisible; + private module AssocFunctionTraitIsVisible = TraitIsVisible; + + bindingset[afc, impl] + pragma[inline_late] + private predicate callVisibleImplTraitCandidate(AssocFunctionCall afc, ImplItemNode impl) { + AssocFunctionTraitIsVisible::traitIsVisible(afc, impl.resolveTraitTy()) + } - private predicate methodCallVisibleTraitCandidate = MethodTraitIsVisible::traitIsVisible/2; + bindingset[implType] + private predicate callTypeQualifierFilter(TypeOption implType, TypeOption typeQualifier) { + // when present, explicit type qualifier must match the type being implemented + typeQualifier = [implType, any(TypeOption non | non.isNone())] + } - bindingset[mc, impl] + bindingset[trait, isMethod] pragma[inline_late] - private predicate methodCallVisibleImplTraitCandidate(MethodCall mc, ImplItemNode impl) { - methodCallVisibleTraitCandidate(mc, impl.resolveTraitTy()) + private predicate callTraitQualifierAndReceiverFilter( + TypeOption trait, Boolean isMethod, TypeOption traitQualifier, boolean hasReceiver + ) { + // when present, explicit trait qualifier must match the trait being implemented or the trait itself + traitQualifier = [trait, any(TypeOption non | non.isNone())] and + // when a receiver is present, the target must be a method + hasReceiver = [isMethod, false] + } + + bindingset[implType, trait, isMethod] + private predicate callFilter( + TypeOption implType, TypeOption trait, Boolean isMethod, TypeOption typeQualifier, + TypeOption traitQualifier, boolean hasReceiver + ) { + callTypeQualifierFilter(implType, typeQualifier) and + callTraitQualifierAndReceiverFilter(trait, isMethod, traitQualifier, hasReceiver) + } + + pragma[nomagic] + private predicate assocFunctionInfoNonBlanketLikeCand( + Function f, string name, int arity, FunctionPosition selfPosAdj, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, + TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver + ) { + exists(TypeOption implType, TypeOption trait, boolean isMethod | + assocFunctionInfo(f, name, arity, selfPosAdj, i, selfType, strippedTypePath, strippedType, + implType, trait, isMethod) and + not BlanketImplementation::isBlanketLike(i, _, _) and + callFilter(implType, trait, isMethod, typeQualifier, traitQualifier, hasReceiver) + ) + } + + pragma[nomagic] + private predicate assocFunctionInfoNonBlanketLikeTypeParamCand( + Function f, string name, int arity, FunctionPosition selfPosAdj, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, TypeOption typeQualifier, + TypeOption traitQualifier, boolean hasReceiver + ) { + exists(TypeOption implType, TypeOption trait, boolean isMethod | + assocFunctionInfo(f, name, arity, selfPosAdj, i, selfType, strippedTypePath, + TTypeParamTypeParameter(_), implType, trait, isMethod) and + not BlanketImplementation::isBlanketLike(i, _, _) and + callFilter(implType, trait, isMethod, typeQualifier, traitQualifier, hasReceiver) + ) } /** - * Holds if method call `mc` may target a method in `i` with `self` parameter having - * type `selfType`. + * Holds if call `afc` may target function `f` in `i` with type `selfType` at + * `selfPosAdj`. * * `strippedTypePath` points to the type `strippedType` inside `selfType`, * which is the (possibly complex-stripped) root type of `selfType`. * - * This predicate only checks for matching method names and arities, and whether - * the trait being implemented by `i` (when `i` is not a trait itself) is visible - * at `mc`. + * This predicate only performs syntactic checks, such as matching function name + * and arity. */ - bindingset[mc, strippedTypePath, strippedType] + bindingset[afc, strippedTypePath, strippedType] pragma[inline_late] - private predicate methodCallNonBlanketCandidate( - MethodCall mc, Method m, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType self, TypePath strippedTypePath, Type strippedType + private predicate nonBlanketLikeCandidate( + AssocFunctionCall afc, Function f, FunctionPosition selfPosAdj, ImplOrTraitItemNode i, + AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType ) { - exists(string name, int arity | - mc.hasNameAndArity(name, arity) and - methodInfoNonBlanket(m, name, arity, selfPos, i, self, strippedTypePath, strippedType) + exists( + string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, + boolean hasReceiver | - i = - any(Impl impl | - not impl.hasTrait() - or - methodCallVisibleImplTraitCandidate(mc, impl) - ) + afc.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and + if not afc.hasATrait() and i.(Impl).hasTrait() + then callVisibleImplTraitCandidate(afc, i) + else any() + | + assocFunctionInfoNonBlanketLikeCand(f, name, arity, selfPosAdj, i, selfType, strippedTypePath, + strippedType, typeQualifier, traitQualifier, hasReceiver) + or + assocFunctionInfoNonBlanketLikeTypeParamCand(f, name, arity, selfPosAdj, i, selfType, + strippedTypePath, typeQualifier, traitQualifier, hasReceiver) + ) + } + + pragma[nomagic] + private predicate assocFunctionSelfInfoBlanketLikeCand0( + Function f, string name, int arity, FunctionPosition selfPosAdj, ImplItemNode impl, + AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam, + TypeOption implType, TypeOption trait, boolean isMethod + ) { + assocFunctionInfoBlanketLike(f, name, arity, impl, implType, trait, selfPosAdj, selfType, + blanketPath, blanketTypeParam, isMethod) and + ( + isMethod = true and + selfPosAdj.asPosition() = 0 or - methodCallVisibleTraitCandidate(mc, i) + selfPosAdj.isTypeQualifier() or - i.(ImplItemNode).resolveTraitTy() = mc.getTrait() + traitImplSelfTypeParameterOccurrence(impl, f, selfPosAdj) + ) + } + + bindingset[name, arity, typeQualifier, traitQualifier, hasReceiver] + pragma[inline_late] + private predicate assocFunctionSelfInfoBlanketLikeCand( + Function f, string name, int arity, FunctionPosition selfPosAdj, ImplItemNode impl, + AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam, + TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver + ) { + exists(TypeOption implType, TypeOption trait, boolean isMethod | + assocFunctionSelfInfoBlanketLikeCand0(f, name, arity, selfPosAdj, impl, selfType, blanketPath, + blanketTypeParam, implType, trait, isMethod) and + ( + if impl.isBlanketImplementation() + then any() + else callTypeQualifierFilter(implType, typeQualifier) + ) and + callTraitQualifierAndReceiverFilter(trait, isMethod, traitQualifier, hasReceiver) ) } /** - * Holds if method call `mc` may target a method in blanket (like) implementation - * `impl` with `self` parameter having type `selfType`. + * Holds if call `afc` may target function `f` in blanket (like) implementation + * `impl` with type `selfType` at `selfPosAdj`. * * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which * is the type parameter used in the blanket implementation. * - * This predicate only checks for matching method names and arities, and whether + * This predicate only checks for matching function names and arities, and whether * the trait being implemented by `i` (when `i` is not a trait itself) is visible - * at `mc`. + * at `afc`. */ - bindingset[mc] + bindingset[afc] pragma[inline_late] - private predicate methodCallBlanketLikeCandidate( - MethodCall mc, Method m, FunctionPosition selfPos, ImplItemNode impl, AssocFunctionType self, - TypePath blanketPath, TypeParam blanketTypeParam + private predicate blanketLikeCandidate( + AssocFunctionCall afc, Function f, FunctionPosition selfPosAdj, ImplItemNode impl, + AssocFunctionType self, TypePath blanketPath, TypeParam blanketTypeParam ) { - exists(string name, int arity | - mc.hasNameAndArity(name, arity) and - methodInfoBlanketLike(m, name, arity, selfPos, impl, _, self, blanketPath, blanketTypeParam) + exists( + string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, + boolean hasReceiver | - methodCallVisibleImplTraitCandidate(mc, impl) - or - impl.resolveTraitTy() = mc.getTrait() + afc.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and + assocFunctionSelfInfoBlanketLikeCand(f, name, arity, selfPosAdj, impl, self, blanketPath, + blanketTypeParam, typeQualifier, traitQualifier, hasReceiver) + | + if not afc.hasATrait() then callVisibleImplTraitCandidate(afc, impl) else any() ) } + private Type getNonTypeParameterTypeQualifier(AssocFunctionCall afc) { + result = getCallExprTypeQualifier(afc, TypePath::nil(), _) and + not result instanceof TypeParameter + } + /** - * A (potential) method call. + * A (potential) call to an associated function. * * This is either: * - * 1. `MethodCallMethodCallExpr`: an actual method call, `x.m()`; - * 2. `MethodCallIndexExpr`: an index expression, `x[i]`, which is [syntactic sugar][1] + * 1. `AssocFunctionCallMethodCallExpr`: a method call, `x.m()`; + * 2. `AssocFunctionCallIndexExpr`: an index expression, `x[i]`, which is [syntactic sugar][1] * for `*x.index(i)`; - * 3. `MethodCallCallExpr`: a qualified function call, `Q::m(x)`, where `m` is a method; - * or - * 4. `MethodCallOperation`: an operation expression, `x + y`, which is syntactic sugar + * 3. `AssocFunctionCallCallExpr`: a qualified function call, `Q::f(x)`, where `f` is an associated + * function; or + * 4. `AssocFunctionCallOperation`: an operation expression, `x + y`, which is syntactic sugar * for `Add::add(x, y)`. * * Note that only in case 1 and 2 is auto-dereferencing and borrowing allowed. * - * Note also that only case 4 is a _potential_ method call; in all other cases, we are - * guaranteed that the target is a method. + * Note also that only case 3 is a _potential_ call; in all other cases, we are guaranteed that + * the target is an associated function. * * [1]: https://doc.rust-lang.org/std/ops/trait.Index.html */ - abstract class MethodCall extends Expr { + abstract class AssocFunctionCall extends Expr { abstract predicate hasNameAndArity(string name, int arity); - abstract Expr getArg(ArgumentPosition pos); + abstract Expr getNonReturnNodeAt(FunctionPosition pos); + + AstNode getNodeAt(FunctionPosition posAdj) { + exists(FunctionPosition pos | + result = this.getNonReturnNodeAt(pos) and + if this.hasReceiver() then posAdj = pos.getFunctionCallAdjusted() else posAdj = pos + ) + or + result = this and posAdj.isReturn() + } + + abstract predicate hasReceiver(); abstract predicate supportsAutoDerefAndBorrow(); @@ -1554,163 +1718,222 @@ private module MethodResolution { /** Holds if this call targets a trait. */ predicate hasTrait() { exists(this.getTrait()) } - AstNode getNodeAt(FunctionPosition apos) { - result = this.getArg(apos.asArgumentPosition()) + Trait getATrait() { + result = this.getTrait() or - result = this and apos.isReturn() + result = getALookupTrait(getCallExprTypeQualifier(this, TypePath::nil(), _)) } - Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) { - result = inferType(this.getArg(pos), path) - } + predicate hasATrait() { exists(this.getATrait()) } /** - * Same as `getACandidateReceiverTypeAt`, but without borrows. + * Holds if this call has the given purely syntactic information, that is, + * information that does not rely on type inference. */ pragma[nomagic] - Type getACandidateReceiverTypeAtNoBorrow( - FunctionPosition selfPos, DerefChain derefChain, TypePath path + predicate hasSyntacticInfo( + string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, + boolean hasReceiver ) { - result = this.getArgumentTypeAt(selfPos.asArgumentPosition(), path) and - selfPos.isSelfOrTypeQualifier() and - derefChain.isEmpty() + this.hasNameAndArity(name, arity) and + (if this.hasReceiver() then hasReceiver = true else hasReceiver = false) and + ( + typeQualifier.asSome() = getNonTypeParameterTypeQualifier(this) + or + not exists(getNonTypeParameterTypeQualifier(this)) and + typeQualifier.isNone() + ) and + ( + traitQualifier.asSome() = TTrait(this.getATrait()) + or + not this.hasATrait() and + traitQualifier.isNone() + ) + } + + Type getTypeAt(FunctionPosition posAdj, TypePath path) { + result = inferType(this.getNodeAt(posAdj), path) + } + + /** + * Holds if `selfPosAdj` is a potentially relevant position for resolving this call. + */ + pragma[nomagic] + private predicate isRelevantSelfPosAdj(FunctionPosition selfPosAdj) { + not this.hasReceiver() and + exists(TypePath strippedTypePath, Type strippedType | + strippedType = substituteLookupTraits(this.getTypeAt(selfPosAdj, strippedTypePath)) and + strippedType != TNeverType() and + strippedType != TUnknownType() + | + nonBlanketLikeCandidate(this, _, selfPosAdj, _, _, strippedTypePath, strippedType) + or + blanketLikeCandidate(this, _, selfPosAdj, _, _, strippedTypePath, _) + ) + } + + predicate hasReceiverPos(FunctionPosition posAdj) { + this.hasReceiver() and posAdj.asPosition() = 0 + } + + /** + * Same as `getSelfTypeAt`, but without borrows. + */ + pragma[nomagic] + Type getSelfTypeAtNoBorrow(FunctionPosition selfPosAdj, DerefChain derefChain, TypePath path) { + result = this.getTypeAt(selfPosAdj, path) and + derefChain.isEmpty() and + ( + this.hasReceiverPos(selfPosAdj) + or + selfPosAdj.isTypeQualifier() + or + this.isRelevantSelfPosAdj(selfPosAdj) + ) or exists(DerefImplItemNode impl, DerefChain suffix | result = - ImplicitDeref::getDereferencedCandidateReceiverType(this, selfPos, impl, suffix, path) and + ImplicitDeref::getDereferencedCandidateReceiverType(this, selfPosAdj, impl, suffix, path) and derefChain = DerefChain::cons(impl, suffix) ) } /** - * Holds if the method inside `i` with matching name and arity can be ruled + * Holds if the function inside `i` with matching name and arity can be ruled * out as a target of this call, because the candidate receiver type represented - * by `derefChain` and `borrow` is incompatible with the `self` parameter type. + * by `derefChain` and `borrow` is incompatible with the type at `selfPosAdj`. * * The types are incompatible because they disagree on a concrete type somewhere * inside `root`. */ pragma[nomagic] private predicate hasIncompatibleTarget( - ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, + ImplOrTraitItemNode i, FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, Type root ) { exists(TypePath path | - ReceiverIsInstantiationOfSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, selfPos, - derefChain, borrow), i, _, path) and + SelfArgIsInstantiationOf::argIsNotInstantiationOf(this, i, selfPosAdj, derefChain, borrow, + path) and path.isCons(root.getATypeParameter(), _) ) + or + exists(AssocFunctionType selfType | + SelfArgIsInstantiationOf::argIsInstantiationOf(this, i, selfPosAdj, derefChain, borrow, + selfType) and + OverloadedCallArgsAreInstantiationsOf::argsAreNotInstantiationsOf(this, i) and + root = selfType.getTypeAt(TypePath::nil()) + ) } /** - * Holds if the method inside blanket-like implementation `impl` with matching name + * Holds if the function inside blanket-like implementation `impl` with matching name * and arity can be ruled out as a target of this call, either because the candidate - * receiver type represented by `derefChain` and `borrow` is incompatible with the `self` - * parameter type, or because the blanket constraint is not satisfied. + * receiver type represented by `derefChain` and `borrow` is incompatible with the type + * at `selfPosAdj`, or because the blanket constraint is not satisfied. */ pragma[nomagic] private predicate hasIncompatibleBlanketLikeTarget( - ImplItemNode impl, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow + ImplItemNode impl, FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow ) { - ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, - selfPos, derefChain, borrow), impl, _, _) + SelfArgIsNotInstantiationOfBlanketLike::argIsNotInstantiationOf(MkAssocFunctionCallCand(this, + selfPosAdj, derefChain, borrow), impl, _, _) or - ReceiverSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkMethodCallCand(this, - selfPos, derefChain, borrow), impl) + ArgSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkAssocFunctionCallCand(this, + selfPosAdj, derefChain, borrow), impl) } /** - * Same as `getACandidateReceiverTypeAt`, but excludes pseudo types `!` and `unknown`. + * Same as `getTypeAt`, but excludes pseudo types `!` and `unknown`. */ pragma[nomagic] - Type getANonPseudoCandidateReceiverTypeAt( - FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path + Type getANonPseudoSelfTypeAt( + FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, TypePath path ) { - result = this.getACandidateReceiverTypeAt(selfPos, derefChain, borrow, path) and + result = this.getSelfTypeAt(selfPosAdj, derefChain, borrow, path) and result != TNeverType() and result != TUnknownType() } pragma[nomagic] - private Type getComplexStrippedType( - FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath + private Type getComplexStrippedSelfType( + FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, + TypePath strippedTypePath ) { - result = - this.getANonPseudoCandidateReceiverTypeAt(selfPos, derefChain, borrow, strippedTypePath) and - isComplexRootStripped(strippedTypePath, result) + result = this.getANonPseudoSelfTypeAt(selfPosAdj, derefChain, borrow, strippedTypePath) and + ( + isComplexRootStripped(strippedTypePath, result) + or + selfPosAdj.isTypeQualifier() and strippedTypePath.isEmpty() + ) } bindingset[derefChain, borrow, strippedTypePath, strippedType] private predicate hasNoCompatibleNonBlanketLikeTargetCheck( - FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, - Type strippedType + FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, + TypePath strippedTypePath, Type strippedType ) { forall(ImplOrTraitItemNode i | - methodCallNonBlanketCandidate(this, _, selfPos, i, _, strippedTypePath, strippedType) + nonBlanketLikeCandidate(this, _, selfPosAdj, i, _, strippedTypePath, strippedType) | - this.hasIncompatibleTarget(i, selfPos, derefChain, borrow, strippedType) + this.hasIncompatibleTarget(i, selfPosAdj, derefChain, borrow, strippedType) ) } bindingset[derefChain, borrow, strippedTypePath, strippedType] private predicate hasNoCompatibleTargetCheck( - FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, - Type strippedType + FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, + TypePath strippedTypePath, Type strippedType ) { - this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath, - strippedType) and - forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, selfPos, i, _, _, _) | - this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPosAdj, derefChain, borrow, + strippedTypePath, strippedType) and + forall(ImplItemNode i | blanketLikeCandidate(this, _, selfPosAdj, i, _, _, _) | + this.hasIncompatibleBlanketLikeTarget(i, selfPosAdj, derefChain, borrow) ) } bindingset[derefChain, borrow, strippedTypePath, strippedType] private predicate hasNoCompatibleNonBlanketTargetCheck( - FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath, - Type strippedType + FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, + TypePath strippedTypePath, Type strippedType ) { - this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath, - strippedType) and + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPosAdj, derefChain, borrow, + strippedTypePath, strippedType) and forall(ImplItemNode i | - methodCallBlanketLikeCandidate(this, _, selfPos, i, _, _, _) and + blanketLikeCandidate(this, _, selfPosAdj, i, _, _, _) and not i.isBlanketImplementation() | - this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) + this.hasIncompatibleBlanketLikeTarget(i, selfPosAdj, derefChain, borrow) ) } // forex using recursion pragma[nomagic] private predicate hasNoCompatibleTargetNoBorrowToIndex( - FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, - int n + FunctionPosition selfPosAdj, DerefChain derefChain, TypePath strippedTypePath, + Type strippedType, int n ) { - ( - this.supportsAutoDerefAndBorrow() - or - // needed for the `hasNoCompatibleTarget` check in - // `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` - derefChain.isEmpty() - ) and + this.supportsAutoDerefAndBorrow() and + this.hasReceiverPos(selfPosAdj) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and + this.getComplexStrippedSelfType(selfPosAdj, derefChain, TNoBorrowKind(), strippedTypePath) and n = -1 or - this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, - n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleTargetCheck(selfPos, derefChain, TNoBorrowKind(), strippedTypePath, t) + this.hasNoCompatibleTargetNoBorrowToIndex(selfPosAdj, derefChain, strippedTypePath, + strippedType, n - 1) and + exists(Type t | + t = getNthLookupType(strippedType, n) and + this.hasNoCompatibleTargetCheck(selfPosAdj, derefChain, TNoBorrowKind(), strippedTypePath, t) ) } /** * Holds if the candidate receiver type represented by `derefChain` does not - * have a matching method target. + * have a matching call target at `selfPosAdj`. */ pragma[nomagic] - predicate hasNoCompatibleTargetNoBorrow(FunctionPosition selfPos, DerefChain derefChain) { + predicate hasNoCompatibleTargetNoBorrow(FunctionPosition selfPosAdj, DerefChain derefChain) { exists(Type strippedType | - this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType, + this.hasNoCompatibleTargetNoBorrowToIndex(selfPosAdj, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1718,38 +1941,44 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleNonBlanketTargetNoBorrowToIndex( - FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, - int n + FunctionPosition selfPosAdj, DerefChain derefChain, TypePath strippedTypePath, + Type strippedType, int n ) { ( - this.supportsAutoDerefAndBorrow() + this.supportsAutoDerefAndBorrow() and + this.hasReceiverPos(selfPosAdj) or - // needed for the `hasNoCompatibleTarget` check in - // `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` - derefChain.isEmpty() + // needed for the `hasNoCompatibleNonBlanketTarget` check in + // `ArgSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` + exists(ImplItemNode i | + derefChain.isEmpty() and + blanketLikeCandidate(this, _, selfPosAdj, i, _, _, _) and + i.isBlanketImplementation() + ) ) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and + this.getComplexStrippedSelfType(selfPosAdj, derefChain, TNoBorrowKind(), strippedTypePath) and n = -1 or - this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, + this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPosAdj, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TNoBorrowKind(), + exists(Type t | + t = getNthLookupType(strippedType, n) and + this.hasNoCompatibleNonBlanketTargetCheck(selfPosAdj, derefChain, TNoBorrowKind(), strippedTypePath, t) ) } /** * Holds if the candidate receiver type represented by `derefChain` does not have - * a matching non-blanket method target. + * a matching non-blanket call target at `selfPosAdj`. */ pragma[nomagic] predicate hasNoCompatibleNonBlanketTargetNoBorrow( - FunctionPosition selfPos, DerefChain derefChain + FunctionPosition selfPosAdj, DerefChain derefChain ) { exists(Type strippedType | - this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType, + this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPosAdj, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1757,30 +1986,32 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleTargetSharedBorrowToIndex( - FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, - int n + FunctionPosition selfPosAdj, DerefChain derefChain, TypePath strippedTypePath, + Type strippedType, int n ) { - this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and + this.hasNoCompatibleTargetNoBorrow(selfPosAdj, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath) and + this.getComplexStrippedSelfType(selfPosAdj, derefChain, TSomeBorrowKind(false), + strippedTypePath) and n = -1 or - this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, + this.hasNoCompatibleTargetSharedBorrowToIndex(selfPosAdj, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), - strippedTypePath, t) + exists(Type t | + t = getNthLookupType(strippedType, n) and + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPosAdj, derefChain, + TSomeBorrowKind(false), strippedTypePath, t) ) } /** * Holds if the candidate receiver type represented by `derefChain`, followed - * by a shared borrow, does not have a matching method target. + * by a shared borrow, does not have a matching call target at `selfPosAdj`. */ pragma[nomagic] - predicate hasNoCompatibleTargetSharedBorrow(FunctionPosition selfPos, DerefChain derefChain) { + predicate hasNoCompatibleTargetSharedBorrow(FunctionPosition selfPosAdj, DerefChain derefChain) { exists(Type strippedType | - this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, _, strippedType, + this.hasNoCompatibleTargetSharedBorrowToIndex(selfPosAdj, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1788,30 +2019,32 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleTargetMutBorrowToIndex( - FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, - int n + FunctionPosition selfPosAdj, DerefChain derefChain, TypePath strippedTypePath, + Type strippedType, int n ) { - this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and + this.hasNoCompatibleTargetSharedBorrow(selfPosAdj, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and + this.getComplexStrippedSelfType(selfPosAdj, derefChain, TSomeBorrowKind(true), + strippedTypePath) and n = -1 or - this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, + this.hasNoCompatibleTargetMutBorrowToIndex(selfPosAdj, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), + exists(Type t | + t = getNthLookupType(strippedType, n) and + this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPosAdj, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) } /** * Holds if the candidate receiver type represented by `derefChain`, followed - * by a `mut` borrow, does not have a matching method target. + * by a `mut` borrow, does not have a matching call target at `selfPosAdj`. */ pragma[nomagic] - predicate hasNoCompatibleTargetMutBorrow(FunctionPosition selfPos, DerefChain derefChain) { + predicate hasNoCompatibleTargetMutBorrow(FunctionPosition selfPosAdj, DerefChain derefChain) { exists(Type strippedType | - this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType, + this.hasNoCompatibleTargetMutBorrowToIndex(selfPosAdj, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1819,32 +2052,35 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleNonBlanketTargetSharedBorrowToIndex( - FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, - int n + FunctionPosition selfPosAdj, DerefChain derefChain, TypePath strippedTypePath, + Type strippedType, int n ) { - this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and + this.hasNoCompatibleTargetNoBorrow(selfPosAdj, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath) and + this.getComplexStrippedSelfType(selfPosAdj, derefChain, TSomeBorrowKind(false), + strippedTypePath) and n = -1 or - this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, - strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), + this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPosAdj, derefChain, + strippedTypePath, strippedType, n - 1) and + exists(Type t | + t = getNthLookupType(strippedType, n) and + this.hasNoCompatibleNonBlanketTargetCheck(selfPosAdj, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) } /** * Holds if the candidate receiver type represented by `derefChain`, followed - * by a shared borrow, does not have a matching non-blanket method target. + * by a shared borrow, does not have a matching non-blanket call target at + * `selfPosAdj`. */ pragma[nomagic] predicate hasNoCompatibleNonBlanketTargetSharedBorrow( - FunctionPosition selfPos, DerefChain derefChain + FunctionPosition selfPosAdj, DerefChain derefChain ) { exists(Type strippedType | - this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, _, + this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPosAdj, derefChain, _, strippedType, getLastLookupTypeIndex(strippedType)) ) } @@ -1852,38 +2088,44 @@ private module MethodResolution { // forex using recursion pragma[nomagic] private predicate hasNoCompatibleNonBlanketTargetMutBorrowToIndex( - FunctionPosition selfPos, DerefChain derefChain, TypePath strippedTypePath, Type strippedType, - int n + FunctionPosition selfPosAdj, DerefChain derefChain, TypePath strippedTypePath, + Type strippedType, int n ) { - this.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and + this.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPosAdj, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and + this.getComplexStrippedSelfType(selfPosAdj, derefChain, TSomeBorrowKind(true), + strippedTypePath) and n = -1 or - this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, + this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPosAdj, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | - this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), + exists(Type t | + t = getNthLookupType(strippedType, n) and + this.hasNoCompatibleNonBlanketTargetCheck(selfPosAdj, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) } /** * Holds if the candidate receiver type represented by `derefChain`, followed - * by a `mut` borrow, does not have a matching non-blanket method target. + * by a `mut` borrow, does not have a matching non-blanket call target at `selfPosAdj`. */ pragma[nomagic] predicate hasNoCompatibleNonBlanketTargetMutBorrow( - FunctionPosition selfPos, DerefChain derefChain + FunctionPosition selfPosAdj, DerefChain derefChain ) { exists(Type strippedType | - this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPosAdj, derefChain, _, + strippedType, getLastLookupTypeIndex(strippedType)) ) } /** - * Gets a [candidate receiver type][1] of this method call at `path`. + * Gets the type of this call at `selfPosAdj` and `path`. + * + * In case this call supports auto-dereferencing and borrowing and `selfPosAdj` is + * position 0 (corresponding to the receiver), the result is a + * [candidate receiver type][1]: * * The type is obtained by repeatedly dereferencing the receiver expression's type, * as long as the method cannot be resolved in an earlier candidate type, and possibly @@ -1895,20 +2137,21 @@ private module MethodResolution { * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers */ pragma[nomagic] - Type getACandidateReceiverTypeAt( - FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path + Type getSelfTypeAt( + FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow, TypePath path ) { - result = this.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, path) and + result = this.getSelfTypeAtNoBorrow(selfPosAdj, derefChain, path) and borrow.isNoBorrow() or exists(RefType rt | // first try shared borrow this.supportsAutoDerefAndBorrow() and - this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and + this.hasReceiverPos(selfPosAdj) and + this.hasNoCompatibleTargetNoBorrow(selfPosAdj, derefChain) and borrow.isSharedBorrow() or // then try mutable borrow - this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and + this.hasNoCompatibleTargetSharedBorrow(selfPosAdj, derefChain) and borrow.isMutableBorrow() | rt = borrow.getRefType() and @@ -1917,7 +2160,7 @@ private module MethodResolution { result = rt or exists(TypePath suffix | - result = this.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, suffix) and + result = this.getSelfTypeAtNoBorrow(selfPosAdj, derefChain, suffix) and path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix) ) ) @@ -1925,15 +2168,15 @@ private module MethodResolution { } /** - * Gets a method that this call resolves to after having applied a sequence of + * Gets a function that this call resolves to after having applied a sequence of * dereferences and possibly a borrow on the receiver type, encoded in `derefChain` * and `borrow`. */ pragma[nomagic] - Method resolveCallTarget(ImplOrTraitItemNode i, DerefChain derefChain, BorrowKind borrow) { - exists(MethodCallCand mcc | - mcc = MkMethodCallCand(this, _, derefChain, borrow) and - result = mcc.resolveCallTarget(i) + AssocFunction resolveCallTarget(ImplOrTraitItemNode i, DerefChain derefChain, BorrowKind borrow) { + exists(AssocFunctionCallCand afcc | + afcc = MkAssocFunctionCallCand(this, _, derefChain, borrow) and + result = afcc.resolveCallTarget(i) ) } @@ -1943,21 +2186,26 @@ private module MethodResolution { * resolve the call target. */ predicate argumentHasImplicitDerefChainBorrow(Expr arg, DerefChain derefChain, BorrowKind borrow) { - exists(this.resolveCallTarget(_, derefChain, borrow)) and - arg = this.getArg(any(ArgumentPosition apos | apos.isSelf())) and - not (derefChain.isEmpty() and borrow.isNoBorrow()) + exists(FunctionPosition selfAdj | + this.hasReceiverPos(selfAdj) and + exists(this.resolveCallTarget(_, derefChain, borrow)) and + arg = this.getNodeAt(selfAdj) and + not (derefChain.isEmpty() and borrow.isNoBorrow()) + ) } } - private class MethodCallMethodCallExpr extends MethodCall instanceof MethodCallExpr { + private class AssocFunctionCallMethodCallExpr extends AssocFunctionCall instanceof MethodCallExpr { pragma[nomagic] override predicate hasNameAndArity(string name, int arity) { name = super.getIdentifier().getText() and - arity = super.getArgList().getNumberOfArgs() + arity = super.getArgList().getNumberOfArgs() + 1 } - override Expr getArg(ArgumentPosition pos) { - result = MethodCallExpr.super.getSyntacticArgument(pos) + override predicate hasReceiver() { any() } + + override Expr getNonReturnNodeAt(FunctionPosition pos) { + result = MethodCallExpr.super.getSyntacticArgument(pos.asArgumentPosition()) } override predicate supportsAutoDerefAndBorrow() { any() } @@ -1965,7 +2213,7 @@ private module MethodResolution { override Trait getTrait() { none() } } - private class MethodCallIndexExpr extends MethodCall instanceof IndexExpr { + private class AssocFunctionCallIndexExpr extends AssocFunctionCall instanceof IndexExpr { private predicate isInMutableContext() { // todo: does not handle all cases yet VariableImpl::assignmentOperationDescendant(_, this) @@ -1974,10 +2222,12 @@ private module MethodResolution { pragma[nomagic] override predicate hasNameAndArity(string name, int arity) { (if this.isInMutableContext() then name = "index_mut" else name = "index") and - arity = 1 + arity = 2 } - override Expr getArg(ArgumentPosition pos) { + override predicate hasReceiver() { any() } + + override Expr getNonReturnNodeAt(FunctionPosition pos) { pos.isSelf() and result = super.getBase() or @@ -1994,95 +2244,90 @@ private module MethodResolution { } } - private class MethodCallCallExpr extends MethodCall instanceof CallExpr { - MethodCallCallExpr() { + class AssocFunctionCallCallExpr extends AssocFunctionCall instanceof CallExpr { + AssocFunctionCallCallExpr() { exists(getCallExprPathQualifier(this)) and - // even if a method cannot be resolved by path resolution, it may still + // even if a target cannot be resolved by path resolution, it may still // be possible to resolve a blanket implementation (so not `forex`) - forall(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | i instanceof Method) + forall(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | i instanceof AssocFunction) } pragma[nomagic] override predicate hasNameAndArity(string name, int arity) { name = CallExprImpl::getFunctionPath(this).getText() and - arity = super.getArgList().getNumberOfArgs() - 1 + arity = super.getArgList().getNumberOfArgs() } - override Expr getArg(ArgumentPosition pos) { - pos.isSelf() and - result = super.getSyntacticPositionalArgument(0) - or - result = super.getSyntacticPositionalArgument(pos.asPosition() + 1) + override predicate hasReceiver() { none() } + + override Expr getNonReturnNodeAt(FunctionPosition pos) { + result = super.getSyntacticPositionalArgument(pos.asPosition()) } - override Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) { - result = super.getArgumentTypeAt(pos, path) + override Type getTypeAt(FunctionPosition posAdj, TypePath path) { + result = super.getTypeAt(posAdj, path) or - pos.isTypeQualifier() and + posAdj.isTypeQualifier() and result = getCallExprTypeQualifier(this, path, _) } - pragma[nomagic] - predicate hasNoInherentTarget() { - // `_` is fine below, because auto-deref/borrow is not supported - MkMethodCallCand(this, _, _, _).(MethodCallCand).hasNoInherentTarget() - } - override predicate supportsAutoDerefAndBorrow() { none() } override Trait getTrait() { result = getCallExprTraitQualifier(this) } } - final class MethodCallOperation extends MethodCall instanceof Operation { + final class AssocFunctionCallOperation extends AssocFunctionCall instanceof Operation { pragma[nomagic] override predicate hasNameAndArity(string name, int arity) { super.isOverloaded(_, name, _) and - arity = super.getNumberOfOperands() - 1 + arity = super.getNumberOfOperands() } - override Expr getArg(ArgumentPosition pos) { + override predicate hasReceiver() { any() } + + override Expr getNonReturnNodeAt(FunctionPosition pos) { pos.isSelf() and result = super.getOperand(0) or result = super.getOperand(pos.asPosition() + 1) } - private predicate implicitBorrowAt(ArgumentPosition pos, boolean isMutable) { + private predicate implicitBorrowAt(FunctionPosition posAdj, boolean isMutable) { exists(int borrows | super.isOverloaded(_, _, borrows) | - pos.isSelf() and + posAdj.asPosition() = 0 and borrows >= 1 and if this instanceof CompoundAssignmentExpr then isMutable = true else isMutable = false or - pos.asPosition() = 0 and + posAdj.asPosition() = 1 and borrows = 2 and isMutable = false ) } - override Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) { + override Type getTypeAt(FunctionPosition posAdj, TypePath path) { exists(boolean isMutable, RefType rt | - this.implicitBorrowAt(pos, isMutable) and + this.implicitBorrowAt(posAdj, isMutable) and rt = getRefType(isMutable) | result = rt and path.isEmpty() or exists(TypePath path0 | - result = inferType(this.getArg(pos), path0) and + result = inferType(this.getNodeAt(posAdj), path0) and path = TypePath::cons(rt.getPositionalTypeParameter(0), path0) ) ) or - not this.implicitBorrowAt(pos, _) and - result = inferType(this.getArg(pos), path) + not this.implicitBorrowAt(posAdj, _) and + result = inferType(this.getNodeAt(posAdj), path) } override predicate argumentHasImplicitDerefChainBorrow( Expr arg, DerefChain derefChain, BorrowKind borrow ) { - exists(ArgumentPosition apos, boolean isMutable | - this.implicitBorrowAt(apos, isMutable) and - arg = this.getArg(apos) and + exists(FunctionPosition posAdj, boolean isMutable | + this.implicitBorrowAt(posAdj, isMutable) and + arg = this.getNodeAt(posAdj) and derefChain = DerefChain::nil() and borrow = TSomeBorrowKind(isMutable) ) @@ -2094,81 +2339,103 @@ private module MethodResolution { } pragma[nomagic] - private Method getMethodSuccessor(ImplOrTraitItemNode i, string name, int arity) { + private AssocFunction getFunctionSuccessor(ImplOrTraitItemNode i, string name, int arity) { result = i.getASuccessor(name) and - arity = result.getParamList().getNumberOfParams() + arity = result.getNumberOfParamsInclSelf() } - private newtype TMethodCallCand = - MkMethodCallCand( - MethodCall mc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow + private newtype TAssocFunctionCallCand = + MkAssocFunctionCallCand( + AssocFunctionCall afc, FunctionPosition selfPosAdj, DerefChain derefChain, BorrowKind borrow ) { - exists(mc.getACandidateReceiverTypeAt(selfPos, derefChain, borrow, _)) + exists(afc.getANonPseudoSelfTypeAt(selfPosAdj, derefChain, borrow, _)) } - /** A method call with a dereference chain and a potential borrow. */ - private class MethodCallCand extends MkMethodCallCand { - MethodCall mc_; - FunctionPosition selfPos; + /** A call with a dereference chain and a potential borrow. */ + final private class AssocFunctionCallCand extends MkAssocFunctionCallCand { + AssocFunctionCall afc_; + FunctionPosition selfPosAdj_; DerefChain derefChain; BorrowKind borrow; - MethodCallCand() { this = MkMethodCallCand(mc_, selfPos, derefChain, borrow) } + AssocFunctionCallCand() { + this = MkAssocFunctionCallCand(afc_, selfPosAdj_, derefChain, borrow) + } - MethodCall getMethodCall() { result = mc_ } + AssocFunctionCall getAssocFunctionCall() { result = afc_ } Type getTypeAt(TypePath path) { result = - substituteLookupTraits(mc_.getANonPseudoCandidateReceiverTypeAt(selfPos, derefChain, borrow, - path)) + substituteLookupTraits(afc_.getANonPseudoSelfTypeAt(selfPosAdj_, derefChain, borrow, path)) } pragma[nomagic] predicate hasNoCompatibleNonBlanketTarget() { - mc_.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and + afc_.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPosAdj_, derefChain) and borrow.isSharedBorrow() or - mc_.hasNoCompatibleNonBlanketTargetMutBorrow(selfPos, derefChain) and + afc_.hasNoCompatibleNonBlanketTargetMutBorrow(selfPosAdj_, derefChain) and borrow.isMutableBorrow() or - mc_.hasNoCompatibleNonBlanketTargetNoBorrow(selfPos, derefChain) and + afc_.hasNoCompatibleNonBlanketTargetNoBorrow(selfPosAdj_, derefChain) and borrow.isNoBorrow() } pragma[nomagic] predicate hasSignature( - MethodCall mc, FunctionPosition selfPos_, TypePath strippedTypePath, Type strippedType, - string name, int arity + AssocFunctionCall afc, FunctionPosition selfPosAdj, TypePath strippedTypePath, + Type strippedType, string name, int arity ) { strippedType = this.getTypeAt(strippedTypePath) and - isComplexRootStripped(strippedTypePath, strippedType) and - mc = mc_ and - mc.hasNameAndArity(name, arity) and - selfPos = selfPos_ + ( + isComplexRootStripped(strippedTypePath, strippedType) + or + selfPosAdj_.isTypeQualifier() and strippedTypePath.isEmpty() + ) and + afc = afc_ and + afc.hasNameAndArity(name, arity) and + selfPosAdj = selfPosAdj_ } /** - * Holds if the inherent method inside `impl` with matching name and arity can be + * Holds if the inherent function inside `impl` with matching name and arity can be * ruled out as a candidate for this call. */ pragma[nomagic] private predicate hasIncompatibleInherentTarget(Impl impl) { - ReceiverIsNotInstantiationOfInherentSelfParam::argIsNotInstantiationOf(this, impl, _, _) + SelfArgIsNotInstantiationOfInherent::argIsNotInstantiationOf(this, impl, _, _) } /** - * Holds if this method call has no inherent target, i.e., it does not - * resolve to a method in an `impl` block for the type of the receiver. + * Holds if this function call has no inherent target, i.e., it does not + * resolve to a function in an `impl` block for the type of the receiver. */ pragma[nomagic] predicate hasNoInherentTarget() { - mc_.hasTrait() + afc_.hasTrait() or - exists(TypePath strippedTypePath, Type strippedType, string name, int arity | - this.hasSignature(_, selfPos, strippedTypePath, strippedType, name, arity) and + exists( + TypePath strippedTypePath, Type strippedType, string name, int arity, + TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver + | + // Calls to inherent functions are always of the form `x.m(...)` or `Foo::bar(...)`, + // where `Foo` is a type. In case `bar` is a method, we can use both the type qualifier + // and the type of the first argument to rule out candidates + selfPosAdj_.isTypeQualifier() and hasReceiver = false + or + selfPosAdj_.asPosition() = 0 and hasReceiver = true + | + afc_.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, _) and + this.hasSignature(_, selfPosAdj_, strippedTypePath, strippedType, name, arity) and forall(Impl i | - methodInfoNonBlanket(_, name, arity, selfPos, i, _, strippedTypePath, strippedType) and - not i.hasTrait() + i.isInherent() and + ( + assocFunctionInfoNonBlanketLikeCand(_, name, arity, selfPosAdj_, i, _, strippedTypePath, + strippedType, typeQualifier, traitQualifier, hasReceiver) + or + assocFunctionInfoNonBlanketLikeTypeParamCand(_, name, arity, selfPosAdj_, i, _, + strippedTypePath, typeQualifier, traitQualifier, hasReceiver) + ) | this.hasIncompatibleInherentTarget(i) ) @@ -2177,77 +2444,77 @@ private module MethodResolution { pragma[nomagic] private predicate argIsInstantiationOf(ImplOrTraitItemNode i, string name, int arity) { - ReceiverIsInstantiationOfSelfParam::argIsInstantiationOf(this, i, _) and - mc_.hasNameAndArity(name, arity) + SelfArgIsInstantiationOf::argIsInstantiationOf(this, i, _) and + afc_.hasNameAndArity(name, arity) } pragma[nomagic] - Method resolveCallTargetCand(ImplOrTraitItemNode i) { + AssocFunction resolveCallTargetCand(ImplOrTraitItemNode i) { exists(string name, int arity | this.argIsInstantiationOf(i, name, arity) and - result = getMethodSuccessor(i, name, arity) + result = getFunctionSuccessor(i, name, arity) ) } - /** Gets a method that matches this method call. */ + /** Gets a function that matches this call. */ pragma[nomagic] - Method resolveCallTarget(ImplOrTraitItemNode i) { + AssocFunction resolveCallTarget(ImplOrTraitItemNode i) { result = this.resolveCallTargetCand(i) and not FunctionOverloading::functionResolutionDependsOnArgument(i, result, _, _) or - MethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result) + OverloadedCallArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result) } string toString() { - result = mc_.toString() + " [" + derefChain.toString() + "; " + borrow + "]" + result = afc_ + " at " + selfPosAdj_ + " [" + derefChain.toString() + "; " + borrow + "]" } - Location getLocation() { result = mc_.getLocation() } + Location getLocation() { result = afc_.getLocation() } } /** * Provides logic for resolving implicit `Deref::deref` calls. */ private module ImplicitDeref { - private newtype TMethodCallDerefCand = - MkMethodCallDerefCand(MethodCall mc, FunctionPosition selfPos, DerefChain derefChain) { - mc.supportsAutoDerefAndBorrow() and - mc.hasNoCompatibleTargetMutBorrow(selfPos, derefChain) and - exists(mc.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) + private newtype TCallDerefCand = + MkCallDerefCand(AssocFunctionCall afc, FunctionPosition selfPosAdj, DerefChain derefChain) { + afc.supportsAutoDerefAndBorrow() and + afc.hasReceiverPos(selfPosAdj) and + afc.hasNoCompatibleTargetMutBorrow(selfPosAdj, derefChain) and + exists(afc.getSelfTypeAtNoBorrow(selfPosAdj, derefChain, TypePath::nil())) } - /** A method call with a dereference chain. */ - private class MethodCallDerefCand extends MkMethodCallDerefCand { - MethodCall mc; - FunctionPosition selfPos; + /** A call with a dereference chain. */ + private class CallDerefCand extends MkCallDerefCand { + AssocFunctionCall afc; + FunctionPosition selfPosAdj; DerefChain derefChain; - MethodCallDerefCand() { this = MkMethodCallDerefCand(mc, selfPos, derefChain) } + CallDerefCand() { this = MkCallDerefCand(afc, selfPosAdj, derefChain) } Type getTypeAt(TypePath path) { - result = - substituteLookupTraits(mc.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, path)) and + result = substituteLookupTraits(afc.getSelfTypeAtNoBorrow(selfPosAdj, derefChain, path)) and result != TNeverType() and result != TUnknownType() } - string toString() { result = mc.toString() + " [" + derefChain.toString() + "]" } + string toString() { result = afc + " [" + derefChain.toString() + "]" } - Location getLocation() { result = mc.getLocation() } + Location getLocation() { result = afc.getLocation() } } - private module MethodCallSatisfiesDerefConstraintInput implements - SatisfiesConstraintInputSig + private module CallSatisfiesDerefConstraintInput implements + SatisfiesConstraintInputSig { pragma[nomagic] - predicate relevantConstraint(MethodCallDerefCand mc, Type constraint) { + predicate relevantConstraint(CallDerefCand mc, Type constraint) { exists(mc) and constraint.(TraitType).getTrait() instanceof DerefTrait } } - private module MethodCallSatisfiesDerefConstraint = - SatisfiesConstraint; + private module CallSatisfiesDerefConstraint = + SatisfiesConstraint; pragma[nomagic] private AssociatedTypeTypeParameter getDerefTargetTypeParameter() { @@ -2255,38 +2522,38 @@ private module MethodResolution { } /** - * Gets the type of the receiver of `mc` at `path` after applying the implicit + * Gets the type of the receiver of `afc` at `path` after applying the implicit * dereference inside `impl`, following the existing dereference chain `derefChain`. */ pragma[nomagic] Type getDereferencedCandidateReceiverType( - MethodCall mc, FunctionPosition selfPos, DerefImplItemNode impl, DerefChain derefChain, - TypePath path + AssocFunctionCall afc, FunctionPosition selfPosAdj, DerefImplItemNode impl, + DerefChain derefChain, TypePath path ) { - exists(MethodCallDerefCand mcc, TypePath exprPath | - mcc = MkMethodCallDerefCand(mc, selfPos, derefChain) and - MethodCallSatisfiesDerefConstraint::satisfiesConstraintTypeThrough(mcc, impl, _, exprPath, - result) and + exists(CallDerefCand cdc, TypePath exprPath | + cdc = MkCallDerefCand(afc, selfPosAdj, derefChain) and + CallSatisfiesDerefConstraint::satisfiesConstraintTypeThrough(cdc, impl, _, exprPath, result) and exprPath.isCons(getDerefTargetTypeParameter(), path) ) } } - private module ReceiverSatisfiesBlanketLikeConstraintInput implements - BlanketImplementation::SatisfiesBlanketConstraintInputSig + private module ArgSatisfiesBlanketLikeConstraintInput implements + BlanketImplementation::SatisfiesBlanketConstraintInputSig { pragma[nomagic] predicate hasBlanketCandidate( - MethodCallCand mcc, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam + AssocFunctionCallCand afcc, ImplItemNode impl, TypePath blanketPath, + TypeParam blanketTypeParam ) { - exists(MethodCall mc, FunctionPosition selfPos, BorrowKind borrow | - mcc = MkMethodCallCand(mc, selfPos, _, borrow) and - methodCallBlanketLikeCandidate(mc, _, selfPos, impl, _, blanketPath, blanketTypeParam) and + exists(AssocFunctionCall afc, FunctionPosition selfPosAdj, BorrowKind borrow | + afcc = MkAssocFunctionCallCand(afc, selfPosAdj, _, borrow) and + blanketLikeCandidate(afc, _, selfPosAdj, impl, _, blanketPath, blanketTypeParam) and // Only apply blanket implementations when no other implementations are possible; // this is to account for codebases that use the (unstable) specialization feature // (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as // cases where our blanket implementation filtering is not precise enough. - (mcc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation()) + if impl.isBlanketImplementation() then afcc.hasNoCompatibleNonBlanketTarget() else any() | borrow.isNoBorrow() or @@ -2295,116 +2562,146 @@ private module MethodResolution { } } - private module ReceiverSatisfiesBlanketLikeConstraint = - BlanketImplementation::SatisfiesBlanketConstraint; + private module ArgSatisfiesBlanketLikeConstraint = + BlanketImplementation::SatisfiesBlanketConstraint; /** - * A configuration for matching the type of a receiver against the type of - * a `self` parameter. + * A configuration for matching the type of an argument against the type of + * a function at a position relevant for dispatch (such as a `self` parameter). */ - private module ReceiverIsInstantiationOfSelfParamInput implements - IsInstantiationOfInputSig + private module SelfArgIsInstantiationOfInput implements + IsInstantiationOfInputSig { pragma[nomagic] additional predicate potentialInstantiationOf0( - MethodCallCand mcc, ImplOrTraitItemNode i, AssocFunctionType selfType + AssocFunctionCallCand afcc, ImplOrTraitItemNode i, AssocFunctionType selfType ) { exists( - MethodCall mc, FunctionPosition selfPos, Method m, TypePath strippedTypePath, + AssocFunctionCall afc, FunctionPosition selfPosAdj, Function f, TypePath strippedTypePath, Type strippedType | - mcc.hasSignature(mc, selfPos, strippedTypePath, strippedType, _, _) + afcc.hasSignature(afc, selfPosAdj, strippedTypePath, strippedType, _, _) | - methodCallNonBlanketCandidate(mc, m, selfPos, i, selfType, strippedTypePath, strippedType) + nonBlanketLikeCandidate(afc, f, selfPosAdj, i, selfType, strippedTypePath, strippedType) or - methodCallBlanketLikeCandidate(mc, m, selfPos, i, selfType, _, _) and - ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i) + blanketLikeCandidate(afc, f, selfPosAdj, i, selfType, _, _) and + ArgSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(afcc, i) ) } pragma[nomagic] predicate potentialInstantiationOf( - MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint + AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint ) { - potentialInstantiationOf0(mcc, abs, constraint) and + potentialInstantiationOf0(afcc, abs, constraint) and if abs.(Impl).hasTrait() then - // inherent methods take precedence over trait methods, so only allow - // trait methods when there are no matching inherent methods - mcc.hasNoInherentTarget() + // inherent functions take precedence over trait functions, so only allow + // trait functions when there are no matching inherent functions + afcc.hasNoInherentTarget() else any() } predicate relevantConstraint(AssocFunctionType constraint) { - methodInfo(_, _, _, _, _, constraint, _, _) + assocFunctionInfo(_, _, _, _, _, constraint, _, _, _, _, _) } } - private module ReceiverIsInstantiationOfSelfParam = - ArgIsInstantiationOf; + private module SelfArgIsInstantiationOf { + import ArgIsInstantiationOf + + pragma[nomagic] + predicate argIsNotInstantiationOf( + AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPosAdj, + DerefChain derefChain, BorrowKind borrow, TypePath path + ) { + argIsNotInstantiationOf(MkAssocFunctionCallCand(afc, selfPosAdj, derefChain, borrow), i, _, + path) + } + + pragma[nomagic] + predicate argIsInstantiationOf( + AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPosAdj, + DerefChain derefChain, BorrowKind borrow, AssocFunctionType selfType + ) { + argIsInstantiationOf(MkAssocFunctionCallCand(afc, selfPosAdj, derefChain, borrow), i, selfType) + } + } /** - * A configuration for anti-matching the type of a receiver against the type of - * a `self` parameter belonging to a blanket (like) implementation. + * A configuration for anti-matching the type of an argument against the type of + * a function at a position relevant for dispatch (such as a `self` parameter) in a + * blanket (like) implementation. */ - private module ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput implements - IsInstantiationOfInputSig + private module SelfArgIsNotInstantiationOfBlanketLikeInput implements + IsInstantiationOfInputSig { pragma[nomagic] predicate potentialInstantiationOf( - MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint + AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint ) { - exists(MethodCall mc, FunctionPosition selfPos | - mcc = MkMethodCallCand(mc, selfPos, _, _) and - methodCallBlanketLikeCandidate(mc, _, selfPos, abs, constraint, _, _) and + exists(AssocFunctionCall afc, FunctionPosition selfPosAdj | + afcc = MkAssocFunctionCallCand(afc, selfPosAdj, _, _) and + blanketLikeCandidate(afc, _, selfPosAdj, abs, constraint, _, _) and if abs.(Impl).hasTrait() then - // inherent methods take precedence over trait methods, so only allow - // trait methods when there are no matching inherent methods - mcc.hasNoInherentTarget() + // inherent functions take precedence over trait functions, so only allow + // trait functions when there are no matching inherent functions + afcc.hasNoInherentTarget() else any() ) } } - private module ReceiverIsNotInstantiationOfBlanketLikeSelfParam = - ArgIsInstantiationOf; + private module SelfArgIsNotInstantiationOfBlanketLike = + ArgIsInstantiationOf; /** - * A configuration for anti-matching the type of a receiver against the type of - * a `self` parameter in an inherent method. + * A configuration for anti-matching the type of an argument against the type of + * a function at a position relevant for dispatch (such as a `self` parameter) in an + * inherent function. */ - private module ReceiverIsNotInstantiationOfInherentSelfParamInput implements - IsInstantiationOfInputSig + private module SelfArgIsNotInstantiationOfInherentInput implements + IsInstantiationOfInputSig { pragma[nomagic] predicate potentialInstantiationOf( - MethodCallCand mcc, TypeAbstraction abs, AssocFunctionType constraint + AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint ) { - ReceiverIsInstantiationOfSelfParamInput::potentialInstantiationOf0(mcc, abs, constraint) and - abs = any(Impl i | not i.hasTrait()) + SelfArgIsInstantiationOfInput::potentialInstantiationOf0(afcc, abs, constraint) and + abs.(Impl).isInherent() and + exists(AssocFunctionCall afc, FunctionPosition selfPosAdj | + afcc = MkAssocFunctionCallCand(afc, selfPosAdj, _, _) + | + selfPosAdj.isTypeQualifier() or + afc.hasReceiverPos(selfPosAdj) + ) } } - private module ReceiverIsNotInstantiationOfInherentSelfParam = - ArgIsInstantiationOf; + private module SelfArgIsNotInstantiationOfInherent = + ArgIsInstantiationOf; /** * A configuration for matching the types of positional arguments against the * types of parameters, when needed to disambiguate the call. */ - private module MethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig { - predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) { - FunctionOverloading::functionResolutionDependsOnArgument(i, f, traitTp, pos) + private module OverloadedCallArgsAreInstantiationsOfInput implements + ArgsAreInstantiationsOfInputSig + { + predicate toCheck( + ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition posAdj + ) { + exists(FunctionPosition pos | + FunctionOverloading::functionResolutionDependsOnArgument(i, f, traitTp, pos) and + posAdj = pos.getFunctionCallAdjusted(f) + ) } - class Call extends MethodCallCand { - Type getArgType(FunctionPosition pos, TypePath path) { - result = mc_.getArgumentTypeAt(pos.asArgumentPosition(), path) - or - pos.isReturn() and - result = inferType(mc_.getNodeAt(pos), path) + class Call extends AssocFunctionCallCand { + Type getArgType(FunctionPosition posAdj, TypePath path) { + result = this.getAssocFunctionCall().getTypeAt(posAdj, path) } predicate hasTargetCand(ImplOrTraitItemNode i, Function f) { @@ -2413,50 +2710,55 @@ private module MethodResolution { } } - private module MethodArgsAreInstantiationsOf = - ArgsAreInstantiationsOf; + private module OverloadedCallArgsAreInstantiationsOf { + import ArgsAreInstantiationsOf + + pragma[nomagic] + predicate argsAreNotInstantiationsOf(AssocFunctionCall afc, ImplOrTraitItemNode i) { + argsAreNotInstantiationsOf(MkAssocFunctionCallCand(afc, _, _, _), i, _) + } + } } /** - * A matching configuration for resolving types of method call expressions - * like `foo.bar(baz)`. + * A matching configuration for resolving types of function call expressions + * like `foo.bar(baz)` and `Foo::bar(baz)`. */ -private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSig { +private module FunctionCallMatchingInput implements MatchingWithEnvironmentInputSig { import FunctionPositionMatchingInput - private class MethodDeclaration extends Method, FunctionDeclaration { } - private newtype TDeclaration = - TMethodFunctionDeclaration(ImplOrTraitItemNode i, MethodDeclaration m) { m.isAssoc(i) } + TFunctionDeclaration(ImplOrTraitItemNodeOption i, FunctionDeclaration f) { f.isFor(i) } - final class Declaration extends TMethodFunctionDeclaration { - ImplOrTraitItemNode parent; - ImplOrTraitItemNodeOption someParent; - MethodDeclaration m; + final class Declaration extends TFunctionDeclaration { + ImplOrTraitItemNodeOption i; + FunctionDeclaration f; - Declaration() { - this = TMethodFunctionDeclaration(parent, m) and - someParent.asSome() = parent - } + Declaration() { this = TFunctionDeclaration(i, f) } - predicate isMethod(ImplOrTraitItemNode i, Method method) { - this = TMethodFunctionDeclaration(i, method) + predicate isAssocFunction(ImplOrTraitItemNode i_, Function f_) { + i_ = i.asSome() and + f_ = f } TypeParameter getTypeParameter(TypeParameterPosition ppos) { - result = m.getTypeParameter(someParent, ppos) + result = f.getTypeParameter(i, ppos) } - Type getDeclaredType(DeclarationPosition dpos, TypePath path) { - result = m.getParameterType(someParent, dpos, path) + Type getDeclaredType(FunctionPosition posAdj, TypePath path) { + result = f.getParameterType(i, posAdj, path) or - dpos.isReturn() and - result = m.getReturnType(someParent, path) + posAdj.isReturn() and + result = f.getReturnType(i, path) } - string toString() { result = m.toStringExt(parent) } + string toString() { + i.isNone() and result = f.toString() + or + result = f.toStringExt(i.asSome()) + } - Location getLocation() { result = m.getLocation() } + Location getLocation() { result = f.getLocation() } } class AccessEnvironment = string; @@ -2477,10 +2779,32 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi ) } - final private class MethodCallFinal = MethodResolution::MethodCall; + private string noDerefChainBorrow() { + exists(DerefChain derefChain, BorrowKind borrow | + derefChain.isEmpty() and + borrow.isNoBorrow() and + result = encodeDerefChainBorrow(derefChain, borrow) + ) + } + + abstract class Access extends ContextTyping::ContextTypedCallCand { + abstract AstNode getNodeAt(FunctionPosition posAdj); + + bindingset[derefChainBorrow] + abstract Type getInferredType(string derefChainBorrow, FunctionPosition posAdj, TypePath path); + + abstract Declaration getTarget(string derefChainBorrow); + + /** + * Holds if the return type of this call at `path` may have to be inferred + * from the context. + */ + abstract predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path); + } - class Access extends MethodCallFinal, ContextTyping::ContextTypedCallCand { - Access() { + private class AssocFunctionCallAccess extends Access instanceof AssocFunctionResolution::AssocFunctionCall + { + AssocFunctionCallAccess() { // handled in the `OperationMatchingInput` module not this instanceof Operation } @@ -2497,89 +2821,135 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi result = getCallExprTypeArgument(this, apos, path) } + override AstNode getNodeAt(FunctionPosition posAdj) { + result = AssocFunctionResolution::AssocFunctionCall.super.getNodeAt(posAdj) + } + pragma[nomagic] - private Type getInferredSelfType(AccessPosition apos, string derefChainBorrow, TypePath path) { + private Type getInferredSelfType(FunctionPosition posAdj, string derefChainBorrow, TypePath path) { exists(DerefChain derefChain, BorrowKind borrow | - result = this.getACandidateReceiverTypeAt(apos, derefChain, borrow, path) and - derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) + result = super.getSelfTypeAt(posAdj, derefChain, borrow, path) and + derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and + super.hasReceiverPos(posAdj) ) } pragma[nomagic] - Type getInferredNonSelfType(AccessPosition apos, TypePath path) { - if - // index expression `x[i]` desugars to `*x.index(i)`, so we must account for - // the implicit deref - apos.isReturn() and - this instanceof IndexExpr - then - path.isEmpty() and - result instanceof RefType - or - exists(TypePath suffix | - result = inferType(this.getNodeAt(apos), suffix) and - path = TypePath::cons(getRefTypeParameter(_), suffix) - ) - else ( - not apos.isSelf() and - result = inferType(this.getNodeAt(apos), path) - ) + private Type getInferredNonSelfType(FunctionPosition posAdj, TypePath path) { + result = super.getTypeAt(posAdj, path) and + not super.hasReceiverPos(posAdj) } bindingset[derefChainBorrow] - Type getInferredType(string derefChainBorrow, AccessPosition apos, TypePath path) { - result = this.getInferredSelfType(apos, derefChainBorrow, path) + override Type getInferredType(string derefChainBorrow, FunctionPosition posAdj, TypePath path) { + result = this.getInferredSelfType(posAdj, derefChainBorrow, path) or - result = this.getInferredNonSelfType(apos, path) + result = this.getInferredNonSelfType(posAdj, path) } - Method getTarget(ImplOrTraitItemNode i, string derefChainBorrow) { + private AssocFunction getTarget(ImplOrTraitItemNode i, string derefChainBorrow) { exists(DerefChain derefChain, BorrowKind borrow | derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and - result = this.resolveCallTarget(i, derefChain, borrow) // mutual recursion; resolving method calls requires resolving types and vice versa + result = super.resolveCallTarget(i, derefChain, borrow) // mutual recursion; resolving method calls requires resolving types and vice versa ) } - Declaration getTarget(string derefChainBorrow) { - exists(ImplOrTraitItemNode i, Method m | - m = this.getTarget(i, derefChainBorrow) and - result = TMethodFunctionDeclaration(i, m) + override Declaration getTarget(string derefChainBorrow) { + exists(ImplOrTraitItemNodeOption i, AssocFunction f | + f = this.getTarget(i.asSome(), derefChainBorrow) and + result = TFunctionDeclaration(i, f) ) } - /** - * Holds if the return type of this call at `path` may have to be inferred - * from the context. - */ pragma[nomagic] - predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { + override predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { exists(ImplOrTraitItemNode i | this.hasUnknownTypeAt(i, this.getTarget(i, derefChainBorrow), pos, path) ) - } - } + or + derefChainBorrow = noDerefChainBorrow() and + forex(ImplOrTraitItemNode i, Function f | + f = CallExprImpl::getResolvedFunction(this) and + f = i.getAnAssocItem() + | + this.hasUnknownTypeAt(i, f, pos, path) + ) + } + } + + private class NonAssocFunctionCallAccess extends Access, NonAssocCallExpr { + pragma[nomagic] + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = getCallExprTypeArgument(this, apos, path) + } + + override AstNode getNodeAt(FunctionPosition posAdj) { + result = this.getSyntacticArgument(posAdj.asArgumentPosition()) + or + result = this and posAdj.isReturn() + } + + pragma[nomagic] + private Type getInferredType(FunctionPosition posAdj, TypePath path) { + posAdj.isTypeQualifier() and + result = getCallExprTypeQualifier(this, path, false) + or + result = inferType(this.getNodeAt(posAdj), path) + } + + bindingset[derefChainBorrow] + override Type getInferredType(string derefChainBorrow, FunctionPosition posAdj, TypePath path) { + exists(derefChainBorrow) and + result = this.getInferredType(posAdj, path) + } + + pragma[nomagic] + private Declaration getTarget() { + exists(ImplOrTraitItemNodeOption i, FunctionDeclaration f | + f = this.resolveCallTargetViaPathResolution() and + f.isDirectlyFor(i) and + result = TFunctionDeclaration(i, f) + ) + } + + override Declaration getTarget(string derefChainBorrow) { + result = this.getTarget() and + derefChainBorrow = noDerefChainBorrow() + } + + pragma[nomagic] + override predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { + derefChainBorrow = noDerefChainBorrow() and + exists(ImplOrTraitItemNodeOption i, FunctionDeclaration f | + TFunctionDeclaration(i, f) = this.getTarget() and + this.hasUnknownTypeAt(i.asSome(), f, pos, path) + ) + } + } } -private module MethodCallMatching = MatchingWithEnvironment; +private module FunctionCallMatching = MatchingWithEnvironment; pragma[nomagic] -private Type inferMethodCallType0( - MethodCallMatchingInput::Access a, MethodCallMatchingInput::AccessPosition apos, AstNode n, - string derefChainBorrow, TypePath path +private Type inferFunctionCallType0( + FunctionCallMatchingInput::Access a, FunctionPosition posAdj, AstNode n, DerefChain derefChain, + BorrowKind borrow, TypePath path ) { exists(TypePath path0 | - n = a.getNodeAt(apos) and - ( - result = MethodCallMatching::inferAccessType(a, derefChainBorrow, apos, path0) + n = a.getNodeAt(posAdj) and + exists(string derefChainBorrow | + FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) + | + result = FunctionCallMatching::inferAccessType(a, derefChainBorrow, posAdj, path0) or - a.hasUnknownTypeAt(derefChainBorrow, apos, path0) and + a.hasUnknownTypeAt(derefChainBorrow, posAdj, path0) and result = TUnknownType() ) | if // index expression `x[i]` desugars to `*x.index(i)`, so we must account for // the implicit deref - apos.isReturn() and + posAdj.isReturn() and a instanceof IndexExpr then path0.isCons(getRefTypeParameter(_), path) else path = path0 @@ -2587,9 +2957,11 @@ private Type inferMethodCallType0( } pragma[nomagic] -private Type inferMethodCallTypeNonSelf(AstNode n, FunctionPosition pos, TypePath path) { - result = inferMethodCallType0(_, pos, n, _, path) and - not pos.isSelf() +private Type inferFunctionCallTypeNonSelf(AstNode n, FunctionPosition pos, TypePath path) { + exists(FunctionCallMatchingInput::Access a | + result = inferFunctionCallType0(a, pos, n, _, _, path) and + not a.(AssocFunctionResolution::AssocFunctionCall).hasReceiverPos(pos) + ) } /** @@ -2600,15 +2972,13 @@ private Type inferMethodCallTypeNonSelf(AstNode n, FunctionPosition pos, TypePat * empty, at which point the inferred type can be applied back to `n`. */ pragma[nomagic] -private Type inferMethodCallTypeSelf(MethodCall mc, AstNode n, DerefChain derefChain, TypePath path) { - exists( - MethodCallMatchingInput::AccessPosition apos, string derefChainBorrow, BorrowKind borrow, - TypePath path0 - | - result = inferMethodCallType0(mc, apos, n, derefChainBorrow, path0) and - apos.isSelf() and - MethodCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) +private Type inferFunctionCallTypeSelf( + MethodCall mc, AstNode n, DerefChain derefChain, TypePath path +) { + exists(FunctionPosition posAdj, BorrowKind borrow, TypePath path0 | + result = inferFunctionCallType0(mc, posAdj, n, derefChain, borrow, path0) | + posAdj.asPosition() = 0 and borrow.isNoBorrow() and path = path0 or @@ -2624,7 +2994,7 @@ private Type inferMethodCallTypeSelf(MethodCall mc, AstNode n, DerefChain derefC DerefChain derefChain0, Type t0, TypePath path0, DerefImplItemNode impl, Type selfParamType, TypePath selfPath | - t0 = inferMethodCallTypeSelf(mc, n, derefChain0, path0) and + t0 = inferFunctionCallTypeSelf(mc, n, derefChain0, path0) and derefChain0.isCons(impl, derefChain) and selfParamType = impl.resolveSelfTypeAt(selfPath) | @@ -2641,445 +3011,23 @@ private Type inferMethodCallTypeSelf(MethodCall mc, AstNode n, DerefChain derefC ) } -private Type inferMethodCallTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { - result = inferMethodCallTypeNonSelf(n, pos, path) +private Type inferFunctionCallTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { + result = inferFunctionCallTypeNonSelf(n, pos, path) or - exists(MethodCall mc | - result = inferMethodCallTypeSelf(mc, n, DerefChain::nil(), path) and - if mc instanceof CallExpr then pos.asPosition() = 0 else pos.isSelf() + exists(FunctionCallMatchingInput::Access a | + result = inferFunctionCallTypeSelf(a, n, DerefChain::nil(), path) and + if a.(AssocFunctionResolution::AssocFunctionCall).hasReceiver() + then pos.isSelf() + else pos.asPosition() = 0 ) } /** - * Gets the type of `n` at `path`, where `n` is either a method call or an - * argument/receiver of a method call. - */ -private predicate inferMethodCallType = - ContextTyping::CheckContextTyping::check/2; - -/** - * Provides logic for resolving calls to non-method items. This includes - * "calls" to tuple variants and tuple structs. + * Gets the type of `n` at `path`, where `n` is either a function call or an + * argument/receiver of a function call. */ -private module NonMethodResolution { - pragma[nomagic] - private predicate traitFunctionResolutionDependsOnArgument0( - TraitItemNode trait, NonMethodFunction traitFunction, FunctionPosition pos, ImplItemNode impl, - NonMethodFunction implFunction, TypePath path, TypeParameter traitTp - ) { - implFunction = impl.getAnAssocItem() and - implFunction.implements(traitFunction) and - FunctionOverloading::traitTypeParameterOccurrence(trait, traitFunction, _, pos, path, traitTp) and - ( - traitTp = TSelfTypeParameter(trait) - or - FunctionOverloading::functionResolutionDependsOnArgument(impl, implFunction, traitTp, pos) - ) - } - - /** - * Holds if resolving the function `implFunction` in `impl` requires inspecting - * the type of applied _arguments_ or possibly knowing the return type. - * - * `traitTp` is a type parameter of the trait being implemented by `impl`, and - * we need to check that the type of `f` corresponding to `traitTp` is satisfied - * at any one of the positions `pos` in which that type occurs in `f` (at `path`). - * - * As for method resolution, we always check the type being implemented (corresponding - * to `traitTp` being the special `Self` type parameter). - */ - pragma[nomagic] - private predicate traitFunctionResolutionDependsOnArgument( - TraitItemNode trait, NonMethodFunction traitFunction, FunctionPosition pos, ImplItemNode impl, - NonMethodFunction implFunction, TypePath path, TypeParameter traitTp - ) { - traitFunctionResolutionDependsOnArgument0(trait, traitFunction, pos, impl, implFunction, path, - traitTp) and - // Exclude functions where we cannot resolve all relevant type mentions; this allows - // for blanket implementations to be applied in those cases - forall(TypeParameter traitTp0 | - traitFunctionResolutionDependsOnArgument0(trait, traitFunction, _, impl, implFunction, _, - traitTp0) - | - exists(FunctionPosition pos0, TypePath path0 | - traitFunctionResolutionDependsOnArgument0(trait, traitFunction, pos0, impl, implFunction, - path0, traitTp0) and - exists(getAssocFunctionTypeAt(implFunction, impl, pos0, path0)) - ) - ) - } - - /** - * Holds if `f` inside `i` either implements trait function `traitFunction` inside `trait` - * or is equal to `traitFunction`, and the type of `f` at `pos` and `path` is `t`, which - * corresponds to the `Self` type parameter of `trait`. - */ - pragma[nomagic] - private predicate traitFunctionHasSelfType( - TraitItemNode trait, NonMethodFunction traitFunction, FunctionPosition pos, TypePath path, - Type t, ImplOrTraitItemNode i, NonMethodFunction f - ) { - exists(ImplItemNode impl, NonMethodFunction implFunction, AssocFunctionType aft | - traitFunctionResolutionDependsOnArgument(trait, traitFunction, pos, impl, implFunction, path, - TSelfTypeParameter(trait)) and - aft.appliesTo(f, i, pos) and - t = aft.getTypeAt(path) - | - i = trait and - f = traitFunction - or - i = impl and - f = implFunction and - not BlanketImplementation::isBlanketLike(i, _, _) - ) - } - - pragma[nomagic] - private predicate functionResolutionDependsOnArgument( - ImplItemNode impl, NonMethodFunction f, FunctionPosition pos, TypeParameter tp - ) { - traitFunctionResolutionDependsOnArgument(_, _, pos, impl, f, _, tp) - or - // For inherent implementations of generic types, we also need to check the type being - // implemented. We arbitrarily choose the first type parameter of the type being implemented - // to represent this case. - f = impl.getAnAssocItem() and - not impl.(Impl).hasTrait() and - tp = TTypeParamTypeParameter(impl.resolveSelfTy().getTypeParam(0)) and - pos.isTypeQualifier() - } - - pragma[nomagic] - private predicate functionInfoBlanketLikeRelevantPos( - NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait, - FunctionPosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam - ) { - functionInfoBlanketLike(f, name, arity, impl, trait, pos, t, blanketPath, blanketTypeParam) and - ( - if pos.isReturn() - then - // We only check that the context of the call provides relevant type information - // when no argument can - not exists(FunctionPosition pos0 | - functionInfoBlanketLike(f, name, arity, impl, trait, pos0, _, _, _) and - not pos0.isReturn() - ) - else any() - ) - } - - pragma[nomagic] - private predicate blanketLikeCallTraitCandidate(Element fc, Trait trait) { - fc = - any(NonMethodCall nmc | - exists(string name, int arity | - nmc.hasNameAndArity(name, arity) and - functionInfoBlanketLikeRelevantPos(_, name, arity, _, trait, _, _, _, _) - | - not nmc.hasTrait() - or - trait = nmc.getTrait() - ) - ) - } - - private module BlanketTraitIsVisible = TraitIsVisible; - - /** A (potential) non-method call, `f(x)`. */ - final class NonMethodCall extends CallExpr { - NonMethodCall() { - // even if a function cannot be resolved by path resolution, it may still - // be possible to resolve a blanket implementation (so not `forex`) - forall(Function f | f = CallExprImpl::getResolvedFunction(this) | - f instanceof NonMethodFunction - ) - } - - pragma[nomagic] - predicate hasNameAndArity(string name, int arity) { - name = CallExprImpl::getFunctionPath(this).getText() and - arity = this.getArgList().getNumberOfArgs() - } - - /** - * Gets the item that this function call resolves to using path resolution, - * if any. - */ - private ItemNode getPathResolutionResolved() { - result = CallExprImpl::getResolvedFunction(this) and - not result.(Function).hasSelfParam() - } - - /** - * Gets the associated function that this function call resolves to using path - * resolution, if any. - */ - pragma[nomagic] - NonMethodFunction getPathResolutionResolved(ImplOrTraitItemNode i) { - result = this.getPathResolutionResolved() and - result = i.getAnAssocItem() - } - - /** - * Gets the blanket function that this call may resolve to, if any. - */ - pragma[nomagic] - NonMethodFunction resolveCallTargetBlanketCand(ImplItemNode impl) { - exists(string name | - this.hasNameAndArity(pragma[only_bind_into](name), _) and - ArgIsInstantiationOfBlanketParam::argIsInstantiationOf(MkCallAndBlanketPos(this, _), impl, _) and - result = impl.getASuccessor(pragma[only_bind_into](name)) - ) - } - - /** Gets the trait targeted by this call, if any. */ - Trait getTrait() { result = getCallExprTraitQualifier(this) } - - /** Holds if this call targets a trait. */ - predicate hasTrait() { exists(this.getTrait()) } - - AstNode getNodeAt(FunctionPosition pos) { - result = this.getSyntacticArgument(pos.asArgumentPosition()) - or - result = this and pos.isReturn() - } - - Type getTypeAt(FunctionPosition pos, TypePath path) { - result = inferType(this.getNodeAt(pos), path) - } - - pragma[nomagic] - NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) { - not this.hasTrait() and - result = this.getPathResolutionResolved(i) and - not exists(this.resolveCallTargetViaPathResolution()) and - functionResolutionDependsOnArgument(i, result, _, _) - } - - pragma[nomagic] - predicate resolveCallTargetBlanketLikeCand( - ImplItemNode impl, FunctionPosition pos, TypePath blanketPath, TypeParam blanketTypeParam - ) { - exists(string name, int arity, Trait trait, AssocFunctionType t | - this.hasNameAndArity(name, arity) and - exists(this.getTypeAt(pos, blanketPath)) and - functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath, - blanketTypeParam) and - BlanketTraitIsVisible::traitIsVisible(this, trait) - | - not this.hasTrait() - or - trait = this.getTrait() - ) - } - - pragma[nomagic] - predicate hasTraitResolved(TraitItemNode trait, NonMethodFunction resolved) { - resolved = this.getPathResolutionResolved() and - trait = this.getTrait() - } - - /** - * Holds if this call has no compatible non-blanket target, and it has some - * candidate blanket target. - */ - pragma[nomagic] - predicate hasNoCompatibleNonBlanketTarget() { - this.resolveCallTargetBlanketLikeCand(_, _, _, _) and - not exists(this.resolveCallTargetViaPathResolution()) and - forall(ImplOrTraitItemNode i, Function f | f = this.resolveCallTargetNonBlanketCand(i) | - NonMethodArgsAreInstantiationsOfNonBlanket::argsAreNotInstantiationsOf(this, i, f) - ) and - ( - not this.hasTraitResolved(_, _) - or - exists( - TraitItemNode trait, NonMethodFunction resolved, FunctionPosition pos, TypePath path, - Type t - | - this.(NonMethodArgsAreInstantiationsOfNonBlanketInput::Call) - .hasTraitResolvedSelfType(trait, resolved, pos, path, t) - | - forall(ImplOrTraitItemNode i, Function f | - traitFunctionHasSelfType(trait, resolved, pos, path, t, i, f) - | - NonMethodArgsAreInstantiationsOfNonBlanket::argsAreNotInstantiationsOf(this, i, f) - ) - ) - ) - } - - /** - * Gets the target of this call, which can be resolved using only path resolution. - */ - pragma[nomagic] - ItemNode resolveCallTargetViaPathResolution() { - not this.hasTrait() and - result = this.getPathResolutionResolved() and - not functionResolutionDependsOnArgument(_, result, _, _) - } - - /** - * Gets the target of this call, which can be resolved using type inference. - */ - pragma[nomagic] - NonMethodFunction resolveCallTargetViaTypeInference(ImplOrTraitItemNode i) { - result = this.resolveCallTargetBlanketCand(i) and - not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _) - or - NonMethodArgsAreInstantiationsOfBlanket::argsAreInstantiationsOf(this, i, result) - or - NonMethodArgsAreInstantiationsOfNonBlanket::argsAreInstantiationsOf(this, i, result) - } - } - - private newtype TCallAndBlanketPos = - MkCallAndBlanketPos(NonMethodCall fc, FunctionPosition pos) { - fc.resolveCallTargetBlanketLikeCand(_, pos, _, _) - } - - /** A call tagged with a position. */ - private class CallAndBlanketPos extends MkCallAndBlanketPos { - NonMethodCall fc; - FunctionPosition pos; - - CallAndBlanketPos() { this = MkCallAndBlanketPos(fc, pos) } - - Location getLocation() { result = fc.getLocation() } - - Type getTypeAt(TypePath path) { result = fc.getTypeAt(pos, path) } - - string toString() { result = fc.toString() + " [arg " + pos + "]" } - } - - private module ArgSatisfiesBlanketConstraintInput implements - BlanketImplementation::SatisfiesBlanketConstraintInputSig - { - pragma[nomagic] - predicate hasBlanketCandidate( - CallAndBlanketPos fcp, ImplItemNode impl, TypePath blanketPath, TypeParam blanketTypeParam - ) { - exists(NonMethodCall fc, FunctionPosition pos | - fcp = MkCallAndBlanketPos(fc, pos) and - fc.resolveCallTargetBlanketLikeCand(impl, pos, blanketPath, blanketTypeParam) and - // Only apply blanket implementations when no other implementations are possible; - // this is to account for codebases that use the (unstable) specialization feature - // (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as - // cases where our blanket implementation filtering is not precise enough. - (fc.hasNoCompatibleNonBlanketTarget() or not impl.isBlanketImplementation()) - ) - } - } - - private module ArgSatisfiesBlanketConstraint = - BlanketImplementation::SatisfiesBlanketConstraint; - - /** - * A configuration for matching the type of an argument against the type of - * a parameter that mentions a satisfied blanket type parameter. - */ - private module ArgIsInstantiationOfBlanketParamInput implements - IsInstantiationOfInputSig - { - pragma[nomagic] - predicate potentialInstantiationOf( - CallAndBlanketPos fcp, TypeAbstraction abs, AssocFunctionType constraint - ) { - exists(FunctionPosition pos | - ArgSatisfiesBlanketConstraint::satisfiesBlanketConstraint(fcp, abs) and - fcp = MkCallAndBlanketPos(_, pos) and - functionInfoBlanketLikeRelevantPos(_, _, _, abs, _, pos, constraint, _, _) - ) - } - - predicate relevantConstraint(AssocFunctionType constraint) { - functionInfoBlanketLikeRelevantPos(_, _, _, _, _, _, constraint, _, _) - } - } - - private module ArgIsInstantiationOfBlanketParam = - ArgIsInstantiationOf; - - private Type getArgType( - NonMethodCall call, FunctionPosition pos, TypePath path, boolean isDefaultTypeArg - ) { - result = inferType(call.getNodeAt(pos), path) and - isDefaultTypeArg = false - or - result = getCallExprTypeQualifier(call, path, isDefaultTypeArg) and - pos.isTypeQualifier() - } - - private module NonMethodArgsAreInstantiationsOfBlanketInput implements - ArgsAreInstantiationsOfInputSig - { - predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos) { - functionResolutionDependsOnArgument(i, f, pos, tp) - } - - final class Call extends NonMethodCall { - Type getArgType(FunctionPosition pos, TypePath path) { - result = getArgType(this, pos, path, false) - } - - predicate hasTargetCand(ImplOrTraitItemNode i, Function f) { - f = this.resolveCallTargetBlanketCand(i) - } - } - } - - private module NonMethodArgsAreInstantiationsOfBlanket = - ArgsAreInstantiationsOf; - - private module NonMethodArgsAreInstantiationsOfNonBlanketInput implements - ArgsAreInstantiationsOfInputSig - { - predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) { - functionResolutionDependsOnArgument(i, f, pos, traitTp) - or - // Also match against the trait function itself - FunctionOverloading::traitTypeParameterOccurrence(i, f, _, pos, _, traitTp) and - traitTp = TSelfTypeParameter(i) - } - - class Call extends NonMethodCall { - Type getArgType(FunctionPosition pos, TypePath path) { - result = getArgType(this, pos, path, _) - } - - /** - * Holds if this call is of the form `Trait::function(args)`, and the type at `pos` and - * `path` matches the `Self` type parameter of `Trait`. - */ - pragma[nomagic] - predicate hasTraitResolvedSelfType( - TraitItemNode trait, NonMethodFunction function, FunctionPosition pos, TypePath path, Type t - ) { - this.hasTraitResolved(trait, function) and - FunctionOverloading::traitTypeParameterOccurrence(trait, function, _, pos, path, - TSelfTypeParameter(trait)) and - t = substituteLookupTraits(this.getArgType(pos, path)) and - t != TUnknownType() - } - - predicate hasTargetCand(ImplOrTraitItemNode i, Function f) { - f = this.resolveCallTargetNonBlanketCand(i) - or - exists( - TraitItemNode trait, NonMethodFunction resolved, FunctionPosition pos, TypePath path, - Type t - | - this.hasTraitResolvedSelfType(trait, resolved, pos, path, t) and - traitFunctionHasSelfType(trait, resolved, pos, path, t, i, f) - ) - } - } - } - - private module NonMethodArgsAreInstantiationsOfNonBlanket = - ArgsAreInstantiationsOf; -} +private predicate inferFunctionCallType = + ContextTyping::CheckContextTyping::check/2; abstract private class TupleLikeConstructor extends Addressable { final TypeParameter getTypeParameter(TypeParameterPosition ppos) { @@ -3130,94 +3078,24 @@ private class TupleLikeVariant extends TupleLikeConstructor instanceof Variant { } /** - * A matching configuration for resolving types of calls like - * `foo::bar(baz)` where the target is not a method. - * - * This also includes "calls" to tuple variants and tuple structs such - * as `Result::Ok(42)`. + * A matching configuration for resolving types of tuple-like variants and tuple + * structs such as `Result::Ok(42)`. */ -private module NonMethodCallMatchingInput implements MatchingInputSig { +private module TupleLikeConstructionMatchingInput implements MatchingInputSig { import FunctionPositionMatchingInput - private class NonMethodFunctionDeclaration extends NonMethodFunction, FunctionDeclaration { } - - private newtype TDeclaration = - TNonMethodFunctionDeclaration(ImplOrTraitItemNodeOption i, NonMethodFunctionDeclaration f) { - f.isFor(i) - } or - TTupleLikeConstructorDeclaration(TupleLikeConstructor tc) - - abstract class Declaration extends TDeclaration { - abstract TypeParameter getTypeParameter(TypeParameterPosition ppos); + class Declaration = TupleLikeConstructor; + class Access extends NonAssocCallExpr, ContextTyping::ContextTypedCallCand { pragma[nomagic] - abstract Type getParameterType(DeclarationPosition dpos, TypePath path); - - abstract Type getReturnType(TypePath path); - - Type getDeclaredType(DeclarationPosition dpos, TypePath path) { - result = this.getParameterType(dpos, path) - or - dpos.isReturn() and - result = this.getReturnType(path) - } - - abstract string toString(); - - abstract Location getLocation(); - } - - private class NonMethodFunctionDecl extends Declaration, TNonMethodFunctionDeclaration { - private ImplOrTraitItemNodeOption i; - private NonMethodFunctionDeclaration f; - - NonMethodFunctionDecl() { this = TNonMethodFunctionDeclaration(i, f) } - - override TypeParameter getTypeParameter(TypeParameterPosition ppos) { - result = f.getTypeParameter(i, ppos) - } - - override Type getParameterType(DeclarationPosition dpos, TypePath path) { - result = f.getParameterType(i, dpos, path) + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = getCallExprTypeArgument(this, apos, path) } - override Type getReturnType(TypePath path) { result = f.getReturnType(i, path) } - - override string toString() { - i.isNone() and result = f.toString() + AstNode getNodeAt(FunctionPosition pos) { + result = this.getSyntacticArgument(pos.asArgumentPosition()) or - result = f.toStringExt(i.asSome()) - } - - override Location getLocation() { result = f.getLocation() } - } - - private class TupleLikeConstructorDeclaration extends Declaration, - TTupleLikeConstructorDeclaration - { - TupleLikeConstructor tc; - - TupleLikeConstructorDeclaration() { this = TTupleLikeConstructorDeclaration(tc) } - - override TypeParameter getTypeParameter(TypeParameterPosition ppos) { - result = tc.getTypeParameter(ppos) - } - - override Type getParameterType(DeclarationPosition dpos, TypePath path) { - result = tc.getParameterType(dpos, path) - } - - override Type getReturnType(TypePath path) { result = tc.getReturnType(path) } - - override string toString() { result = tc.toString() } - - override Location getLocation() { result = tc.getLocation() } - } - - class Access extends NonMethodResolution::NonMethodCall, ContextTyping::ContextTypedCallCand { - pragma[nomagic] - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - result = getCallExprTypeArgument(this, apos, path) + result = this and pos.isReturn() } pragma[nomagic] @@ -3228,42 +3106,18 @@ private module NonMethodCallMatchingInput implements MatchingInputSig { result = inferType(this.getNodeAt(apos), path) } - pragma[inline] - Declaration getTarget() { - exists(ImplOrTraitItemNodeOption i, NonMethodFunctionDeclaration f | - result = TNonMethodFunctionDeclaration(i, f) - | - f = this.resolveCallTargetViaTypeInference(i.asSome()) // mutual recursion; resolving some associated function calls requires resolving types - or - f = this.resolveCallTargetViaPathResolution() and - f.isDirectlyFor(i) - ) - or - exists(ItemNode i | i = this.resolveCallTargetViaPathResolution() | - result = TTupleLikeConstructorDeclaration(i) - ) - } + pragma[nomagic] + Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } /** - * Holds if the return type of this call at `path` may have to be inferred - * from the context. + * Holds if the return type of this tuple-like construction at `path` may have to be inferred + * from the context, for example in `Result::Ok(42)` the error type has to be inferred from the + * context. */ pragma[nomagic] predicate hasUnknownTypeAt(FunctionPosition pos, TypePath path) { - exists(ImplOrTraitItemNodeOption i, NonMethodFunctionDeclaration f | - TNonMethodFunctionDeclaration(i, f) = this.getTarget() and - this.hasUnknownTypeAt(i.asSome(), f, pos, path) - ) - or - forex(ImplOrTraitItemNode i, NonMethodFunctionDeclaration f | - f = this.getPathResolutionResolved(i) - | - this.hasUnknownTypeAt(i, f, pos, path) - ) - or - // Tuple declarations, such as `Result::Ok(...)`, may also be context typed exists(TupleLikeConstructor tc, TypeParameter tp | - tc = this.resolveCallTargetViaPathResolution() and + tc = this.getTarget() and pos.isReturn() and tp = tc.getReturnType(path) and not tp = tc.getParameterType(_, _) and @@ -3277,20 +3131,20 @@ private module NonMethodCallMatchingInput implements MatchingInputSig { } } -private module NonMethodCallMatching = Matching; +private module TupleLikeConstructionMatching = Matching; pragma[nomagic] -private Type inferNonMethodCallType0(AstNode n, FunctionPosition pos, TypePath path) { - exists(NonMethodCallMatchingInput::Access a | n = a.getNodeAt(pos) | - result = NonMethodCallMatching::inferAccessType(a, pos, path) +private Type inferTupleLikeConstructionType0(AstNode n, FunctionPosition pos, TypePath path) { + exists(TupleLikeConstructionMatchingInput::Access a | n = a.getNodeAt(pos) | + result = TupleLikeConstructionMatching::inferAccessType(a, pos, path) or a.hasUnknownTypeAt(pos, path) and result = TUnknownType() ) } -private predicate inferNonMethodCallType = - ContextTyping::CheckContextTyping::check/2; +private predicate inferTupleLikeConstructionType = + ContextTyping::CheckContextTyping::check/2; /** * A matching configuration for resolving types of operations like `a + b`. @@ -3299,22 +3153,22 @@ private module OperationMatchingInput implements MatchingInputSig { private import codeql.rust.elements.internal.OperationImpl::Impl as OperationImpl import FunctionPositionMatchingInput - class Declaration extends MethodCallMatchingInput::Declaration { + class Declaration extends FunctionCallMatchingInput::Declaration { private Method getSelfOrImpl() { - result = m + result = f or - m.implements(result) + f.implements(result) } pragma[nomagic] - private predicate borrowsAt(DeclarationPosition pos) { + private predicate borrowsAt(FunctionPosition posAdj) { exists(TraitItemNode t, string path, string method | this.getSelfOrImpl() = t.getAssocItem(method) and path = t.getCanonicalPath(_) and exists(int borrows | OperationImpl::isOverloaded(_, _, path, method, borrows) | - pos.isSelf() and borrows >= 1 + posAdj.asPosition() = 0 and borrows >= 1 or - pos.asPosition() = 0 and + posAdj.asPosition() = 1 and borrows >= 2 ) ) @@ -3323,30 +3177,30 @@ private module OperationMatchingInput implements MatchingInputSig { pragma[nomagic] private predicate derefsReturn() { this.getSelfOrImpl() = any(DerefTrait t).getDerefFunction() } - Type getDeclaredType(DeclarationPosition dpos, TypePath path) { + Type getDeclaredType(FunctionPosition posAdj, TypePath path) { exists(TypePath path0 | - result = super.getDeclaredType(dpos, path0) and + result = super.getDeclaredType(posAdj, path0) and if - this.borrowsAt(dpos) + this.borrowsAt(posAdj) or - dpos.isReturn() and this.derefsReturn() + posAdj.isReturn() and this.derefsReturn() then path0.isCons(getRefTypeParameter(_), path) else path0 = path ) } } - class Access extends MethodResolution::MethodCallOperation { + class Access extends AssocFunctionResolution::AssocFunctionCallOperation { Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } pragma[nomagic] - Type getInferredType(AccessPosition apos, TypePath path) { - result = inferType(this.getNodeAt(apos), path) + Type getInferredType(FunctionPosition posAdj, TypePath path) { + result = inferType(this.getNodeAt(posAdj), path) } Declaration getTarget() { exists(ImplOrTraitItemNode i | - result.isMethod(i, this.resolveCallTarget(i, _, _)) // mutual recursion + result.isAssocFunction(i, this.resolveCallTarget(i, _, _)) // mutual recursion ) } } @@ -3356,9 +3210,10 @@ private module OperationMatching = Matching; pragma[nomagic] private Type inferOperationType0(AstNode n, FunctionPosition pos, TypePath path) { - exists(OperationMatchingInput::Access a | - n = a.getNodeAt(pos) and - result = OperationMatching::inferAccessType(a, pos, path) + exists(OperationMatchingInput::Access a, FunctionPosition posAdj | + n = a.getNodeAt(posAdj) and + posAdj = pos.getFunctionCallAdjusted() and + result = OperationMatching::inferAccessType(a, posAdj, path) ) } @@ -4057,7 +3912,8 @@ private module Cached { cached predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow) { exists(BorrowKind bk | - any(MethodResolution::MethodCall mc).argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and + any(AssocFunctionResolution::AssocFunctionCall afc) + .argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and if bk.isNoBorrow() then borrow = false else borrow = true ) or @@ -4082,15 +3938,14 @@ private module Cached { cached Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { dispatch = false and - result = call.(NonMethodResolution::NonMethodCall).resolveCallTargetViaPathResolution() + result = call.(NonAssocCallExpr).resolveCallTargetViaPathResolution() or exists(ImplOrTraitItemNode i | i instanceof TraitItemNode and dispatch = true or i instanceof ImplItemNode and dispatch = false | - result = call.(MethodResolution::MethodCall).resolveCallTarget(i, _, _) or - result = call.(NonMethodResolution::NonMethodCall).resolveCallTargetViaTypeInference(i) + result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _) ) } @@ -4179,9 +4034,9 @@ private module Cached { or result = inferStructExprType(n, path) or - result = inferMethodCallType(n, path) + result = inferFunctionCallType(n, path) or - result = inferNonMethodCallType(n, path) + result = inferTupleLikeConstructionType(n, path) or result = inferOperationType(n, path) or @@ -4249,14 +4104,14 @@ private module Debug { t = self.getTypeAt(path) } - predicate debugInferMethodCallType(AstNode n, TypePath path, Type t) { + predicate debugInferFunctionCallType(AstNode n, TypePath path, Type t) { n = getRelevantLocatable() and - t = inferMethodCallType(n, path) + t = inferFunctionCallType(n, path) } - predicate debugInferNonMethodCallType(AstNode n, TypePath path, Type t) { + predicate debugInferTupleLikeConstructionType(AstNode n, TypePath path, Type t) { n = getRelevantLocatable() and - t = inferNonMethodCallType(n, path) + t = inferTupleLikeConstructionType(n, path) } predicate debugTypeMention(TypeMention tm, TypePath path, Type type) { diff --git a/rust/ql/test/library-tests/dataflow/sources/net/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/library-tests/dataflow/sources/net/CONSISTENCY/PathResolutionConsistency.expected index 8ca58acd1d06..e69de29bb2d1 100644 --- a/rust/ql/test/library-tests/dataflow/sources/net/CONSISTENCY/PathResolutionConsistency.expected +++ b/rust/ql/test/library-tests/dataflow/sources/net/CONSISTENCY/PathResolutionConsistency.expected @@ -1,6 +0,0 @@ -multipleResolvedTargets -| test.rs:389:30:389:67 | pinned.poll_read(...) | -| test.rs:416:26:416:54 | pinned.poll_fill_buf(...) | -| test.rs:423:27:423:71 | ... .poll_fill_buf(...) | -| test.rs:447:30:447:67 | pinned.poll_read(...) | -| test.rs:470:26:470:54 | pinned.poll_fill_buf(...) | diff --git a/rust/ql/test/library-tests/dataflow/taint/inline-taint-flow.expected b/rust/ql/test/library-tests/dataflow/taint/inline-taint-flow.expected index 255af4cc86ed..763bff966d3f 100644 --- a/rust/ql/test/library-tests/dataflow/taint/inline-taint-flow.expected +++ b/rust/ql/test/library-tests/dataflow/taint/inline-taint-flow.expected @@ -100,7 +100,9 @@ edges | main.rs:161:10:161:18 | source(...) | main.rs:161:10:161:25 | ... .shr(...) | provenance | MaD:30 | | main.rs:162:19:162:27 | source(...) | main.rs:162:10:162:28 | 1i64.shr(...) | provenance | MaD:30 | | main.rs:164:10:164:18 | source(...) | main.rs:164:10:164:30 | ... .bitor(...) | provenance | MaD:19 | +| main.rs:165:10:165:18 | source(...) | main.rs:165:10:165:27 | ... .bitor(...) | provenance | MaD:19 | | main.rs:166:21:166:29 | source(...) | main.rs:166:10:166:30 | 1i64.bitor(...) | provenance | MaD:19 | +| main.rs:167:18:167:26 | source(...) | main.rs:167:10:167:27 | 1.bitor(...) | provenance | MaD:19 | | main.rs:170:5:170:5 | [post] a | main.rs:171:5:171:5 | a | provenance | | | main.rs:170:5:170:5 | [post] a | main.rs:172:5:172:5 | a | provenance | | | main.rs:170:5:170:5 | [post] a | main.rs:173:5:173:5 | a | provenance | | @@ -351,8 +353,12 @@ nodes | main.rs:162:19:162:27 | source(...) | semmle.label | source(...) | | main.rs:164:10:164:18 | source(...) | semmle.label | source(...) | | main.rs:164:10:164:30 | ... .bitor(...) | semmle.label | ... .bitor(...) | +| main.rs:165:10:165:18 | source(...) | semmle.label | source(...) | +| main.rs:165:10:165:27 | ... .bitor(...) | semmle.label | ... .bitor(...) | | main.rs:166:10:166:30 | 1i64.bitor(...) | semmle.label | 1i64.bitor(...) | | main.rs:166:21:166:29 | source(...) | semmle.label | source(...) | +| main.rs:167:10:167:27 | 1.bitor(...) | semmle.label | 1.bitor(...) | +| main.rs:167:18:167:26 | source(...) | semmle.label | source(...) | | main.rs:170:5:170:5 | [post] a | semmle.label | [post] a | | main.rs:170:18:170:26 | source(...) | semmle.label | source(...) | | main.rs:171:5:171:5 | [post] a | semmle.label | [post] a | @@ -516,7 +522,9 @@ testFailures | main.rs:161:10:161:25 | ... .shr(...) | main.rs:161:10:161:18 | source(...) | main.rs:161:10:161:25 | ... .shr(...) | $@ | main.rs:161:10:161:18 | source(...) | source(...) | | main.rs:162:10:162:28 | 1i64.shr(...) | main.rs:162:19:162:27 | source(...) | main.rs:162:10:162:28 | 1i64.shr(...) | $@ | main.rs:162:19:162:27 | source(...) | source(...) | | main.rs:164:10:164:30 | ... .bitor(...) | main.rs:164:10:164:18 | source(...) | main.rs:164:10:164:30 | ... .bitor(...) | $@ | main.rs:164:10:164:18 | source(...) | source(...) | +| main.rs:165:10:165:27 | ... .bitor(...) | main.rs:165:10:165:18 | source(...) | main.rs:165:10:165:27 | ... .bitor(...) | $@ | main.rs:165:10:165:18 | source(...) | source(...) | | main.rs:166:10:166:30 | 1i64.bitor(...) | main.rs:166:21:166:29 | source(...) | main.rs:166:10:166:30 | 1i64.bitor(...) | $@ | main.rs:166:21:166:29 | source(...) | source(...) | +| main.rs:167:10:167:27 | 1.bitor(...) | main.rs:167:18:167:26 | source(...) | main.rs:167:10:167:27 | 1.bitor(...) | $@ | main.rs:167:18:167:26 | source(...) | source(...) | | main.rs:176:10:176:10 | a | main.rs:170:18:170:26 | source(...) | main.rs:176:10:176:10 | a | $@ | main.rs:170:18:170:26 | source(...) | source(...) | | main.rs:176:10:176:10 | a | main.rs:171:18:171:26 | source(...) | main.rs:176:10:176:10 | a | $@ | main.rs:171:18:171:26 | source(...) | source(...) | | main.rs:176:10:176:10 | a | main.rs:172:18:172:26 | source(...) | main.rs:176:10:176:10 | a | $@ | main.rs:172:18:172:26 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/taint/main.rs b/rust/ql/test/library-tests/dataflow/taint/main.rs index 07770cc71189..05dbd1eb702f 100644 --- a/rust/ql/test/library-tests/dataflow/taint/main.rs +++ b/rust/ql/test/library-tests/dataflow/taint/main.rs @@ -162,9 +162,9 @@ fn std_ops() { sink(1i64.shr(source(2))); // $ hasTaintFlow=2 sink(source(1).bitor(2i64)); // $ hasTaintFlow=1 - sink(source(1).bitor(2)); // $ MISSING: hasTaintFlow=1 + sink(source(1).bitor(2)); // $ hasTaintFlow=1 sink(1i64.bitor(source(2))); // $ hasTaintFlow=2 - sink(1.bitor(source(2))); // $ MISSING: hasTaintFlow=2 + sink(1.bitor(source(2))); // $ hasTaintFlow=2 let mut a: i64 = 1; a.add_assign(source(2)); 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 7732523ecd3a..2bea1447a264 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -14813,202 +14813,16 @@ inferType | regressions.rs:57:22:57:22 | x | | regressions.rs:57:18:57:19 | T2 | | regressions.rs:61:5:65:5 | { ... } | | regressions.rs:57:18:57:19 | T2 | | regressions.rs:62:13:62:13 | y | | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T | {EXTERNAL LOCATION} | Option | | regressions.rs:62:13:62:13 | y | T | regressions.rs:57:14:57:15 | T1 | | regressions.rs:62:13:62:13 | y | T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:13:62:13 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | | regressions.rs:62:17:62:34 | ...::my_from(...) | | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T | {EXTERNAL LOCATION} | Option | | regressions.rs:62:17:62:34 | ...::my_from(...) | T | regressions.rs:57:14:57:15 | T1 | | regressions.rs:62:17:62:34 | ...::my_from(...) | T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:62:17:62:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | | regressions.rs:62:33:62:33 | x | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | | regressions.rs:57:14:57:15 | T1 | | regressions.rs:63:13:63:13 | z | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:13:63:13 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | | regressions.rs:57:14:57:15 | T1 | | regressions.rs:63:17:63:34 | ...::my_from(...) | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:17:63:34 | ...::my_from(...) | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | | regressions.rs:63:33:63:33 | y | | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T | {EXTERNAL LOCATION} | Option | | regressions.rs:63:33:63:33 | y | T | regressions.rs:57:14:57:15 | T1 | | regressions.rs:63:33:63:33 | y | T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:63:33:63:33 | y | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | | regressions.rs:57:14:57:15 | T1 | | regressions.rs:64:9:64:9 | z | | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T.T | {EXTERNAL LOCATION} | Option | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:14:57:15 | T1 | -| regressions.rs:64:9:64:9 | z | T.T.T.T.T.T.T.T.T.T | regressions.rs:57:18:57:19 | T2 | testFailures diff --git a/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected b/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected index ef0a9e0d8063..a04fd96739cd 100644 --- a/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected +++ b/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected @@ -21,3 +21,9 @@ | test_cipher.rs:109:23:109:56 | ...::new_with_eff_key_len(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:109:23:109:56 | ...::new_with_eff_key_len(...) | The cryptographic algorithm RC2 | | test_cipher.rs:114:23:114:50 | ...::new(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:114:23:114:50 | ...::new(...) | The cryptographic algorithm RC5 | | test_cipher.rs:118:23:118:55 | ...::new_from_slice(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:118:23:118:55 | ...::new_from_slice(...) | The cryptographic algorithm RC5 | +| test_cipher.rs:136:23:136:76 | ...::new(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:136:23:136:76 | ...::new(...) | The cryptographic algorithm DES | +| test_cipher.rs:139:23:139:64 | ...::new(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:139:23:139:64 | ...::new(...) | The cryptographic algorithm DES | +| test_cipher.rs:142:23:142:76 | ...::new_from_slices(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:142:23:142:76 | ...::new_from_slices(...) | The cryptographic algorithm DES | +| test_cipher.rs:145:23:145:76 | ...::new(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:145:23:145:76 | ...::new(...) | The cryptographic algorithm DES | +| test_cipher.rs:171:23:171:65 | ...::new(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:171:23:171:65 | ...::new(...) | The cryptographic algorithm DES | +| test_cipher.rs:175:23:175:65 | ...::new(...) | $@ is broken or weak, and should not be used. | test_cipher.rs:175:23:175:65 | ...::new(...) | The cryptographic algorithm RC2 | diff --git a/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/CONSISTENCY/PathResolutionConsistency.expected deleted file mode 100644 index 18400b7ab59b..000000000000 --- a/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/CONSISTENCY/PathResolutionConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -multipleResolvedTargets -| test_cipher.rs:114:23:114:50 | ...::new(...) | diff --git a/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/test_cipher.rs b/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/test_cipher.rs index 17db0f9ceb19..81964436720d 100644 --- a/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/test_cipher.rs +++ b/rust/ql/test/query-tests/security/CWE-327/BrokenCryptoAlgorithm/test_cipher.rs @@ -133,16 +133,16 @@ fn test_cbc( _ = aes_cipher1.encrypt_padded_mut::(data, data_len).unwrap(); // des (broken) - let des_cipher1 = cbc::Encryptor::::new(key.into(), iv.into()); // $ MISSING: Alert[rust/weak-cryptographic-algorithm] + let des_cipher1 = cbc::Encryptor::::new(key.into(), iv.into()); // $ Alert[rust/weak-cryptographic-algorithm] _ = des_cipher1.encrypt_padded_mut::(data, data_len).unwrap(); - let des_cipher2 = MyDesEncryptor::new(key.into(), iv.into()); // $ MISSING: Alert[rust/weak-cryptographic-algorithm] + let des_cipher2 = MyDesEncryptor::new(key.into(), iv.into()); // $ Alert[rust/weak-cryptographic-algorithm] _ = des_cipher2.encrypt_padded_mut::(data, data_len).unwrap(); - let des_cipher3 = cbc::Encryptor::::new_from_slices(&key, &iv).unwrap(); // $ MISSING: Alert[rust/weak-cryptographic-algorithm] + let des_cipher3 = cbc::Encryptor::::new_from_slices(&key, &iv).unwrap(); // $ Alert[rust/weak-cryptographic-algorithm] _ = des_cipher3.encrypt_padded_mut::(data, data_len).unwrap(); - let des_cipher4 = cbc::Encryptor::::new(key.into(), iv.into()); // $ MISSING: Alert[rust/weak-cryptographic-algorithm] + let des_cipher4 = cbc::Encryptor::::new(key.into(), iv.into()); // $ Alert[rust/weak-cryptographic-algorithm] _ = des_cipher4.encrypt_padded_b2b_mut::(input, data).unwrap(); } @@ -168,10 +168,10 @@ fn test_ecb( _ = aes_cipher4.encrypt_padded_b2b_mut::(input, data).unwrap(); // des with ECB (broken cipher + weak block mode) - let des_cipher1 = ecb::Encryptor::::new(key.into()); // $ MISSING: Alert[rust/weak-cryptographic-algorithm] + let des_cipher1 = ecb::Encryptor::::new(key.into()); // $ Alert[rust/weak-cryptographic-algorithm] _ = des_cipher1.encrypt_padded_mut::(data, data_len).unwrap(); // rc2 with ECB (broken cipher + weak block mode) - let rc2_cipher1 = ecb::Encryptor::::new(key.into()); // $ MISSING: Alert[rust/weak-cryptographic-algorithm] + let rc2_cipher1 = ecb::Encryptor::::new(key.into()); // $ Alert[rust/weak-cryptographic-algorithm] _ = rc2_cipher1.encrypt_padded_mut::(data, data_len).unwrap(); }