Skip to content

Commit 1e9520c

Browse files
authored
Merge pull request #19995 from hvitved/rust/disambiguate-assoc-function-calls
Rust: Disambiguate associated function calls
2 parents 6d8e2f8 + 70476c0 commit 1e9520c

File tree

22 files changed

+1033
-687
lines changed

22 files changed

+1033
-687
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* 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).

rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ private import codeql.rust.elements.Resolvable
1313
*/
1414
module Impl {
1515
private import rust
16+
private import codeql.rust.internal.TypeInference as TypeInference
1617

1718
pragma[nomagic]
1819
Resolvable getCallResolvable(CallExprBase call) {
@@ -27,7 +28,7 @@ module Impl {
2728
*/
2829
class CallExprBase extends Generated::CallExprBase {
2930
/** Gets the static target of this call, if any. */
30-
Callable getStaticTarget() { none() } // overridden by subclasses, but cannot be made abstract
31+
final Function getStaticTarget() { result = TypeInference::resolveCallTarget(this) }
3132

3233
override Expr getArg(int index) { result = this.getArgList().getArg(index) }
3334
}

rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ private import codeql.rust.elements.PathExpr
1414
module Impl {
1515
private import rust
1616
private import codeql.rust.internal.PathResolution as PathResolution
17-
private import codeql.rust.internal.TypeInference as TypeInference
1817

1918
pragma[nomagic]
2019
Path getFunctionPath(CallExpr ce) { result = ce.getFunction().(PathExpr).getPath() }
@@ -37,15 +36,6 @@ module Impl {
3736
class CallExpr extends Generated::CallExpr {
3837
override string toStringImpl() { result = this.getFunction().toAbbreviatedString() + "(...)" }
3938

40-
override Callable getStaticTarget() {
41-
// If this call is to a trait method, e.g., `Trait::foo(bar)`, then check
42-
// if type inference can resolve it to the correct trait implementation.
43-
result = TypeInference::resolveMethodCallTarget(this)
44-
or
45-
not exists(TypeInference::resolveMethodCallTarget(this)) and
46-
result = getResolvedFunction(this)
47-
}
48-
4939
/** Gets the struct that this call resolves to, if any. */
5040
Struct getStruct() { result = getResolvedFunction(this) }
5141

rust/ql/lib/codeql/rust/elements/internal/CallImpl.qll

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ module Impl {
4040
/** Gets the trait targeted by this call, if any. */
4141
abstract Trait getTrait();
4242

43+
/** Holds if this call targets a trait. */
44+
predicate hasTrait() { exists(this.getTrait()) }
45+
4346
/** Gets the name of the method called if this call is a method call. */
4447
abstract string getMethodName();
4548

@@ -59,12 +62,7 @@ module Impl {
5962
Expr getReceiver() { result = this.getArgument(TSelfArgumentPosition()) }
6063

6164
/** Gets the static target of this call, if any. */
62-
Function getStaticTarget() {
63-
result = TypeInference::resolveMethodCallTarget(this)
64-
or
65-
not exists(TypeInference::resolveMethodCallTarget(this)) and
66-
result = this.(CallExpr).getStaticTarget()
67-
}
65+
Function getStaticTarget() { result = TypeInference::resolveCallTarget(this) }
6866

6967
/** Gets a runtime target of this call, if any. */
7068
pragma[nomagic]
@@ -78,23 +76,44 @@ module Impl {
7876
}
7977
}
8078

79+
private predicate callHasQualifier(CallExpr call, Path path, Path qualifier) {
80+
path = call.getFunction().(PathExpr).getPath() and
81+
qualifier = path.getQualifier()
82+
}
83+
84+
private predicate callHasTraitQualifier(CallExpr call, Trait qualifier) {
85+
exists(RelevantPath qualifierPath |
86+
callHasQualifier(call, _, qualifierPath) and
87+
qualifier = resolvePath(qualifierPath) and
88+
// When the qualifier is `Self` and resolves to a trait, it's inside a
89+
// trait method's default implementation. This is not a dispatch whose
90+
// target is inferred from the type of the receiver, but should always
91+
// resolve to the function in the trait block as path resolution does.
92+
not qualifierPath.isUnqualified("Self")
93+
)
94+
}
95+
8196
/** Holds if the call expression dispatches to a method. */
82-
private predicate callIsMethodCall(CallExpr call, Path qualifier, string methodName) {
97+
private predicate callIsMethodCall(
98+
CallExpr call, Path qualifier, string methodName, boolean selfIsRef
99+
) {
83100
exists(Path path, Function f |
84-
path = call.getFunction().(PathExpr).getPath() and
101+
callHasQualifier(call, path, qualifier) and
85102
f = resolvePath(path) and
86-
f.getParamList().hasSelfParam() and
87-
qualifier = path.getQualifier() and
88-
path.getSegment().getIdentifier().getText() = methodName
103+
path.getSegment().getIdentifier().getText() = methodName and
104+
exists(SelfParam self |
105+
self = f.getParamList().getSelfParam() and
106+
if self.isRef() then selfIsRef = true else selfIsRef = false
107+
)
89108
)
90109
}
91110

92-
private class CallExprCall extends Call instanceof CallExpr {
93-
CallExprCall() { not callIsMethodCall(this, _, _) }
111+
class CallExprCall extends Call instanceof CallExpr {
112+
CallExprCall() { not callIsMethodCall(this, _, _, _) }
94113

95114
override string getMethodName() { none() }
96115

97-
override Trait getTrait() { none() }
116+
override Trait getTrait() { callHasTraitQualifier(this, result) }
98117

99118
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) { none() }
100119

@@ -103,22 +122,23 @@ module Impl {
103122
}
104123
}
105124

106-
private class CallExprMethodCall extends Call instanceof CallExpr {
125+
class CallExprMethodCall extends Call instanceof CallExpr {
107126
Path qualifier;
108127
string methodName;
128+
boolean selfIsRef;
129+
130+
CallExprMethodCall() { callIsMethodCall(this, qualifier, methodName, selfIsRef) }
109131

110-
CallExprMethodCall() { callIsMethodCall(this, qualifier, methodName) }
132+
/**
133+
* Holds if this call must have an explicit borrow for the `self` argument,
134+
* because the corresponding parameter is `&self`. Explicit borrows are not
135+
* needed when using method call syntax.
136+
*/
137+
predicate hasExplicitSelfBorrow() { selfIsRef = true }
111138

112139
override string getMethodName() { result = methodName }
113140

114-
override Trait getTrait() {
115-
result = resolvePath(qualifier) and
116-
// When the qualifier is `Self` and resolves to a trait, it's inside a
117-
// trait method's default implementation. This is not a dispatch whose
118-
// target is inferred from the type of the receiver, but should always
119-
// resolve to the function in the trait block as path resolution does.
120-
qualifier.toString() != "Self"
121-
}
141+
override Trait getTrait() { callHasTraitQualifier(this, result) }
122142

123143
override predicate implicitBorrowAt(ArgumentPosition pos, boolean certain) { none() }
124144

rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
private import rust
88
private import codeql.rust.elements.internal.generated.MethodCallExpr
9-
private import codeql.rust.internal.PathResolution
10-
private import codeql.rust.internal.TypeInference
119

1210
/**
1311
* INTERNAL: This module contains the customizable definition of `MethodCallExpr` and should not
@@ -23,8 +21,6 @@ module Impl {
2321
* ```
2422
*/
2523
class MethodCallExpr extends Generated::MethodCallExpr {
26-
override Function getStaticTarget() { result = resolveMethodCallTarget(this) }
27-
2824
private string toStringPart(int index) {
2925
index = 0 and
3026
result = this.getReceiver().toAbbreviatedString()

rust/ql/lib/codeql/rust/internal/Type.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ final class TraitTypeAbstraction extends TypeAbstraction, Trait {
425425
result.(TypeParamTypeParameter).getTypeParam() = this.getGenericParamList().getATypeParam()
426426
or
427427
result.(AssociatedTypeTypeParameter).getTrait() = this
428+
or
429+
result.(SelfTypeParameter).getTrait() = this
428430
}
429431
}
430432

0 commit comments

Comments
 (0)