From ddc407257fbcca466b7114e7902208d8dc8fc7a0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 24 Mar 2026 14:10:43 +0100 Subject: [PATCH 1/3] Rust: Type inference test --- .../type-inference/regressions.rs | 24 +++++++++++++++++++ .../type-inference/type-inference.expected | 23 ++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/rust/ql/test/library-tests/type-inference/regressions.rs b/rust/ql/test/library-tests/type-inference/regressions.rs index 37c61b2b7e77..df033a889760 100644 --- a/rust/ql/test/library-tests/type-inference/regressions.rs +++ b/rust/ql/test/library-tests/type-inference/regressions.rs @@ -106,3 +106,27 @@ mod regression3 { z } } + +mod regression4 { + trait MyTrait { + // MyTrait::m + fn m(self); + } + + impl MyTrait for &T { + // RefAsMyTrait::m + fn m(self) {} + } + + struct S(T); + + impl S { + fn call_m(self) + where + T: MyTrait, + { + let S(s) = self; + s.m(); // $ MISSING: target=MyTrait::m $ SPURIOUS: target=RefAsMyTrait::m + } + } +} 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 a25a9daf003d..1e2c753b2421 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -5083,6 +5083,15 @@ inferCertainType | regressions.rs:99:22:99:22 | x | | regressions.rs:99:18:99:19 | T2 | | regressions.rs:103:5:107:5 | { ... } | | regressions.rs:99:18:99:19 | T2 | | regressions.rs:104:33:104:33 | x | | regressions.rs:99:18:99:19 | T2 | +| regressions.rs:113:14:113:17 | SelfParam | | regressions.rs:111:5:114:5 | Self [trait MyTrait] | +| regressions.rs:118:14:118:17 | SelfParam | | {EXTERNAL LOCATION} | & | +| regressions.rs:118:14:118:17 | SelfParam | TRef | regressions.rs:116:10:116:10 | T | +| regressions.rs:118:20:118:21 | { ... } | | {EXTERNAL LOCATION} | () | +| regressions.rs:124:19:124:22 | SelfParam | | regressions.rs:121:5:121:19 | S | +| regressions.rs:124:19:124:22 | SelfParam | T | regressions.rs:123:10:123:10 | T | +| regressions.rs:127:9:130:9 | { ... } | | {EXTERNAL LOCATION} | () | +| regressions.rs:128:24:128:27 | self | | regressions.rs:121:5:121:19 | S | +| regressions.rs:128:24:128:27 | self | T | regressions.rs:123:10:123:10 | T | 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 | @@ -15135,4 +15144,18 @@ inferType | regressions.rs:105:33:105:33 | y | T | regressions.rs:99:14:99:15 | T1 | | regressions.rs:105:33:105:33 | y | T | regressions.rs:99:18:99:19 | T2 | | regressions.rs:106:9:106:9 | z | | regressions.rs:99:18:99:19 | T2 | +| regressions.rs:113:14:113:17 | SelfParam | | regressions.rs:111:5:114:5 | Self [trait MyTrait] | +| regressions.rs:118:14:118:17 | SelfParam | | {EXTERNAL LOCATION} | & | +| regressions.rs:118:14:118:17 | SelfParam | TRef | regressions.rs:116:10:116:10 | T | +| regressions.rs:118:20:118:21 | { ... } | | {EXTERNAL LOCATION} | () | +| regressions.rs:124:19:124:22 | SelfParam | | regressions.rs:121:5:121:19 | S | +| regressions.rs:124:19:124:22 | SelfParam | T | regressions.rs:123:10:123:10 | T | +| regressions.rs:127:9:130:9 | { ... } | | {EXTERNAL LOCATION} | () | +| regressions.rs:128:17:128:20 | S(...) | | regressions.rs:121:5:121:19 | S | +| regressions.rs:128:17:128:20 | S(...) | T | regressions.rs:123:10:123:10 | T | +| regressions.rs:128:19:128:19 | s | | regressions.rs:123:10:123:10 | T | +| regressions.rs:128:24:128:27 | self | | regressions.rs:121:5:121:19 | S | +| regressions.rs:128:24:128:27 | self | T | regressions.rs:123:10:123:10 | T | +| regressions.rs:129:13:129:13 | s | | regressions.rs:123:10:123:10 | T | +| regressions.rs:129:13:129:17 | s.m() | | {EXTERNAL LOCATION} | () | testFailures From 6295f57a8733761cd3196023d6128ee2b362af3f Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 24 Mar 2026 12:58:26 +0100 Subject: [PATCH 2/3] Rust: Take additional type parameter constraints into account --- .../rust/elements/internal/TypeParamImpl.qll | 28 +++++- .../codeql/rust/internal/PathResolution.qll | 37 ++++++- .../internal/typeinference/FunctionType.qll | 99 ++++++++++++++++--- .../internal/typeinference/TypeInference.qll | 54 +++++++--- .../type-inference/regressions.rs | 2 +- .../test/utils-tests/modelgenerator/option.rs | 4 +- .../typeinference/internal/TypeInference.qll | 47 +++++++-- 7 files changed, 225 insertions(+), 46 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll index 755c5399a204..bba95442f6a4 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll @@ -32,7 +32,8 @@ module Impl { * Gets the `index`th type bound of this type parameter, if any. * * This includes type bounds directly on this type parameter and bounds from - * any `where` clauses for this type parameter. + * any `where` clauses for this type parameter, but restricted to `where` + * clauses from the item that declares this type parameter. */ TypeBound getTypeBound(int index) { result = @@ -43,13 +44,36 @@ module Impl { * Gets a type bound of this type parameter. * * This includes type bounds directly on this type parameter and bounds from - * any `where` clauses for this type parameter. + * any `where` clauses for this type parameter, but restricted to `where` + * clauses from the item that declares this type parameter. */ TypeBound getATypeBound() { result = this.getTypeBound(_) } /** Holds if this type parameter has at least one type bound. */ predicate hasTypeBound() { exists(this.getATypeBound()) } + /** + * Gets the `index`th additional type bound of this type parameter, + * which applies to `constrainingItem`, if any. + * + * For example, in + * + * ```rust + * impl SomeType where T: Clone { + * fn foo() where T: Debug { } + * } + * ``` + * + * The constraint `Debug` additionally applies to `T` in `foo`. + */ + TypeBound getAdditionalTypeBound(Item constrainingItem, int index) { + result = + rank[index + 1](int i, int j | + | + this.(TypeParamItemNode).getAdditionalTypeBoundAt(constrainingItem, i, j) order by i, j + ) + } + override string toAbbreviatedString() { result = this.getName().getText() } override string toStringImpl() { result = this.getName().getText() } diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 76c15485bb9a..c38e9bff91c2 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -1162,21 +1162,39 @@ private Path getWherePredPath(WherePred wp) { result = wp.getTypeRepr().(PathTyp final class TypeParamItemNode extends NamedItemNode, TypeItemNode instanceof TypeParam { /** Gets a where predicate for this type parameter, if any */ pragma[nomagic] - private WherePred getAWherePred() { + private WherePred getAWherePred(ItemNode constrainingItem, boolean isAdditional) { exists(ItemNode declaringItem | + this = declaringItem.getTypeParam(_) and this = resolvePath(getWherePredPath(result)) and - result = declaringItem.getADescendant() and - this = declaringItem.getADescendant() + result = constrainingItem.getADescendant() + | + constrainingItem = declaringItem and + isAdditional = false + or + constrainingItem = declaringItem.getADescendant() and + isAdditional = true ) } pragma[nomagic] TypeBound getTypeBoundAt(int i, int j) { exists(TypeBoundList tbl | result = tbl.getBound(j) | - tbl = super.getTypeBoundList() and i = 0 + tbl = super.getTypeBoundList() and + i = 0 or exists(WherePred wp | - wp = this.getAWherePred() and + wp = this.getAWherePred(_, false) and + tbl = wp.getTypeBoundList() and + wp = any(WhereClause wc).getPredicate(i) + ) + ) + } + + pragma[nomagic] + TypeBound getAdditionalTypeBoundAt(Item constrainingItem, int i, int j) { + exists(TypeBoundList tbl | result = tbl.getBound(j) | + exists(WherePred wp | + wp = this.getAWherePred(constrainingItem, true) and tbl = wp.getTypeBoundList() and wp = any(WhereClause wc).getPredicate(i) ) @@ -1197,6 +1215,15 @@ final class TypeParamItemNode extends NamedItemNode, TypeItemNode instanceof Typ ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) } + pragma[nomagic] + ItemNode resolveAdditionalBound(ItemNode constrainingItem) { + result = + resolvePath(this.getAdditionalTypeBoundAt(constrainingItem, _, _) + .getTypeRepr() + .(PathTypeRepr) + .getPath()) + } + override string getName() { result = TypeParam.super.getName().getText() } override Namespace getNamespace() { result.isType() } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll index 26627450add5..0816ce49c295 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll @@ -87,6 +87,7 @@ private newtype TAssocFunctionType = } bindingset[abs, constraint, tp] +pragma[inline_late] private Type getTraitConstraintTypeAt( TypeAbstraction abs, TypeMention constraint, TypeParameter tp, TypePath path ) { @@ -203,7 +204,7 @@ class AssocFunctionType extends MkAssocFunctionType { } pragma[nomagic] -Trait getALookupTrait(Type t) { +private Trait getALookupTrait(Type t) { result = t.(TypeParamTypeParameter).getTypeParam().(TypeParamItemNode).resolveABound() or result = t.(SelfTypeParameter).getTrait() @@ -213,23 +214,47 @@ Trait getALookupTrait(Type t) { result = t.(DynTraitType).getTrait() } -/** - * Gets the type obtained by substituting in relevant traits in which to do function - * lookup, or `t` itself when no such trait exist. - */ pragma[nomagic] -Type substituteLookupTraits(Type t) { +private Trait getAdditionalLookupTrait(ItemNode i, Type t) { + result = + t.(TypeParamTypeParameter) + .getTypeParam() + .(TypeParamItemNode) + .resolveAdditionalBound(i.getImmediateParent*()) +} + +bindingset[n, t] +pragma[inline_late] +Trait getALookupTrait(AstNode n, Type t) { + result = getALookupTrait(t) + or + result = getAdditionalLookupTrait(any(ItemNode i | n = i.getADescendant()), t) +} + +bindingset[i, t] +pragma[inline_late] +private Type substituteLookupTraits0(ItemNode i, Type t) { not exists(getALookupTrait(t)) and + not exists(getAdditionalLookupTrait(i, t)) and result = t or result = TTrait(getALookupTrait(t)) + or + result = TTrait(getAdditionalLookupTrait(i, t)) } /** - * Gets the `n`th `substituteLookupTraits` type for `t`, per some arbitrary order. + * Gets the type obtained by substituting in relevant traits in which to do function + * lookup, or `t` itself when no such trait exist, in the context of AST node `n`. */ +bindingset[n, t] +pragma[inline_late] +Type substituteLookupTraits(AstNode n, Type t) { + result = substituteLookupTraits0(any(ItemNode i | n = i.getADescendant()), t) +} + pragma[nomagic] -Type getNthLookupType(Type t, int n) { +private Type getNthLookupType(Type t, int n) { not exists(getALookupTrait(t)) and result = t and n = 0 @@ -244,24 +269,66 @@ Type getNthLookupType(Type t, int n) { } /** - * Gets the index of the last `substituteLookupTraits` type for `t`. + * Gets the `n`th `substituteLookupTraits` type for `t`, per some arbitrary order, + * in the context of AST node `node`. */ +bindingset[node, t] +pragma[inline_late] +Type getNthLookupType(AstNode node, Type t, int n) { + exists(ItemNode i | node = i.getADescendant() | + if exists(getAdditionalLookupTrait(i, t)) + then + result = + TTrait(rank[n + 1](Trait trait, int j | + trait = [getALookupTrait(t), getAdditionalLookupTrait(i, t)] and + j = idOfTypeParameterAstNode(trait) + | + trait order by j + )) + else result = getNthLookupType(t, n) + ) +} + pragma[nomagic] -int getLastLookupTypeIndex(Type t) { result = max(int n | exists(getNthLookupType(t, n))) } +private int getLastLookupTypeIndex(Type t) { result = max(int n | exists(getNthLookupType(t, n))) } + +/** + * Gets the index of the last `substituteLookupTraits` type for `t`, + * in the context of AST node `node`. + */ +bindingset[node, t] +pragma[inline_late] +int getLastLookupTypeIndex(AstNode node, Type t) { + if exists(getAdditionalLookupTrait(node, t)) + then result = max(int n | exists(getNthLookupType(node, t, n))) + else result = getLastLookupTypeIndex(t) +} + +signature class ArgSig { + /** Gets the type of this argument at `path`. */ + Type getTypeAt(TypePath path); + + /** Gets the enclosing item node of this argument. */ + ItemNode getEnclosingItemNode(); + + /** Gets a textual representation of this argument. */ + string toString(); + + /** Gets the location of this argument. */ + Location getLocation(); +} /** * A wrapper around `IsInstantiationOf` which ensures to substitute in lookup * traits when checking whether argument types are instantiations of function * types. */ -module ArgIsInstantiationOf< - HasTypeTreeSig Arg, IsInstantiationOfInputSig Input> -{ +module ArgIsInstantiationOf Input> { final private class ArgFinal = Arg; private class ArgSubst extends ArgFinal { Type getTypeAt(TypePath path) { - result = substituteLookupTraits(super.getTypeAt(path)) and + result = substituteLookupTraits0(this.getEnclosingItemNode(), super.getTypeAt(path)) and not result = TNeverType() and not result = TUnknownType() } @@ -318,6 +385,8 @@ signature module ArgsAreInstantiationsOfInputSig { Location getLocation(); + ItemNode getEnclosingItemNode(); + Type getArgType(FunctionPosition pos, TypePath path); predicate hasTargetCand(ImplOrTraitItemNode i, Function f); @@ -366,6 +435,8 @@ module ArgsAreInstantiationsOf { FunctionPosition getPos() { result = pos } + ItemNode getEnclosingItemNode() { result = call.getEnclosingItemNode() } + Location getLocation() { result = call.getLocation() } Type getTypeAt(TypePath path) { result = call.getArgType(pos, path) } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 78e37681b9d0..0f9afcd06bbd 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -1589,7 +1589,7 @@ private module AssocFunctionResolution { Trait getATrait() { result = this.getTrait() or - result = getALookupTrait(getCallExprTypeQualifier(this, TypePath::nil(), _)) + result = getALookupTrait(this, getCallExprTypeQualifier(this, TypePath::nil(), _)) } predicate hasATrait() { exists(this.getATrait()) } @@ -1639,7 +1639,7 @@ private module AssocFunctionResolution { private predicate isRelevantSelfPos(FunctionPosition selfPos) { not this.hasReceiver() and exists(TypePath strippedTypePath, Type strippedType | - strippedType = substituteLookupTraits(this.getTypeAt(selfPos, strippedTypePath)) and + strippedType = substituteLookupTraits(this, this.getTypeAt(selfPos, strippedTypePath)) and strippedType != TNeverType() and strippedType != TUnknownType() | @@ -1803,7 +1803,7 @@ private module AssocFunctionResolution { this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | - t = getNthLookupType(strippedType, n) and + t = getNthLookupType(this, strippedType, n) and this.hasNoCompatibleTargetCheck(selfPos, derefChain, TNoBorrowKind(), strippedTypePath, t) ) } @@ -1816,7 +1816,7 @@ private module AssocFunctionResolution { predicate hasNoCompatibleTargetNoBorrow(FunctionPosition selfPos, DerefChain derefChain) { exists(Type strippedType | this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + getLastLookupTypeIndex(this, strippedType)) ) } @@ -1845,7 +1845,7 @@ private module AssocFunctionResolution { this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | - t = getNthLookupType(strippedType, n) and + t = getNthLookupType(this, strippedType, n) and this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TNoBorrowKind(), strippedTypePath, t) ) @@ -1861,7 +1861,7 @@ private module AssocFunctionResolution { ) { exists(Type strippedType | this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + getLastLookupTypeIndex(this, strippedType)) ) } @@ -1880,7 +1880,7 @@ private module AssocFunctionResolution { this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | - t = getNthLookupType(strippedType, n) and + t = getNthLookupType(this, strippedType, n) and this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) @@ -1895,7 +1895,7 @@ private module AssocFunctionResolution { predicate hasNoCompatibleTargetSharedBorrow(FunctionPosition selfPos, DerefChain derefChain) { exists(Type strippedType | this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + getLastLookupTypeIndex(this, strippedType)) ) } @@ -1913,7 +1913,7 @@ private module AssocFunctionResolution { this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | - t = getNthLookupType(strippedType, n) and + t = getNthLookupType(this, strippedType, n) and this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) @@ -1928,7 +1928,7 @@ private module AssocFunctionResolution { predicate hasNoCompatibleTargetMutBorrow(FunctionPosition selfPos, DerefChain derefChain) { exists(Type strippedType | this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + getLastLookupTypeIndex(this, strippedType)) ) } @@ -1947,7 +1947,7 @@ private module AssocFunctionResolution { this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | - t = getNthLookupType(strippedType, n) and + t = getNthLookupType(this, strippedType, n) and this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) @@ -1964,7 +1964,7 @@ private module AssocFunctionResolution { ) { exists(Type strippedType | this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, _, - strippedType, getLastLookupTypeIndex(strippedType)) + strippedType, getLastLookupTypeIndex(this, strippedType)) ) } @@ -1982,7 +1982,7 @@ private module AssocFunctionResolution { this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and exists(Type t | - t = getNthLookupType(strippedType, n) and + t = getNthLookupType(this, strippedType, n) and this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) @@ -1999,7 +1999,7 @@ private module AssocFunctionResolution { ) { exists(Type strippedType | this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, _, strippedType, - getLastLookupTypeIndex(strippedType)) + getLastLookupTypeIndex(this, strippedType)) ) } @@ -2268,9 +2268,12 @@ private module AssocFunctionResolution { AssocFunctionCall getAssocFunctionCall() { result = afc_ } + ItemNode getEnclosingItemNode() { result.getADescendant() = afc_ } + Type getTypeAt(TypePath path) { result = - substituteLookupTraits(afc_.getANonPseudoSelfTypeAt(selfPos_, derefChain, borrow, path)) + substituteLookupTraits(afc_, + afc_.getANonPseudoSelfTypeAt(selfPos_, derefChain, borrow, path)) } pragma[nomagic] @@ -2404,7 +2407,7 @@ private module AssocFunctionResolution { CallDerefCand() { this = MkCallDerefCand(afc, selfPos, derefChain) } Type getTypeAt(TypePath path) { - result = substituteLookupTraits(afc.getSelfTypeAtNoBorrow(selfPos, derefChain, path)) and + result = substituteLookupTraits(afc, afc.getSelfTypeAtNoBorrow(selfPos, derefChain, path)) and result != TNeverType() and result != TUnknownType() } @@ -2641,6 +2644,8 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput Declaration() { this = TFunctionDeclaration(i, f) } + FunctionDeclaration getFunction() { result = f } + predicate isAssocFunction(ImplOrTraitItemNode i_, Function f_) { i_ = i.asSome() and f_ = f @@ -2666,6 +2671,23 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput Location getLocation() { result = f.getLocation() } } + pragma[nomagic] + private TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp, Declaration decl) { + result = + tp.(TypeParamTypeParameter) + .getTypeParam() + .getAdditionalTypeBound(decl.getFunction(), _) + .getTypeRepr() + } + + bindingset[decl] + TypeMention getATypeParameterConstraint(TypeParameter tp, Declaration decl) { + result = Input2::getATypeParameterConstraint(tp) and + exists(decl) + or + result = getAdditionalTypeParameterConstraint(tp, decl) + } + class AccessEnvironment = string; bindingset[derefChain, borrow] diff --git a/rust/ql/test/library-tests/type-inference/regressions.rs b/rust/ql/test/library-tests/type-inference/regressions.rs index df033a889760..e1b47479f5d0 100644 --- a/rust/ql/test/library-tests/type-inference/regressions.rs +++ b/rust/ql/test/library-tests/type-inference/regressions.rs @@ -126,7 +126,7 @@ mod regression4 { T: MyTrait, { let S(s) = self; - s.m(); // $ MISSING: target=MyTrait::m $ SPURIOUS: target=RefAsMyTrait::m + s.m(); // $ target=MyTrait::m } } } diff --git a/rust/ql/test/utils-tests/modelgenerator/option.rs b/rust/ql/test/utils-tests/modelgenerator/option.rs index fd5cd649c2cf..701073564b18 100644 --- a/rust/ql/test/utils-tests/modelgenerator/option.rs +++ b/rust/ql/test/utils-tests/modelgenerator/option.rs @@ -416,7 +416,7 @@ impl MyOption<&T> { } } - // MISSING: summary=::cloned;Argument[self].Field[test::option::MyOption::MySome(0)].Reference;ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated + // summary=::cloned;Argument[self].Field[test::option::MyOption::MySome(0)].Reference;ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated pub fn cloned(self) -> MyOption where T: Clone, @@ -440,7 +440,7 @@ impl MyOption<&mut T> { } } - // MISSING: summary=::cloned;Argument[self].Field[test::option::MyOption::MySome(0)].Reference;ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated + // summary=::cloned;Argument[self].Field[test::option::MyOption::MySome(0)].Reference;ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated pub fn cloned(self) -> MyOption where T: Clone, diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 7cd4dab479d7..0b23b08aad67 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -1296,6 +1296,15 @@ module Make1 Input1> { Type getDeclaredType(DeclarationPosition dpos, TypePath path); } + /** + * Gets a type constraint on the type parameter `tp` that applies to `decl`, + * if any. + */ + bindingset[decl] + default TypeMention getATypeParameterConstraint(TypeParameter tp, Declaration decl) { + result = getATypeParameterConstraint(tp) + } + /** * A position inside an access. For example, the integer position of an * argument inside a method call. @@ -1643,6 +1652,24 @@ module Make1 Input1> { t = getTypeArgument(a, target, tp, path) } + /** + * Holds if the type parameter `constrainedTp` occurs in the declared type of + * `target` at `apos` and `pathToConstrained`, and there is a constraint + * `constraint` on `constrainedTp`. + */ + pragma[nomagic] + private predicate typeParameterHasConstraint( + Declaration target, AccessPosition apos, TypeParameter constrainedTp, + TypePath pathToConstrained, TypeMention constraint + ) { + exists(DeclarationPosition dpos | + accessDeclarationPositionMatch(apos, dpos) and + constrainedTp = target.getTypeParameter(_) and + constrainedTp = target.getDeclaredType(dpos, pathToConstrained) and + constraint = getATypeParameterConstraint(constrainedTp, target) + ) + } + /** * Holds if the declared type of `target` contains a type parameter at * `apos` and `pathToConstrained` that must satisfy `constraint` and `tp` @@ -1665,14 +1692,11 @@ module Make1 Input1> { Declaration target, AccessPosition apos, TypePath pathToConstrained, TypeMention constraint, TypePath pathToTp, TypeParameter tp ) { - exists(DeclarationPosition dpos, TypeParameter constrainedTp | - accessDeclarationPositionMatch(apos, dpos) and - constrainedTp = target.getTypeParameter(_) and - constraint = getATypeParameterConstraint(constrainedTp) and + exists(TypeParameter constrainedTp | + typeParameterHasConstraint(target, apos, constrainedTp, pathToConstrained, constraint) and tp = target.getTypeParameter(_) and tp = constraint.getTypeAt(pathToTp) and - constrainedTp != tp and - constrainedTp = target.getDeclaredType(dpos, pathToConstrained) + constrainedTp != tp ) } @@ -1799,6 +1823,15 @@ module Make1 Input1> { Type getDeclaredType(DeclarationPosition dpos, TypePath path); } + /** + * Gets a type constraint on the type parameter `tp` that applies to `decl`, + * if any. + */ + bindingset[decl] + default TypeMention getATypeParameterConstraint(TypeParameter tp, Declaration decl) { + result = getATypeParameterConstraint(tp) + } + /** * A position inside an access. For example, the integer position of an * argument inside a method call. @@ -1856,6 +1889,8 @@ module Make1 Input1> { private import codeql.util.Unit import Input + predicate getATypeParameterConstraint = Input::getATypeParameterConstraint/2; + class AccessEnvironment = Unit; final private class AccessFinal = Input::Access; From 09a2dd4a2e539f505e11aa4e1ae79c6f4038f64f Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 25 Mar 2026 15:01:39 +0100 Subject: [PATCH 3/3] Update rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll index 0816ce49c295..d128875eda7e 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/FunctionType.qll @@ -245,7 +245,7 @@ private Type substituteLookupTraits0(ItemNode i, Type t) { /** * Gets the type obtained by substituting in relevant traits in which to do function - * lookup, or `t` itself when no such trait exist, in the context of AST node `n`. + * lookup, or `t` itself when no such trait exists, in the context of AST node `n`. */ bindingset[n, t] pragma[inline_late]