From 3fa5c952b3aa4551c1041ff9703e828363aa4535 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 7 Apr 2026 13:39:09 +0200 Subject: [PATCH 1/2] Rust: Add more closure flow tests --- .../dataflow/lambdas/inline-flow.expected | 65 +++++++++++++++++++ .../library-tests/dataflow/lambdas/main.rs | 26 ++++++++ 2 files changed, 91 insertions(+) diff --git a/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected b/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected index 7c99702aec63..31b42cc228a5 100644 --- a/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected @@ -30,6 +30,33 @@ edges | main.rs:77:13:77:22 | f(...) | main.rs:77:9:77:9 | b | provenance | | | main.rs:77:21:77:21 | a | main.rs:66:24:66:32 | ...: i64 | provenance | | | main.rs:77:21:77:21 | a | main.rs:77:13:77:22 | f(...) | provenance | | +| main.rs:81:28:81:33 | ...: i64 | main.rs:82:7:82:7 | x | provenance | | +| main.rs:81:28:81:33 | ...: i64 | main.rs:82:7:82:7 | x | provenance | | +| main.rs:81:28:81:33 | ...: i64 | main.rs:82:7:82:7 | x | provenance | | +| main.rs:82:7:82:7 | x | main.rs:87:12:87:12 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:89:12:89:12 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:99:17:99:17 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:101:17:101:17 | ... | provenance | | +| main.rs:82:7:82:7 | x | main.rs:102:17:102:17 | ... | provenance | | +| main.rs:86:9:86:9 | a | main.rs:87:24:87:24 | a | provenance | | +| main.rs:86:13:86:22 | source(...) | main.rs:86:9:86:9 | a | provenance | | +| main.rs:87:12:87:12 | ... | main.rs:87:20:87:20 | x | provenance | | +| main.rs:87:24:87:24 | a | main.rs:81:28:81:33 | ...: i64 | provenance | | +| main.rs:88:9:88:9 | b | main.rs:89:24:89:24 | b | provenance | | +| main.rs:88:13:88:22 | source(...) | main.rs:88:9:88:9 | b | provenance | | +| main.rs:89:12:89:12 | ... | main.rs:89:20:89:20 | x | provenance | | +| main.rs:89:24:89:24 | b | main.rs:81:28:81:33 | ...: i64 | provenance | | +| main.rs:93:33:93:38 | ...: i64 | main.rs:94:14:94:14 | x | provenance | | +| main.rs:94:14:94:14 | x | main.rs:81:28:81:33 | ...: i64 | provenance | | +| main.rs:98:9:98:9 | a | main.rs:99:29:99:29 | a | provenance | | +| main.rs:98:13:98:22 | source(...) | main.rs:98:9:98:9 | a | provenance | | +| main.rs:99:17:99:17 | ... | main.rs:99:25:99:25 | x | provenance | | +| main.rs:99:29:99:29 | a | main.rs:93:33:93:38 | ...: i64 | provenance | | +| main.rs:100:9:100:9 | b | main.rs:101:29:101:29 | b | provenance | | +| main.rs:100:13:100:22 | source(...) | main.rs:100:9:100:9 | b | provenance | | +| main.rs:101:17:101:17 | ... | main.rs:101:25:101:25 | x | provenance | | +| main.rs:101:29:101:29 | b | main.rs:93:33:93:38 | ...: i64 | provenance | | +| main.rs:102:17:102:17 | ... | main.rs:102:25:102:25 | x | provenance | | nodes | main.rs:10:20:10:52 | if cond {...} else {...} | semmle.label | if cond {...} else {...} | | main.rs:10:30:10:39 | source(...) | semmle.label | source(...) | @@ -68,6 +95,36 @@ nodes | main.rs:77:13:77:22 | f(...) | semmle.label | f(...) | | main.rs:77:21:77:21 | a | semmle.label | a | | main.rs:78:10:78:10 | b | semmle.label | b | +| main.rs:81:28:81:33 | ...: i64 | semmle.label | ...: i64 | +| main.rs:81:28:81:33 | ...: i64 | semmle.label | ...: i64 | +| main.rs:81:28:81:33 | ...: i64 | semmle.label | ...: i64 | +| main.rs:82:7:82:7 | x | semmle.label | x | +| main.rs:82:7:82:7 | x | semmle.label | x | +| main.rs:82:7:82:7 | x | semmle.label | x | +| main.rs:86:9:86:9 | a | semmle.label | a | +| main.rs:86:13:86:22 | source(...) | semmle.label | source(...) | +| main.rs:87:12:87:12 | ... | semmle.label | ... | +| main.rs:87:20:87:20 | x | semmle.label | x | +| main.rs:87:24:87:24 | a | semmle.label | a | +| main.rs:88:9:88:9 | b | semmle.label | b | +| main.rs:88:13:88:22 | source(...) | semmle.label | source(...) | +| main.rs:89:12:89:12 | ... | semmle.label | ... | +| main.rs:89:20:89:20 | x | semmle.label | x | +| main.rs:89:24:89:24 | b | semmle.label | b | +| main.rs:93:33:93:38 | ...: i64 | semmle.label | ...: i64 | +| main.rs:94:14:94:14 | x | semmle.label | x | +| main.rs:98:9:98:9 | a | semmle.label | a | +| main.rs:98:13:98:22 | source(...) | semmle.label | source(...) | +| main.rs:99:17:99:17 | ... | semmle.label | ... | +| main.rs:99:25:99:25 | x | semmle.label | x | +| main.rs:99:29:99:29 | a | semmle.label | a | +| main.rs:100:9:100:9 | b | semmle.label | b | +| main.rs:100:13:100:22 | source(...) | semmle.label | source(...) | +| main.rs:101:17:101:17 | ... | semmle.label | ... | +| main.rs:101:25:101:25 | x | semmle.label | x | +| main.rs:101:29:101:29 | b | semmle.label | b | +| main.rs:102:17:102:17 | ... | semmle.label | ... | +| main.rs:102:25:102:25 | x | semmle.label | x | subpaths | main.rs:29:21:29:21 | a | main.rs:27:20:27:23 | ... | main.rs:27:26:27:52 | if cond {...} else {...} | main.rs:29:13:29:22 | f(...) | | main.rs:77:21:77:21 | a | main.rs:66:24:66:32 | ...: i64 | main.rs:66:42:72:1 | { ... } | main.rs:77:13:77:22 | f(...) | @@ -81,3 +138,11 @@ testFailures | main.rs:52:10:52:13 | data | main.rs:62:13:62:22 | source(...) | main.rs:52:10:52:13 | data | $@ | main.rs:62:13:62:22 | source(...) | source(...) | | main.rs:57:10:57:12 | f(...) | main.rs:48:5:48:14 | source(...) | main.rs:57:10:57:12 | f(...) | $@ | main.rs:48:5:48:14 | source(...) | source(...) | | main.rs:78:10:78:10 | b | main.rs:76:13:76:22 | source(...) | main.rs:78:10:78:10 | b | $@ | main.rs:76:13:76:22 | source(...) | source(...) | +| main.rs:87:20:87:20 | x | main.rs:86:13:86:22 | source(...) | main.rs:87:20:87:20 | x | $@ | main.rs:86:13:86:22 | source(...) | source(...) | +| main.rs:89:20:89:20 | x | main.rs:88:13:88:22 | source(...) | main.rs:89:20:89:20 | x | $@ | main.rs:88:13:88:22 | source(...) | source(...) | +| main.rs:99:25:99:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:99:25:99:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | +| main.rs:99:25:99:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:99:25:99:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | +| main.rs:101:25:101:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:101:25:101:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | +| main.rs:101:25:101:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:101:25:101:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | +| main.rs:102:25:102:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:102:25:102:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | +| main.rs:102:25:102:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:102:25:102:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/lambdas/main.rs b/rust/ql/test/library-tests/dataflow/lambdas/main.rs index 252b132ec744..a2dfc7e9a1ce 100644 --- a/rust/ql/test/library-tests/dataflow/lambdas/main.rs +++ b/rust/ql/test/library-tests/dataflow/lambdas/main.rs @@ -78,6 +78,30 @@ fn function_flows_through() { sink(b); // $ hasValueFlow=56 } +fn apply(f: F, x: i64) { + f(x); +} + +fn test_apply() { + let a = source(77); + apply(|x| sink(x), a); // $ hasValueFlow=77 + let b = source(78); + apply(|x| sink(x), b); // $ hasValueFlow=78 + apply(|x| sink(x), 0); +} + +fn apply_wrap(f: F, x: i64) { + apply(f, x); +} + +fn test_apply_wrap() { + let a = source(79); + apply_wrap(|x| sink(x), a); // $ hasValueFlow=79 $ SPURIOUS: hasValueFlow=80 + let b = source(80); + apply_wrap(|x| sink(x), b); // $ hasValueFlow=80 $ SPURIOUS: hasValueFlow=79 + apply_wrap(|x| sink(x), 0); // $ SPURIOUS: hasValueFlow=79 hasValueFlow=80 +} + fn main() { closure_flow_out(); closure_flow_in(); @@ -86,4 +110,6 @@ fn main() { function_flow_in(); function_flow_out(); function_flows_through(); + test_apply(); + test_apply_wrap(); } From 23f081006ea3ce087e6e18e2715fa33342a39958 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 27 Mar 2026 12:53:45 +0100 Subject: [PATCH 2/2] Rust: Track closure types in data flow --- .../rust/dataflow/internal/DataFlowImpl.qll | 113 ++++++++++++++++-- .../rust/dataflow/internal/ModelsAsData.qll | 6 +- .../dataflow/lambdas/inline-flow.expected | 14 +-- .../library-tests/dataflow/lambdas/main.rs | 2 +- 4 files changed, 109 insertions(+), 26 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 27773758fc46..6ae18d8ce149 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -193,9 +193,7 @@ module LocalFlow { } pragma[nomagic] - predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { - nodeFrom.asExpr() = getALastEvalNode(nodeTo.asExpr()) - or + predicate localMustFlowStep(Node nodeFrom, Node nodeTo) { // An edge from the right-hand side of a let statement to the left-hand side. exists(LetStmt s | nodeFrom.asExpr() = s.getInitializer() and @@ -238,6 +236,15 @@ module LocalFlow { nodeTo.asPat() = match.getAnArm().getPat() ) or + nodeFrom.asExpr() = nodeTo.asExpr().(ParenExpr).getExpr() + } + + pragma[nomagic] + predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { + localMustFlowStep(nodeFrom, nodeTo) + or + nodeFrom.asExpr() = getALastEvalNode(nodeTo.asExpr()) + or nodeFrom.asPat().(OrPat).getAPat() = nodeTo.asPat() or nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = @@ -263,10 +270,84 @@ predicate lambdaCallExpr(CallExprImpl::DynamicCallExpr call, LambdaCallKind kind exists(kind) } +// NOTE: We do not yet track type information, except for closures where +// we use the closure itself to represent the unique type. +final class DataFlowType extends TDataFlowType { + Expr asClosureExpr() { this = TClosureExprType(result) } + + predicate isUnknownType() { this = TUnknownType() } + + predicate isSourceContextParameterType() { this = TSourceContextParameterType() } + + string toString() { + exists(this.asClosureExpr()) and + result = "... => .." + or + this.isUnknownType() and + result = "" + or + this.isSourceContextParameterType() and + result = "" + } +} + +pragma[nomagic] +private predicate compatibleTypesSourceContextParameterTypeLeft(DataFlowType t1, DataFlowType t2) { + t1.isSourceContextParameterType() and not exists(t2.asClosureExpr()) +} + +pragma[nomagic] +private predicate compatibleTypesLeft(DataFlowType t1, DataFlowType t2) { + t1.isUnknownType() and exists(t2) + or + t1.asClosureExpr() = t2.asClosureExpr() + or + compatibleTypesSourceContextParameterTypeLeft(t1, t2) +} + +predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { + compatibleTypesLeft(t1, t2) + or + compatibleTypesLeft(t2, t1) +} + +pragma[nomagic] +predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { + not t1.isUnknownType() and t2.isUnknownType() + or + compatibleTypesSourceContextParameterTypeLeft(t1, t2) +} + +DataFlowType getNodeType(NodePublic node) { + result.asClosureExpr() = node.asExpr() + or + result.asClosureExpr() = node.(ClosureParameterNode).getCfgScope() + or + exists(VariableCapture::Flow::SynthesizedCaptureNode scn | + scn = node.(CaptureNode).getSynthesizedCaptureNode() and + if scn.isInstanceAccess() + then result.asClosureExpr() = scn.getEnclosingCallable() + else result.isUnknownType() + ) + or + not lambdaCreationExpr(node.asExpr()) and + not node instanceof ClosureParameterNode and + not node instanceof CaptureNode and + result.isUnknownType() +} + // Defines a set of aliases needed for the `RustDataFlow` module private module Aliases { class DataFlowCallableAlias = DataFlowCallable; + class DataFlowTypeAlias = DataFlowType; + + predicate compatibleTypesAlias = compatibleTypes/2; + + predicate typeStrongerThanAlias = typeStrongerThan/2; + + predicate getNodeTypeAlias = getNodeType/1; + class ReturnKindAlias = ReturnKind; class DataFlowCallAlias = DataFlowCall; @@ -398,8 +479,6 @@ module RustDataFlowGen implements InputSig result = node.(Node::Node).getEnclosingCallable() } - DataFlowType getNodeType(Node node) { any() } - predicate nodeIsHidden(Node node) { node instanceof SsaNode or node.(FlowSummaryNode).getSummaryNode().isHidden() or @@ -486,15 +565,17 @@ module RustDataFlowGen implements InputSig */ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } - // NOTE: For now we use the type `Unit` and do not benefit from type - // information in the data flow analysis. - final class DataFlowType extends Unit { - string toString() { result = "" } - } + class DataFlowType = DataFlowTypeAlias; - predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } + predicate compatibleTypes = compatibleTypesAlias/2; - predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() } + predicate typeStrongerThan = typeStrongerThanAlias/2; + + DataFlowType getSourceContextParameterNodeType(Node p) { + exists(p) and result.isSourceContextParameterType() + } + + predicate getNodeType = getNodeTypeAlias/1; class Content = ContentAlias; @@ -897,6 +978,8 @@ module RustDataFlowGen implements InputSig predicate localMustFlowStep(Node node1, Node node2) { SsaFlow::localMustFlowStep(node1, node2) or + LocalFlow::localMustFlowStep(node1, node2) + or FlowSummaryImpl::Private::Steps::summaryLocalMustFlowStep(node1 .(FlowSummaryNode) .getSummaryNode(), node2.(FlowSummaryNode).getSummaryNode()) @@ -1110,6 +1193,12 @@ private module Cached { TCfgScope(CfgScope scope) or TSummarizedCallable(SummarizedCallable c) + cached + newtype TDataFlowType = + TClosureExprType(Expr e) { lambdaCreationExpr(e) } or + TUnknownType() or + TSourceContextParameterType() + /** This is the local flow predicate that is exposed. */ cached predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index 4d28dd8de812..0a7288a330fa 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -280,7 +280,7 @@ private module Debug { private import Content private import codeql.rust.dataflow.internal.DataFlowImpl private import codeql.rust.internal.typeinference.TypeMention - private import codeql.rust.internal.typeinference.Type + private import codeql.rust.internal.typeinference.Type as Type private predicate relevantManualModel(SummarizedCallableImpl sc, string can) { exists(Provenance manual | @@ -298,7 +298,7 @@ private module Debug { sc.propagatesFlow(input, _, _, _, _, _) and input.head() = SummaryComponent::argument(pos) and p = pos.getParameterIn(sc.getParamList()) and - tm.getType() instanceof RefType and + tm.getType() instanceof Type::RefType and not input.tail().head() = SummaryComponent::content(TSingletonContentSet(TReferenceContent())) | tm = p.getTypeRepr() @@ -313,7 +313,7 @@ private module Debug { exists(TypeMention tm | relevantManualModel(sc, can) and sc.propagatesFlow(_, output, _, _, _, _) and - tm.getType() instanceof RefType and + tm.getType() instanceof Type::RefType and output.head() = SummaryComponent::return(_) and not output.tail().head() = SummaryComponent::content(TSingletonContentSet(TReferenceContent())) and diff --git a/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected b/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected index 31b42cc228a5..ca4bbe77479a 100644 --- a/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/lambdas/inline-flow.expected @@ -13,9 +13,9 @@ edges | main.rs:29:13:29:22 | f(...) | main.rs:29:9:29:9 | b | provenance | | | main.rs:29:21:29:21 | a | main.rs:27:20:27:23 | ... | provenance | | | main.rs:29:21:29:21 | a | main.rs:29:13:29:22 | f(...) | provenance | | -| main.rs:37:16:37:25 | source(...) | main.rs:39:5:39:5 | [post] f [captured capt] | provenance | | -| main.rs:39:5:39:5 | [post] f [captured capt] | main.rs:40:10:40:13 | capt | provenance | | -| main.rs:39:5:39:5 | [post] f [captured capt] | main.rs:44:5:44:5 | g [captured capt] | provenance | | +| main.rs:37:16:37:25 | source(...) | main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | provenance | | +| main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | main.rs:40:10:40:13 | capt | provenance | | +| main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | main.rs:44:5:44:5 | g [captured capt] | provenance | | | main.rs:44:5:44:5 | g [captured capt] | main.rs:42:14:42:17 | capt | provenance | | | main.rs:47:29:49:1 | { ... } | main.rs:57:10:57:12 | f(...) | provenance | | | main.rs:48:5:48:14 | source(...) | main.rs:47:29:49:1 | { ... } | provenance | | @@ -37,7 +37,6 @@ edges | main.rs:82:7:82:7 | x | main.rs:89:12:89:12 | ... | provenance | | | main.rs:82:7:82:7 | x | main.rs:99:17:99:17 | ... | provenance | | | main.rs:82:7:82:7 | x | main.rs:101:17:101:17 | ... | provenance | | -| main.rs:82:7:82:7 | x | main.rs:102:17:102:17 | ... | provenance | | | main.rs:86:9:86:9 | a | main.rs:87:24:87:24 | a | provenance | | | main.rs:86:13:86:22 | source(...) | main.rs:86:9:86:9 | a | provenance | | | main.rs:87:12:87:12 | ... | main.rs:87:20:87:20 | x | provenance | | @@ -56,7 +55,6 @@ edges | main.rs:100:13:100:22 | source(...) | main.rs:100:9:100:9 | b | provenance | | | main.rs:101:17:101:17 | ... | main.rs:101:25:101:25 | x | provenance | | | main.rs:101:29:101:29 | b | main.rs:93:33:93:38 | ...: i64 | provenance | | -| main.rs:102:17:102:17 | ... | main.rs:102:25:102:25 | x | provenance | | nodes | main.rs:10:20:10:52 | if cond {...} else {...} | semmle.label | if cond {...} else {...} | | main.rs:10:30:10:39 | source(...) | semmle.label | source(...) | @@ -75,7 +73,7 @@ nodes | main.rs:29:21:29:21 | a | semmle.label | a | | main.rs:30:10:30:10 | b | semmle.label | b | | main.rs:37:16:37:25 | source(...) | semmle.label | source(...) | -| main.rs:39:5:39:5 | [post] f [captured capt] | semmle.label | [post] f [captured capt] | +| main.rs:39:5:39:5 | [post] f : ... => .. [captured capt] | semmle.label | [post] f : ... => .. [captured capt] | | main.rs:40:10:40:13 | capt | semmle.label | capt | | main.rs:42:14:42:17 | capt | semmle.label | capt | | main.rs:44:5:44:5 | g [captured capt] | semmle.label | g [captured capt] | @@ -123,8 +121,6 @@ nodes | main.rs:101:17:101:17 | ... | semmle.label | ... | | main.rs:101:25:101:25 | x | semmle.label | x | | main.rs:101:29:101:29 | b | semmle.label | b | -| main.rs:102:17:102:17 | ... | semmle.label | ... | -| main.rs:102:25:102:25 | x | semmle.label | x | subpaths | main.rs:29:21:29:21 | a | main.rs:27:20:27:23 | ... | main.rs:27:26:27:52 | if cond {...} else {...} | main.rs:29:13:29:22 | f(...) | | main.rs:77:21:77:21 | a | main.rs:66:24:66:32 | ...: i64 | main.rs:66:42:72:1 | { ... } | main.rs:77:13:77:22 | f(...) | @@ -144,5 +140,3 @@ testFailures | main.rs:99:25:99:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:99:25:99:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | | main.rs:101:25:101:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:101:25:101:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | | main.rs:101:25:101:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:101:25:101:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | -| main.rs:102:25:102:25 | x | main.rs:98:13:98:22 | source(...) | main.rs:102:25:102:25 | x | $@ | main.rs:98:13:98:22 | source(...) | source(...) | -| main.rs:102:25:102:25 | x | main.rs:100:13:100:22 | source(...) | main.rs:102:25:102:25 | x | $@ | main.rs:100:13:100:22 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/lambdas/main.rs b/rust/ql/test/library-tests/dataflow/lambdas/main.rs index a2dfc7e9a1ce..66295f004a22 100644 --- a/rust/ql/test/library-tests/dataflow/lambdas/main.rs +++ b/rust/ql/test/library-tests/dataflow/lambdas/main.rs @@ -99,7 +99,7 @@ fn test_apply_wrap() { apply_wrap(|x| sink(x), a); // $ hasValueFlow=79 $ SPURIOUS: hasValueFlow=80 let b = source(80); apply_wrap(|x| sink(x), b); // $ hasValueFlow=80 $ SPURIOUS: hasValueFlow=79 - apply_wrap(|x| sink(x), 0); // $ SPURIOUS: hasValueFlow=79 hasValueFlow=80 + apply_wrap(|x| sink(x), 0); } fn main() {