Skip to content

Commit 3441af1

Browse files
authored
Merge pull request #19146 from paldepind/rust-ti-alias
Rust: Implement support for inference of type aliases
2 parents 5cde4dd + acc565f commit 3441af1

File tree

3 files changed

+497
-375
lines changed

3 files changed

+497
-375
lines changed

Diff for: rust/ql/lib/codeql/rust/internal/TypeMention.qll

+48-1
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,31 @@ class TypeReprMention extends TypeMention, TypeRepr {
5353
or
5454
result = this.(PathTypeRepr).getPath().(PathMention).resolveType()
5555
}
56+
57+
override Type resolveTypeAt(TypePath path) {
58+
result = this.(PathTypeRepr).getPath().(PathMention).resolveTypeAt(path)
59+
or
60+
not exists(this.(PathTypeRepr).getPath()) and
61+
result = super.resolveTypeAt(path)
62+
}
63+
}
64+
65+
/** Holds if `path` resolves to the type alias `alias` with the definition `rhs`. */
66+
private predicate resolvePathAlias(Path path, TypeAlias alias, TypeReprMention rhs) {
67+
alias = resolvePath(path) and rhs = alias.getTypeRepr()
5668
}
5769

58-
class PathMention extends TypeMention, Path {
70+
abstract class PathMention extends TypeMention, Path {
5971
override TypeMention getTypeArgument(int i) {
6072
result = this.getSegment().getGenericArgList().getTypeArg(i)
73+
}
74+
}
75+
76+
class NonAliasPathMention extends PathMention {
77+
NonAliasPathMention() { not resolvePathAlias(this, _, _) }
78+
79+
override TypeMention getTypeArgument(int i) {
80+
result = super.getTypeArgument(i)
6181
or
6282
// `Self` paths inside `impl` blocks have implicit type arguments that are
6383
// the type parameters of the `impl` block. For example, in
@@ -98,6 +118,33 @@ class PathMention extends TypeMention, Path {
98118
}
99119
}
100120

121+
class AliasPathMention extends PathMention {
122+
TypeAlias alias;
123+
TypeReprMention rhs;
124+
125+
AliasPathMention() { resolvePathAlias(this, alias, rhs) }
126+
127+
/** Get the `i`th type parameter of the alias itself. */
128+
private TypeParameter getTypeParameter(int i) {
129+
result = TTypeParamTypeParameter(alias.getGenericParamList().getTypeParam(i))
130+
}
131+
132+
override Type resolveType() { result = rhs.resolveType() }
133+
134+
override Type resolveTypeAt(TypePath path) {
135+
result = rhs.resolveTypeAt(path) and
136+
not result = this.getTypeParameter(_)
137+
or
138+
exists(TypeParameter tp, TypeMention arg, TypePath prefix, TypePath suffix, int i |
139+
tp = rhs.resolveTypeAt(prefix) and
140+
tp = this.getTypeParameter(i) and
141+
arg = this.getTypeArgument(i) and
142+
result = arg.resolveTypeAt(suffix) and
143+
path = prefix.append(suffix)
144+
)
145+
}
146+
}
147+
101148
// Used to represent implicit `Self` type arguments in traits and `impl` blocks,
102149
// see `PathMention` for details.
103150
class TypeParamMention extends TypeMention, TypeParam {

Diff for: rust/ql/test/library-tests/type-inference/main.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,17 @@ mod type_aliases {
540540
PairBoth(Fst, Snd),
541541
}
542542

543+
impl<Fst, Snd> PairOption<Fst, Snd> {
544+
fn unwrapSnd(self) -> Snd {
545+
match self {
546+
PairOption::PairNone() => panic!("PairNone has no second element"),
547+
PairOption::PairFst(_) => panic!("PairFst has no second element"),
548+
PairOption::PairSnd(snd) => snd,
549+
PairOption::PairBoth(_, snd) => snd,
550+
}
551+
}
552+
}
553+
543554
#[derive(Debug)]
544555
struct S1;
545556

@@ -553,24 +564,37 @@ mod type_aliases {
553564
type MyPair = PairOption<S1, S2>;
554565

555566
// Generic type alias that partially applies the generic type
556-
type AnotherPair<Thr> = PairOption<S2, Thr>;
567+
type AnotherPair<A3> = PairOption<S2, A3>;
568+
569+
// Alias to another alias
570+
type AliasToAlias<A4> = AnotherPair<A4>;
571+
572+
// Alias that appears nested within another alias
573+
type NestedAlias<A5> = AnotherPair<AliasToAlias<A5>>;
574+
575+
fn g(t: NestedAlias<S3>) {
576+
let x = t.unwrapSnd().unwrapSnd(); // $ method=unwrapSnd type=x:S3
577+
println!("{:?}", x);
578+
}
557579

558580
pub fn f() {
559581
// Type can be inferred from the constructor
560582
let p1: MyPair = PairOption::PairBoth(S1, S2);
561583
println!("{:?}", p1);
562584

563585
// Type can be only inferred from the type alias
564-
let p2: MyPair = PairOption::PairNone(); // types for `Fst` and `Snd` missing
586+
let p2: MyPair = PairOption::PairNone(); // $ type=p2:Fst.S1 type=p2:Snd.S2
565587
println!("{:?}", p2);
566588

567589
// First type from alias, second from constructor
568-
let p3: AnotherPair<_> = PairOption::PairSnd(S3); // type for `Fst` missing
590+
let p3: AnotherPair<_> = PairOption::PairSnd(S3); // $ type=p3:Fst.S2
569591
println!("{:?}", p3);
570592

571593
// First type from alias definition, second from argument to alias
572-
let p3: AnotherPair<S3> = PairOption::PairNone(); // type for `Snd` missing, spurious `S3` for `Fst`
594+
let p3: AnotherPair<S3> = PairOption::PairNone(); // $ type=p3:Fst.S2 type=p3:Snd.S3
573595
println!("{:?}", p3);
596+
597+
g(PairOption::PairSnd(PairOption::PairSnd(S3)));
574598
}
575599
}
576600

0 commit comments

Comments
 (0)