Skip to content

Commit d8161df

Browse files
committed
Rust: Simple type inference for index expressions
1 parent 12b6335 commit d8161df

File tree

5 files changed

+133
-6
lines changed

5 files changed

+133
-6
lines changed

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ newtype TType =
1616
TArrayType() or // todo: add size?
1717
TRefType() or // todo: add mut?
1818
TImplTraitType(ImplTraitTypeRepr impl) or
19+
TSliceType() or
1920
TTypeParamTypeParameter(TypeParam t) or
2021
TAssociatedTypeTypeParameter(TypeAlias t) { any(TraitItemNode trait).getAnAssocItem() = t } or
22+
TArrayTypeParameter() or
2123
TRefTypeParameter() or
22-
TSelfTypeParameter(Trait t)
24+
TSelfTypeParameter(Trait t) or
25+
TSliceTypeParameter()
2326

2427
/**
2528
* A type without type arguments.
@@ -149,7 +152,8 @@ class ArrayType extends Type, TArrayType {
149152
override TupleField getTupleField(int i) { none() }
150153

151154
override TypeParameter getTypeParameter(int i) {
152-
none() // todo
155+
result = TArrayTypeParameter() and
156+
i = 0
153157
}
154158

155159
override string toString() { result = "[]" }
@@ -227,6 +231,29 @@ class ImplTraitReturnType extends ImplTraitType {
227231
override Function getFunction() { result = function }
228232
}
229233

234+
/**
235+
* A slice type.
236+
*
237+
* Slice types like `[i64]` are modeled as normal generic types
238+
* with a single type argument.
239+
*/
240+
class SliceType extends Type, TSliceType {
241+
SliceType() { this = TSliceType() }
242+
243+
override StructField getStructField(string name) { none() }
244+
245+
override TupleField getTupleField(int i) { none() }
246+
247+
override TypeParameter getTypeParameter(int i) {
248+
result = TSliceTypeParameter() and
249+
i = 0
250+
}
251+
252+
override string toString() { result = "[]" }
253+
254+
override Location getLocation() { result instanceof EmptyLocation }
255+
}
256+
230257
/** A type parameter. */
231258
abstract class TypeParameter extends Type {
232259
override StructField getStructField(string name) { none() }
@@ -306,13 +333,27 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
306333
override Location getLocation() { result = typeAlias.getLocation() }
307334
}
308335

336+
/** An implicit array type parameter. */
337+
class ArrayTypeParameter extends TypeParameter, TArrayTypeParameter {
338+
override string toString() { result = "[T;...]" }
339+
340+
override Location getLocation() { result instanceof EmptyLocation }
341+
}
342+
309343
/** An implicit reference type parameter. */
310344
class RefTypeParameter extends TypeParameter, TRefTypeParameter {
311345
override string toString() { result = "&T" }
312346

313347
override Location getLocation() { result instanceof EmptyLocation }
314348
}
315349

350+
/** An implicit slice type parameter. */
351+
class SliceTypeParameter extends TypeParameter, TSliceTypeParameter {
352+
override string toString() { result = "[T]" }
353+
354+
override Location getLocation() { result instanceof EmptyLocation }
355+
}
356+
316357
/**
317358
* The implicit `Self` type parameter of a trait, that refers to the
318359
* implementing type of the trait.

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,18 @@ private module Input1 implements InputSig1<Location> {
8080
int getTypeParameterId(TypeParameter tp) {
8181
tp =
8282
rank[result](TypeParameter tp0, int kind, int id |
83-
tp0 instanceof RefTypeParameter and
83+
tp0 instanceof ArrayTypeParameter and
8484
kind = 0 and
8585
id = 0
8686
or
87+
tp0 instanceof RefTypeParameter and
88+
kind = 0 and
89+
id = 1
90+
or
91+
tp0 instanceof SliceTypeParameter and
92+
kind = 0 and
93+
id = 2
94+
or
8795
kind = 1 and
8896
exists(AstNode node | id = idOfTypeParameterAstNode(node) |
8997
node = tp0.(TypeParamTypeParameter).getTypeParam() or
@@ -1158,6 +1166,50 @@ private Type inferAwaitExprType(AstNode n, TypePath path) {
11581166
)
11591167
}
11601168

1169+
private class Vec extends Struct {
1170+
Vec() { this.getCanonicalPath() = "alloc::vec::Vec" }
1171+
1172+
TypeParamTypeParameter getElementTypeParameter() {
1173+
result.getTypeParam() = this.getGenericParamList().getTypeParam(0)
1174+
}
1175+
}
1176+
1177+
/**
1178+
* According to [the Rust reference][1]: _"array and slice-typed expressions
1179+
* can be indexed with a `usize` index ... For other types an index expression
1180+
* `a[b]` is equivalent to *std::ops::Index::index(&a, b)"_.
1181+
*
1182+
* The logic below handles array and slice indexing, but for other types it is
1183+
* currently limited to `Vec`.
1184+
*
1185+
* [1]: https://doc.rust-lang.org/reference/expressions/array-expr.html#r-expr.array.index
1186+
*/
1187+
pragma[nomagic]
1188+
private Type inferIndexExprType(IndexExpr ie, TypePath path) {
1189+
// TODO: Should be implemented as method resolution, using the special
1190+
// `std::ops::Index` trait.
1191+
exists(TypePath exprPath, Builtins::BuiltinType t |
1192+
TStruct(t) = inferType(ie.getIndex()) and
1193+
(
1194+
// also allow `i32`, since that is currently the type that we infer for
1195+
// integer literals like `0`
1196+
t instanceof Builtins::I32
1197+
or
1198+
t instanceof Builtins::Usize
1199+
) and
1200+
result = inferType(ie.getBase(), exprPath)
1201+
|
1202+
exprPath.isCons(any(Vec v).getElementTypeParameter(), path)
1203+
or
1204+
exprPath.isCons(any(ArrayTypeParameter tp), path)
1205+
or
1206+
exists(TypePath path0 |
1207+
exprPath.isCons(any(RefTypeParameter tp), path0) and
1208+
path0.isCons(any(SliceTypeParameter tp), path)
1209+
)
1210+
)
1211+
}
1212+
11611213
private module MethodCall {
11621214
/** An expression that calls a method. */
11631215
abstract private class MethodCallImpl extends Expr {
@@ -1516,6 +1568,8 @@ private module Cached {
15161568
path.isEmpty()
15171569
or
15181570
result = inferAwaitExprType(n, path)
1571+
or
1572+
result = inferIndexExprType(n, path)
15191573
}
15201574
}
15211575

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ class RefTypeReprMention extends TypeMention instanceof RefTypeRepr {
4343
override Type resolveType() { result = TRefType() }
4444
}
4545

46+
class SliceTypeReprMention extends TypeMention instanceof SliceTypeRepr {
47+
override TypeMention getTypeArgument(int i) { result = super.getTypeRepr() and i = 0 }
48+
49+
override Type resolveType() { result = TSliceType() }
50+
}
51+
4652
class PathTypeReprMention extends TypeMention instanceof PathTypeRepr {
4753
Path path;
4854
ItemNode resolved;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,16 +1771,16 @@ mod indexers {
17711771
}
17721772

17731773
fn analyze_slice(slice: &[S]) {
1774-
let x = slice[0].foo(); // $ MISSING: method=foo MISSING: type=x:S
1774+
let x = slice[0].foo(); // $ method=foo type=x:S
17751775
}
17761776

17771777
pub fn f() {
17781778
let mut vec = MyVec::new(); // $ type=vec:T.S
17791779
vec.push(S); // $ method=push
1780-
vec[0].foo(); // $ MISSING: method=foo
1780+
vec[0].foo(); // $ MISSING: method=foo -- type inference does not support the `Index` trait yet
17811781

17821782
let xs: [S; 1] = [S];
1783-
let x = xs[0].foo(); // $ MISSING: method=foo MISSING: type=x:S
1783+
let x = xs[0].foo(); // $ method=foo type=x:S
17841784

17851785
analyze_slice(&xs);
17861786
}

rust/ql/test/library-tests/type-inference/type-inference.expected

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,9 +2495,17 @@ inferType
24952495
| main.rs:1769:14:1769:17 | self | &T.T | main.rs:1764:10:1764:10 | T |
24962496
| main.rs:1769:14:1769:22 | self.data | | {EXTERNAL LOCATION} | Vec |
24972497
| main.rs:1769:14:1769:22 | self.data | T | main.rs:1764:10:1764:10 | T |
2498+
| main.rs:1769:14:1769:29 | ...[index] | | main.rs:1764:10:1764:10 | T |
24982499
| main.rs:1769:24:1769:28 | index | | {EXTERNAL LOCATION} | usize |
24992500
| main.rs:1773:22:1773:26 | slice | | file://:0:0:0:0 | & |
2501+
| main.rs:1773:22:1773:26 | slice | &T | file://:0:0:0:0 | [] |
2502+
| main.rs:1773:22:1773:26 | slice | &T.[T] | main.rs:1740:5:1741:13 | S |
2503+
| main.rs:1774:13:1774:13 | x | | main.rs:1740:5:1741:13 | S |
25002504
| main.rs:1774:17:1774:21 | slice | | file://:0:0:0:0 | & |
2505+
| main.rs:1774:17:1774:21 | slice | &T | file://:0:0:0:0 | [] |
2506+
| main.rs:1774:17:1774:21 | slice | &T.[T] | main.rs:1740:5:1741:13 | S |
2507+
| main.rs:1774:17:1774:24 | slice[0] | | main.rs:1740:5:1741:13 | S |
2508+
| main.rs:1774:17:1774:30 | ... .foo() | | main.rs:1740:5:1741:13 | S |
25012509
| main.rs:1774:23:1774:23 | 0 | | {EXTERNAL LOCATION} | i32 |
25022510
| main.rs:1778:13:1778:19 | mut vec | | main.rs:1749:5:1752:5 | MyVec |
25032511
| main.rs:1778:13:1778:19 | mut vec | T | main.rs:1740:5:1741:13 | S |
@@ -2510,14 +2518,32 @@ inferType
25102518
| main.rs:1780:9:1780:11 | vec | T | main.rs:1740:5:1741:13 | S |
25112519
| main.rs:1780:13:1780:13 | 0 | | {EXTERNAL LOCATION} | i32 |
25122520
| main.rs:1782:13:1782:14 | xs | | file://:0:0:0:0 | [] |
2521+
| main.rs:1782:13:1782:14 | xs | | file://:0:0:0:0 | [] |
2522+
| main.rs:1782:13:1782:14 | xs | [T;...] | main.rs:1740:5:1741:13 | S |
2523+
| main.rs:1782:13:1782:14 | xs | [T] | main.rs:1740:5:1741:13 | S |
25132524
| main.rs:1782:21:1782:21 | 1 | | {EXTERNAL LOCATION} | i32 |
25142525
| main.rs:1782:26:1782:28 | [...] | | file://:0:0:0:0 | [] |
2526+
| main.rs:1782:26:1782:28 | [...] | | file://:0:0:0:0 | [] |
2527+
| main.rs:1782:26:1782:28 | [...] | [T;...] | main.rs:1740:5:1741:13 | S |
2528+
| main.rs:1782:26:1782:28 | [...] | [T] | main.rs:1740:5:1741:13 | S |
25152529
| main.rs:1782:27:1782:27 | S | | main.rs:1740:5:1741:13 | S |
2530+
| main.rs:1783:13:1783:13 | x | | main.rs:1740:5:1741:13 | S |
2531+
| main.rs:1783:17:1783:18 | xs | | file://:0:0:0:0 | [] |
25162532
| main.rs:1783:17:1783:18 | xs | | file://:0:0:0:0 | [] |
2533+
| main.rs:1783:17:1783:18 | xs | [T;...] | main.rs:1740:5:1741:13 | S |
2534+
| main.rs:1783:17:1783:18 | xs | [T] | main.rs:1740:5:1741:13 | S |
2535+
| main.rs:1783:17:1783:21 | xs[0] | | main.rs:1740:5:1741:13 | S |
2536+
| main.rs:1783:17:1783:27 | ... .foo() | | main.rs:1740:5:1741:13 | S |
25172537
| main.rs:1783:20:1783:20 | 0 | | {EXTERNAL LOCATION} | i32 |
25182538
| main.rs:1785:23:1785:25 | &xs | | file://:0:0:0:0 | & |
25192539
| main.rs:1785:23:1785:25 | &xs | &T | file://:0:0:0:0 | [] |
2540+
| main.rs:1785:23:1785:25 | &xs | &T | file://:0:0:0:0 | [] |
2541+
| main.rs:1785:23:1785:25 | &xs | &T.[T;...] | main.rs:1740:5:1741:13 | S |
2542+
| main.rs:1785:23:1785:25 | &xs | &T.[T] | main.rs:1740:5:1741:13 | S |
2543+
| main.rs:1785:24:1785:25 | xs | | file://:0:0:0:0 | [] |
25202544
| main.rs:1785:24:1785:25 | xs | | file://:0:0:0:0 | [] |
2545+
| main.rs:1785:24:1785:25 | xs | [T;...] | main.rs:1740:5:1741:13 | S |
2546+
| main.rs:1785:24:1785:25 | xs | [T] | main.rs:1740:5:1741:13 | S |
25212547
| main.rs:1791:5:1791:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
25222548
| main.rs:1792:5:1792:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
25232549
| main.rs:1792:20:1792:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |

0 commit comments

Comments
 (0)