Skip to content

Commit 3fc9da7

Browse files
authored
Merge pull request #19475 from hvitved/rust/literal-sub-classes
Rust: Add `LiteralExpr` sub classes
2 parents 169ae19 + ae54c62 commit 3fc9da7

File tree

6 files changed

+320
-0
lines changed

6 files changed

+320
-0
lines changed
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

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

rust/ql/lib/rust.qll

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

0 commit comments

Comments
 (0)