Skip to content

Commit 133aca0

Browse files
committed
Rust: Simple type inference for index expressions
1 parent 301bd44 commit 133aca0

File tree

5 files changed

+132
-6
lines changed

5 files changed

+132
-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
@@ -1128,6 +1136,50 @@ private Type inferAwaitExprType(AstNode n, TypePath path) {
11281136
)
11291137
}
11301138

1139+
private class Vec extends Struct {
1140+
Vec() { this.getCanonicalPath() = "alloc::vec::Vec" }
1141+
1142+
TypeParamTypeParameter getElementTypeParameter() {
1143+
result.getTypeParam() = this.getGenericParamList().getTypeParam(0)
1144+
}
1145+
}
1146+
1147+
/**
1148+
* According to [the Rust reference][1]: _"array and slice-typed expressions
1149+
* can be indexed with a `usize` index ... For other types an index expression
1150+
* `a[b]` is equivalent to *std::ops::Index::index(&a, b)"_.
1151+
*
1152+
* The logic below handles array and slice indexing, but for other types it is
1153+
* currently limited to `Vec`.
1154+
*
1155+
* [1]: https://doc.rust-lang.org/reference/expressions/array-expr.html#r-expr.array.index
1156+
*/
1157+
pragma[nomagic]
1158+
private Type inferIndexExprType(IndexExpr ie, TypePath path) {
1159+
// TODO: Should be implemented as method resolution, using the special
1160+
// `std::ops::Index` trait.
1161+
exists(TypePath exprPath, Builtins::BuiltinType t |
1162+
TStruct(t) = inferType(ie.getIndex()) and
1163+
(
1164+
// also allow `i32`, since that is currently the type that we infer for
1165+
// integer literals like `0`
1166+
t instanceof Builtins::I32
1167+
or
1168+
t instanceof Builtins::Usize
1169+
) and
1170+
result = inferType(ie.getBase(), exprPath)
1171+
|
1172+
exprPath.isCons(any(Vec v).getElementTypeParameter(), path)
1173+
or
1174+
exprPath.isCons(any(ArrayTypeParameter tp), path)
1175+
or
1176+
exists(TypePath path0 |
1177+
exprPath.isCons(any(RefTypeParameter tp), path0) and
1178+
path0.isCons(any(SliceTypeParameter tp), path)
1179+
)
1180+
)
1181+
}
1182+
11311183
private module MethodCall {
11321184
/** An expression that calls a method. */
11331185
abstract private class MethodCallImpl extends Expr {
@@ -1487,6 +1539,8 @@ private module Cached {
14871539
path.isEmpty()
14881540
or
14891541
result = inferAwaitExprType(n, path)
1542+
or
1543+
result = inferIndexExprType(n, path)
14901544
}
14911545
}
14921546

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
@@ -1790,16 +1790,16 @@ mod indexers {
17901790
}
17911791

17921792
fn analyze_slice(slice: &[S]) {
1793-
let x = slice[0].foo(); // $ MISSING: method=foo MISSING: type=x:S
1793+
let x = slice[0].foo(); // $ method=foo type=x:S
17941794
}
17951795

17961796
pub fn f() {
17971797
let mut vec = MyVec::new(); // $ type=vec:T.S
17981798
vec.push(S); // $ method=push
1799-
vec[0].foo(); // $ MISSING: method=foo
1799+
vec[0].foo(); // $ MISSING: method=foo -- type inference does not support the `Index` trait yet
18001800

18011801
let xs: [S; 1] = [S];
1802-
let x = xs[0].foo(); // $ MISSING: method=foo MISSING: type=x:S
1802+
let x = xs[0].foo(); // $ method=foo type=x:S
18031803

18041804
analyze_slice(&xs);
18051805
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,7 +2561,14 @@ inferType
25612561
| main.rs:1788:14:1788:29 | ...[index] | | main.rs:1783:10:1783:10 | T |
25622562
| main.rs:1788:24:1788:28 | index | | {EXTERNAL LOCATION} | usize |
25632563
| main.rs:1792:22:1792:26 | slice | | file://:0:0:0:0 | & |
2564+
| main.rs:1792:22:1792:26 | slice | &T | file://:0:0:0:0 | [] |
2565+
| main.rs:1792:22:1792:26 | slice | &T.[T] | main.rs:1759:5:1760:13 | S |
2566+
| main.rs:1793:13:1793:13 | x | | main.rs:1759:5:1760:13 | S |
25642567
| main.rs:1793:17:1793:21 | slice | | file://:0:0:0:0 | & |
2568+
| main.rs:1793:17:1793:21 | slice | &T | file://:0:0:0:0 | [] |
2569+
| main.rs:1793:17:1793:21 | slice | &T.[T] | main.rs:1759:5:1760:13 | S |
2570+
| main.rs:1793:17:1793:24 | slice[0] | | main.rs:1759:5:1760:13 | S |
2571+
| main.rs:1793:17:1793:30 | ... .foo() | | main.rs:1759:5:1760:13 | S |
25652572
| main.rs:1793:23:1793:23 | 0 | | {EXTERNAL LOCATION} | i32 |
25662573
| main.rs:1797:13:1797:19 | mut vec | | main.rs:1768:5:1771:5 | MyVec |
25672574
| main.rs:1797:13:1797:19 | mut vec | T | main.rs:1759:5:1760:13 | S |
@@ -2574,14 +2581,32 @@ inferType
25742581
| main.rs:1799:9:1799:11 | vec | T | main.rs:1759:5:1760:13 | S |
25752582
| main.rs:1799:13:1799:13 | 0 | | {EXTERNAL LOCATION} | i32 |
25762583
| main.rs:1801:13:1801:14 | xs | | file://:0:0:0:0 | [] |
2584+
| main.rs:1801:13:1801:14 | xs | | file://:0:0:0:0 | [] |
2585+
| main.rs:1801:13:1801:14 | xs | [T;...] | main.rs:1759:5:1760:13 | S |
2586+
| main.rs:1801:13:1801:14 | xs | [T] | main.rs:1759:5:1760:13 | S |
25772587
| main.rs:1801:21:1801:21 | 1 | | {EXTERNAL LOCATION} | i32 |
25782588
| main.rs:1801:26:1801:28 | [...] | | file://:0:0:0:0 | [] |
2589+
| main.rs:1801:26:1801:28 | [...] | | file://:0:0:0:0 | [] |
2590+
| main.rs:1801:26:1801:28 | [...] | [T;...] | main.rs:1759:5:1760:13 | S |
2591+
| main.rs:1801:26:1801:28 | [...] | [T] | main.rs:1759:5:1760:13 | S |
25792592
| main.rs:1801:27:1801:27 | S | | main.rs:1759:5:1760:13 | S |
2593+
| main.rs:1802:13:1802:13 | x | | main.rs:1759:5:1760:13 | S |
2594+
| main.rs:1802:17:1802:18 | xs | | file://:0:0:0:0 | [] |
25802595
| main.rs:1802:17:1802:18 | xs | | file://:0:0:0:0 | [] |
2596+
| main.rs:1802:17:1802:18 | xs | [T;...] | main.rs:1759:5:1760:13 | S |
2597+
| main.rs:1802:17:1802:18 | xs | [T] | main.rs:1759:5:1760:13 | S |
2598+
| main.rs:1802:17:1802:21 | xs[0] | | main.rs:1759:5:1760:13 | S |
2599+
| main.rs:1802:17:1802:27 | ... .foo() | | main.rs:1759:5:1760:13 | S |
25812600
| main.rs:1802:20:1802:20 | 0 | | {EXTERNAL LOCATION} | i32 |
25822601
| main.rs:1804:23:1804:25 | &xs | | file://:0:0:0:0 | & |
25832602
| main.rs:1804:23:1804:25 | &xs | &T | file://:0:0:0:0 | [] |
2603+
| main.rs:1804:23:1804:25 | &xs | &T | file://:0:0:0:0 | [] |
2604+
| main.rs:1804:23:1804:25 | &xs | &T.[T;...] | main.rs:1759:5:1760:13 | S |
2605+
| main.rs:1804:23:1804:25 | &xs | &T.[T] | main.rs:1759:5:1760:13 | S |
2606+
| main.rs:1804:24:1804:25 | xs | | file://:0:0:0:0 | [] |
25842607
| main.rs:1804:24:1804:25 | xs | | file://:0:0:0:0 | [] |
2608+
| main.rs:1804:24:1804:25 | xs | [T;...] | main.rs:1759:5:1760:13 | S |
2609+
| main.rs:1804:24:1804:25 | xs | [T] | main.rs:1759:5:1760:13 | S |
25852610
| main.rs:1810:5:1810:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
25862611
| main.rs:1811:5:1811:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
25872612
| main.rs:1811:20:1811:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |

0 commit comments

Comments
 (0)