Skip to content

Commit bfa6530

Browse files
authored
Commit 9: Fix hasLocationInfo for all string literal forms; fix unicode escape
RegexTreeView.qll - hasLocationInfo fix (RULE 4): - Add regexpContentOffset(RegExp re) private helper that computes the correct number of source chars before the first content char, from getValueText(): - Plain "...": offset 1 - L"...": offset 2 (L" prefix) - u8"...": offset 3 (u8" prefix) - R"(...)": offset 3 (R"( opener) - R"x(...)x": offset 4 (R"x( with custom delim) - LR"(...)": offset 4 (LR"() - Uses vt.matches("%R\"%(%") to detect raw strings; finds '(' position - For non-raw, finds '"' position - Replace hardcoded `+ 1` with `+ regexpContentOffset(re)` in hasLocationInfo - Update comment: documents plain/encoding/raw/combined forms, notes that escaped non-raw strings are approximate (mirroring Java/Python) test.cpp: - Fix r_uni: change "\\u{9879}" to "\\u9879" — braced \u{...} is not standard C++ regex syntax; the 4-digit \uHHHH form is valid ECMAScript \u escape Regenerated all 3 .expected files via codeql test run --learn (CodeQL 2.26.1). All 3 tests pass. Location test confirms: - Plain "a+b" (line 144): litStartCol 22 → termStartCol 23 (22+1) UNCHANGED ✓ - Raw R"(a+b)" (line 147): litStartCol 20 → termStartCol 23 (20+3) FIXED ✓ - Raw R"x(a+b)x" (line 156): litStartCol 21 → termStartCol 25 (21+4) FIXED ✓ - L"a+b" (line 159): litStartCol 22 → termStartCol 24 (22+2) FIXED ✓ - LR"(a+b)" (line 162): litStartCol 26 → termStartCol 30 (26+4) FIXED ✓
1 parent e7ccb1f commit bfa6530

5 files changed

Lines changed: 127 additions & 85 deletions

File tree

cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,36 @@ private module Impl implements RegexTreeViewSig {
114114
override string getAPrimaryQlClass() { result = "RegExpLiteral" }
115115
}
116116

117+
/**
118+
* Gets the number of source characters from the start of the string literal `re`
119+
* to the first content character (i.e., past the opening delimiter).
120+
*
121+
* This is derived from the raw source spelling via `StringLiteral.getValueText()`:
122+
* - Plain `"..."`: offset 1 (just the `"`)
123+
* - Encoding prefix `L"..."`: offset 2 (`L"`)
124+
* - Encoding prefix `u8"..."`: offset 3 (`u8"`)
125+
* - Raw `R"(...)"` offset 3 (`R"(`)
126+
* - Custom-delim raw `R"x(...)x"` offset 4 (`R"x(` — delim length 1)
127+
* - Combined `LR"(...)"` offset 4 (`LR"(`)
128+
*
129+
* For non-raw strings, the value-offset maps 1:1 to source columns only when
130+
* there are no escape sequences; escaped non-raw strings are approximate
131+
* (mirroring Java/Python regex libraries).
132+
*/
133+
private int regexpContentOffset(RegExp re) {
134+
exists(string vt | vt = re.getValueText() |
135+
// Raw string: find the '(' that opens the raw content.
136+
// getValueText() for raw strings looks like: [prefix]R"[delim](...)[delim]"
137+
// The opening '(' is after the optional prefix, 'R', '"', and custom delimiter.
138+
vt.matches("%R\"%(%") and
139+
result = 1 + min(int i | vt.charAt(i) = "(" and i >= 1)
140+
or
141+
// Non-raw string: find the opening '"'.
142+
not vt.matches("%R\"%(%") and
143+
result = 1 + min(int i | vt.charAt(i) = "\"")
144+
)
145+
}
146+
117147
/**
118148
* A regular expression term, that is, a syntactic part of a regular expression.
119149
*/
@@ -211,18 +241,30 @@ private module Impl implements RegexTreeViewSig {
211241
*/
212242
Location getLocation() { result = re.getLocation() }
213243

214-
/** Holds if this term is found at the specified location offsets.
244+
/**
245+
* Holds if this term is found at the specified location offsets.
246+
*
247+
* The content offset is computed from `StringLiteral.getValueText()` (the raw source
248+
* spelling including delimiters), so it is correct for:
249+
* - Plain `"..."`: offset 1 (`"`)
250+
* - Encoding prefixes `L"..."`: offset 2 (`L"`)
251+
* - Encoding prefix `u8"..."`: offset 3 (`u8"`)
252+
* - Raw `R"(...)"` offset 3 (`R"(`)
253+
* - Custom-delim raw `R"x(...)x"`: offset 4+len(delim) (`R"` + delim + `(`)
254+
* - Combined, e.g. `LR"(...)"` offset 4 (`LR"(`)
215255
*
216-
* Note: for commit 2, this uses an approximate offset of 1 for the
217-
* opening delimiter. Commits 8-9 fix this for all string literal forms.
256+
* Note: for non-raw strings, the value-string offset equals the source-column offset
257+
* only when there are no escape sequences. Escaped characters are an approximation,
258+
* mirroring the Java/Python regex libraries.
218259
*/
219260
predicate hasLocationInfo(
220261
string filepath, int startline, int startcolumn, int endline, int endcolumn
221262
) {
222-
exists(int re_start |
263+
exists(int re_start, int offset |
223264
re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, _) and
224-
startcolumn = re_start + 1 + start and
225-
endcolumn = re_start + 1 + end - 1
265+
offset = regexpContentOffset(re) and
266+
startcolumn = re_start + offset + start and
267+
endcolumn = re_start + offset + end - 1
226268
)
227269
}
228270

cpp/ql/test/library-tests/regex/locations.expected

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,36 @@
22
| test.cpp:144:23:144:24 | a+ | 22 | 0 | 2 | 23 | 24 |
33
| test.cpp:144:23:144:25 | a+b | 22 | 0 | 3 | 23 | 25 |
44
| test.cpp:144:25:144:25 | b | 22 | 2 | 3 | 25 | 25 |
5-
| test.cpp:147:21:147:21 | a | 20 | 0 | 1 | 21 | 21 |
6-
| test.cpp:147:21:147:22 | a+ | 20 | 0 | 2 | 21 | 22 |
7-
| test.cpp:147:21:147:23 | a+b | 20 | 0 | 3 | 21 | 23 |
8-
| test.cpp:147:23:147:23 | b | 20 | 2 | 3 | 23 | 23 |
9-
| test.cpp:150:22:150:23 | \\s | 21 | 0 | 2 | 22 | 23 |
10-
| test.cpp:150:22:150:24 | \\s+ | 21 | 0 | 3 | 22 | 24 |
11-
| test.cpp:150:22:150:25 | \\s+$ | 21 | 0 | 4 | 22 | 25 |
12-
| test.cpp:150:25:150:25 | $ | 21 | 3 | 4 | 25 | 25 |
13-
| test.cpp:153:22:153:23 | \\( | 21 | 0 | 2 | 22 | 23 |
14-
| test.cpp:153:22:153:35 | \\(([,\\w]+)+\\)$ | 21 | 0 | 14 | 22 | 35 |
15-
| test.cpp:153:24:153:31 | ([,\\w]+) | 21 | 2 | 10 | 24 | 31 |
16-
| test.cpp:153:24:153:32 | ([,\\w]+)+ | 21 | 2 | 11 | 24 | 32 |
17-
| test.cpp:153:25:153:29 | [,\\w] | 21 | 3 | 8 | 25 | 29 |
18-
| test.cpp:153:25:153:30 | [,\\w]+ | 21 | 3 | 9 | 25 | 30 |
19-
| test.cpp:153:26:153:26 | , | 21 | 4 | 5 | 26 | 26 |
20-
| test.cpp:153:27:153:28 | \\w | 21 | 5 | 7 | 27 | 28 |
21-
| test.cpp:153:33:153:34 | \\) | 21 | 11 | 13 | 33 | 34 |
22-
| test.cpp:153:35:153:35 | $ | 21 | 13 | 14 | 35 | 35 |
23-
| test.cpp:156:22:156:22 | a | 21 | 0 | 1 | 22 | 22 |
24-
| test.cpp:156:22:156:23 | a+ | 21 | 0 | 2 | 22 | 23 |
25-
| test.cpp:156:22:156:24 | a+b | 21 | 0 | 3 | 22 | 24 |
26-
| test.cpp:156:24:156:24 | b | 21 | 2 | 3 | 24 | 24 |
27-
| test.cpp:159:23:159:23 | a | 22 | 0 | 1 | 23 | 23 |
28-
| test.cpp:159:23:159:24 | a+ | 22 | 0 | 2 | 23 | 24 |
29-
| test.cpp:159:23:159:25 | a+b | 22 | 0 | 3 | 23 | 25 |
30-
| test.cpp:159:25:159:25 | b | 22 | 2 | 3 | 25 | 25 |
31-
| test.cpp:162:27:162:27 | a | 26 | 0 | 1 | 27 | 27 |
32-
| test.cpp:162:27:162:28 | a+ | 26 | 0 | 2 | 27 | 28 |
33-
| test.cpp:162:27:162:29 | a+b | 26 | 0 | 3 | 27 | 29 |
34-
| test.cpp:162:29:162:29 | b | 26 | 2 | 3 | 29 | 29 |
5+
| test.cpp:147:23:147:23 | a | 20 | 0 | 1 | 23 | 23 |
6+
| test.cpp:147:23:147:24 | a+ | 20 | 0 | 2 | 23 | 24 |
7+
| test.cpp:147:23:147:25 | a+b | 20 | 0 | 3 | 23 | 25 |
8+
| test.cpp:147:25:147:25 | b | 20 | 2 | 3 | 25 | 25 |
9+
| test.cpp:150:24:150:25 | \\s | 21 | 0 | 2 | 24 | 25 |
10+
| test.cpp:150:24:150:26 | \\s+ | 21 | 0 | 3 | 24 | 26 |
11+
| test.cpp:150:24:150:27 | \\s+$ | 21 | 0 | 4 | 24 | 27 |
12+
| test.cpp:150:27:150:27 | $ | 21 | 3 | 4 | 27 | 27 |
13+
| test.cpp:153:24:153:25 | \\( | 21 | 0 | 2 | 24 | 25 |
14+
| test.cpp:153:24:153:37 | \\(([,\\w]+)+\\)$ | 21 | 0 | 14 | 24 | 37 |
15+
| test.cpp:153:26:153:33 | ([,\\w]+) | 21 | 2 | 10 | 26 | 33 |
16+
| test.cpp:153:26:153:34 | ([,\\w]+)+ | 21 | 2 | 11 | 26 | 34 |
17+
| test.cpp:153:27:153:31 | [,\\w] | 21 | 3 | 8 | 27 | 31 |
18+
| test.cpp:153:27:153:32 | [,\\w]+ | 21 | 3 | 9 | 27 | 32 |
19+
| test.cpp:153:28:153:28 | , | 21 | 4 | 5 | 28 | 28 |
20+
| test.cpp:153:29:153:30 | \\w | 21 | 5 | 7 | 29 | 30 |
21+
| test.cpp:153:35:153:36 | \\) | 21 | 11 | 13 | 35 | 36 |
22+
| test.cpp:153:37:153:37 | $ | 21 | 13 | 14 | 37 | 37 |
23+
| test.cpp:156:25:156:25 | a | 21 | 0 | 1 | 25 | 25 |
24+
| test.cpp:156:25:156:26 | a+ | 21 | 0 | 2 | 25 | 26 |
25+
| test.cpp:156:25:156:27 | a+b | 21 | 0 | 3 | 25 | 27 |
26+
| test.cpp:156:27:156:27 | b | 21 | 2 | 3 | 27 | 27 |
27+
| test.cpp:159:24:159:24 | a | 22 | 0 | 1 | 24 | 24 |
28+
| test.cpp:159:24:159:25 | a+ | 22 | 0 | 2 | 24 | 25 |
29+
| test.cpp:159:24:159:26 | a+b | 22 | 0 | 3 | 24 | 26 |
30+
| test.cpp:159:26:159:26 | b | 22 | 2 | 3 | 26 | 26 |
31+
| test.cpp:162:30:162:30 | a | 26 | 0 | 1 | 30 | 30 |
32+
| test.cpp:162:30:162:31 | a+ | 26 | 0 | 2 | 30 | 31 |
33+
| test.cpp:162:30:162:32 | a+b | 26 | 0 | 3 | 30 | 32 |
34+
| test.cpp:162:32:162:32 | b | 26 | 2 | 3 | 32 | 32 |
3535
| test.cpp:166:22:166:23 | \\s | 21 | 0 | 2 | 22 | 23 |
3636
| test.cpp:166:22:166:24 | \\s+ | 21 | 0 | 3 | 22 | 24 |
3737
| test.cpp:169:22:169:22 | a | 21 | 0 | 1 | 22 | 22 |

cpp/ql/test/library-tests/regex/parse.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ test.cpp:
521521

522522
# 131| [RegExpNamedCharacterProperty] [:digit:]
523523

524-
# 136| [RegExpConstant, RegExpEscape] \u{987
524+
# 136| [RegExpConstant, RegExpEscape] \u9879
525525

526526
# 144| [RegExpConstant, RegExpNormalChar] a
527527

cpp/ql/test/library-tests/regex/regexp.expected

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ groupNumber
88
| test.cpp:108:21:108:30 | (?<id>\\w+) | 1 |
99
| test.cpp:112:23:112:26 | (a+) | 1 |
1010
| test.cpp:113:23:113:32 | (?<qux>q+) | 1 |
11-
| test.cpp:153:24:153:31 | ([,\\w]+) | 1 |
11+
| test.cpp:153:26:153:33 | ([,\\w]+) | 1 |
1212
term
1313
| test.cpp:33:21:33:23 | abc | RegExpConstant,RegExpNormalChar |
1414
| test.cpp:36:22:36:22 | a | RegExpConstant,RegExpNormalChar |
@@ -201,41 +201,41 @@ term
201201
| test.cpp:128:37:128:39 | a-f | RegExpCharacterRange |
202202
| test.cpp:128:39:128:39 | f | RegExpConstant,RegExpNormalChar |
203203
| test.cpp:131:24:131:32 | [:digit:] | RegExpNamedCharacterProperty |
204-
| test.cpp:136:21:136:26 | \\u{987 | RegExpConstant,RegExpEscape |
204+
| test.cpp:136:21:136:26 | \\u9879 | RegExpConstant,RegExpEscape |
205205
| test.cpp:144:23:144:23 | a | RegExpConstant,RegExpNormalChar |
206206
| test.cpp:144:23:144:24 | a+ | RegExpPlus |
207207
| test.cpp:144:23:144:25 | a+b | RegExpSequence |
208208
| test.cpp:144:25:144:25 | b | RegExpConstant,RegExpNormalChar |
209-
| test.cpp:147:21:147:21 | a | RegExpConstant,RegExpNormalChar |
210-
| test.cpp:147:21:147:22 | a+ | RegExpPlus |
211-
| test.cpp:147:21:147:23 | a+b | RegExpSequence |
212-
| test.cpp:147:23:147:23 | b | RegExpConstant,RegExpNormalChar |
213-
| test.cpp:150:22:150:23 | \\s | RegExpCharacterClassEscape |
214-
| test.cpp:150:22:150:24 | \\s+ | RegExpPlus |
215-
| test.cpp:150:22:150:25 | \\s+$ | RegExpSequence |
216-
| test.cpp:150:25:150:25 | $ | RegExpDollar |
217-
| test.cpp:153:22:153:23 | \\( | RegExpConstant,RegExpEscape |
218-
| test.cpp:153:22:153:35 | \\(([,\\w]+)+\\)$ | RegExpSequence |
219-
| test.cpp:153:24:153:31 | ([,\\w]+) | RegExpGroup |
220-
| test.cpp:153:24:153:32 | ([,\\w]+)+ | RegExpPlus |
221-
| test.cpp:153:25:153:29 | [,\\w] | RegExpCharacterClass |
222-
| test.cpp:153:25:153:30 | [,\\w]+ | RegExpPlus |
223-
| test.cpp:153:26:153:26 | , | RegExpConstant,RegExpNormalChar |
224-
| test.cpp:153:27:153:28 | \\w | RegExpCharacterClassEscape |
225-
| test.cpp:153:33:153:34 | \\) | RegExpConstant,RegExpEscape |
226-
| test.cpp:153:35:153:35 | $ | RegExpDollar |
227-
| test.cpp:156:22:156:22 | a | RegExpConstant,RegExpNormalChar |
228-
| test.cpp:156:22:156:23 | a+ | RegExpPlus |
229-
| test.cpp:156:22:156:24 | a+b | RegExpSequence |
230-
| test.cpp:156:24:156:24 | b | RegExpConstant,RegExpNormalChar |
231-
| test.cpp:159:23:159:23 | a | RegExpConstant,RegExpNormalChar |
232-
| test.cpp:159:23:159:24 | a+ | RegExpPlus |
233-
| test.cpp:159:23:159:25 | a+b | RegExpSequence |
234-
| test.cpp:159:25:159:25 | b | RegExpConstant,RegExpNormalChar |
235-
| test.cpp:162:27:162:27 | a | RegExpConstant,RegExpNormalChar |
236-
| test.cpp:162:27:162:28 | a+ | RegExpPlus |
237-
| test.cpp:162:27:162:29 | a+b | RegExpSequence |
238-
| test.cpp:162:29:162:29 | b | RegExpConstant,RegExpNormalChar |
209+
| test.cpp:147:23:147:23 | a | RegExpConstant,RegExpNormalChar |
210+
| test.cpp:147:23:147:24 | a+ | RegExpPlus |
211+
| test.cpp:147:23:147:25 | a+b | RegExpSequence |
212+
| test.cpp:147:25:147:25 | b | RegExpConstant,RegExpNormalChar |
213+
| test.cpp:150:24:150:25 | \\s | RegExpCharacterClassEscape |
214+
| test.cpp:150:24:150:26 | \\s+ | RegExpPlus |
215+
| test.cpp:150:24:150:27 | \\s+$ | RegExpSequence |
216+
| test.cpp:150:27:150:27 | $ | RegExpDollar |
217+
| test.cpp:153:24:153:25 | \\( | RegExpConstant,RegExpEscape |
218+
| test.cpp:153:24:153:37 | \\(([,\\w]+)+\\)$ | RegExpSequence |
219+
| test.cpp:153:26:153:33 | ([,\\w]+) | RegExpGroup |
220+
| test.cpp:153:26:153:34 | ([,\\w]+)+ | RegExpPlus |
221+
| test.cpp:153:27:153:31 | [,\\w] | RegExpCharacterClass |
222+
| test.cpp:153:27:153:32 | [,\\w]+ | RegExpPlus |
223+
| test.cpp:153:28:153:28 | , | RegExpConstant,RegExpNormalChar |
224+
| test.cpp:153:29:153:30 | \\w | RegExpCharacterClassEscape |
225+
| test.cpp:153:35:153:36 | \\) | RegExpConstant,RegExpEscape |
226+
| test.cpp:153:37:153:37 | $ | RegExpDollar |
227+
| test.cpp:156:25:156:25 | a | RegExpConstant,RegExpNormalChar |
228+
| test.cpp:156:25:156:26 | a+ | RegExpPlus |
229+
| test.cpp:156:25:156:27 | a+b | RegExpSequence |
230+
| test.cpp:156:27:156:27 | b | RegExpConstant,RegExpNormalChar |
231+
| test.cpp:159:24:159:24 | a | RegExpConstant,RegExpNormalChar |
232+
| test.cpp:159:24:159:25 | a+ | RegExpPlus |
233+
| test.cpp:159:24:159:26 | a+b | RegExpSequence |
234+
| test.cpp:159:26:159:26 | b | RegExpConstant,RegExpNormalChar |
235+
| test.cpp:162:30:162:30 | a | RegExpConstant,RegExpNormalChar |
236+
| test.cpp:162:30:162:31 | a+ | RegExpPlus |
237+
| test.cpp:162:30:162:32 | a+b | RegExpSequence |
238+
| test.cpp:162:32:162:32 | b | RegExpConstant,RegExpNormalChar |
239239
| test.cpp:166:22:166:23 | \\s | RegExpCharacterClassEscape |
240240
| test.cpp:166:22:166:24 | \\s+ | RegExpPlus |
241241
| test.cpp:169:22:169:22 | a | RegExpConstant,RegExpNormalChar |
@@ -318,22 +318,22 @@ regExpNormalCharValue
318318
| test.cpp:128:27:128:27 | F | F |
319319
| test.cpp:128:37:128:37 | a | a |
320320
| test.cpp:128:39:128:39 | f | f |
321-
| test.cpp:136:21:136:26 | \\u{987 | \u0987 |
321+
| test.cpp:136:21:136:26 | \\u9879 | \u9879 |
322322
| test.cpp:144:23:144:23 | a | a |
323323
| test.cpp:144:25:144:25 | b | b |
324-
| test.cpp:147:21:147:21 | a | a |
325-
| test.cpp:147:23:147:23 | b | b |
326-
| test.cpp:150:22:150:23 | \\s | s |
327-
| test.cpp:153:22:153:23 | \\( | ( |
328-
| test.cpp:153:26:153:26 | , | , |
329-
| test.cpp:153:27:153:28 | \\w | w |
330-
| test.cpp:153:33:153:34 | \\) | ) |
331-
| test.cpp:156:22:156:22 | a | a |
332-
| test.cpp:156:24:156:24 | b | b |
333-
| test.cpp:159:23:159:23 | a | a |
334-
| test.cpp:159:25:159:25 | b | b |
335-
| test.cpp:162:27:162:27 | a | a |
336-
| test.cpp:162:29:162:29 | b | b |
324+
| test.cpp:147:23:147:23 | a | a |
325+
| test.cpp:147:25:147:25 | b | b |
326+
| test.cpp:150:24:150:25 | \\s | s |
327+
| test.cpp:153:24:153:25 | \\( | ( |
328+
| test.cpp:153:28:153:28 | , | , |
329+
| test.cpp:153:29:153:30 | \\w | w |
330+
| test.cpp:153:35:153:36 | \\) | ) |
331+
| test.cpp:156:25:156:25 | a | a |
332+
| test.cpp:156:27:156:27 | b | b |
333+
| test.cpp:159:24:159:24 | a | a |
334+
| test.cpp:159:26:159:26 | b | b |
335+
| test.cpp:162:30:162:30 | a | a |
336+
| test.cpp:162:32:162:32 | b | b |
337337
| test.cpp:166:22:166:23 | \\s | s |
338338
| test.cpp:169:22:169:22 | a | a |
339339
| test.cpp:169:23:169:24 | \\. | . |

cpp/ql/test/library-tests/regex/test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ void test() {
132132

133133
// Dropped: /#{A}bc/ — Ruby string interpolation, no C++ string-literal form.
134134

135-
// unicode: \u{9879} in C++ (Ruby unicode escape syntax)
136-
std::regex r_uni("\\u{9879}");
135+
// unicode: \uHHHH 4-digit hex form (valid ECMAScript \u escape in std::regex)
136+
std::regex r_uni("\\u9879");
137137
}
138138

139139
// Location tests (commit 8: pre-fix columns; commit 9: fixes raw/prefixed offsets).

0 commit comments

Comments
 (0)