Skip to content

Commit 2652887

Browse files
committed
Rust: Simple type inference for index expressions
1 parent 9b815c2 commit 2652887

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
@@ -15,10 +15,13 @@ newtype TType =
1515
TTrait(Trait t) or
1616
TArrayType() or // todo: add size?
1717
TRefType() or // todo: add mut?
18+
TSliceType() or
1819
TTypeParamTypeParameter(TypeParam t) or
1920
TAssociatedTypeTypeParameter(TypeAlias t) { any(TraitItemNode trait).getAnAssocItem() = t } or
21+
TArrayTypeParameter() or
2022
TRefTypeParameter() or
21-
TSelfTypeParameter(Trait t)
23+
TSelfTypeParameter(Trait t) or
24+
TSliceTypeParameter()
2225

2326
/**
2427
* A type without type arguments.
@@ -145,7 +148,8 @@ class ArrayType extends Type, TArrayType {
145148
override TupleField getTupleField(int i) { none() }
146149

147150
override TypeParameter getTypeParameter(int i) {
148-
none() // todo
151+
result = TArrayTypeParameter() and
152+
i = 0
149153
}
150154

151155
override string toString() { result = "[]" }
@@ -176,6 +180,29 @@ class RefType extends Type, TRefType {
176180
override Location getLocation() { result instanceof EmptyLocation }
177181
}
178182

183+
/**
184+
* A slice type.
185+
*
186+
* Slice types like `[i64]` are modeled as normal generic types
187+
* with a single type argument.
188+
*/
189+
class SliceType extends Type, TSliceType {
190+
SliceType() { this = TSliceType() }
191+
192+
override StructField getStructField(string name) { none() }
193+
194+
override TupleField getTupleField(int i) { none() }
195+
196+
override TypeParameter getTypeParameter(int i) {
197+
result = TSliceTypeParameter() and
198+
i = 0
199+
}
200+
201+
override string toString() { result = "[]" }
202+
203+
override Location getLocation() { result instanceof EmptyLocation }
204+
}
205+
179206
/** A type parameter. */
180207
abstract class TypeParameter extends Type {
181208
override StructField getStructField(string name) { none() }
@@ -255,13 +282,27 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
255282
override Location getLocation() { result = typeAlias.getLocation() }
256283
}
257284

285+
/** An implicit array type parameter. */
286+
class ArrayTypeParameter extends TypeParameter, TArrayTypeParameter {
287+
override string toString() { result = "[T;...]" }
288+
289+
override Location getLocation() { result instanceof EmptyLocation }
290+
}
291+
258292
/** An implicit reference type parameter. */
259293
class RefTypeParameter extends TypeParameter, TRefTypeParameter {
260294
override string toString() { result = "&T" }
261295

262296
override Location getLocation() { result instanceof EmptyLocation }
263297
}
264298

299+
/** An implicit slice type parameter. */
300+
class SliceTypeParameter extends TypeParameter, TSliceTypeParameter {
301+
override string toString() { result = "[T]" }
302+
303+
override Location getLocation() { result instanceof EmptyLocation }
304+
}
305+
265306
/**
266307
* The implicit `Self` type parameter of a trait, that refers to the
267308
* 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
@@ -1010,6 +1018,50 @@ private StructType inferLiteralType(LiteralExpr le) {
10101018
)
10111019
}
10121020

1021+
private class Vec extends Struct {
1022+
Vec() { this.getCanonicalPath() = "alloc::vec::Vec" }
1023+
1024+
TypeParamTypeParameter getElementTypeParameter() {
1025+
result.getTypeParam() = this.getGenericParamList().getTypeParam(0)
1026+
}
1027+
}
1028+
1029+
/**
1030+
* According to [the Rust reference][1]: _"array and slice-typed expressions
1031+
* can be indexed with a `usize` index ... For other types an index expression
1032+
* `a[b]` is equivalent to *std::ops::Index::index(&a, b)"_.
1033+
*
1034+
* The logic below handles array and slice indexing, but for other types it is
1035+
* currently limited to `Vec`.
1036+
*
1037+
* [1]: https://doc.rust-lang.org/reference/expressions/array-expr.html#r-expr.array.index
1038+
*/
1039+
pragma[nomagic]
1040+
private Type inferIndexExprType(IndexExpr ie, TypePath path) {
1041+
// TODO: Should be implemented as method resolution, using the special
1042+
// `std::ops::Index` trait.
1043+
exists(TypePath exprPath, Builtins::BuiltinType t |
1044+
TStruct(t) = inferType(ie.getIndex()) and
1045+
(
1046+
// also allow `i32`, since that is currently the type that we infer for
1047+
// integer literals like `0`
1048+
t instanceof Builtins::I32
1049+
or
1050+
t instanceof Builtins::Usize
1051+
) and
1052+
result = inferType(ie.getBase(), exprPath)
1053+
|
1054+
exprPath.isCons(any(Vec v).getElementTypeParameter(), path)
1055+
or
1056+
exprPath.isCons(any(ArrayTypeParameter tp), path)
1057+
or
1058+
exists(TypePath path0 |
1059+
exprPath.isCons(any(RefTypeParameter tp), path0) and
1060+
path0.isCons(any(SliceTypeParameter tp), path)
1061+
)
1062+
)
1063+
}
1064+
10131065
private module MethodCall {
10141066
/** An expression that calls a method. */
10151067
abstract private class MethodCallImpl extends Expr {
@@ -1347,6 +1399,8 @@ private module Cached {
13471399
or
13481400
result = inferLiteralType(n) and
13491401
path.isEmpty()
1402+
or
1403+
result = inferIndexExprType(n, path)
13501404
}
13511405
}
13521406

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
@@ -1667,16 +1667,16 @@ mod indexers {
16671667
}
16681668

16691669
fn analyze_slice(slice: &[S]) {
1670-
let x = slice[0].foo(); // $ MISSING: method=foo MISSING: type=x:S
1670+
let x = slice[0].foo(); // $ method=foo type=x:S
16711671
}
16721672

16731673
pub fn f() {
16741674
let mut vec = MyVec::new(); // $ type=vec:T.S
16751675
vec.push(S); // $ method=push
1676-
vec[0].foo(); // $ MISSING: method=foo
1676+
vec[0].foo(); // $ MISSING: method=foo -- type inference does not support the `Index` trait yet
16771677

16781678
let xs: [S; 1] = [S];
1679-
let x = xs[0].foo(); // $ MISSING: method=foo MISSING: type=x:S
1679+
let x = xs[0].foo(); // $ method=foo type=x:S
16801680

16811681
analyze_slice(&xs);
16821682
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,7 +2404,14 @@ inferType
24042404
| main.rs:1665:14:1665:17 | self | &T.T | main.rs:1660:10:1660:10 | T |
24052405
| main.rs:1665:24:1665:28 | index | | file:///BUILTINS/types.rs:20:1:21:17 | usize |
24062406
| main.rs:1669:22:1669:26 | slice | | file://:0:0:0:0 | & |
2407+
| main.rs:1669:22:1669:26 | slice | &T | file://:0:0:0:0 | [] |
2408+
| main.rs:1669:22:1669:26 | slice | &T.[T] | main.rs:1636:5:1637:13 | S |
2409+
| main.rs:1670:13:1670:13 | x | | main.rs:1636:5:1637:13 | S |
24072410
| main.rs:1670:17:1670:21 | slice | | file://:0:0:0:0 | & |
2411+
| main.rs:1670:17:1670:21 | slice | &T | file://:0:0:0:0 | [] |
2412+
| main.rs:1670:17:1670:21 | slice | &T.[T] | main.rs:1636:5:1637:13 | S |
2413+
| main.rs:1670:17:1670:24 | slice[0] | | main.rs:1636:5:1637:13 | S |
2414+
| main.rs:1670:17:1670:30 | ... .foo() | | main.rs:1636:5:1637:13 | S |
24082415
| main.rs:1670:23:1670:23 | 0 | | file:///BUILTINS/types.rs:12:1:12:15 | i32 |
24092416
| main.rs:1674:13:1674:19 | mut vec | | main.rs:1645:5:1648:5 | MyVec |
24102417
| main.rs:1674:13:1674:19 | mut vec | T | main.rs:1636:5:1637:13 | S |
@@ -2417,14 +2424,32 @@ inferType
24172424
| main.rs:1676:9:1676:11 | vec | T | main.rs:1636:5:1637:13 | S |
24182425
| main.rs:1676:13:1676:13 | 0 | | file:///BUILTINS/types.rs:12:1:12:15 | i32 |
24192426
| main.rs:1678:13:1678:14 | xs | | file://:0:0:0:0 | [] |
2427+
| main.rs:1678:13:1678:14 | xs | | file://:0:0:0:0 | [] |
2428+
| main.rs:1678:13:1678:14 | xs | [T;...] | main.rs:1636:5:1637:13 | S |
2429+
| main.rs:1678:13:1678:14 | xs | [T] | main.rs:1636:5:1637:13 | S |
24202430
| main.rs:1678:21:1678:21 | 1 | | file:///BUILTINS/types.rs:12:1:12:15 | i32 |
24212431
| main.rs:1678:26:1678:28 | [...] | | file://:0:0:0:0 | [] |
2432+
| main.rs:1678:26:1678:28 | [...] | | file://:0:0:0:0 | [] |
2433+
| main.rs:1678:26:1678:28 | [...] | [T;...] | main.rs:1636:5:1637:13 | S |
2434+
| main.rs:1678:26:1678:28 | [...] | [T] | main.rs:1636:5:1637:13 | S |
24222435
| main.rs:1678:27:1678:27 | S | | main.rs:1636:5:1637:13 | S |
2436+
| main.rs:1679:13:1679:13 | x | | main.rs:1636:5:1637:13 | S |
2437+
| main.rs:1679:17:1679:18 | xs | | file://:0:0:0:0 | [] |
24232438
| main.rs:1679:17:1679:18 | xs | | file://:0:0:0:0 | [] |
2439+
| main.rs:1679:17:1679:18 | xs | [T;...] | main.rs:1636:5:1637:13 | S |
2440+
| main.rs:1679:17:1679:18 | xs | [T] | main.rs:1636:5:1637:13 | S |
2441+
| main.rs:1679:17:1679:21 | xs[0] | | main.rs:1636:5:1637:13 | S |
2442+
| main.rs:1679:17:1679:27 | ... .foo() | | main.rs:1636:5:1637:13 | S |
24242443
| main.rs:1679:20:1679:20 | 0 | | file:///BUILTINS/types.rs:12:1:12:15 | i32 |
24252444
| main.rs:1681:23:1681:25 | &xs | | file://:0:0:0:0 | & |
24262445
| main.rs:1681:23:1681:25 | &xs | &T | file://:0:0:0:0 | [] |
2446+
| main.rs:1681:23:1681:25 | &xs | &T | file://:0:0:0:0 | [] |
2447+
| main.rs:1681:23:1681:25 | &xs | &T.[T;...] | main.rs:1636:5:1637:13 | S |
2448+
| main.rs:1681:23:1681:25 | &xs | &T.[T] | main.rs:1636:5:1637:13 | S |
2449+
| main.rs:1681:24:1681:25 | xs | | file://:0:0:0:0 | [] |
24272450
| main.rs:1681:24:1681:25 | xs | | file://:0:0:0:0 | [] |
2451+
| main.rs:1681:24:1681:25 | xs | [T;...] | main.rs:1636:5:1637:13 | S |
2452+
| main.rs:1681:24:1681:25 | xs | [T] | main.rs:1636:5:1637:13 | S |
24282453
| main.rs:1687:5:1687:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
24292454
| main.rs:1688:5:1688:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
24302455
| main.rs:1688:20:1688:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |

0 commit comments

Comments
 (0)