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/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..251d424aa27d 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,7 +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 + pos.isSelfOrTypeQualifier() and not f.hasSelfParam() and result = [i.(Impl).getSelfTy().(AstNode), i.(Trait).getName()] ) @@ -209,7 +211,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() diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 70dfe9e90056..441b36d84a90 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -343,6 +343,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) | @@ -1246,7 +1250,7 @@ private predicate assocFunctionInfo( AssocFunctionType t ) { f = i.getASuccessor(name) and - arity = f.getParamList().getNumberOfParams() and + arity = f.getNumberOfParamsInclSelf() and t.appliesTo(f, i, pos) } @@ -1259,7 +1263,7 @@ private predicate assocFunctionInfo( * is the type parameter used in the blanket implementation. */ pragma[nomagic] -private predicate functionInfoBlanketLike( +private predicate assocFunctionInfoBlanketLike( Function f, string name, int arity, ImplItemNode impl, Trait trait, FunctionPosition pos, AssocFunctionType t, TypePath blanketPath, TypeParam blanketTypeParam ) { @@ -1327,7 +1331,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 +1365,343 @@ 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 method `m` with the name `name` and the arity `arity` exists in - * `i`, and the type of the `self` parameter is `selfType`. - * - * `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. + * Holds if the non-method trait function `f` mentions the implicit `Self` type + * parameter at `pos`. */ pragma[nomagic] - private predicate methodInfo( - Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType + private predicate traitSelfTypeParameterOccurrence( + TraitItemNode trait, NonMethodFunction f, FunctionPosition pos ) { - assocFunctionInfo(m, name, arity, i, selfPos, selfType) and - strippedType = selfType.getTypeAt(strippedTypePath) and - isComplexRootStripped(strippedTypePath, strippedType) and - selfPos.isSelfOrTypeQualifier() + FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _, TSelfTypeParameter(trait)) } + /** + * Holds if the non-method function `f` implements a trait function that mentions + * the implicit `Self` type parameter at `pos`. + */ pragma[nomagic] - private predicate methodInfoTypeParam( - Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType selfType, TypePath strippedTypePath, TypeParam tp + private predicate traitImplSelfTypeParameterOccurrence( + ImplItemNode impl, NonMethodFunction f, FunctionPosition pos ) { - methodInfo(m, name, arity, selfPos, i, selfType, strippedTypePath, TTypeParamTypeParameter(tp)) + exists(NonMethodFunction traitFunction | + f = impl.getAnAssocItem() and + f.implements(traitFunction) and + traitSelfTypeParameterOccurrence(_, traitFunction, pos) + ) } + private module TypeOption = Option; + + private class TypeOption = TypeOption::Option; + /** - * 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 function `f` with the name `name` and the arity `arity` exists in + * `i`, and the type at `selfPos` is `selfType`. + * + * `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. + * + * `selfPosAdj` is the function-call adjusted version of `selfPos`. + * + * `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[inline] - private predicate methodInfoNonBlanket( - Method m, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, - AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType + pragma[nomagic] + private predicate assocFunctionInfo( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplOrTraitItemNode i, AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, + TypeOption implType, TypeOption trait, boolean isMethod ) { + assocFunctionInfo(f, name, arity, i, selfPos, selfType) and + strippedType = selfType.getTypeAt(strippedTypePath) and ( - methodInfo(m, name, arity, selfPos, i, selfType, strippedTypePath, strippedType) or - methodInfoTypeParam(m, name, arity, selfPos, i, selfType, strippedTypePath, _) + isComplexRootStripped(strippedTypePath, strippedType) + or + selfPos.isTypeQualifier() and strippedTypePath.isEmpty() ) and + selfPosAdj = selfPos.getFunctionCallAdjusted(f) and + ( + selfPos.isSelfOrTypeQualifier() + or + traitSelfTypeParameterOccurrence(i, f, selfPos) + or + traitImplSelfTypeParameterOccurrence(i, f, selfPos) + ) 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 assocFunctionInfoNonBlanketLike( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplOrTraitItemNode i, AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, + TypeOption implType, TypeOption trait, boolean isMethod + ) { + assocFunctionInfo(f, name, arity, selfPos, selfPosAdj, i, selfType, strippedTypePath, + strippedType, implType, trait, isMethod) and not BlanketImplementation::isBlanketLike(i, _, _) } + pragma[nomagic] + private predicate assocFunctionInfoNonBlanketLikeTypeParam( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplOrTraitItemNode i, AssocFunctionType selfType, TypePath strippedTypePath, + TypeOption implType, TypeOption trait, boolean isMethod + ) { + assocFunctionInfoNonBlanketLike(f, name, arity, selfPos, selfPosAdj, i, selfType, + strippedTypePath, TTypeParamTypeParameter(_), implType, trait, isMethod) + } + /** - * 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 associated function `f` with the name `name` and the arity `arity` exists + * in blanket (like) implementation `impl` of `trait`, and the type at `selfPos` is + * `selfType`. * * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which * is the type parameter used in the blanket implementation. + * + * `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 assocFunctionSelfInfoBlanketLike( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplItemNode impl, Trait trait, AssocFunctionType selfType, TypePath blanketPath, + TypeParam blanketTypeParam, boolean isMethod ) { - functionInfoBlanketLike(m, name, arity, impl, trait, selfPos, selfType, blanketPath, + assocFunctionInfoBlanketLike(f, name, arity, impl, trait, selfPos, selfType, blanketPath, blanketTypeParam) and - selfPos.isSelfOrTypeQualifier() + selfPosAdj = selfPos.getFunctionCallAdjusted(f) and + ( + selfPos.isSelfOrTypeQualifier() + or + traitImplSelfTypeParameterOccurrence(impl, f, selfPos) + ) 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 predicate methodCallVisibleTraitCandidate = MethodTraitIsVisible::traitIsVisible/2; + private module AssocFunctionTraitIsVisible = TraitIsVisible; - bindingset[mc, impl] + bindingset[afc, impl] pragma[inline_late] - private predicate methodCallVisibleImplTraitCandidate(MethodCall mc, ImplItemNode impl) { - methodCallVisibleTraitCandidate(mc, impl.resolveTraitTy()) + private predicate callVisibleImplTraitCandidate(AssocFunctionCall afc, ImplItemNode impl) { + AssocFunctionTraitIsVisible::traitIsVisible(afc, impl.resolveTraitTy()) + } + + private Type getNonTypeParameterTypeQualifier(AssocFunctionCall afc) { + result = getCallExprTypeQualifier(afc, TypePath::nil(), _) and + not result instanceof TypeParameter + } + + pragma[nomagic] + private predicate callInfo( + AssocFunctionCall afc, string name, int arity, TypeOption typeQualifier, + TypeOption traitQualifier, boolean hasReceiver + ) { + afc.hasNameAndArity(name, arity) and + (if afc.hasReceiver() then hasReceiver = true else hasReceiver = false) and + ( + typeQualifier.asSome() = getNonTypeParameterTypeQualifier(afc) + or + not exists(getNonTypeParameterTypeQualifier(afc)) and + typeQualifier.isNone() + ) and + ( + traitQualifier.asSome() = TTrait(afc.getATrait()) + or + not afc.hasATrait() and + traitQualifier.isNone() + ) + } + + bindingset[implType, trait, isMethod] + private predicate callMatching( + TypeOption implType, TypeOption trait, boolean isMethod, TypeOption typeQualifier, + TypeOption traitQualifier, boolean hasReceiver + ) { + // when present, explicit type qualifier must match the type being implemented + typeQualifier = [implType, any(TypeOption non | non.isNone())] and + // 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] + } + + pragma[nomagic] + private predicate assocFunctionInfoNonBlanketLikeCand( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplOrTraitItemNode i, AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, + TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver + ) { + exists(TypeOption implType, TypeOption trait, boolean isMethod | + assocFunctionInfoNonBlanketLike(f, name, arity, selfPos, selfPosAdj, i, selfType, + strippedTypePath, strippedType, implType, trait, isMethod) and + callMatching(implType, trait, isMethod, typeQualifier, traitQualifier, hasReceiver) + ) + } + + pragma[nomagic] + private predicate assocFunctionInfoNonBlanketLikeTypeParamCand( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplOrTraitItemNode i, AssocFunctionType selfType, TypePath strippedTypePath, + TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver + ) { + exists(TypeOption implType, TypeOption trait, boolean isMethod | + assocFunctionInfoNonBlanketLikeTypeParam(f, name, arity, selfPos, selfPosAdj, i, selfType, + strippedTypePath, implType, trait, isMethod) and + callMatching(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 + * `selfPos`. * * `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 + * 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, 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 selfPos, 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) - ) - or - methodCallVisibleTraitCandidate(mc, i) + callInfo(afc, name, arity, typeQualifier, traitQualifier, hasReceiver) and + if not afc.hasATrait() and i.(Impl).hasTrait() + then callVisibleImplTraitCandidate(afc, i) + else any() + | + assocFunctionInfoNonBlanketLikeCand(f, name, arity, selfPos, selfPosAdj, i, selfType, + strippedTypePath, strippedType, typeQualifier, traitQualifier, hasReceiver) or - i.(ImplItemNode).resolveTraitTy() = mc.getTrait() + assocFunctionInfoNonBlanketLikeTypeParamCand(f, name, arity, selfPos, selfPosAdj, i, selfType, + strippedTypePath, typeQualifier, traitQualifier, hasReceiver) + ) + } + + pragma[nomagic] + private predicate assocFunctionSelfInfoBlanketLikeCand( + Function f, string name, int arity, FunctionPosition selfPos, FunctionPosition selfPosAdj, + ImplItemNode impl, AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam, + TypeOption traitQualifier, boolean hasReceiver + ) { + exists(Trait trait, boolean isMethod | + assocFunctionSelfInfoBlanketLike(f, name, arity, selfPos, selfPosAdj, impl, trait, selfType, + blanketPath, blanketTypeParam, isMethod) and + traitQualifier = [TypeOption::some(TTrait(trait)), any(TypeOption non | non.isNone())] and + hasReceiver = [isMethod, false] ) } /** - * 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 `selfPos`. * * `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 selfPos, 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 traitQualifier, boolean hasReceiver | + callInfo(afc, name, arity, _, traitQualifier, hasReceiver) and + assocFunctionSelfInfoBlanketLikeCand(f, name, arity, selfPos, selfPosAdj, impl, self, + blanketPath, blanketTypeParam, traitQualifier, hasReceiver) | - methodCallVisibleImplTraitCandidate(mc, impl) - or - impl.resolveTraitTy() = mc.getTrait() + if not afc.hasATrait() then callVisibleImplTraitCandidate(afc, impl) else any() ) } /** - * 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 apos); + + AstNode getNodeAt(FunctionPosition pos) { + result = this.getNonReturnNodeAt(pos) + or + result = this and pos.isReturn() + } + + predicate hasReceiver() { exists(this.getNodeAt(any(FunctionPosition self | self.isSelf()))) } abstract predicate supportsAutoDerefAndBorrow(); @@ -1554,26 +1711,47 @@ 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()) } + + Type getTypeAt(FunctionPosition pos, TypePath path) { + result = inferType(this.getNodeAt(pos), path) } /** - * Same as `getACandidateReceiverTypeAt`, but without borrows. + * Holds if `selfPos` is a potentially relevant position for resolving this call. */ pragma[nomagic] - Type getACandidateReceiverTypeAtNoBorrow( - FunctionPosition selfPos, DerefChain derefChain, TypePath path - ) { - result = this.getArgumentTypeAt(selfPos.asArgumentPosition(), path) and - selfPos.isSelfOrTypeQualifier() and - derefChain.isEmpty() + private predicate isRelevantSelfPos(FunctionPosition selfPos) { + not this.hasReceiver() and + exists(TypePath strippedTypePath, Type strippedType | + strippedType = substituteLookupTraits(this.getTypeAt(selfPos, strippedTypePath)) and + strippedType != TNeverType() and + strippedType != TUnknownType() + | + nonBlanketLikeCandidate(this, _, _, selfPos, _, _, strippedTypePath, strippedType) + or + blanketLikeCandidate(this, _, _, selfPos, _, _, strippedTypePath, _) + ) + } + + /** + * Same as `getSelfTypeAt`, but without borrows. + */ + pragma[nomagic] + Type getSelfTypeAtNoBorrow(FunctionPosition selfPos, DerefChain derefChain, TypePath path) { + result = this.getTypeAt(selfPos, path) and + derefChain.isEmpty() and + ( + selfPos.isSelfOrTypeQualifier() + or + this.isRelevantSelfPos(selfPos) + ) or exists(DerefImplItemNode impl, DerefChain suffix | result = @@ -1583,9 +1761,9 @@ private module MethodResolution { } /** - * 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 `selfPos`. * * The types are incompatible because they disagree on a concrete type somewhere * inside `root`. @@ -1596,48 +1774,58 @@ private module MethodResolution { Type root ) { exists(TypePath path | - ReceiverIsInstantiationOfSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, selfPos, - derefChain, borrow), i, _, path) and + ArgIsInstantiationOfSelfParam::argIsNotInstantiationOf(this, i, selfPos, derefChain, borrow, + path) and path.isCons(root.getATypeParameter(), _) ) + or + exists(AssocFunctionType selfType | + ArgIsInstantiationOfSelfParam::argIsInstantiationOf(this, i, selfPos, derefChain, borrow, + selfType) and + CallArgsAreInstantiationsOf::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 `selfPos`, or because the blanket constraint is not satisfied. */ pragma[nomagic] private predicate hasIncompatibleBlanketLikeTarget( ImplItemNode impl, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow ) { - ReceiverIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkMethodCallCand(this, - selfPos, derefChain, borrow), impl, _, _) + ArgIsNotInstantiationOfBlanketLikeSelfParam::argIsNotInstantiationOf(MkAssocFunctionCallCand(this, + selfPos, _, derefChain, borrow), impl, _, _) or - ReceiverSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkMethodCallCand(this, - selfPos, derefChain, borrow), impl) + ArgSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkAssocFunctionCallCand(this, + selfPos, _, 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( + Type getANonPseudoSelfTypeAt( FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path ) { - result = this.getACandidateReceiverTypeAt(selfPos, derefChain, borrow, path) and + result = this.getSelfTypeAt(selfPos, derefChain, borrow, path) and result != TNeverType() and result != TUnknownType() } pragma[nomagic] - private Type getComplexStrippedType( + private Type getComplexStrippedSelfType( FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath ) { - result = - this.getANonPseudoCandidateReceiverTypeAt(selfPos, derefChain, borrow, strippedTypePath) and - isComplexRootStripped(strippedTypePath, result) + result = this.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, strippedTypePath) and + ( + isComplexRootStripped(strippedTypePath, result) + or + selfPos.isTypeQualifier() and strippedTypePath.isEmpty() + ) } bindingset[derefChain, borrow, strippedTypePath, strippedType] @@ -1646,7 +1834,7 @@ private module MethodResolution { Type strippedType ) { forall(ImplOrTraitItemNode i | - methodCallNonBlanketCandidate(this, _, selfPos, i, _, strippedTypePath, strippedType) + nonBlanketLikeCandidate(this, _, selfPos, _, i, _, strippedTypePath, strippedType) | this.hasIncompatibleTarget(i, selfPos, derefChain, borrow, strippedType) ) @@ -1659,7 +1847,7 @@ private module MethodResolution { ) { this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath, strippedType) and - forall(ImplItemNode i | methodCallBlanketLikeCandidate(this, _, selfPos, i, _, _, _) | + forall(ImplItemNode i | blanketLikeCandidate(this, _, selfPos, _, i, _, _, _) | this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) ) } @@ -1672,7 +1860,7 @@ private module MethodResolution { this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, borrow, strippedTypePath, strippedType) and forall(ImplItemNode i | - methodCallBlanketLikeCandidate(this, _, selfPos, i, _, _, _) and + blanketLikeCandidate(this, _, selfPos, _, i, _, _, _) and not i.isBlanketImplementation() | this.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) @@ -1685,27 +1873,23 @@ private module MethodResolution { FunctionPosition selfPos, 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 + selfPos.isSelf() and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and + this.getComplexStrippedSelfType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and n = -1 or this.hasNoCompatibleTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | + exists(Type t | + t = getNthLookupType(strippedType, n) and this.hasNoCompatibleTargetCheck(selfPos, 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 `selfPos`. */ pragma[nomagic] predicate hasNoCompatibleTargetNoBorrow(FunctionPosition selfPos, DerefChain derefChain) { @@ -1722,19 +1906,28 @@ private module MethodResolution { int n ) { ( - this.supportsAutoDerefAndBorrow() + this.supportsAutoDerefAndBorrow() and + selfPos.isSelf() or - // needed for the `hasNoCompatibleTarget` check in - // `ReceiverSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` - derefChain.isEmpty() + // needed for the `hasNoCompatibleNonBlanketTarget` check in + // `ArgSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` + exists(ImplItemNode i, FunctionPosition selfPosAdj | + derefChain.isEmpty() and + blanketLikeCandidate(this, _, _, selfPosAdj, i, _, _, _) and + i.isBlanketImplementation() and + if this.hasReceiver() + then selfPosAdj = selfPos.getFunctionCallAdjusted() + else selfPosAdj = selfPos + ) ) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and + this.getComplexStrippedSelfType(selfPos, derefChain, TNoBorrowKind(), strippedTypePath) and n = -1 or this.hasNoCompatibleNonBlanketTargetNoBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | + exists(Type t | + t = getNthLookupType(strippedType, n) and this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TNoBorrowKind(), strippedTypePath, t) ) @@ -1742,7 +1935,7 @@ private module MethodResolution { /** * 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 `selfPos`. */ pragma[nomagic] predicate hasNoCompatibleNonBlanketTargetNoBorrow( @@ -1762,12 +1955,14 @@ private module MethodResolution { ) { this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath) and + this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(false), + strippedTypePath) and n = -1 or this.hasNoCompatibleTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | + exists(Type t | + t = getNthLookupType(strippedType, n) and this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) @@ -1775,7 +1970,7 @@ private module MethodResolution { /** * 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 `selfPos`. */ pragma[nomagic] predicate hasNoCompatibleTargetSharedBorrow(FunctionPosition selfPos, DerefChain derefChain) { @@ -1793,12 +1988,13 @@ private module MethodResolution { ) { this.hasNoCompatibleTargetSharedBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and + this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and n = -1 or this.hasNoCompatibleTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | + exists(Type t | + t = getNthLookupType(strippedType, n) and this.hasNoCompatibleNonBlanketLikeTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) @@ -1806,7 +2002,7 @@ private module MethodResolution { /** * 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 `selfPos`. */ pragma[nomagic] predicate hasNoCompatibleTargetMutBorrow(FunctionPosition selfPos, DerefChain derefChain) { @@ -1824,12 +2020,14 @@ private module MethodResolution { ) { this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath) and + this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(false), + strippedTypePath) and n = -1 or this.hasNoCompatibleNonBlanketTargetSharedBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | + exists(Type t | + t = getNthLookupType(strippedType, n) and this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(false), strippedTypePath, t) ) @@ -1837,7 +2035,7 @@ private module MethodResolution { /** * 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 `selfPos`. */ pragma[nomagic] predicate hasNoCompatibleNonBlanketTargetSharedBorrow( @@ -1857,12 +2055,13 @@ private module MethodResolution { ) { this.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and strippedType = - this.getComplexStrippedType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and + this.getComplexStrippedSelfType(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath) and n = -1 or this.hasNoCompatibleNonBlanketTargetMutBorrowToIndex(selfPos, derefChain, strippedTypePath, strippedType, n - 1) and - exists(Type t | t = getNthLookupType(strippedType, n) | + exists(Type t | + t = getNthLookupType(strippedType, n) and this.hasNoCompatibleNonBlanketTargetCheck(selfPos, derefChain, TSomeBorrowKind(true), strippedTypePath, t) ) @@ -1870,7 +2069,7 @@ private module MethodResolution { /** * 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 `selfPos`. */ pragma[nomagic] predicate hasNoCompatibleNonBlanketTargetMutBorrow( @@ -1883,7 +2082,10 @@ private module MethodResolution { } /** - * Gets a [candidate receiver type][1] of this method call at `path`. + * Gets the type of this call at `selfPos` and `path`. + * + * In case this call supports auto-dereferencing and borrowing and `selfPos` is the + * `self` parameter position, 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,15 +2097,16 @@ private module MethodResolution { * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers */ pragma[nomagic] - Type getACandidateReceiverTypeAt( + Type getSelfTypeAt( FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path ) { - result = this.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, path) and + result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, path) and borrow.isNoBorrow() or exists(RefType rt | // first try shared borrow this.supportsAutoDerefAndBorrow() and + selfPos.isSelf() and this.hasNoCompatibleTargetNoBorrow(selfPos, derefChain) and borrow.isSharedBorrow() or @@ -1917,7 +2120,7 @@ private module MethodResolution { result = rt or exists(TypePath suffix | - result = this.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, suffix) and + result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, suffix) and path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix) ) ) @@ -1925,15 +2128,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 +2146,24 @@ 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 self | + self.isSelf() and + exists(this.resolveCallTarget(_, derefChain, borrow)) and + arg = this.getNodeAt(self) 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 Expr getNonReturnNodeAt(FunctionPosition pos) { + result = MethodCallExpr.super.getSyntacticArgument(pos.asArgumentPosition()) } override predicate supportsAutoDerefAndBorrow() { any() } @@ -1965,7 +2171,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 +2180,10 @@ 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 Expr getNonReturnNodeAt(FunctionPosition pos) { pos.isSelf() and result = super.getBase() or @@ -1994,60 +2200,51 @@ 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 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 pos, TypePath path) { + result = super.getTypeAt(pos, path) or pos.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 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 pos, boolean isMutable) { exists(int borrows | super.isOverloaded(_, _, borrows) | pos.isSelf() and borrows >= 1 and @@ -2059,7 +2256,7 @@ private module MethodResolution { ) } - override Type getArgumentTypeAt(ArgumentPosition pos, TypePath path) { + override Type getTypeAt(FunctionPosition pos, TypePath path) { exists(boolean isMutable, RefType rt | this.implicitBorrowAt(pos, isMutable) and rt = getRefType(isMutable) @@ -2068,21 +2265,21 @@ private module MethodResolution { path.isEmpty() or exists(TypePath path0 | - result = inferType(this.getArg(pos), path0) and + result = inferType(this.getNodeAt(pos), path0) and path = TypePath::cons(rt.getPositionalTypeParameter(0), path0) ) ) or not this.implicitBorrowAt(pos, _) and - result = inferType(this.getArg(pos), path) + result = inferType(this.getNodeAt(pos), 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 pos, boolean isMutable | + this.implicitBorrowAt(pos, isMutable) and + arg = this.getNodeAt(pos) and derefChain = DerefChain::nil() and borrow = TSomeBorrowKind(isMutable) ) @@ -2094,81 +2291,98 @@ 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 selfPos, FunctionPosition selfPosAdj, + DerefChain derefChain, BorrowKind borrow ) { - exists(mc.getACandidateReceiverTypeAt(selfPos, derefChain, borrow, _)) + exists(afc.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, _)) and + if afc.hasReceiver() + then selfPosAdj = selfPos.getFunctionCallAdjusted() + else selfPosAdj = selfPos } - /** A method call with a dereference chain and a potential borrow. */ - private class MethodCallCand extends MkMethodCallCand { - MethodCall mc_; + /** A call with a dereference chain and a potential borrow. */ + final private class AssocFunctionCallCand extends MkAssocFunctionCallCand { + AssocFunctionCall afc_; FunctionPosition selfPos; + FunctionPosition selfPosAdj; DerefChain derefChain; BorrowKind borrow; - MethodCallCand() { this = MkMethodCallCand(mc_, selfPos, derefChain, borrow) } + AssocFunctionCallCand() { + this = MkAssocFunctionCallCand(afc_, selfPos, 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(selfPos, derefChain, borrow, path)) } pragma[nomagic] predicate hasNoCompatibleNonBlanketTarget() { - mc_.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and + afc_.hasNoCompatibleNonBlanketTargetSharedBorrow(selfPos, derefChain) and borrow.isSharedBorrow() or - mc_.hasNoCompatibleNonBlanketTargetMutBorrow(selfPos, derefChain) and + afc_.hasNoCompatibleNonBlanketTargetMutBorrow(selfPos, derefChain) and borrow.isMutableBorrow() or - mc_.hasNoCompatibleNonBlanketTargetNoBorrow(selfPos, derefChain) and + afc_.hasNoCompatibleNonBlanketTargetNoBorrow(selfPos, derefChain) and borrow.isNoBorrow() } pragma[nomagic] predicate hasSignature( - MethodCall mc, FunctionPosition selfPos_, TypePath strippedTypePath, Type strippedType, + AssocFunctionCall afc, FunctionPosition pos, 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 + selfPos.isTypeQualifier() and strippedTypePath.isEmpty() + ) and + afc = afc_ and + afc.hasNameAndArity(name, arity) and + pos = 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, _, _) + ArgIsNotInstantiationOfInherentSelfParam::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 + selfPosAdj.isTypeQualifier() or selfPosAdj.asPosition() = 0 + | + this.hasSignature(_, selfPosAdj, strippedTypePath, strippedType, name, arity) and forall(Impl i | - methodInfoNonBlanket(_, name, arity, selfPos, i, _, strippedTypePath, strippedType) and - not i.hasTrait() + ( + assocFunctionInfoNonBlanketLike(_, name, arity, _, selfPosAdj, i, _, strippedTypePath, + strippedType, _, _, _) or + assocFunctionInfoNonBlanketLikeTypeParam(_, name, arity, _, selfPosAdj, i, _, + strippedTypePath, _, _, _) + ) and + i.isInherent() | this.hasIncompatibleInherentTarget(i) ) @@ -2177,32 +2391,32 @@ private module MethodResolution { pragma[nomagic] private predicate argIsInstantiationOf(ImplOrTraitItemNode i, string name, int arity) { - ReceiverIsInstantiationOfSelfParam::argIsInstantiationOf(this, i, _) and - mc_.hasNameAndArity(name, arity) + ArgIsInstantiationOfSelfParam::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) + CallArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result) } string toString() { - result = mc_.toString() + " [" + derefChain.toString() + "; " + borrow + "]" + result = afc_ + " at " + selfPos + " [" + derefChain.toString() + "; " + borrow + "]" } - Location getLocation() { result = mc_.getLocation() } + Location getLocation() { result = afc_.getLocation() } } /** @@ -2210,23 +2424,23 @@ private module MethodResolution { */ private module ImplicitDeref { private newtype TMethodCallDerefCand = - MkMethodCallDerefCand(MethodCall mc, FunctionPosition selfPos, DerefChain derefChain) { + MkMethodCallDerefCand(AssocFunctionCall mc, FunctionPosition selfPos, DerefChain derefChain) { mc.supportsAutoDerefAndBorrow() and + selfPos.isSelf() and mc.hasNoCompatibleTargetMutBorrow(selfPos, derefChain) and - exists(mc.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) + exists(mc.getSelfTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) } /** A method call with a dereference chain. */ private class MethodCallDerefCand extends MkMethodCallDerefCand { - MethodCall mc; + AssocFunctionCall mc; FunctionPosition selfPos; DerefChain derefChain; MethodCallDerefCand() { this = MkMethodCallDerefCand(mc, selfPos, derefChain) } Type getTypeAt(TypePath path) { - result = - substituteLookupTraits(mc.getACandidateReceiverTypeAtNoBorrow(selfPos, derefChain, path)) and + result = substituteLookupTraits(mc.getSelfTypeAtNoBorrow(selfPos, derefChain, path)) and result != TNeverType() and result != TUnknownType() } @@ -2255,16 +2469,16 @@ 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 selfPos, DerefImplItemNode impl, + DerefChain derefChain, TypePath path ) { exists(MethodCallDerefCand mcc, TypePath exprPath | - mcc = MkMethodCallDerefCand(mc, selfPos, derefChain) and + mcc = MkMethodCallDerefCand(afc, selfPos, derefChain) and MethodCallSatisfiesDerefConstraint::satisfiesConstraintTypeThrough(mcc, impl, _, exprPath, result) and exprPath.isCons(getDerefTargetTypeParameter(), path) @@ -2272,21 +2486,22 @@ private module MethodResolution { } } - 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 +2510,135 @@ 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 `self` parameter or similar parameter used to determine dispatch. */ - private module ReceiverIsInstantiationOfSelfParamInput implements - IsInstantiationOfInputSig + private module ArgIsInstantiationOfSelfParamInput 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 pos, Function f, TypePath strippedTypePath, Type strippedType | - mcc.hasSignature(mc, selfPos, strippedTypePath, strippedType, _, _) + afcc.hasSignature(afc, pos, strippedTypePath, strippedType, _, _) | - methodCallNonBlanketCandidate(mc, m, selfPos, i, selfType, strippedTypePath, strippedType) + nonBlanketLikeCandidate(afc, f, _, pos, i, selfType, strippedTypePath, strippedType) or - methodCallBlanketLikeCandidate(mc, m, selfPos, i, selfType, _, _) and - ReceiverSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(mcc, i) + blanketLikeCandidate(afc, f, _, pos, 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 ArgIsInstantiationOfSelfParam { + import ArgIsInstantiationOf + + pragma[nomagic] + predicate argIsNotInstantiationOf( + AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, + BorrowKind borrow, TypePath path + ) { + argIsNotInstantiationOf(MkAssocFunctionCallCand(afc, selfPos, _, derefChain, borrow), i, _, + path) + } + + pragma[nomagic] + predicate argIsInstantiationOf( + AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, + BorrowKind borrow, AssocFunctionType selfType + ) { + argIsInstantiationOf(MkAssocFunctionCallCand(afc, selfPos, _, derefChain, borrow), i, selfType) + } + } /** - * A configuration for anti-matching the type of a receiver against the type of + * A configuration for anti-matching the type of an argument against the type of * a `self` parameter belonging to a blanket (like) implementation. */ - private module ReceiverIsNotInstantiationOfBlanketLikeSelfParamInput implements - IsInstantiationOfInputSig + private module ArgIsNotInstantiationOfBlanketLikeSelfParamInput 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 ArgIsNotInstantiationOfBlanketLikeSelfParam = + ArgIsInstantiationOf; /** - * A configuration for anti-matching the type of a receiver against the type of + * A configuration for anti-matching the type of an argument against the type of * a `self` parameter in an inherent method. */ - private module ReceiverIsNotInstantiationOfInherentSelfParamInput implements - IsInstantiationOfInputSig + private module ArgIsNotInstantiationOfInherentSelfParamInput 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()) + ArgIsInstantiationOfSelfParamInput::potentialInstantiationOf0(afcc, abs, constraint) and + abs.(Impl).isInherent() and + // todo: comment + exists(FunctionPosition selfPosAdj | afcc = MkAssocFunctionCallCand(_, _, selfPosAdj, _, _) | + selfPosAdj.isTypeQualifier() or selfPosAdj.asPosition() = 0 + ) } } - private module ReceiverIsNotInstantiationOfInherentSelfParam = - ArgIsInstantiationOf; + private module ArgIsNotInstantiationOfInherentSelfParam = + 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 { + private module CallArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig { predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) { FunctionOverloading::functionResolutionDependsOnArgument(i, f, traitTp, pos) } - class Call extends MethodCallCand { + class Call extends AssocFunctionCallCand { Type getArgType(FunctionPosition pos, TypePath path) { - result = mc_.getArgumentTypeAt(pos.asArgumentPosition(), path) - or - pos.isReturn() and - result = inferType(mc_.getNodeAt(pos), path) + result = this.getAssocFunctionCall().getTypeAt(pos, path) } predicate hasTargetCand(ImplOrTraitItemNode i, Function f) { @@ -2413,8 +2647,14 @@ private module MethodResolution { } } - private module MethodArgsAreInstantiationsOf = - ArgsAreInstantiationsOf; + private module CallArgsAreInstantiationsOf { + import ArgsAreInstantiationsOf + + pragma[nomagic] + predicate argsAreNotInstantiationsOf(AssocFunctionCall afc, ImplOrTraitItemNode i) { + argsAreNotInstantiationsOf(MkAssocFunctionCallCand(afc, _, _, _, _), i, _) + } + } } /** @@ -2477,9 +2717,9 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi ) } - final private class MethodCallFinal = MethodResolution::MethodCall; + final private class AssocFunctionCallFinal = AssocFunctionResolution::AssocFunctionCall; - class Access extends MethodCallFinal, ContextTyping::ContextTypedCallCand { + class Access extends AssocFunctionCallFinal, ContextTyping::ContextTypedCallCand { Access() { // handled in the `OperationMatchingInput` module not this instanceof Operation @@ -2498,31 +2738,20 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi } pragma[nomagic] - private Type getInferredSelfType(AccessPosition apos, string derefChainBorrow, TypePath path) { + private Type getInferredSelfType(FunctionPosition pos, string derefChainBorrow, TypePath path) { exists(DerefChain derefChain, BorrowKind borrow | - result = this.getACandidateReceiverTypeAt(apos, derefChain, borrow, path) and - derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) + result = this.getSelfTypeAt(pos, derefChain, borrow, path) and + derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and + pos.isSelf() ) } 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(AccessPosition apos, TypePath path) { + exists(FunctionPosition pos | + result = this.getTypeAt(pos, path) and + not pos.isSelf() and + if this.hasReceiver() then apos = pos else pos = apos.getFunctionCallAdjusted() ) } @@ -2557,6 +2786,20 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi this.hasUnknownTypeAt(i, this.getTarget(i, derefChainBorrow), pos, path) ) } + + /** + * Holds if the return type of this call at `path` may have to be inferred + * from the context. + */ + pragma[nomagic] + predicate hasUnknownTypeAt(FunctionPosition pos, TypePath path) { + forex(ImplOrTraitItemNode i, Function f | + f = CallExprImpl::getResolvedFunction(this) and + f = i.getAnAssocItem() + | + this.hasUnknownTypeAt(i, f, pos, path) + ) + } } } @@ -2565,15 +2808,26 @@ private module MethodCallMatching = MatchingWithEnvironment; - /** 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() + forall(Function f | f = CallExprImpl::getResolvedFunction(this) | not f instanceof Method) } - /** - * 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) - ) - } + result = CallExprImpl::getResolvedFunction(this) and + not result instanceof AssocFunction } } - - private module NonMethodArgsAreInstantiationsOfNonBlanket = - ArgsAreInstantiationsOf; } abstract private class TupleLikeConstructor extends Addressable { @@ -3233,7 +3089,7 @@ private module NonMethodCallMatchingInput implements MatchingInputSig { exists(ImplOrTraitItemNodeOption i, NonMethodFunctionDeclaration f | result = TNonMethodFunctionDeclaration(i, f) | - f = this.resolveCallTargetViaTypeInference(i.asSome()) // mutual recursion; resolving some associated function calls requires resolving types + f = this.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i.asSome(), _, _) // mutual recursion; resolving some associated function calls requires resolving types or f = this.resolveCallTargetViaPathResolution() and f.isDirectlyFor(i) @@ -3255,12 +3111,6 @@ private module NonMethodCallMatchingInput implements MatchingInputSig { 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 @@ -3336,7 +3186,7 @@ private module OperationMatchingInput implements MatchingInputSig { } } - class Access extends MethodResolution::MethodCallOperation { + class Access extends AssocFunctionResolution::AssocFunctionCallOperation { Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } pragma[nomagic] @@ -4057,7 +3907,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 @@ -4089,8 +3940,7 @@ private module Cached { 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, _, _) ) } @@ -4222,8 +4072,8 @@ private module Debug { Locatable getRelevantLocatable() { exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - filepath.matches("%/main.rs") and - startline = 103 + filepath.matches("%/overloading.rs") and + startline = 57 ) } 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/regressions.rs b/rust/ql/test/library-tests/type-inference/regressions.rs index 17475d50166b..2830ca5aa2be 100644 --- a/rust/ql/test/library-tests/type-inference/regressions.rs +++ b/rust/ql/test/library-tests/type-inference/regressions.rs @@ -32,3 +32,41 @@ 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(T); + + impl MyFrom> for Option { + fn my_from(s: S) -> Self { + Some(s.0) // $ fieldof=S + } + } + + 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..78358a8adab8 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,23 @@ 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:58:20:58:20 | s | | regressions.rs:55:5:55:23 | S | +| regressions.rs:58:20:58:20 | s | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:58:37:60:9 | { ... } | | {EXTERNAL LOCATION} | Option | +| regressions.rs:58:37:60:9 | { ... } | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:59:18:59:18 | s | | regressions.rs:55:5:55:23 | S | +| regressions.rs:59:18:59:18 | s | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:63:22:63:22 | x | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:67:5:71:5 | { ... } | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:68:33:68:33 | x | | regressions.rs:63:18:63: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 +14806,38 @@ 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:58:20:58:20 | s | | regressions.rs:55:5:55:23 | S | +| regressions.rs:58:20:58:20 | s | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:58:37:60:9 | { ... } | | {EXTERNAL LOCATION} | Option | +| regressions.rs:58:37:60:9 | { ... } | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:59:13:59:21 | Some(...) | | {EXTERNAL LOCATION} | Option | +| regressions.rs:59:13:59:21 | Some(...) | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:59:18:59:18 | s | | regressions.rs:55:5:55:23 | S | +| regressions.rs:59:18:59:18 | s | T | regressions.rs:57:10:57:10 | T | +| regressions.rs:59:18:59:20 | s.0 | | regressions.rs:57:10:57:10 | T | +| regressions.rs:63:22:63:22 | x | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:67:5:71:5 | { ... } | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:68:13:68:13 | y | | {EXTERNAL LOCATION} | Option | +| regressions.rs:68:13:68:13 | y | T | regressions.rs:63:14:63:15 | T1 | +| regressions.rs:68:13:68:13 | y | T | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:68:17:68:34 | ...::my_from(...) | | {EXTERNAL LOCATION} | Option | +| regressions.rs:68:17:68:34 | ...::my_from(...) | T | regressions.rs:63:14:63:15 | T1 | +| regressions.rs:68:17:68:34 | ...::my_from(...) | T | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:68:33:68:33 | x | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:69:13:69:13 | z | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:69:17:69:34 | ...::my_from(...) | | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:69:33:69:33 | y | | {EXTERNAL LOCATION} | Option | +| regressions.rs:69:33:69:33 | y | T | regressions.rs:63:14:63:15 | T1 | +| regressions.rs:69:33:69:33 | y | T | regressions.rs:63:18:63:19 | T2 | +| regressions.rs:70:9:70:9 | z | | regressions.rs:63:18:63: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(); } 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..40da53dca45b 100644 --- a/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected +++ b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected @@ -155,6 +155,10 @@ edges | lifetime.rs:798:9:798:12 | &val | lifetime.rs:798:2:798:12 | return ... | provenance | | | lifetime.rs:802:6:802:8 | ptr | lifetime.rs:808:23:808:25 | ptr | provenance | | | lifetime.rs:802:12:802:24 | get_pointer(...) | lifetime.rs:802:6:802:8 | ptr | provenance | | +| lifetime.rs:841:13:841:27 | ...: ... | lifetime.rs:843:12:843:14 | ptr | provenance | | +| lifetime.rs:851:6:851:8 | ptr | lifetime.rs:853:20:853:22 | ptr | provenance | | +| lifetime.rs:851:12:851:23 | &local_value | lifetime.rs:851:6:851:8 | ptr | provenance | | +| lifetime.rs:853:20:853:22 | ptr | lifetime.rs:841:13:841:27 | ...: ... | provenance | | | main.rs:18:9:18:10 | p1 [&ref] | main.rs:21:19:21:20 | p1 | provenance | | | main.rs:18:9:18:10 | p1 [&ref] | main.rs:29:19:29:20 | p1 | provenance | | | main.rs:18:14:18:29 | ...::as_ptr(...) [&ref] | main.rs:18:9:18:10 | p1 [&ref] | provenance | | @@ -330,6 +334,11 @@ nodes | lifetime.rs:802:6:802:8 | ptr | semmle.label | ptr | | lifetime.rs:802:12:802:24 | get_pointer(...) | semmle.label | get_pointer(...) | | lifetime.rs:808:23:808:25 | ptr | semmle.label | ptr | +| lifetime.rs:841:13:841:27 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:843:12:843:14 | ptr | semmle.label | ptr | +| lifetime.rs:851:6:851:8 | ptr | semmle.label | ptr | +| lifetime.rs:851:12:851:23 | &local_value | semmle.label | &local_value | +| lifetime.rs:853:20:853:22 | ptr | semmle.label | ptr | | main.rs:18:9:18:10 | p1 [&ref] | semmle.label | p1 [&ref] | | main.rs:18:14:18:29 | ...::as_ptr(...) [&ref] | semmle.label | ...::as_ptr(...) [&ref] | | main.rs:18:26:18:28 | &b1 | semmle.label | &b1 |