Merge pull request #19995 from hvitved/rust/disambiguate-assoc-function-calls

Rust: Disambiguate associated function calls
This commit is contained in:
Tom Hvitved
2025-07-10 19:38:06 +02:00
committed by GitHub
22 changed files with 1036 additions and 690 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Call resolution for calls to associated functions has been improved, so it now disambiguates the targets based on type information at the call sites (either type information about the arguments or about the expected return types).

View File

@@ -13,6 +13,7 @@ private import codeql.rust.elements.Resolvable
*/
module Impl {
private import rust
private import codeql.rust.internal.TypeInference as TypeInference
pragma[nomagic]
Resolvable getCallResolvable(CallExprBase call) {
@@ -27,7 +28,7 @@ module Impl {
*/
class CallExprBase extends Generated::CallExprBase {
/** Gets the static target of this call, if any. */
Callable getStaticTarget() { none() } // overridden by subclasses, but cannot be made abstract
final Function getStaticTarget() { result = TypeInference::resolveCallTarget(this) }
override Expr getArg(int index) { result = this.getArgList().getArg(index) }
}

View File

@@ -14,7 +14,6 @@ private import codeql.rust.elements.PathExpr
module Impl {
private import rust
private import codeql.rust.internal.PathResolution as PathResolution
private import codeql.rust.internal.TypeInference as TypeInference
pragma[nomagic]
Path getFunctionPath(CallExpr ce) { result = ce.getFunction().(PathExpr).getPath() }
@@ -37,15 +36,6 @@ module Impl {
class CallExpr extends Generated::CallExpr {
override string toStringImpl() { result = this.getFunction().toAbbreviatedString() + "(...)" }
override Callable getStaticTarget() {
// If this call is to a trait method, e.g., `Trait::foo(bar)`, then check
// if type inference can resolve it to the correct trait implementation.
result = TypeInference::resolveMethodCallTarget(this)
or
not exists(TypeInference::resolveMethodCallTarget(this)) and
result = getResolvedFunction(this)
}
/** Gets the struct that this call resolves to, if any. */
Struct getStruct() { result = getResolvedFunction(this) }

View File

@@ -40,6 +40,9 @@ module Impl {
/** Gets the trait targeted by this call, if any. */
abstract Trait getTrait();
/** Holds if this call targets a trait. */
predicate hasTrait() { exists(this.getTrait()) }
/** Gets the name of the method called if this call is a method call. */
abstract string getMethodName();
@@ -59,12 +62,7 @@ module Impl {
Expr getReceiver() { result = this.getArgument(TSelfArgumentPosition()) }
/** Gets the static target of this call, if any. */
Function getStaticTarget() {
result = TypeInference::resolveMethodCallTarget(this)
or
not exists(TypeInference::resolveMethodCallTarget(this)) and
result = this.(CallExpr).getStaticTarget()
}
Function getStaticTarget() { result = TypeInference::resolveCallTarget(this) }
/** Gets a runtime target of this call, if any. */
pragma[nomagic]
@@ -78,23 +76,44 @@ module Impl {
}
}
/** Holds if the call expression dispatches to a method. */
private predicate callIsMethodCall(CallExpr call, Path qualifier, string methodName) {
exists(Path path, Function f |
path = call.getFunction().(PathExpr).getPath() and
f = resolvePath(path) and
f.getParamList().hasSelfParam() and
qualifier = path.getQualifier() and
path.getSegment().getIdentifier().getText() = methodName
private predicate callHasQualifier(CallExpr call, Path path, Path qualifier) {
path = call.getFunction().(PathExpr).getPath() and
qualifier = path.getQualifier()
}
private predicate callHasTraitQualifier(CallExpr call, Trait qualifier) {
exists(RelevantPath qualifierPath |
callHasQualifier(call, _, qualifierPath) and
qualifier = resolvePath(qualifierPath) and
// When the qualifier is `Self` and resolves to a trait, it's inside a
// trait method's default implementation. This is not a dispatch whose
// target is inferred from the type of the receiver, but should always
// resolve to the function in the trait block as path resolution does.
not qualifierPath.isUnqualified("Self")
)
}
private class CallExprCall extends Call instanceof CallExpr {
CallExprCall() { not callIsMethodCall(this, _, _) }
/** Holds if the call expression dispatches to a method. */
private predicate callIsMethodCall(
CallExpr call, Path qualifier, string methodName, boolean selfIsRef
) {
exists(Path path, Function f |
callHasQualifier(call, path, qualifier) and
f = resolvePath(path) and
path.getSegment().getIdentifier().getText() = methodName and
exists(SelfParam self |
self = f.getParamList().getSelfParam() and
if self.isRef() then selfIsRef = true else selfIsRef = false
)
)
}
class CallExprCall extends Call instanceof CallExpr {
CallExprCall() { not callIsMethodCall(this, _, _, _) }
override string getMethodName() { none() }
override Trait getTrait() { none() }
override Trait getTrait() { callHasTraitQualifier(this, result) }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) { none() }
@@ -103,22 +122,23 @@ module Impl {
}
}
private class CallExprMethodCall extends Call instanceof CallExpr {
class CallExprMethodCall extends Call instanceof CallExpr {
Path qualifier;
string methodName;
boolean selfIsRef;
CallExprMethodCall() { callIsMethodCall(this, qualifier, methodName) }
CallExprMethodCall() { callIsMethodCall(this, qualifier, methodName, selfIsRef) }
/**
* Holds if this call must have an explicit borrow for the `self` argument,
* because the corresponding parameter is `&self`. Explicit borrows are not
* needed when using method call syntax.
*/
predicate hasExplicitSelfBorrow() { selfIsRef = true }
override string getMethodName() { result = methodName }
override Trait getTrait() {
result = resolvePath(qualifier) and
// When the qualifier is `Self` and resolves to a trait, it's inside a
// trait method's default implementation. This is not a dispatch whose
// target is inferred from the type of the receiver, but should always
// resolve to the function in the trait block as path resolution does.
qualifier.toString() != "Self"
}
override Trait getTrait() { callHasTraitQualifier(this, result) }
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) { none() }

View File

@@ -6,8 +6,6 @@
private import rust
private import codeql.rust.elements.internal.generated.MethodCallExpr
private import codeql.rust.internal.PathResolution
private import codeql.rust.internal.TypeInference
/**
* INTERNAL: This module contains the customizable definition of `MethodCallExpr` and should not
@@ -23,8 +21,6 @@ module Impl {
* ```
*/
class MethodCallExpr extends Generated::MethodCallExpr {
override Function getStaticTarget() { result = resolveMethodCallTarget(this) }
private string toStringPart(int index) {
index = 0 and
result = this.getReceiver().toAbbreviatedString()

View File

@@ -425,6 +425,8 @@ final class TraitTypeAbstraction extends TypeAbstraction, Trait {
result.(TypeParamTypeParameter).getTypeParam() = this.getGenericParamList().getATypeParam()
or
result.(AssociatedTypeTypeParameter).getTrait() = this
or
result.(SelfTypeParameter).getTrait() = this
}
}

View File

@@ -11,6 +11,7 @@ private import codeql.rust.frameworks.stdlib.Stdlib
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
private import codeql.rust.elements.Call
private import codeql.rust.elements.internal.CallImpl::Impl as CallImpl
private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl
class Type = T::Type;
@@ -522,6 +523,15 @@ private Type inferPathExprType(PathExpr pe, TypePath path) {
)
}
/** Gets the explicit type qualifier of the call `ce`, if any. */
private Type getTypeQualifier(CallExpr ce, TypePath path) {
exists(PathExpr pe, TypeMention tm |
pe = ce.getFunction() and
tm = pe.getPath().getQualifier() and
result = tm.resolveTypeAt(path)
)
}
/**
* A matching configuration for resolving types of call expressions
* like `foo::bar(baz)` and `foo.bar(baz)`.
@@ -724,8 +734,6 @@ private module CallExprBaseMatchingInput implements MatchingInputSig {
}
}
private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl
final class Access extends Call {
pragma[nomagic]
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) {
@@ -761,17 +769,13 @@ private module CallExprBaseMatchingInput implements MatchingInputSig {
or
// The `Self` type is supplied explicitly as a type qualifier, e.g. `Foo::<Bar>::baz()`
apos = TArgumentAccessPosition(CallImpl::TSelfArgumentPosition(), false, false) and
exists(PathExpr pe, TypeMention tm |
pe = this.(CallExpr).getFunction() and
tm = pe.getPath().getQualifier() and
result = tm.resolveTypeAt(path)
)
result = getTypeQualifier(this, path)
}
Declaration getTarget() {
result = resolveMethodCallTarget(this) // mutual recursion; resolving method calls requires resolving types and vice versa
or
result = CallExprImpl::getResolvedFunction(this)
result = resolveFunctionCallTarget(this) // potential mutual recursion; resolving some associated function calls requires resolving types
}
}
@@ -1220,15 +1224,28 @@ private Type inferForLoopExprType(AstNode n, TypePath path) {
)
}
pragma[nomagic]
private Type inferCastExprType(CastExpr ce, TypePath path) {
result = ce.getTypeRepr().(TypeMention).resolveTypeAt(path)
}
final class MethodCall extends Call {
MethodCall() { exists(this.getReceiver()) }
private Type getReceiverTypeAt(TypePath path) {
result = inferType(super.getReceiver(), path)
or
result = getTypeQualifier(this, path)
}
/** Gets the type of the receiver of the method call at `path`. */
Type getTypeAt(TypePath path) {
if this.receiverImplicitlyBorrowed()
if
this.receiverImplicitlyBorrowed() or
this.(CallImpl::CallExprMethodCall).hasExplicitSelfBorrow()
then
exists(TypePath path0, Type t0 |
t0 = inferType(super.getReceiver(), path0) and
t0 = this.getReceiverTypeAt(path0) and
(
path0.isCons(TRefTypeParameter(), path)
or
@@ -1256,7 +1273,7 @@ final class MethodCall extends Call {
t0.(StructType).asItemNode() instanceof StringStruct and
result.(StructType).asItemNode() instanceof Builtins::Str
)
else result = inferType(super.getReceiver(), path)
else result = this.getReceiverTypeAt(path)
}
}
@@ -1349,8 +1366,6 @@ private predicate implSiblingCandidate(
// contains the same `impl` block so considering both would give spurious
// siblings).
not exists(impl.getAttributeMacroExpansion()) and
// We use this for resolving methods, so exclude traits that do not have methods.
exists(Function f | f = trait.getASuccessor(_) and f.getParamList().hasSelfParam()) and
selfTy = impl.getSelfTy() and
rootType = selfTy.resolveType()
}
@@ -1385,42 +1400,49 @@ private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
pragma[nomagic]
private predicate implHasSibling(Impl impl, Trait trait) { implSiblings(trait, impl, _) }
pragma[nomagic]
private predicate functionTypeAtPath(Function f, int pos, TypePath path, Type type) {
exists(TypeMention tm | type = tm.resolveTypeAt(path) |
tm = f.getParam(pos).getTypeRepr()
or
pos = -1 and
tm = f.getRetType().getTypeRepr()
)
}
/**
* Holds if a type parameter of `trait` occurs in the method with the name
* `methodName` at the `pos`th parameter at `path`.
* Holds if type parameter `tp` of `trait` occurs in the function with the name
* `functionName` at the `pos`th parameter at `path`.
*
* The special position `-1` refers to the return type of the function, which
* is sometimes needed to disambiguate associated function calls like
* `Default::default()` (in this case, `tp` is the special `Self` type parameter).
*/
bindingset[trait]
pragma[inline_late]
private predicate traitTypeParameterOccurrence(
TraitItemNode trait, string methodName, int pos, TypePath path
TraitItemNode trait, Function f, string functionName, int pos, TypePath path, TypeParameter tp
) {
exists(Function f | f = trait.getASuccessor(methodName) |
f.getParam(pos).getTypeRepr().(TypeMention).resolveTypeAt(path) =
trait.(TraitTypeAbstraction).getATypeParameter()
)
}
bindingset[f, pos, path]
pragma[inline_late]
private predicate methodTypeAtPath(Function f, int pos, TypePath path, Type type) {
f.getParam(pos).getTypeRepr().(TypeMention).resolveTypeAt(path) = type
f = trait.getAssocItem(functionName) and
functionTypeAtPath(f, pos, path, tp) and
tp = trait.(TraitTypeAbstraction).getATypeParameter()
}
/**
* Holds if resolving the method `f` in `impl` with the name `methodName`
* Holds if resolving the function `f` in `impl` with the name `functionName`
* requires inspecting the types of applied _arguments_ in order to determine
* whether it is the correct resolution.
*/
pragma[nomagic]
private predicate methodResolutionDependsOnArgument(
Impl impl, string methodName, Function f, int pos, TypePath path, Type type
private predicate functionResolutionDependsOnArgument(
ImplItemNode impl, string functionName, Function f, int pos, TypePath path, Type type
) {
/*
* As seen in the example below, when an implementation has a sibling for a
* trait we find occurrences of a type parameter of the trait in a method
* trait we find occurrences of a type parameter of the trait in a function
* signature in the trait. We then find the type given in the implementation
* at the same position, which is a position that might disambiguate the
* method from its siblings.
* function from its siblings.
*
* ```rust
* trait MyTrait<T> {
@@ -1442,9 +1464,10 @@ private predicate methodResolutionDependsOnArgument(
exists(TraitItemNode trait |
implHasSibling(impl, trait) and
traitTypeParameterOccurrence(trait, methodName, pos, path) and
methodTypeAtPath(getMethodSuccessor(impl, methodName), pos, path, type) and
f = getMethodSuccessor(impl, methodName)
traitTypeParameterOccurrence(trait, _, functionName, pos, path, _) and
functionTypeAtPath(f, pos, path, type) and
f = impl.getAssocItem(functionName) and
pos >= 0
)
}
@@ -1484,11 +1507,12 @@ private Function getMethodFromImpl(MethodCall mc) {
name = mc.getMethodName() and
result = getMethodSuccessor(impl, name)
|
not methodResolutionDependsOnArgument(impl, _, _, _, _, _)
not functionResolutionDependsOnArgument(impl, name, _, _, _, _)
or
exists(int pos, TypePath path, Type type |
methodResolutionDependsOnArgument(impl, name, result, pos, path, type) and
inferType(mc.getPositionalArgument(pos), path) = type
functionResolutionDependsOnArgument(impl, name, result, pos, pragma[only_bind_into](path),
type) and
inferType(mc.getPositionalArgument(pos), pragma[only_bind_into](path)) = type
)
)
}
@@ -1499,6 +1523,162 @@ private Function getTraitMethod(ImplTraitReturnType trait, string name) {
result = getMethodSuccessor(trait.getImplTraitTypeRepr(), name)
}
pragma[nomagic]
private Function resolveMethodCallTarget(MethodCall mc) {
// The method comes from an `impl` block targeting the type of the receiver.
result = getMethodFromImpl(mc)
or
// The type of the receiver is a type parameter and the method comes from a
// trait bound on the type parameter.
result = getTypeParameterMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName())
or
// The type of the receiver is an `impl Trait` type.
result = getTraitMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName())
}
pragma[nomagic]
private predicate assocFuncResolutionDependsOnArgument(Function f, Impl impl, int pos) {
functionResolutionDependsOnArgument(impl, _, f, pos, _, _) and
not f.getParamList().hasSelfParam()
}
private class FunctionCallExpr extends CallImpl::CallExprCall {
ItemNode getResolvedFunction() { result = CallExprImpl::getResolvedFunction(this) }
/**
* Holds if the target of this call is ambigous, and type information is required
* to disambiguate.
*/
predicate isAmbigous() {
this.hasTrait()
or
assocFuncResolutionDependsOnArgument(this.getResolvedFunction(), _, _)
}
/**
* Gets a target candidate of this ambigous call, which belongs to `impl`.
*
* In order for the candidate to be a match, the argument type at `pos` must be
* checked against the type of the function at the same position.
*
* `resolved` is the corresponding function resolved through path resolution.
*/
pragma[nomagic]
Function getAnAmbigousCandidate(ImplItemNode impl, int pos, Function resolved) {
resolved = this.getResolvedFunction() and
(
exists(TraitItemNode trait |
trait = this.getTrait() and
result.implements(resolved) and
result = impl.getAnAssocItem()
|
assocFuncResolutionDependsOnArgument(result, impl, pos)
or
exists(TypeParameter tp | traitTypeParameterOccurrence(trait, resolved, _, pos, _, tp) |
pos >= 0
or
// We only check that the context of the call provides relevant type information
// when no argument can
not traitTypeParameterOccurrence(trait, resolved, _, any(int pos0 | pos0 >= 0), _, tp)
)
)
or
result = resolved and
assocFuncResolutionDependsOnArgument(result, impl, pos)
)
}
/**
* Same as `getAnAmbigousCandidate`, ranks the positions to be checked.
*/
Function getAnAmbigousCandidateRanked(ImplItemNode impl, int pos, Function f, int rnk) {
pos = rank[rnk + 1](int pos0 | result = this.getAnAmbigousCandidate(impl, pos0, f) | pos0)
}
}
private newtype TAmbigousAssocFunctionCallExpr =
MkAmbigousAssocFunctionCallExpr(FunctionCallExpr call, Function resolved, int pos) {
exists(call.getAnAmbigousCandidate(_, pos, resolved))
}
private class AmbigousAssocFunctionCallExpr extends MkAmbigousAssocFunctionCallExpr {
FunctionCallExpr call;
Function resolved;
int pos;
AmbigousAssocFunctionCallExpr() { this = MkAmbigousAssocFunctionCallExpr(call, resolved, pos) }
pragma[nomagic]
Type getTypeAt(TypePath path) {
result = inferType(call.(CallExpr).getArg(pos), path)
or
pos = -1 and
result = inferType(call, path)
}
string toString() { result = call.toString() }
Location getLocation() { result = call.getLocation() }
}
private module AmbigousAssocFuncIsInstantiationOfInput implements
IsInstantiationOfInputSig<AmbigousAssocFunctionCallExpr>
{
pragma[nomagic]
predicate potentialInstantiationOf(
AmbigousAssocFunctionCallExpr ce, TypeAbstraction impl, TypeMention constraint
) {
exists(FunctionCallExpr call, Function resolved, Function cand, int pos |
ce = MkAmbigousAssocFunctionCallExpr(call, resolved, pos) and
cand = call.getAnAmbigousCandidate(impl, pos, resolved)
|
constraint = cand.getParam(pos).getTypeRepr()
or
pos = -1 and
constraint = cand.getRetType().getTypeRepr()
)
}
}
/**
* Gets the target of `call`, where resolution does not rely on type inference.
*/
pragma[nomagic]
private ItemNode resolveUnambigousFunctionCallTarget(FunctionCallExpr call) {
result = call.getResolvedFunction() and
not call.isAmbigous()
}
pragma[nomagic]
private Function resolveAmbigousFunctionCallTargetFromIndex(FunctionCallExpr call, int index) {
exists(Impl impl, int pos, Function resolved |
IsInstantiationOf<AmbigousAssocFunctionCallExpr, AmbigousAssocFuncIsInstantiationOfInput>::isInstantiationOf(MkAmbigousAssocFunctionCallExpr(call,
resolved, pos), impl, _) and
result = call.getAnAmbigousCandidateRanked(impl, pos, resolved, index)
|
index = 0
or
result = resolveAmbigousFunctionCallTargetFromIndex(call, index - 1)
)
}
/**
* Gets the target of `call`, where resolution relies on type inference.
*/
pragma[nomagic]
private Function resolveAmbigousFunctionCallTarget(FunctionCallExpr call) {
result =
resolveAmbigousFunctionCallTargetFromIndex(call,
max(int index | result = call.getAnAmbigousCandidateRanked(_, _, _, index)))
}
pragma[inline]
private ItemNode resolveFunctionCallTarget(FunctionCallExpr call) {
result = resolveUnambigousFunctionCallTarget(call)
or
result = resolveAmbigousFunctionCallTarget(call)
}
cached
private module Cached {
private import codeql.rust.internal.CachedStages
@@ -1527,18 +1707,12 @@ private module Cached {
)
}
/** Gets a method that the method call `mc` resolves to, if any. */
/** Gets a function that `call` resolves to, if any. */
cached
Function resolveMethodCallTarget(MethodCall mc) {
// The method comes from an `impl` block targeting the type of the receiver.
result = getMethodFromImpl(mc)
Function resolveCallTarget(Call call) {
result = resolveMethodCallTarget(call)
or
// The type of the receiver is a type parameter and the method comes from a
// trait bound on the type parameter.
result = getTypeParameterMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName())
or
// The type of the receiver is an `impl Trait` type.
result = getTraitMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName())
result = resolveFunctionCallTarget(call)
}
pragma[inline]
@@ -1660,6 +1834,8 @@ private module Cached {
result = inferIndexExprType(n, path)
or
result = inferForLoopExprType(n, path)
or
result = inferCastExprType(n, path)
}
}
@@ -1685,9 +1861,9 @@ private module Debug {
result = inferType(n, path)
}
Function debugResolveMethodCallTarget(Call mce) {
mce = getRelevantLocatable() and
result = resolveMethodCallTarget(mce)
Function debugResolveCallTarget(Call c) {
c = getRelevantLocatable() and
result = resolveCallTarget(c)
}
predicate debugInferImplicitSelfType(SelfParam self, TypePath path, Type t) {
@@ -1705,6 +1881,11 @@ private module Debug {
tm.resolveTypeAt(path) = type
}
Type debugInferAnnotatedType(AstNode n, TypePath path) {
n = getRelevantLocatable() and
result = inferAnnotatedType(n, path)
}
pragma[nomagic]
private int countTypesAtPath(AstNode n, TypePath path, Type t) {
t = inferType(n, path) and

View File

@@ -1,19 +1,10 @@
multipleCallTargets
| proc_macro.rs:6:18:6:61 | ...::from(...) |
| proc_macro.rs:7:15:7:58 | ...::from(...) |
| proc_macro.rs:15:5:17:5 | ...::new(...) |
| proc_macro.rs:16:12:16:16 | ...::to_tokens(...) |
| proc_macro.rs:22:15:22:58 | ...::from(...) |
| proc_macro.rs:25:5:28:5 | ...::new(...) |
| proc_macro.rs:26:10:26:12 | ...::to_tokens(...) |
| proc_macro.rs:27:10:27:16 | ...::to_tokens(...) |
| proc_macro.rs:38:15:38:64 | ...::from(...) |
| proc_macro.rs:41:5:49:5 | ...::new(...) |
| proc_macro.rs:41:5:49:5 | ...::new(...) |
| proc_macro.rs:41:5:49:5 | ...::new(...) |
| proc_macro.rs:41:5:49:5 | ...::new(...) |
| proc_macro.rs:42:16:42:26 | ...::to_tokens(...) |
| proc_macro.rs:44:27:44:30 | ...::to_tokens(...) |
| proc_macro.rs:46:18:46:28 | ...::to_tokens(...) |
multiplePathResolutions
| macro_expansion.rs:1:5:1:14 | proc_macro |

View File

@@ -117,6 +117,11 @@ edges
| main.rs:228:11:228:14 | [post] self [&ref, MyInt] | main.rs:227:19:227:27 | SelfParam [Return] [&ref, MyInt] | provenance | |
| main.rs:228:25:228:27 | rhs [MyInt] | main.rs:228:25:228:33 | rhs.value | provenance | |
| main.rs:228:25:228:33 | rhs.value | main.rs:228:10:228:14 | [post] * ... [MyInt] | provenance | |
| main.rs:235:14:235:18 | SelfParam [&ref, MyInt] | main.rs:236:12:236:15 | self [&ref, MyInt] | provenance | |
| main.rs:236:9:236:22 | &... [&ref] | main.rs:235:38:237:5 | { ... } [&ref] | provenance | |
| main.rs:236:10:236:22 | ... .value | main.rs:236:9:236:22 | &... [&ref] | provenance | |
| main.rs:236:11:236:15 | * ... [MyInt] | main.rs:236:10:236:22 | ... .value | provenance | |
| main.rs:236:12:236:15 | self [&ref, MyInt] | main.rs:236:11:236:15 | * ... [MyInt] | provenance | |
| main.rs:242:9:242:9 | a [MyInt] | main.rs:244:13:244:13 | a [MyInt] | provenance | |
| main.rs:242:13:242:38 | MyInt {...} [MyInt] | main.rs:242:9:242:9 | a [MyInt] | provenance | |
| main.rs:242:28:242:36 | source(...) | main.rs:242:13:242:38 | MyInt {...} [MyInt] | provenance | |
@@ -140,6 +145,15 @@ edges
| main.rs:261:35:261:35 | b [MyInt] | main.rs:227:30:227:39 | ...: MyInt [MyInt] | provenance | |
| main.rs:261:35:261:35 | b [MyInt] | main.rs:261:27:261:32 | [post] &mut a [&ref, MyInt] | provenance | |
| main.rs:262:10:262:10 | a [MyInt] | main.rs:262:10:262:16 | a.value | provenance | |
| main.rs:270:9:270:9 | a [MyInt] | main.rs:272:28:272:28 | a [MyInt] | provenance | |
| main.rs:270:13:270:39 | MyInt {...} [MyInt] | main.rs:270:9:270:9 | a [MyInt] | provenance | |
| main.rs:270:28:270:37 | source(...) | main.rs:270:13:270:39 | MyInt {...} [MyInt] | provenance | |
| main.rs:272:9:272:9 | c | main.rs:273:10:273:10 | c | provenance | |
| main.rs:272:13:272:29 | * ... | main.rs:272:9:272:9 | c | provenance | |
| main.rs:272:14:272:29 | ...::deref(...) [&ref] | main.rs:272:13:272:29 | * ... | provenance | |
| main.rs:272:27:272:28 | &a [&ref, MyInt] | main.rs:235:14:235:18 | SelfParam [&ref, MyInt] | provenance | |
| main.rs:272:27:272:28 | &a [&ref, MyInt] | main.rs:272:14:272:29 | ...::deref(...) [&ref] | provenance | |
| main.rs:272:28:272:28 | a [MyInt] | main.rs:272:27:272:28 | &a [&ref, MyInt] | provenance | |
| main.rs:289:18:289:21 | SelfParam [MyInt] | main.rs:289:48:291:5 | { ... } [MyInt] | provenance | |
| main.rs:293:26:293:37 | ...: MyInt [MyInt] | main.rs:293:49:295:5 | { ... } [MyInt] | provenance | |
| main.rs:299:9:299:9 | a [MyInt] | main.rs:301:50:301:50 | a [MyInt] | provenance | |
@@ -297,6 +311,12 @@ nodes
| main.rs:228:11:228:14 | [post] self [&ref, MyInt] | semmle.label | [post] self [&ref, MyInt] |
| main.rs:228:25:228:27 | rhs [MyInt] | semmle.label | rhs [MyInt] |
| main.rs:228:25:228:33 | rhs.value | semmle.label | rhs.value |
| main.rs:235:14:235:18 | SelfParam [&ref, MyInt] | semmle.label | SelfParam [&ref, MyInt] |
| main.rs:235:38:237:5 | { ... } [&ref] | semmle.label | { ... } [&ref] |
| main.rs:236:9:236:22 | &... [&ref] | semmle.label | &... [&ref] |
| main.rs:236:10:236:22 | ... .value | semmle.label | ... .value |
| main.rs:236:11:236:15 | * ... [MyInt] | semmle.label | * ... [MyInt] |
| main.rs:236:12:236:15 | self [&ref, MyInt] | semmle.label | self [&ref, MyInt] |
| main.rs:242:9:242:9 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:242:13:242:38 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] |
| main.rs:242:28:242:36 | source(...) | semmle.label | source(...) |
@@ -320,6 +340,15 @@ nodes
| main.rs:261:35:261:35 | b [MyInt] | semmle.label | b [MyInt] |
| main.rs:262:10:262:10 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:262:10:262:16 | a.value | semmle.label | a.value |
| main.rs:270:9:270:9 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:270:13:270:39 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] |
| main.rs:270:28:270:37 | source(...) | semmle.label | source(...) |
| main.rs:272:9:272:9 | c | semmle.label | c |
| main.rs:272:13:272:29 | * ... | semmle.label | * ... |
| main.rs:272:14:272:29 | ...::deref(...) [&ref] | semmle.label | ...::deref(...) [&ref] |
| main.rs:272:27:272:28 | &a [&ref, MyInt] | semmle.label | &a [&ref, MyInt] |
| main.rs:272:28:272:28 | a [MyInt] | semmle.label | a [MyInt] |
| main.rs:273:10:273:10 | c | semmle.label | c |
| main.rs:289:18:289:21 | SelfParam [MyInt] | semmle.label | SelfParam [MyInt] |
| main.rs:289:48:291:5 | { ... } [MyInt] | semmle.label | { ... } [MyInt] |
| main.rs:293:26:293:37 | ...: MyInt [MyInt] | semmle.label | ...: MyInt [MyInt] |
@@ -367,6 +396,7 @@ subpaths
| main.rs:244:13:244:13 | a [MyInt] | main.rs:220:12:220:15 | SelfParam [MyInt] | main.rs:220:42:223:5 | { ... } [MyInt] | main.rs:244:13:244:17 | ... + ... [MyInt] |
| main.rs:252:9:252:9 | a [MyInt] | main.rs:220:12:220:15 | SelfParam [MyInt] | main.rs:220:42:223:5 | { ... } [MyInt] | main.rs:254:13:254:20 | a.add(...) [MyInt] |
| main.rs:261:35:261:35 | b [MyInt] | main.rs:227:30:227:39 | ...: MyInt [MyInt] | main.rs:227:19:227:27 | SelfParam [Return] [&ref, MyInt] | main.rs:261:27:261:32 | [post] &mut a [&ref, MyInt] |
| main.rs:272:27:272:28 | &a [&ref, MyInt] | main.rs:235:14:235:18 | SelfParam [&ref, MyInt] | main.rs:235:38:237:5 | { ... } [&ref] | main.rs:272:14:272:29 | ...::deref(...) [&ref] |
| main.rs:301:50:301:50 | a [MyInt] | main.rs:289:18:289:21 | SelfParam [MyInt] | main.rs:289:48:291:5 | { ... } [MyInt] | main.rs:301:30:301:54 | ...::take_self(...) [MyInt] |
| main.rs:306:55:306:55 | b [MyInt] | main.rs:293:26:293:37 | ...: MyInt [MyInt] | main.rs:293:49:295:5 | { ... } [MyInt] | main.rs:306:30:306:56 | ...::take_second(...) [MyInt] |
testFailures
@@ -393,6 +423,7 @@ testFailures
| main.rs:245:10:245:16 | c.value | main.rs:242:28:242:36 | source(...) | main.rs:245:10:245:16 | c.value | $@ | main.rs:242:28:242:36 | source(...) | source(...) |
| main.rs:255:10:255:16 | d.value | main.rs:252:28:252:36 | source(...) | main.rs:255:10:255:16 | d.value | $@ | main.rs:252:28:252:36 | source(...) | source(...) |
| main.rs:262:10:262:16 | a.value | main.rs:259:28:259:37 | source(...) | main.rs:262:10:262:16 | a.value | $@ | main.rs:259:28:259:37 | source(...) | source(...) |
| main.rs:273:10:273:10 | c | main.rs:270:28:270:37 | source(...) | main.rs:273:10:273:10 | c | $@ | main.rs:270:28:270:37 | source(...) | source(...) |
| main.rs:302:10:302:10 | c | main.rs:299:28:299:36 | source(...) | main.rs:302:10:302:10 | c | $@ | main.rs:299:28:299:36 | source(...) | source(...) |
| main.rs:307:10:307:10 | c | main.rs:305:28:305:37 | source(...) | main.rs:307:10:307:10 | c | $@ | main.rs:305:28:305:37 | source(...) | source(...) |
| main.rs:317:10:317:10 | a | main.rs:316:13:316:21 | source(...) | main.rs:317:10:317:10 | a | $@ | main.rs:316:13:316:21 | source(...) | source(...) |

View File

@@ -270,7 +270,7 @@ fn test_operator_overloading() {
let a = MyInt { value: source(27) };
// The line below is what the prefix `*` desugars to.
let c = *Deref::deref(&a);
sink(c); // $ MISSING: hasValueFlow=27
sink(c); // $ hasValueFlow=27
let a = MyInt { value: source(28) };
let c = *a;

View File

@@ -77,6 +77,7 @@
| main.rs:266:5:266:10 | ... *= ... | main.rs:227:5:229:5 | fn mul_assign |
| main.rs:267:5:267:17 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:270:28:270:37 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:272:14:272:29 | ...::deref(...) | main.rs:235:5:237:5 | fn deref |
| main.rs:273:5:273:11 | sink(...) | main.rs:5:1:7:1 | fn sink |
| main.rs:275:28:275:37 | source(...) | main.rs:1:1:3:1 | fn source |
| main.rs:276:13:276:14 | * ... | main.rs:235:5:237:5 | fn deref |

View File

@@ -1,3 +0,0 @@
multipleCallTargets
| main.rs:532:10:532:21 | ...::from(...) |
| main.rs:538:10:538:21 | ...::from(...) |

View File

@@ -529,7 +529,7 @@ fn conversions() {
sink(a as i64); // $ hasTaintFlow=50
sink(a.into()); // $ MISSING: hasValueFlow=50
sink(i64::from(a)); // $ hasTaintFlow=50
sink(i64::from(a)); // $ MISSING: hasTaintFlow=50 -- we cannot resolve the `impl<T> From<T> for T` implementation
let b: i32 = source(51) as i32;

View File

@@ -32,7 +32,6 @@ multipleCallTargets
| test.rs:737:30:737:43 | ...::_print(...) |
| test.rs:752:14:752:43 | ...::_print(...) |
| test.rs:766:14:766:34 | ...::_print(...) |
| test.rs:777:23:777:80 | ...::try_from(...) |
| test.rs:806:50:806:66 | ...::from(...) |
| test.rs:806:50:806:66 | ...::from(...) |
| test.rs:808:14:808:31 | ...::_print(...) |
@@ -74,14 +73,11 @@ multipleCallTargets
| test.rs:883:14:883:29 | ...::_print(...) |
| test.rs:885:27:885:36 | ...::_print(...) |
| test.rs:886:28:886:41 | ...::_print(...) |
| test_futures_io.rs:25:23:25:80 | ...::try_from(...) |
| test_futures_io.rs:35:26:35:63 | pinned.poll_read(...) |
| test_futures_io.rs:62:22:62:50 | pinned.poll_fill_buf(...) |
| test_futures_io.rs:69:23:69:67 | ... .poll_fill_buf(...) |
| test_futures_io.rs:93:26:93:63 | pinned.poll_read(...) |
| test_futures_io.rs:116:22:116:50 | pinned.poll_fill_buf(...) |
| test_futures_io.rs:145:26:145:49 | ...::with_capacity(...) |
| web_frameworks.rs:40:5:40:26 | ...::write_str(...) |
| web_frameworks.rs:40:5:40:26 | ...::write_str(...) |
| web_frameworks.rs:101:14:101:23 | a.as_str() |
| web_frameworks.rs:102:14:102:25 | a.as_bytes() |

View File

@@ -42,8 +42,8 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> {
{
// using the `AsyncReadExt::read` extension method (higher-level)
let mut buffer1 = [0u8; 64];
let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader, &mut buffer1).await?;
sink(&buffer1[..bytes_read1]); // $ hasTaintFlow=url
let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader, &mut buffer1).await?; // we cannot resolve the `read` call, which comes from `impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}` in `async_read_ext.rs`
sink(&buffer1[..bytes_read1]); // $ MISSING: hasTaintFlow=url
let mut buffer2 = [0u8; 64];
let bytes_read2 = reader.read(&mut buffer2).await?; // we cannot resolve the `read` call, which comes from `impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}` in `async_read_ext.rs`
@@ -100,8 +100,8 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> {
{
// using the `AsyncReadExt::read` extension method (higher-level)
let mut buffer1 = [0u8; 64];
let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader2, &mut buffer1).await?;
sink(&buffer1[..bytes_read1]); // $ hasTaintFlow=url
let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader2, &mut buffer1).await?; // we cannot resolve the `read` call, which comes from `impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}` in `async_read_ext.rs`
sink(&buffer1[..bytes_read1]); // $ MISSING: hasTaintFlow=url
let mut buffer2 = [0u8; 64];
let bytes_read2 = reader2.read(&mut buffer2).await?; // we cannot resolve the `read` call, which comes from `impl<R: AsyncRead + ?Sized> AsyncReadExt for R {}` in `async_read_ext.rs`

View File

@@ -1,9 +1,6 @@
multipleCallTargets
| main.rs:118:9:118:11 | f(...) |
| proc_macro.rs:6:16:6:59 | ...::from(...) |
| proc_macro.rs:7:19:7:62 | ...::from(...) |
| proc_macro.rs:9:5:11:5 | ...::new(...) |
| proc_macro.rs:10:10:10:12 | ...::to_tokens(...) |
multiplePathResolutions
| main.rs:626:3:626:12 | proc_macro |
| main.rs:632:7:632:16 | proc_macro |

View File

@@ -1,9 +1,8 @@
multipleCallTargets
| dereference.rs:61:15:61:24 | e1.deref() |
| main.rs:2096:13:2096:31 | ...::from(...) |
| main.rs:2097:13:2097:31 | ...::from(...) |
| main.rs:2098:13:2098:31 | ...::from(...) |
| main.rs:2104:13:2104:31 | ...::from(...) |
| main.rs:2105:13:2105:31 | ...::from(...) |
| main.rs:2106:13:2106:31 | ...::from(...) |
| main.rs:2142:21:2142:43 | ...::from(...) |
| main.rs:2186:13:2186:31 | ...::from(...) |
| main.rs:2187:13:2187:31 | ...::from(...) |
| main.rs:2188:13:2188:31 | ...::from(...) |
| main.rs:2194:13:2194:31 | ...::from(...) |
| main.rs:2195:13:2195:31 | ...::from(...) |
| main.rs:2196:13:2196:31 | ...::from(...) |

View File

@@ -2042,6 +2042,80 @@ mod method_determined_by_argument_type {
}
}
trait MyFrom<T> {
// MyFrom::my_from
fn my_from(value: T) -> Self;
}
impl MyFrom<i64> for i64 {
// MyFrom<i64>::my_from
fn my_from(value: i64) -> Self {
value
}
}
impl MyFrom<bool> for i64 {
// MyFrom<bool>::my_from
fn my_from(value: bool) -> Self {
if value { 1 } else { 0 }
}
}
trait MyFrom2<T> {
// MyFrom2::my_from2
fn my_from2(value: T, x: Self) -> ();
}
impl MyFrom2<i64> for i64 {
// MyFrom2<i64>::my_from2
fn my_from2(value: i64, _: Self) -> () {
value;
}
}
impl MyFrom2<bool> for i64 {
// MyFrom2<bool>::my_from2
fn my_from2(value: bool, _: Self) -> () {
if value {
1
} else {
0
};
}
}
trait MySelfTrait {
// MySelfTrait::f1
fn f1(x: Self) -> i64;
// MySelfTrait::f2
fn f2(x: Self) -> Self;
}
impl MySelfTrait for i64 {
// MySelfTrait<i64>::f1
fn f1(x: Self) -> i64 {
x + 1
}
// MySelfTrait<i64>::f2
fn f2(x: Self) -> Self {
x + 1
}
}
impl MySelfTrait for bool {
// MySelfTrait<bool>::f1
fn f1(x: Self) -> i64 {
0
}
// MySelfTrait<bool>::f2
fn f2(x: Self) -> Self {
x
}
}
pub fn f() {
let x: i64 = 73;
x.my_add(5i64); // $ method=MyAdd<i64>::my_add
@@ -2051,6 +2125,22 @@ mod method_determined_by_argument_type {
S(1i64).my_add(S(2i64)); // $ method=S::my_add1
S(1i64).my_add(3i64); // $ MISSING: method=S::my_add2
S(1i64).my_add(&3i64); // $ method=S::my_add3
let x = i64::my_from(73i64); // $ method=MyFrom<i64>::my_from
let y = i64::my_from(true); // $ method=MyFrom<bool>::my_from
let z: i64 = MyFrom::my_from(73i64); // $ method=MyFrom<i64>::my_from
i64::my_from2(73i64, 0i64); // $ method=MyFrom2<i64>::my_from2
i64::my_from2(true, 0i64); // $ method=MyFrom2<bool>::my_from2
MyFrom2::my_from2(73i64, 0i64); // $ method=MyFrom2<i64>::my_from2
i64::f1(73i64); // $ method=MySelfTrait<i64>::f1
i64::f2(73i64); // $ method=MySelfTrait<i64>::f2
bool::f1(true); // $ method=MySelfTrait<bool>::f1
bool::f2(true); // $ method=MySelfTrait<bool>::f2
MySelfTrait::f1(73i64); // $ method=MySelfTrait<i64>::f1
MySelfTrait::f2(73i64); // $ method=MySelfTrait<i64>::f2
MySelfTrait::f1(true); // $ method=MySelfTrait<bool>::f1
MySelfTrait::f2(true); // $ method=MySelfTrait<bool>::f2
}
}

View File

@@ -1,9 +0,0 @@
multipleCallTargets
| src/main.rs:8:21:8:44 | ...::from(...) |
| src/main.rs:19:21:19:44 | ...::from(...) |
| src/main.rs:25:23:25:59 | ...::from(...) |
| src/main.rs:26:38:26:61 | ...::from(...) |
| src/main.rs:39:23:39:59 | ...::from(...) |
| src/main.rs:40:38:40:61 | ...::from(...) |
| src/main.rs:52:23:52:59 | ...::from(...) |
| src/main.rs:53:38:53:61 | ...::from(...) |

View File

@@ -1,11 +1,11 @@
#select
| src/main.rs:10:5:10:22 | ...::read_to_string | src/main.rs:6:11:6:19 | file_name | src/main.rs:10:5:10:22 | ...::read_to_string | This path depends on a $@. | src/main.rs:6:11:6:19 | file_name | user-provided value |
edges
| src/main.rs:6:11:6:19 | file_name | src/main.rs:8:35:8:43 | file_name | provenance | |
| src/main.rs:6:11:6:19 | file_name | src/main.rs:8:35:8:53 | file_name as String | provenance | |
| src/main.rs:8:9:8:17 | file_path | src/main.rs:10:24:10:32 | file_path | provenance | |
| src/main.rs:8:21:8:44 | ...::from(...) | src/main.rs:8:9:8:17 | file_path | provenance | |
| src/main.rs:8:35:8:43 | file_name | src/main.rs:8:21:8:44 | ...::from(...) | provenance | MaD:2 |
| src/main.rs:8:35:8:43 | file_name | src/main.rs:8:21:8:44 | ...::from(...) | provenance | MaD:2 |
| src/main.rs:8:21:8:54 | ...::from(...) | src/main.rs:8:9:8:17 | file_path | provenance | |
| src/main.rs:8:35:8:53 | file_name as String | src/main.rs:8:21:8:54 | ...::from(...) | provenance | MaD:2 |
| src/main.rs:8:35:8:53 | file_name as String | src/main.rs:8:21:8:54 | ...::from(...) | provenance | MaD:2 |
| src/main.rs:10:24:10:32 | file_path | src/main.rs:10:5:10:22 | ...::read_to_string | provenance | MaD:1 Sink:MaD:1 |
models
| 1 | Sink: std::fs::read_to_string; Argument[0]; path-injection |
@@ -13,8 +13,8 @@ models
nodes
| src/main.rs:6:11:6:19 | file_name | semmle.label | file_name |
| src/main.rs:8:9:8:17 | file_path | semmle.label | file_path |
| src/main.rs:8:21:8:44 | ...::from(...) | semmle.label | ...::from(...) |
| src/main.rs:8:35:8:43 | file_name | semmle.label | file_name |
| src/main.rs:8:21:8:54 | ...::from(...) | semmle.label | ...::from(...) |
| src/main.rs:8:35:8:53 | file_name as String | semmle.label | file_name as String |
| src/main.rs:10:5:10:22 | ...::read_to_string | semmle.label | ...::read_to_string |
| src/main.rs:10:24:10:32 | file_path | semmle.label | file_path |
subpaths

View File

@@ -5,8 +5,8 @@ use std::{fs, path::Path, path::PathBuf};
fn tainted_path_handler_bad(
Query(file_name): Query<String>, // $ Source=remote1
) -> Result<String> {
let file_path = PathBuf::from(file_name);
// BAD: This could read any file on the filesystem.
let file_path = PathBuf::from(file_name as String); // TODO: Remove `as String` when type inference handles patterns
// BAD: This could read any file on the filesystem.
fs::read_to_string(file_path).map_err(InternalServerError) // $ path-injection-sink Alert[rust/path-injection]=remote1
}