Skip to content

Commit 649481e

Browse files
authored
Merge pull request #19657 from hvitved/rust/type-inference-index-expr-simple
Rust: Simple type inference for index expressions
2 parents 23cbc6a + 133aca0 commit 649481e

File tree

6 files changed

+248
-9
lines changed

6 files changed

+248
-9
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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,58 @@ mod impl_trait {
17531753
}
17541754
}
17551755

1756+
mod indexers {
1757+
use std::ops::Index;
1758+
1759+
#[derive(Debug)]
1760+
struct S;
1761+
1762+
impl S {
1763+
fn foo(&self) -> Self {
1764+
S
1765+
}
1766+
}
1767+
1768+
#[derive(Debug)]
1769+
struct MyVec<T> {
1770+
data: Vec<T>,
1771+
}
1772+
1773+
impl<T> MyVec<T> {
1774+
fn new() -> Self {
1775+
MyVec { data: Vec::new() }
1776+
}
1777+
1778+
fn push(&mut self, value: T) {
1779+
self.data.push(value); // $ fieldof=MyVec method=push
1780+
}
1781+
}
1782+
1783+
impl<T> Index<usize> for MyVec<T> {
1784+
type Output = T;
1785+
1786+
// MyVec::index
1787+
fn index(&self, index: usize) -> &Self::Output {
1788+
&self.data[index] // $ fieldof=MyVec
1789+
}
1790+
}
1791+
1792+
fn analyze_slice(slice: &[S]) {
1793+
let x = slice[0].foo(); // $ method=foo type=x:S
1794+
}
1795+
1796+
pub fn f() {
1797+
let mut vec = MyVec::new(); // $ type=vec:T.S
1798+
vec.push(S); // $ method=push
1799+
vec[0].foo(); // $ MISSING: method=foo -- type inference does not support the `Index` trait yet
1800+
1801+
let xs: [S; 1] = [S];
1802+
let x = xs[0].foo(); // $ method=foo type=x:S
1803+
1804+
analyze_slice(&xs);
1805+
}
1806+
}
1807+
17561808
fn main() {
17571809
field_access::f();
17581810
method_impl::f();
@@ -1774,4 +1826,5 @@ fn main() {
17741826
operators::f();
17751827
async_::f();
17761828
impl_trait::f();
1829+
indexers::f();
17771830
}

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

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,9 +2525,91 @@ inferType
25252525
| main.rs:1752:13:1752:13 | d | | main.rs:1700:5:1700:14 | S2 |
25262526
| main.rs:1752:17:1752:34 | uses_my_trait2(...) | | main.rs:1700:5:1700:14 | S2 |
25272527
| main.rs:1752:32:1752:33 | S1 | | main.rs:1699:5:1699:14 | S1 |
2528-
| main.rs:1758:5:1758:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
2529-
| main.rs:1759:5:1759:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
2530-
| main.rs:1759:20:1759:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
2531-
| main.rs:1759:41:1759:59 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
2532-
| main.rs:1775:5:1775:15 | ...::f(...) | | {EXTERNAL LOCATION} | trait Future |
2528+
| main.rs:1763:16:1763:20 | SelfParam | | file://:0:0:0:0 | & |
2529+
| main.rs:1763:16:1763:20 | SelfParam | &T | main.rs:1759:5:1760:13 | S |
2530+
| main.rs:1763:31:1765:9 | { ... } | | main.rs:1759:5:1760:13 | S |
2531+
| main.rs:1764:13:1764:13 | S | | main.rs:1759:5:1760:13 | S |
2532+
| main.rs:1774:26:1776:9 | { ... } | | main.rs:1768:5:1771:5 | MyVec |
2533+
| main.rs:1774:26:1776:9 | { ... } | T | main.rs:1773:10:1773:10 | T |
2534+
| main.rs:1775:13:1775:38 | MyVec {...} | | main.rs:1768:5:1771:5 | MyVec |
2535+
| main.rs:1775:13:1775:38 | MyVec {...} | T | main.rs:1773:10:1773:10 | T |
2536+
| main.rs:1775:27:1775:36 | ...::new(...) | | {EXTERNAL LOCATION} | Vec |
2537+
| main.rs:1775:27:1775:36 | ...::new(...) | T | main.rs:1773:10:1773:10 | T |
2538+
| main.rs:1778:17:1778:25 | SelfParam | | file://:0:0:0:0 | & |
2539+
| main.rs:1778:17:1778:25 | SelfParam | &T | main.rs:1768:5:1771:5 | MyVec |
2540+
| main.rs:1778:17:1778:25 | SelfParam | &T.T | main.rs:1773:10:1773:10 | T |
2541+
| main.rs:1778:28:1778:32 | value | | main.rs:1773:10:1773:10 | T |
2542+
| main.rs:1779:13:1779:16 | self | | file://:0:0:0:0 | & |
2543+
| main.rs:1779:13:1779:16 | self | &T | main.rs:1768:5:1771:5 | MyVec |
2544+
| main.rs:1779:13:1779:16 | self | &T.T | main.rs:1773:10:1773:10 | T |
2545+
| main.rs:1779:13:1779:21 | self.data | | {EXTERNAL LOCATION} | Vec |
2546+
| main.rs:1779:13:1779:21 | self.data | T | main.rs:1773:10:1773:10 | T |
2547+
| main.rs:1779:28:1779:32 | value | | main.rs:1773:10:1773:10 | T |
2548+
| main.rs:1787:18:1787:22 | SelfParam | | file://:0:0:0:0 | & |
2549+
| main.rs:1787:18:1787:22 | SelfParam | &T | main.rs:1768:5:1771:5 | MyVec |
2550+
| main.rs:1787:18:1787:22 | SelfParam | &T.T | main.rs:1783:10:1783:10 | T |
2551+
| main.rs:1787:25:1787:29 | index | | {EXTERNAL LOCATION} | usize |
2552+
| main.rs:1787:56:1789:9 | { ... } | | file://:0:0:0:0 | & |
2553+
| main.rs:1787:56:1789:9 | { ... } | &T | main.rs:1783:10:1783:10 | T |
2554+
| main.rs:1788:13:1788:29 | &... | | file://:0:0:0:0 | & |
2555+
| main.rs:1788:13:1788:29 | &... | &T | main.rs:1783:10:1783:10 | T |
2556+
| main.rs:1788:14:1788:17 | self | | file://:0:0:0:0 | & |
2557+
| main.rs:1788:14:1788:17 | self | &T | main.rs:1768:5:1771:5 | MyVec |
2558+
| main.rs:1788:14:1788:17 | self | &T.T | main.rs:1783:10:1783:10 | T |
2559+
| main.rs:1788:14:1788:22 | self.data | | {EXTERNAL LOCATION} | Vec |
2560+
| main.rs:1788:14:1788:22 | self.data | T | main.rs:1783:10:1783:10 | T |
2561+
| main.rs:1788:14:1788:29 | ...[index] | | main.rs:1783:10:1783:10 | T |
2562+
| main.rs:1788:24:1788:28 | index | | {EXTERNAL LOCATION} | usize |
2563+
| 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 |
2567+
| 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 |
2572+
| main.rs:1793:23:1793:23 | 0 | | {EXTERNAL LOCATION} | i32 |
2573+
| main.rs:1797:13:1797:19 | mut vec | | main.rs:1768:5:1771:5 | MyVec |
2574+
| main.rs:1797:13:1797:19 | mut vec | T | main.rs:1759:5:1760:13 | S |
2575+
| main.rs:1797:23:1797:34 | ...::new(...) | | main.rs:1768:5:1771:5 | MyVec |
2576+
| main.rs:1797:23:1797:34 | ...::new(...) | T | main.rs:1759:5:1760:13 | S |
2577+
| main.rs:1798:9:1798:11 | vec | | main.rs:1768:5:1771:5 | MyVec |
2578+
| main.rs:1798:9:1798:11 | vec | T | main.rs:1759:5:1760:13 | S |
2579+
| main.rs:1798:18:1798:18 | S | | main.rs:1759:5:1760:13 | S |
2580+
| main.rs:1799:9:1799:11 | vec | | main.rs:1768:5:1771:5 | MyVec |
2581+
| main.rs:1799:9:1799:11 | vec | T | main.rs:1759:5:1760:13 | S |
2582+
| main.rs:1799:13:1799:13 | 0 | | {EXTERNAL LOCATION} | i32 |
2583+
| 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 |
2587+
| main.rs:1801:21:1801:21 | 1 | | {EXTERNAL LOCATION} | i32 |
2588+
| 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 |
2592+
| 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 | [] |
2595+
| 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 |
2600+
| main.rs:1802:20:1802:20 | 0 | | {EXTERNAL LOCATION} | i32 |
2601+
| main.rs:1804:23:1804:25 | &xs | | file://:0:0:0:0 | & |
2602+
| 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 | [] |
2607+
| 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 |
2610+
| main.rs:1810:5:1810:20 | ...::f(...) | | main.rs:67:5:67:21 | Foo |
2611+
| main.rs:1811:5:1811:60 | ...::g(...) | | main.rs:67:5:67:21 | Foo |
2612+
| main.rs:1811:20:1811:38 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
2613+
| main.rs:1811:41:1811:59 | ...::Foo {...} | | main.rs:67:5:67:21 | Foo |
2614+
| main.rs:1827:5:1827:15 | ...::f(...) | | {EXTERNAL LOCATION} | trait Future |
25332615
testFailures

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,13 @@ module TypeTest implements TestSig {
5555
exists(AstNode n, TypePath path, Type t |
5656
t = TypeInference::inferType(n, path) and
5757
location = n.getLocation() and
58-
element = n.toString() and
5958
if path.isEmpty()
6059
then value = element + ":" + t
6160
else value = element + ":" + path.toString() + "." + t.toString()
61+
|
62+
element = n.toString()
63+
or
64+
element = n.(IdentPat).getName().getText()
6265
)
6366
}
6467
}

0 commit comments

Comments
 (0)