Skip to content

Commit cd01bd0

Browse files
committed
Rust: Add LiteralExpr sub classes
1 parent 14ede4e commit cd01bd0

File tree

6 files changed

+323
-0
lines changed

6 files changed

+323
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/** Provides sub classes of literal expressions. */
2+
3+
private import internal.LiteralExprImpl
4+
5+
final class CharLiteralExpr = Impl::CharLiteralExpr;
6+
7+
final class StringLiteralExpr = Impl::StringLiteralExpr;
8+
9+
final class NumberLiteralExpr = Impl::NumberLiteralExpr;
10+
11+
final class IntegerLiteralExpr = Impl::IntegerLiteralExpr;
12+
13+
final class FloatLiteralExpr = Impl::FloatLiteralExpr;
14+
15+
final class BooleanLiteralExpr = Impl::BooleanLiteralExpr;

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

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,191 @@ module Impl {
4242
)
4343
}
4444
}
45+
46+
/**
47+
* A [character literal][1]. For example:
48+
*
49+
* ```rust
50+
* 'x';
51+
* ```
52+
*
53+
* [1]: https://doc.rust-lang.org/reference/tokens.html#character-literals
54+
*/
55+
class CharLiteralExpr extends LiteralExpr {
56+
CharLiteralExpr() {
57+
// todo: proper implementation
58+
this.getTextValue().regexpMatch("'.*'")
59+
}
60+
61+
override string getAPrimaryQlClass() { result = "CharLiteralExpr" }
62+
}
63+
64+
/**
65+
* A [string literal][1]. For example:
66+
*
67+
* ```rust
68+
* "Hello, world!";
69+
* ```
70+
*
71+
* [1]: https://doc.rust-lang.org/reference/tokens.html#string-literals
72+
*/
73+
class StringLiteralExpr extends LiteralExpr {
74+
StringLiteralExpr() {
75+
// todo: proper implementation
76+
this.getTextValue().regexpMatch("r?#*\".*\"#*")
77+
}
78+
79+
override string getAPrimaryQlClass() { result = "StringLiteralExpr" }
80+
}
81+
82+
/**
83+
* A number literal.
84+
*/
85+
abstract class NumberLiteralExpr extends LiteralExpr { }
86+
87+
// https://doc.rust-lang.org/reference/tokens.html#integer-literals
88+
private module IntegerLiteralRegexs {
89+
bindingset[s]
90+
string paren(string s) { result = "(" + s + ")" }
91+
92+
string integerLiteral() {
93+
result =
94+
paren(paren(decLiteral()) + "|" + paren(binLiteral()) + "|" + paren(octLiteral()) + "|" +
95+
paren(hexLiteral())) + paren(suffix()) + "?"
96+
}
97+
98+
private string suffix() { result = "u8|i8|u16|i16|u32|i32|u64|i64|u128|i128|usize|isize" }
99+
100+
string decLiteral() { result = decDigit() + "(" + decDigit() + "|_)*" }
101+
102+
string binLiteral() {
103+
result = "0b(" + binDigit() + "|_)*" + binDigit() + "(" + binDigit() + "|_)*"
104+
}
105+
106+
string octLiteral() {
107+
result = "0o(" + octDigit() + "|_)*" + octDigit() + "(" + octDigit() + "|_)*"
108+
}
109+
110+
string hexLiteral() {
111+
result = "0x(" + hexDigit() + "|_)*" + hexDigit() + "(" + hexDigit() + "|_)*"
112+
}
113+
114+
string decDigit() { result = "[0-9]" }
115+
116+
string binDigit() { result = "[01]" }
117+
118+
string octDigit() { result = "[0-7]" }
119+
120+
string hexDigit() { result = "[0-9a-fA-F]" }
121+
}
122+
123+
/**
124+
* An [integer literal][1]. For example:
125+
*
126+
* ```rust
127+
* 42;
128+
* ```
129+
*
130+
* [1]: https://doc.rust-lang.org/reference/tokens.html#integer-literals
131+
*/
132+
class IntegerLiteralExpr extends NumberLiteralExpr {
133+
IntegerLiteralExpr() { this.getTextValue().regexpMatch(IntegerLiteralRegexs::integerLiteral()) }
134+
135+
/**
136+
* Get the suffix of this integer literal, if any.
137+
*
138+
* For example, `42u8` has the suffix `u8`.
139+
*/
140+
string getSuffix() {
141+
exists(string s, string reg, int last |
142+
s = this.getTextValue() and
143+
reg = IntegerLiteralRegexs::integerLiteral() and
144+
last = strictcount(reg.indexOf("(")) and
145+
result = s.regexpCapture(reg, last)
146+
)
147+
}
148+
149+
override string getAPrimaryQlClass() { result = "IntegerLiteralExpr" }
150+
}
151+
152+
// https://doc.rust-lang.org/reference/tokens.html#floating-point-literals
153+
private module FloatLiteralRegexs {
154+
private import IntegerLiteralRegexs
155+
156+
string floatLiteral() {
157+
result =
158+
paren(decLiteral() + "\\.") + "|" + paren(floatLiteralSuffix1()) + "|" +
159+
paren(floatLiteralSuffix2())
160+
}
161+
162+
string floatLiteralSuffix1() {
163+
result = decLiteral() + "\\." + decLiteral() + paren(suffix()) + "?"
164+
}
165+
166+
string floatLiteralSuffix2() {
167+
result =
168+
decLiteral() + paren("\\." + decLiteral()) + "?" + paren(exponent()) + paren(suffix()) + "?"
169+
}
170+
171+
string integerSuffixLiteral() {
172+
result =
173+
paren(paren(decLiteral()) + "|" + paren(binLiteral()) + "|" + paren(octLiteral()) + "|" +
174+
paren(hexLiteral())) + paren(suffix())
175+
}
176+
177+
private string suffix() { result = "f32|f64" }
178+
179+
string exponent() {
180+
result = "(e|E)(\\+|-)?(" + decDigit() + "|_)*" + decDigit() + "(" + decDigit() + "|_)*"
181+
}
182+
}
183+
184+
/**
185+
* A [floating-point literal][1]. For example:
186+
*
187+
* ```rust
188+
* 42.0;
189+
* ```
190+
*
191+
* [1]: https://doc.rust-lang.org/reference/tokens.html#floating-point-literals
192+
*/
193+
class FloatLiteralExpr extends NumberLiteralExpr {
194+
FloatLiteralExpr() {
195+
this.getTextValue()
196+
.regexpMatch([
197+
FloatLiteralRegexs::floatLiteral(), FloatLiteralRegexs::integerSuffixLiteral()
198+
]) and
199+
// E.g. `0x01_f32` is an integer, not a float
200+
not this instanceof IntegerLiteralExpr
201+
}
202+
203+
/**
204+
* Get the suffix of this floating-point literal, if any.
205+
*
206+
* For example, `42.0f32` has the suffix `f32`.
207+
*/
208+
string getSuffix() {
209+
exists(string s, string reg, int last |
210+
s = this.getTextValue() and
211+
reg =
212+
[
213+
FloatLiteralRegexs::floatLiteralSuffix1(), FloatLiteralRegexs::floatLiteralSuffix2(),
214+
FloatLiteralRegexs::integerSuffixLiteral()
215+
] and
216+
last = strictcount(reg.indexOf("(")) and
217+
result = s.regexpCapture(reg, last)
218+
)
219+
}
220+
221+
override string getAPrimaryQlClass() { result = "FloatLiteralExpr" }
222+
}
223+
224+
/**
225+
* A Boolean literal. Either `true` or `false`.
226+
*/
227+
class BooleanLiteralExpr extends LiteralExpr {
228+
BooleanLiteralExpr() { this.getTextValue() = ["false", "true"] }
229+
230+
override string getAPrimaryQlClass() { result = "BooleanLiteralExpr" }
231+
}
45232
}

rust/ql/lib/rust.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import codeql.rust.elements
44
import codeql.Locations
55
import codeql.files.FileSystem
66
import codeql.rust.elements.AssignmentOperation
7+
import codeql.rust.elements.LiteralExprExt
78
import codeql.rust.elements.LogicalOperation
89
import codeql.rust.elements.AsyncBlockExpr
910
import codeql.rust.elements.Variable
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
charLiteral
2+
| literal.rs:2:5:2:7 | 'a' |
3+
| literal.rs:3:5:3:7 | 'b' |
4+
stringLiteral
5+
| literal.rs:8:5:8:9 | "foo" |
6+
| literal.rs:9:5:9:10 | r"foo" |
7+
| literal.rs:10:5:10:13 | "\\"foo\\"" |
8+
| literal.rs:11:5:11:14 | r#""foo""# |
9+
| literal.rs:13:5:13:18 | "foo #\\"# bar" |
10+
| literal.rs:14:5:14:22 | r##"foo #"# bar"## |
11+
| literal.rs:16:5:16:10 | "\\x52" |
12+
| literal.rs:17:5:17:7 | "R" |
13+
| literal.rs:18:5:18:8 | r"R" |
14+
| literal.rs:19:5:19:11 | "\\\\x52" |
15+
| literal.rs:20:5:20:11 | r"\\x52" |
16+
integerLiteral
17+
| literal.rs:25:5:25:7 | 123 | |
18+
| literal.rs:26:5:26:10 | 123i32 | i32 |
19+
| literal.rs:27:5:27:10 | 123u32 | u32 |
20+
| literal.rs:28:5:28:11 | 123_u32 | u32 |
21+
| literal.rs:30:5:30:8 | 0xff | |
22+
| literal.rs:31:5:31:11 | 0xff_u8 | u8 |
23+
| literal.rs:32:5:32:12 | 0x01_f32 | |
24+
| literal.rs:33:5:33:11 | 0x01_e3 | |
25+
| literal.rs:35:5:35:8 | 0o70 | |
26+
| literal.rs:36:5:36:12 | 0o70_i16 | i16 |
27+
| literal.rs:38:5:38:25 | 0b1111_1111_1001_0000 | |
28+
| literal.rs:39:5:39:28 | 0b1111_1111_1001_0000i64 | i64 |
29+
| literal.rs:40:5:40:15 | 0b________1 | |
30+
| literal.rs:42:5:42:10 | 0usize | usize |
31+
| literal.rs:45:5:46:10 | 128_i8 | i8 |
32+
| literal.rs:47:5:48:10 | 256_u8 | u8 |
33+
floatLiteral
34+
| literal.rs:53:5:53:8 | 5f32 | f32 |
35+
| literal.rs:55:5:55:12 | 123.0f64 | f64 |
36+
| literal.rs:56:5:56:10 | 0.1f64 | f64 |
37+
| literal.rs:57:5:57:10 | 0.1f32 | f32 |
38+
| literal.rs:58:5:58:14 | 12E+99_f64 | f64 |
39+
| literal.rs:59:18:59:19 | 2. | |
40+
booleanLiteral
41+
| literal.rs:63:5:63:8 | true |
42+
| literal.rs:64:5:64:9 | false |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import rust
2+
3+
query predicate charLiteral(CharLiteralExpr e) { any() }
4+
5+
query predicate stringLiteral(StringLiteralExpr e) { any() }
6+
7+
query predicate integerLiteral(IntegerLiteralExpr e, string suffix) {
8+
suffix = concat(e.getSuffix())
9+
}
10+
11+
query predicate floatLiteral(FloatLiteralExpr e, string suffix) { suffix = concat(e.getSuffix()) }
12+
13+
query predicate booleanLiteral(BooleanLiteralExpr e) { any() }
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
fn char_literals() {
2+
'a';
3+
'b';
4+
}
5+
6+
fn string_literals() {
7+
// from https://doc.rust-lang.org/reference/tokens.html#string-literals
8+
"foo";
9+
r"foo"; // foo
10+
"\"foo\"";
11+
r#""foo""#; // "foo"
12+
13+
"foo #\"# bar";
14+
r##"foo #"# bar"##; // foo #"# bar
15+
16+
"\x52";
17+
"R";
18+
r"R"; // R
19+
"\\x52";
20+
r"\x52"; // \x52
21+
}
22+
23+
fn integer_literals() {
24+
// from https://doc.rust-lang.org/reference/tokens.html#integer-literals
25+
123;
26+
123i32;
27+
123u32;
28+
123_u32;
29+
30+
0xff;
31+
0xff_u8;
32+
0x01_f32; // integer 7986, not floating-point 1.0
33+
0x01_e3; // integer 483, not floating-point 1000.0
34+
35+
0o70;
36+
0o70_i16;
37+
38+
0b1111_1111_1001_0000;
39+
0b1111_1111_1001_0000i64;
40+
0b________1;
41+
42+
0usize;
43+
44+
// These are too big for their type, but are accepted as literal expressions.
45+
#[allow(overflowing_literals)]
46+
128_i8;
47+
#[allow(overflowing_literals)]
48+
256_u8;
49+
}
50+
51+
fn float_literals() {
52+
// This is an integer literal, accepted as a floating-point literal expression.
53+
5f32;
54+
55+
123.0f64;
56+
0.1f64;
57+
0.1f32;
58+
12E+99_f64;
59+
let x: f64 = 2.;
60+
}
61+
62+
fn boolean_literals() {
63+
true;
64+
false;
65+
}

0 commit comments

Comments
 (0)