Skip to content

Commit 60cc63f

Browse files
authored
Merge pull request #19474 from hvitved/rust/builtins-resolution
Rust: Type inference and path resolution for builtins
2 parents 51229a6 + 9d37597 commit 60cc63f

File tree

15 files changed

+651
-288
lines changed

15 files changed

+651
-288
lines changed

rust/ql/lib/codeql/rust/elements/internal/LiteralExprImpl.qll

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,14 @@ module Impl {
7676
/**
7777
* A number literal.
7878
*/
79-
abstract class NumberLiteralExpr extends LiteralExpr { }
79+
abstract class NumberLiteralExpr extends LiteralExpr {
80+
/**
81+
* Get the suffix of this number literal, if any.
82+
*
83+
* For example, `42u8` has the suffix `u8`.
84+
*/
85+
abstract string getSuffix();
86+
}
8087

8188
// https://doc.rust-lang.org/reference/tokens.html#integer-literals
8289
private module IntegerLiteralRegexs {
@@ -126,12 +133,7 @@ module Impl {
126133
class IntegerLiteralExpr extends NumberLiteralExpr {
127134
IntegerLiteralExpr() { this.getTextValue().regexpMatch(IntegerLiteralRegexs::integerLiteral()) }
128135

129-
/**
130-
* Get the suffix of this integer literal, if any.
131-
*
132-
* For example, `42u8` has the suffix `u8`.
133-
*/
134-
string getSuffix() {
136+
override string getSuffix() {
135137
exists(string s, string reg |
136138
s = this.getTextValue() and
137139
reg = IntegerLiteralRegexs::integerLiteral() and
@@ -193,12 +195,7 @@ module Impl {
193195
not this instanceof IntegerLiteralExpr
194196
}
195197

196-
/**
197-
* Get the suffix of this floating-point literal, if any.
198-
*
199-
* For example, `42.0f32` has the suffix `f32`.
200-
*/
201-
string getSuffix() {
198+
override string getSuffix() {
202199
exists(string s, string reg |
203200
reg =
204201
IntegerLiteralRegexs::paren(FloatLiteralRegexs::floatLiteralSuffix1()) + "|" +
Lines changed: 117 additions & 0 deletions
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

Lines changed: 17 additions & 0 deletions
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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,36 @@ 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+
private StructType inferLiteralType(LiteralExpr le) {
892+
exists(Builtins::BuiltinType t | result = TStruct(t) |
893+
le instanceof CharLiteralExpr and
894+
t instanceof Builtins::Char
895+
or
896+
le instanceof StringLiteralExpr and
897+
t instanceof Builtins::Str
898+
or
899+
le =
900+
any(NumberLiteralExpr ne |
901+
t.getName() = ne.getSuffix()
902+
or
903+
not exists(ne.getSuffix()) and
904+
(
905+
ne instanceof IntegerLiteralExpr and
906+
t instanceof Builtins::I32
907+
or
908+
ne instanceof FloatLiteralExpr and
909+
t instanceof Builtins::F64
910+
)
911+
)
912+
or
913+
le instanceof BooleanLiteralExpr and
914+
t instanceof Builtins::Bool
915+
)
916+
}
917+
888918
cached
889919
private module Cached {
890920
private import codeql.rust.internal.CachedStages
@@ -1026,6 +1056,9 @@ private module Cached {
10261056
result = inferRefExprType(n, path)
10271057
or
10281058
result = inferTryExprType(n, path)
1059+
or
1060+
result = inferLiteralType(n) and
1061+
path.isEmpty()
10291062
}
10301063
}
10311064

Lines changed: 15 additions & 0 deletions
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

Lines changed: 15 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ fn i() {
7575

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

8181
let _ = Foo { x: 0 }; // $ item=I30
@@ -121,9 +121,13 @@ mod m6 {
121121

122122
mod m7 {
123123
pub enum MyEnum {
124-
A(i32), // I42
125-
B { x: i32 }, // I43
126-
C, // I44
124+
A(
125+
i32, // $ item=i32
126+
), // I42
127+
B {
128+
x: i32, // $ item=i32
129+
}, // I43
130+
C, // I44
127131
} // I41
128132

129133
#[rustfmt::skip]

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

Lines changed: 3 additions & 3 deletions
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, //
29-
y: i32,
30-
) -> Result<i32> // $ item=my::Result
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());

0 commit comments

Comments
 (0)