Skip to content

Commit 6ddb9c7

Browse files
authored
Merge pull request #20853 from hvitved/rust/path-resolution-impl-self
Rust: Refine `Self` resolution inside `impl` blocks
2 parents b8cff77 + d45f8f7 commit 6ddb9c7

File tree

11 files changed

+554
-353
lines changed

11 files changed

+554
-353
lines changed

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

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ abstract class ItemNode extends Locatable {
264264
pragma[nomagic]
265265
ItemNode getImmediateParent() { this = result.getADescendant() }
266266

267+
/** Gets a child item of this item, if any. */
268+
ItemNode getAChild() { this = result.getImmediateParent() }
269+
267270
/** Gets the immediately enclosing module (or source file) of this item. */
268271
pragma[nomagic]
269272
ModuleLikeNode getImmediateParentModule() {
@@ -339,10 +342,13 @@ abstract class ItemNode extends Locatable {
339342
typeImplEdge(this, _, name, kind, result, useOpt)
340343
or
341344
// trait items with default implementations made available in an implementation
342-
exists(ImplItemNodeImpl impl, ItemNode trait |
345+
exists(ImplItemNodeImpl impl, TraitItemNode trait |
343346
this = impl and
344347
trait = impl.resolveTraitTyCand() and
345348
result = trait.getASuccessor(name, kind, useOpt) and
349+
// do not inherit default implementations from super traits; those are inherited by
350+
// their `impl` blocks
351+
result = trait.getAssocItem(name) and
346352
result.(AssocItemNode).hasImplementation() and
347353
kind.isExternalOrBoth() and
348354
not impl.hasAssocItem(name)
@@ -402,8 +408,14 @@ abstract class ItemNode extends Locatable {
402408
this instanceof SourceFile and
403409
builtin(name, result)
404410
or
405-
name = "Self" and
406-
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
411+
exists(ImplOrTraitItemNode i |
412+
name = "Self" and
413+
this = i.getAnItemInSelfScope()
414+
|
415+
result = i.(Trait)
416+
or
417+
result = i.(ImplItemNodeImpl).resolveSelfTyCand()
418+
)
407419
or
408420
name = "crate" and
409421
this = result.(CrateItemNode).getASourceFile()
@@ -734,7 +746,7 @@ abstract class ImplOrTraitItemNode extends ItemNode {
734746
Path getASelfPath() {
735747
Stages::PathResolutionStage::ref() and
736748
isUnqualifiedSelfPath(result) and
737-
this = unqualifiedPathLookup(result, _, _)
749+
result = this.getAnItemInSelfScope().getADescendant()
738750
}
739751

740752
/** Gets an associated item belonging to this trait or `impl` block. */
@@ -960,7 +972,7 @@ private class ImplItemNodeImpl extends ImplItemNode {
960972
result = this.resolveSelfTyBuiltin()
961973
}
962974

963-
TraitItemNode resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }
975+
TraitItemNodeImpl resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }
964976
}
965977

966978
private class StructItemNode extends TypeItemNode, ParameterizableItemNode instanceof Struct {
@@ -1813,15 +1825,7 @@ private module DollarCrateResolution {
18131825

18141826
pragma[nomagic]
18151827
private ItemNode resolvePathCand0(PathExt path, Namespace ns) {
1816-
exists(ItemNode res |
1817-
res = unqualifiedPathLookup(path, ns, _) and
1818-
if
1819-
not any(PathExt parent).getQualifier() = path and
1820-
isUnqualifiedSelfPath(path) and
1821-
res instanceof ImplItemNode
1822-
then result = res.(ImplItemNodeImpl).resolveSelfTyCand()
1823-
else result = res
1824-
)
1828+
result = unqualifiedPathLookup(path, ns, _)
18251829
or
18261830
DollarCrateResolution::resolveDollarCrate(path, result) and
18271831
ns = result.getNamespace()
@@ -1883,12 +1887,35 @@ private predicate checkQualifiedVisibility(
18831887
not i instanceof TypeParam
18841888
}
18851889

1890+
pragma[nomagic]
1891+
private predicate isImplSelfQualifiedPath(
1892+
ImplItemNode impl, PathExt qualifier, PathExt path, string name
1893+
) {
1894+
qualifier = impl.getASelfPath() and
1895+
qualifier = path.getQualifier() and
1896+
name = path.getText()
1897+
}
1898+
1899+
private ItemNode resolveImplSelfQualified(PathExt qualifier, PathExt path, Namespace ns) {
1900+
exists(ImplItemNode impl, string name |
1901+
isImplSelfQualifiedPath(impl, qualifier, path, name) and
1902+
result = impl.getAssocItem(name) and
1903+
ns = result.getNamespace()
1904+
)
1905+
}
1906+
18861907
/**
18871908
* Gets the item that `path` resolves to in `ns` when `qualifier` is the
18881909
* qualifier of `path` and `qualifier` resolves to `q`, if any.
18891910
*/
18901911
pragma[nomagic]
18911912
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
1913+
// Special case for `Self::Assoc`; this always refers to the associated
1914+
// item in the enclosing `impl` block, if available.
1915+
q = resolvePathCandQualifier(qualifier, path, _) and
1916+
result = resolveImplSelfQualified(qualifier, path, ns)
1917+
or
1918+
not exists(resolveImplSelfQualified(qualifier, path, ns)) and
18921919
exists(string name, SuccessorKind kind, UseOption useOpt |
18931920
q = resolvePathCandQualifier(qualifier, path, name) and
18941921
result = getASuccessor(q, name, ns, kind, useOpt) and

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ TypeParamTypeParameter getPtrTypeParameter() {
385385
/** A type parameter. */
386386
abstract class TypeParameter extends Type {
387387
override TypeParameter getPositionalTypeParameter(int i) { none() }
388+
389+
abstract ItemNode getDeclaringItem();
388390
}
389391

390392
private class RawTypeParameter = @type_param or @trait or @type_alias or @impl_trait_type_repr;
@@ -403,6 +405,8 @@ class TypeParamTypeParameter extends TypeParameter, TTypeParamTypeParameter {
403405

404406
TypeParam getTypeParam() { result = typeParam }
405407

408+
override ItemNode getDeclaringItem() { result.getTypeParam(_) = typeParam }
409+
406410
override string toString() { result = typeParam.toString() }
407411

408412
override Location getLocation() { result = typeParam.getLocation() }
@@ -436,6 +440,8 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
436440
/** Gets the trait that contains this associated type declaration. */
437441
TraitItemNode getTrait() { result.getAnAssocItem() = typeAlias }
438442

443+
override ItemNode getDeclaringItem() { result = this.getTrait() }
444+
439445
override string toString() { result = typeAlias.getName().getText() }
440446

441447
override Location getLocation() { result = typeAlias.getLocation() }
@@ -468,6 +474,8 @@ class DynTraitTypeParameter extends TypeParameter, TDynTraitTypeParameter {
468474
result = [this.getTypeParam().toString(), this.getTypeAlias().getName().toString()]
469475
}
470476

477+
override ItemNode getDeclaringItem() { none() }
478+
471479
override string toString() { result = "dyn(" + this.toStringInner() + ")" }
472480

473481
override Location getLocation() { result = n.getLocation() }
@@ -483,6 +491,8 @@ class ImplTraitTypeParameter extends TypeParameter, TImplTraitTypeParameter {
483491

484492
ImplTraitTypeRepr getImplTraitTypeRepr() { result = implTrait }
485493

494+
override ItemNode getDeclaringItem() { none() }
495+
486496
override string toString() { result = "impl(" + typeParam.toString() + ")" }
487497

488498
override Location getLocation() { result = typeParam.getLocation() }
@@ -502,6 +512,8 @@ class SelfTypeParameter extends TypeParameter, TSelfTypeParameter {
502512

503513
Trait getTrait() { result = trait }
504514

515+
override ItemNode getDeclaringItem() { result = trait }
516+
505517
override string toString() { result = "Self [" + trait.toString() + "]" }
506518

507519
override Location getLocation() { result = trait.getLocation() }
@@ -529,6 +541,8 @@ class ImplTraitTypeTypeParameter extends ImplTraitType, TypeParameter {
529541

530542
ImplTraitTypeTypeParameter() { impl = function.getAParam().getTypeRepr() }
531543

544+
override ItemNode getDeclaringItem() { none() }
545+
532546
override Function getFunction() { result = function }
533547

534548
override TypeParameter getPositionalTypeParameter(int i) { none() }

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ import TypeInference::Consistency
1111

1212
query predicate illFormedTypeMention(TypeMention tm) {
1313
Consistency::illFormedTypeMention(tm) and
14-
not tm instanceof PathTypeReprMention and // avoid overlap with `PathTypeMention`
14+
// avoid overlap with `PathTypeMention`
15+
not tm instanceof PathTypeReprMention and
16+
// known limitation for type mentions that would mention an escaping type parameter
17+
not tm =
18+
any(PathTypeMention ptm |
19+
exists(ptm.resolvePathTypeAt(TypePath::nil())) and
20+
not exists(ptm.resolveType())
21+
) and
1522
// Only include inconsistencies in the source, as we otherwise get
1623
// inconsistencies from library code in every project.
1724
tm.fromSource()

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,19 @@ class SliceTypeReprMention extends TypeMention instanceof SliceTypeRepr {
7777
}
7878
}
7979

80-
abstract class PathTypeMention extends TypeMention, Path { }
80+
abstract class PathTypeMention extends TypeMention, Path {
81+
abstract Type resolvePathTypeAt(TypePath typePath);
82+
83+
final override Type resolveTypeAt(TypePath typePath) {
84+
result = this.resolvePathTypeAt(typePath) and
85+
(
86+
not result instanceof TypeParameter
87+
or
88+
// Prevent type parameters from escaping their scope
89+
this = result.(TypeParameter).getDeclaringItem().getAChild*().getADescendant()
90+
)
91+
}
92+
}
8193

8294
class AliasPathTypeMention extends PathTypeMention {
8395
TypeAlias resolved;
@@ -94,7 +106,7 @@ class AliasPathTypeMention extends PathTypeMention {
94106
* Holds if this path resolved to a type alias with a rhs. that has the
95107
* resulting type at `typePath`.
96108
*/
97-
override Type resolveTypeAt(TypePath typePath) {
109+
override Type resolvePathTypeAt(TypePath typePath) {
98110
result = rhs.resolveTypeAt(typePath) and
99111
not result = pathGetTypeParameter(resolved, _)
100112
or
@@ -275,7 +287,7 @@ class NonAliasPathTypeMention extends PathTypeMention {
275287
result = TAssociatedTypeTypeParameter(resolved)
276288
}
277289

278-
override Type resolveTypeAt(TypePath typePath) {
290+
override Type resolvePathTypeAt(TypePath typePath) {
279291
typePath.isEmpty() and
280292
result = this.resolveRootType()
281293
or
@@ -307,7 +319,9 @@ class ImplSelfMention extends PathTypeMention {
307319

308320
ImplSelfMention() { this = impl.getASelfPath() }
309321

310-
override Type resolveTypeAt(TypePath typePath) { result = resolveImplSelfTypeAt(impl, typePath) }
322+
override Type resolvePathTypeAt(TypePath typePath) {
323+
result = resolveImplSelfTypeAt(impl, typePath)
324+
}
311325
}
312326

313327
class PathTypeReprMention extends TypeMention, PathTypeRepr {

rust/ql/test/library-tests/path-resolution/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ multipleCallTargets
22
| main.rs:126:9:126:11 | f(...) |
33
| main.rs:366:9:368:16 | ...::f(...) |
44
| main.rs:369:9:371:16 | ...::f(...) |
5-
| main.rs:448:9:452:16 | ...::f(...) |
6-
| main.rs:453:9:457:16 | ...::f(...) |
5+
| main.rs:450:9:454:16 | ...::f(...) |
6+
| main.rs:455:9:459:16 | ...::f(...) |
7+
| main.rs:565:9:566:15 | ...::Assoc(...) |
8+
| main.rs:568:9:569:12 | ...::f1(...) |
9+
| main.rs:571:9:572:12 | ...::f1(...) |

rust/ql/test/library-tests/path-resolution/main.rs

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,9 @@ mod m16 {
381381
> {
382382
fn f(&self) -> T; // $ item=I84
383383

384-
fn g(&self) -> T // $ item=I84
385-
; // I85
384+
fn g(&self) -> T {// $ item=I84
385+
self.f() // $ item=f
386+
} // I85
386387

387388
fn h(&self) -> T { // $ item=I84
388389
Self::g(&self); // $ item=I85
@@ -436,8 +437,9 @@ mod m16 {
436437
> // $ item=I89
437438
for S { // $ item=I90
438439
fn f(&self) -> S { // $ item=I90
440+
Self::g(&self); // $ item=I92
439441
println!("m16::<S as Trait2<S>>::f"); // $ item=println
440-
Self::c // $ MISSING: item=I95
442+
Self::c // $ item=I95
441443
} // I93
442444
}
443445

@@ -466,6 +468,109 @@ mod m16 {
466468
> // $ item=I86
467469
>::c; // $ MISSING: item=I95
468470
} // I83
471+
472+
trait Trait3 {
473+
type AssocType;
474+
475+
fn f(&self);
476+
}
477+
478+
trait Trait4 {
479+
type AssocType;
480+
481+
fn g(&self);
482+
}
483+
484+
struct S2;
485+
486+
#[rustfmt::skip]
487+
impl Trait3 for S2 { // $ item=Trait3 item=S2
488+
type AssocType = i32 // $ item=i32
489+
; // S2Trait3AssocType
490+
491+
fn f(&self) {
492+
let x: Self::AssocType = 42; // $ item=S2Trait3AssocType
493+
} // S2asTrait3::f
494+
}
495+
496+
#[rustfmt::skip]
497+
impl Trait4 for S2 { // $ item=Trait4 item=S2
498+
type AssocType = bool // $ item=bool
499+
; // S2Trait4AssocType
500+
501+
fn g(&self) {
502+
Self::f(&self); // $ item=S2asTrait3::f
503+
S2::f(&self); // $ item=S2asTrait3::f
504+
let x: Self::AssocType = true; // $ item=S2Trait4AssocType
505+
}
506+
}
507+
508+
trait Trait5 {
509+
type Assoc; // Trait5Assoc
510+
511+
fn Assoc() -> Self::Assoc; // $ item=Trait5Assoc
512+
}
513+
514+
#[rustfmt::skip]
515+
impl Trait5 for S { // $ item=Trait5 item=I90
516+
type Assoc = i32 // $ item=i32
517+
; // AssocType
518+
519+
fn Assoc()
520+
-> Self::Assoc { // $ item=AssocType
521+
Self::Assoc() + 1 // $ item=AssocFunc
522+
} // AssocFunc
523+
}
524+
525+
struct S3<T3>(T3); // $ item=T3
526+
527+
#[rustfmt::skip]
528+
impl Trait5 for S3<i32> { // $ item=Trait5 item=S3 item=i32
529+
type Assoc = i32 // $ item=i32
530+
; // S3i32AssocType
531+
532+
fn Assoc()
533+
-> Self::Assoc { // $ item=S3i32AssocType
534+
Self::Assoc() + 1 // $ item=S3i32AssocFunc
535+
} // S3i32AssocFunc
536+
}
537+
538+
#[rustfmt::skip]
539+
impl Trait5 for S3<bool> { // $ item=Trait5 item=S3 item=bool
540+
type Assoc = bool // $ item=bool
541+
; // S3boolAssocType
542+
543+
fn Assoc()
544+
-> Self::Assoc { // $ item=S3boolAssocType
545+
!Self::Assoc() // $ item=S3boolAssocFunc
546+
} // S3boolAssocFunc
547+
}
548+
549+
#[rustfmt::skip]
550+
impl S3<i32> { // $ item=S3 item=i32
551+
fn f1() -> i32 { // $ item=i32
552+
0
553+
} // S3i32f1
554+
}
555+
556+
#[rustfmt::skip]
557+
impl S3<bool> { // $ item=S3 item=bool
558+
fn f1() -> bool { // $ item=bool
559+
true
560+
} // S3boolf1
561+
}
562+
563+
#[rustfmt::skip]
564+
fn foo() {
565+
S3::<i32>:: // $ item=i32
566+
Assoc(); // $ item=S3i32AssocFunc $ SPURIOUS: item=S3boolAssocFunc
567+
568+
S3::<bool>:: // $ item=bool
569+
f1(); // $ item=S3boolf1 $ SPURIOUS: item=S3i32f1
570+
571+
S3::<i32>:: // $ item=i32
572+
f1(); // $ item=S3i32f1 $ SPURIOUS: item=S3boolf1
573+
}
469574
}
470575

471576
mod trait_visibility {
@@ -805,7 +910,7 @@ mod patterns {
805910
N0ne => // local variable
806911
N0ne
807912
}
808-
} // patterns::test
913+
} // patterns::test
809914

810915
#[rustfmt::skip]
811916
fn test2() -> Option<i32> { // $ item=Option $ item=i32

0 commit comments

Comments
 (0)