Skip to content

Commit a02bf18

Browse files
committed
Rust: Type inference and path resolution for builtins
1 parent 9db38bc commit a02bf18

File tree

14 files changed

+365
-18
lines changed

14 files changed

+365
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Provides classes for builtins.
3+
*/
4+
5+
private import rust
6+
7+
/** The folder containing builtins. */
8+
class BuiltinsFolder extends Folder {
9+
BuiltinsFolder() {
10+
this.getBaseName() = "builtins" and
11+
this.getParentContainer().getBaseName() = "tools"
12+
}
13+
}
14+
15+
private class BuiltinsTypesFile extends File {
16+
BuiltinsTypesFile() {
17+
this.getBaseName() = "types.rs" and
18+
this.getParentContainer() instanceof BuiltinsFolder
19+
}
20+
}
21+
22+
/**
23+
* A builtin type, such as `bool` and `i32`.
24+
*
25+
* Builtin types are represented as structs.
26+
*/
27+
class BuiltinType extends Struct {
28+
BuiltinType() { this.getFile() instanceof BuiltinsTypesFile }
29+
30+
/** Gets the name of this type. */
31+
string getName() { result = super.getName().getText() }
32+
}
33+
34+
/** The builtin `bool` type. */
35+
class Bool extends BuiltinType {
36+
Bool() { this.getName() = "bool" }
37+
}
38+
39+
/** The builtin `char` type. */
40+
class Char extends BuiltinType {
41+
Char() { this.getName() = "char" }
42+
}
43+
44+
/** The builtin `str` type. */
45+
class Str extends BuiltinType {
46+
Str() { this.getName() = "str" }
47+
}
48+
49+
/** The builtin `i8` type. */
50+
class I8 extends BuiltinType {
51+
I8() { this.getName() = "i8" }
52+
}
53+
54+
/** The builtin `i16` type. */
55+
class I16 extends BuiltinType {
56+
I16() { this.getName() = "i16" }
57+
}
58+
59+
/** The builtin `i32` type. */
60+
class I32 extends BuiltinType {
61+
I32() { this.getName() = "i32" }
62+
}
63+
64+
/** The builtin `i64` type. */
65+
class I64 extends BuiltinType {
66+
I64() { this.getName() = "i64" }
67+
}
68+
69+
/** The builtin `i128` type. */
70+
class I128 extends BuiltinType {
71+
I128() { this.getName() = "i128" }
72+
}
73+
74+
/** The builtin `u8` type. */
75+
class U8 extends BuiltinType {
76+
U8() { this.getName() = "u8" }
77+
}
78+
79+
/** The builtin `u16` type. */
80+
class U16 extends BuiltinType {
81+
U16() { this.getName() = "u16" }
82+
}
83+
84+
/** The builtin `u32` type. */
85+
class U32 extends BuiltinType {
86+
U32() { this.getName() = "u32" }
87+
}
88+
89+
/** The builtin `u64` type. */
90+
class U64 extends BuiltinType {
91+
U64() { this.getName() = "u64" }
92+
}
93+
94+
/** The builtin `u128` type. */
95+
class U128 extends BuiltinType {
96+
U128() { this.getName() = "u128" }
97+
}
98+
99+
/** The builtin `usize` type. */
100+
class USize extends BuiltinType {
101+
USize() { this.getName() = "usize" }
102+
}
103+
104+
/** The builtin `isize` type. */
105+
class ISize extends BuiltinType {
106+
ISize() { this.getName() = "isize" }
107+
}
108+
109+
/** The builtin `f32` type. */
110+
class F32 extends BuiltinType {
111+
F32() { this.getName() = "f32" }
112+
}
113+
114+
/** The builtin `f64` type. */
115+
class F64 extends BuiltinType {
116+
F64() { this.getName() = "f64" }
117+
}

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

+17
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ abstract class ItemNode extends Locatable {
180180
or
181181
preludeEdge(this, name, result) and not declares(this, _, name)
182182
or
183+
builtinEdge(this, name, result)
184+
or
183185
name = "super" and
184186
if this instanceof Module or this instanceof SourceFile
185187
then result = this.getImmediateParentModule()
@@ -1184,6 +1186,21 @@ private predicate preludeEdge(SourceFile f, string name, ItemNode i) {
11841186
)
11851187
}
11861188

1189+
private import codeql.rust.frameworks.stdlib.Bultins as Builtins
1190+
1191+
pragma[nomagic]
1192+
private predicate builtinEdge(ModuleLikeNode m, string name, ItemNode i) {
1193+
(
1194+
m instanceof SourceFile
1195+
or
1196+
m = any(CrateItemNode c).getModuleNode()
1197+
) and
1198+
exists(SourceFileItemNode builtins |
1199+
builtins.getFile().getParentContainer() instanceof Builtins::BuiltinsFolder and
1200+
i = builtins.getASuccessorRec(name)
1201+
)
1202+
}
1203+
11871204
/** Provides predicates for debugging the path resolution implementation. */
11881205
private module Debug {
11891206
private Locatable getRelevantLocatable() {

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

+38
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,41 @@ private Type inferTryExprType(TryExpr te, TypePath path) {
885885
)
886886
}
887887

888+
private import codeql.rust.frameworks.stdlib.Bultins as Builtins
889+
890+
pragma[nomagic]
891+
StructType getBuiltinType(string name) {
892+
result = TStruct(any(Builtins::BuiltinType t | name = t.getName()))
893+
}
894+
895+
pragma[nomagic]
896+
private StructType inferLiteralType(LiteralExpr le) {
897+
le instanceof CharLiteralExpr and
898+
result = TStruct(any(Builtins::Char t))
899+
or
900+
le instanceof StringLiteralExpr and
901+
result = TStruct(any(Builtins::Str t))
902+
or
903+
le =
904+
any(IntegerLiteralExpr n |
905+
not exists(n.getSuffix()) and
906+
result = getBuiltinType("i32")
907+
or
908+
result = getBuiltinType(n.getSuffix())
909+
)
910+
or
911+
le =
912+
any(FloatLiteralExpr n |
913+
not exists(n.getSuffix()) and
914+
result = getBuiltinType("f32")
915+
or
916+
result = getBuiltinType(n.getSuffix())
917+
)
918+
or
919+
le instanceof BooleanLiteralExpr and
920+
result = TStruct(any(Builtins::Bool t))
921+
}
922+
888923
cached
889924
private module Cached {
890925
private import codeql.rust.internal.CachedStages
@@ -1026,6 +1061,9 @@ private module Cached {
10261061
result = inferRefExprType(n, path)
10271062
or
10281063
result = inferTryExprType(n, path)
1064+
or
1065+
result = inferLiteralType(n) and
1066+
path.isEmpty()
10291067
}
10301068
}
10311069

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
multiplePathResolutions
2+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
3+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
4+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
5+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
6+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
7+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
8+
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
9+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
10+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
11+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
12+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
13+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
14+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
15+
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |

rust/ql/test/library-tests/dataflow/modeled/inline-flow.expected

+15-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ models
33
| 2 | Summary: lang:core; <crate::option::Option>::unwrap; Argument[self].Field[crate::option::Option::Some(0)]; ReturnValue; value |
44
| 3 | Summary: lang:core; <crate::option::Option>::zip; Argument[0].Field[crate::option::Option::Some(0)]; ReturnValue.Field[crate::option::Option::Some(0)].Field[1]; value |
55
| 4 | Summary: lang:core; <crate::result::Result>::unwrap; Argument[self].Field[crate::result::Result::Ok(0)]; ReturnValue; value |
6-
| 5 | Summary: lang:core; crate::ptr::read; Argument[0].Reference; ReturnValue; value |
7-
| 6 | Summary: lang:core; crate::ptr::write; Argument[1]; Argument[0].Reference; value |
6+
| 5 | Summary: lang:core; <i64 as crate::clone::Clone>::clone; Argument[self].Reference; ReturnValue; value |
7+
| 6 | Summary: lang:core; crate::ptr::read; Argument[0].Reference; ReturnValue; value |
8+
| 7 | Summary: lang:core; crate::ptr::write; Argument[1]; Argument[0].Reference; value |
89
edges
910
| main.rs:12:9:12:9 | a [Some] | main.rs:13:10:13:19 | a.unwrap() | provenance | MaD:2 |
1011
| main.rs:12:9:12:9 | a [Some] | main.rs:14:13:14:13 | a [Some] | provenance | |
@@ -22,7 +23,12 @@ edges
2223
| main.rs:21:13:21:13 | a [Ok] | main.rs:21:13:21:21 | a.clone() [Ok] | provenance | generated |
2324
| main.rs:21:13:21:21 | a.clone() [Ok] | main.rs:21:9:21:9 | b [Ok] | provenance | |
2425
| main.rs:26:9:26:9 | a | main.rs:27:10:27:10 | a | provenance | |
26+
| main.rs:26:9:26:9 | a | main.rs:28:13:28:13 | a | provenance | |
2527
| main.rs:26:13:26:22 | source(...) | main.rs:26:9:26:9 | a | provenance | |
28+
| main.rs:28:9:28:9 | b | main.rs:29:10:29:10 | b | provenance | |
29+
| main.rs:28:13:28:13 | a | main.rs:28:13:28:21 | a.clone() | provenance | MaD:5 |
30+
| main.rs:28:13:28:13 | a | main.rs:28:13:28:21 | a.clone() | provenance | generated |
31+
| main.rs:28:13:28:21 | a.clone() | main.rs:28:9:28:9 | b | provenance | |
2632
| main.rs:41:13:41:13 | w [Wrapper] | main.rs:42:15:42:15 | w [Wrapper] | provenance | |
2733
| main.rs:41:17:41:41 | Wrapper {...} [Wrapper] | main.rs:41:13:41:13 | w [Wrapper] | provenance | |
2834
| main.rs:41:30:41:39 | source(...) | main.rs:41:17:41:41 | Wrapper {...} [Wrapper] | provenance | |
@@ -47,8 +53,8 @@ edges
4753
| main.rs:61:18:61:23 | TuplePat [tuple.1] | main.rs:61:22:61:22 | m | provenance | |
4854
| main.rs:61:22:61:22 | m | main.rs:63:22:63:22 | m | provenance | |
4955
| main.rs:84:29:84:29 | [post] y [&ref] | main.rs:85:33:85:33 | y [&ref] | provenance | |
50-
| main.rs:84:32:84:41 | source(...) | main.rs:84:29:84:29 | [post] y [&ref] | provenance | MaD:6 |
51-
| main.rs:85:33:85:33 | y [&ref] | main.rs:85:18:85:34 | ...::read(...) | provenance | MaD:5 |
56+
| main.rs:84:32:84:41 | source(...) | main.rs:84:29:84:29 | [post] y [&ref] | provenance | MaD:7 |
57+
| main.rs:85:33:85:33 | y [&ref] | main.rs:85:18:85:34 | ...::read(...) | provenance | MaD:6 |
5258
nodes
5359
| main.rs:12:9:12:9 | a [Some] | semmle.label | a [Some] |
5460
| main.rs:12:13:12:28 | Some(...) [Some] | semmle.label | Some(...) [Some] |
@@ -69,6 +75,10 @@ nodes
6975
| main.rs:26:9:26:9 | a | semmle.label | a |
7076
| main.rs:26:13:26:22 | source(...) | semmle.label | source(...) |
7177
| main.rs:27:10:27:10 | a | semmle.label | a |
78+
| main.rs:28:9:28:9 | b | semmle.label | b |
79+
| main.rs:28:13:28:13 | a | semmle.label | a |
80+
| main.rs:28:13:28:21 | a.clone() | semmle.label | a.clone() |
81+
| main.rs:29:10:29:10 | b | semmle.label | b |
7282
| main.rs:41:13:41:13 | w [Wrapper] | semmle.label | w [Wrapper] |
7383
| main.rs:41:17:41:41 | Wrapper {...} [Wrapper] | semmle.label | Wrapper {...} [Wrapper] |
7484
| main.rs:41:30:41:39 | source(...) | semmle.label | source(...) |
@@ -106,6 +116,7 @@ testFailures
106116
| main.rs:20:10:20:19 | a.unwrap() | main.rs:19:34:19:43 | source(...) | main.rs:20:10:20:19 | a.unwrap() | $@ | main.rs:19:34:19:43 | source(...) | source(...) |
107117
| main.rs:22:10:22:19 | b.unwrap() | main.rs:19:34:19:43 | source(...) | main.rs:22:10:22:19 | b.unwrap() | $@ | main.rs:19:34:19:43 | source(...) | source(...) |
108118
| main.rs:27:10:27:10 | a | main.rs:26:13:26:22 | source(...) | main.rs:27:10:27:10 | a | $@ | main.rs:26:13:26:22 | source(...) | source(...) |
119+
| main.rs:29:10:29:10 | b | main.rs:26:13:26:22 | source(...) | main.rs:29:10:29:10 | b | $@ | main.rs:26:13:26:22 | source(...) | source(...) |
109120
| main.rs:43:38:43:38 | n | main.rs:41:30:41:39 | source(...) | main.rs:43:38:43:38 | n | $@ | main.rs:41:30:41:39 | source(...) | source(...) |
110121
| main.rs:47:38:47:38 | n | main.rs:41:30:41:39 | source(...) | main.rs:47:38:47:38 | n | $@ | main.rs:41:30:41:39 | source(...) | source(...) |
111122
| main.rs:63:22:63:22 | m | main.rs:58:22:58:31 | source(...) | main.rs:63:22:63:22 | m | $@ | main.rs:58:22:58:31 | source(...) | source(...) |

rust/ql/test/library-tests/dataflow/modeled/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn i64_clone() {
2626
let a = source(12);
2727
sink(a); // $ hasValueFlow=12
2828
let b = a.clone();
29-
sink(b); // $ MISSING: hasValueFlow=12 - lack of builtins means that we cannot resolve clone call above, and hence not insert implicit borrow
29+
sink(b); // $ hasValueFlow=12
3030
}
3131

3232
mod my_clone {

rust/ql/test/library-tests/path-resolution/main.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ fn i() {
7575

7676
{
7777
struct Foo {
78-
x: i32, // $ MISSING: item=i32
78+
x: i32, // $ item=i32
7979
} // I30
8080

8181
let _ = Foo { x: 0 }; // $ item=I30
@@ -122,10 +122,10 @@ mod m6 {
122122
mod m7 {
123123
pub enum MyEnum {
124124
A(
125-
i32, // $ MISSING: item=i32
125+
i32, // $ item=i32
126126
), // I42
127127
B {
128-
x: i32, // $ MISSING: item=i32
128+
x: i32, // $ item=i32
129129
}, // I43
130130
C, // I44
131131
} // I41

rust/ql/test/library-tests/path-resolution/my.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ type Result<
2525
>; // my::Result
2626

2727
fn int_div(
28-
x: i32, // $ MISSING: item=i32
29-
y: i32, // $ MISSING: item=i32
30-
) -> Result<i32> // $ item=my::Result $ MISSING: item=i32
28+
x: i32, // $ item=i32
29+
y: i32, // $ item=i32
30+
) -> Result<i32> // $ item=my::Result $ item=i32
3131
{
3232
if y == 0 {
3333
return Err("Div by zero".to_string());

rust/ql/test/library-tests/path-resolution/path-resolution.expected

+6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ resolvePath
7777
| main.rs:68:5:68:8 | self | main.rs:1:1:653:2 | SourceFile |
7878
| main.rs:68:5:68:11 | ...::i | main.rs:71:1:83:1 | fn i |
7979
| main.rs:74:13:74:15 | Foo | main.rs:48:1:48:13 | struct Foo |
80+
| main.rs:78:16:78:18 | i32 | file:///BUILTINS/types.rs:12:1:12:15 | struct i32 |
8081
| main.rs:81:17:81:19 | Foo | main.rs:77:9:79:9 | struct Foo |
8182
| main.rs:85:5:85:7 | my2 | main.rs:7:1:7:8 | mod my2 |
8283
| main.rs:85:5:85:16 | ...::nested2 | my2/mod.rs:1:1:1:16 | mod nested2 |
@@ -92,6 +93,8 @@ resolvePath
9293
| main.rs:117:13:117:21 | ...::m5 | main.rs:103:1:107:1 | mod m5 |
9394
| main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f |
9495
| main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f |
96+
| main.rs:125:13:125:15 | i32 | file:///BUILTINS/types.rs:12:1:12:15 | struct i32 |
97+
| main.rs:128:16:128:18 | i32 | file:///BUILTINS/types.rs:12:1:12:15 | struct i32 |
9598
| main.rs:134:19:134:24 | MyEnum | main.rs:123:5:131:5 | enum MyEnum |
9699
| main.rs:137:17:137:22 | MyEnum | main.rs:123:5:131:5 | enum MyEnum |
97100
| main.rs:137:17:137:25 | ...::A | main.rs:124:9:126:9 | A |
@@ -352,7 +355,10 @@ resolvePath
352355
| my.rs:22:5:22:17 | ...::result | file://:0:0:0:0 | mod result |
353356
| my.rs:22:5:25:1 | ...::Result::<...> | file://:0:0:0:0 | enum Result |
354357
| my.rs:23:5:23:5 | T | my.rs:21:5:21:5 | T |
358+
| my.rs:28:8:28:10 | i32 | file:///BUILTINS/types.rs:12:1:12:15 | struct i32 |
359+
| my.rs:29:8:29:10 | i32 | file:///BUILTINS/types.rs:12:1:12:15 | struct i32 |
355360
| my.rs:30:6:30:16 | Result::<...> | my.rs:20:1:25:2 | type Result<...> |
361+
| my.rs:30:13:30:15 | i32 | file:///BUILTINS/types.rs:12:1:12:15 | struct i32 |
356362
| my.rs:33:16:33:18 | Err | file://:0:0:0:0 | Err |
357363
| my.rs:35:5:35:6 | Ok | file://:0:0:0:0 | Ok |
358364
| my/nested.rs:9:13:9:13 | f | my/nested.rs:3:9:5:9 | fn f |

rust/ql/test/library-tests/path-resolution/path-resolution.ql

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ import TestUtils
55

66
query predicate mod(Module m) { toBeTested(m) }
77

8-
class ItemNodeLoc extends Locatable instanceof ItemNode {
8+
final private class ItemNodeFinal = ItemNode;
9+
10+
class ItemNodeLoc extends ItemNodeFinal {
911
predicate hasLocationInfo(
1012
string filepath, int startline, int startcolumn, int endline, int endcolumn
1113
) {
1214
exists(string file |
13-
super.getLocation().hasLocationInfo(file, startline, startcolumn, endline, endcolumn) and
14-
filepath = file.regexpReplaceAll("^/.*/.rustup/toolchains/[^/]+/", "/RUSTUP_HOME/toolchain/")
15+
this.getLocation().hasLocationInfo(file, startline, startcolumn, endline, endcolumn) and
16+
filepath =
17+
file.regexpReplaceAll("^/.*/.rustup/toolchains/[^/]+/", "/RUSTUP_HOME/toolchain/")
18+
.regexpReplaceAll("^/.*/tools/builtins/", "/BUILTINS/")
1519
)
1620
}
1721
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -980,10 +980,10 @@ mod try_expressions {
980980

981981
mod builtins {
982982
pub fn f() {
983-
let x: i32 = 1; // $ MISSING: type=x:i32
984-
let y = 2; // $ MISSING: type=y:i32
983+
let x: i32 = 1; // $ type=x:i32
984+
let y = 2; // $ type=y:i32
985985
let z = x + y; // $ MISSING: type=z:i32
986-
let z = x.abs(); // $ MISSING: method=abs $ MISSING: type=z:i32
986+
let z = x.abs(); // $ method=abs $ type=z:i32
987987
'c';
988988
"Hello";
989989
123.0f64;

0 commit comments

Comments
 (0)