Skip to content

Commit 52b35ef

Browse files
authored
Commit 2: Adapt Ruby regex library to compile against C++
Changes from verbatim Ruby baseline (Commit 1) to compilable C++: ParseRegExp.qll: - Remove: private import codeql.ruby.AST as Ast - Remove: private import codeql.Locations - Add: private import semmle.code.cpp.exprs.Literal - Change RegExp base: Ast::StringlikeLiteral -> StringLiteral - Replace getText(): removes getConstantValue() Ruby accessor, uses getValue() (C++ StringLiteral.getValue() returns the string content, analogous to Ruby's getConstantValue().getString()) RegexTreeView.qll: - Remove Ruby-specific imports (codeql.ruby.AST, codeql.Locations, codeql.regex.nfa.NfaUtils) - Add: private import semmle.code.cpp.exprs.Literal - Keep: codeql.util.Numbers (for parseHexInt in RegExpEscape.getUnicode()) - Keep: codeql.regex.RegexTreeView (satisfies RegexTreeViewSig) - Change getParsedRegExp: Ast::RegExpLiteral -> StringLiteral - Simplify hasLocationInfo to use re.getLocation() directly with +1 offset for opening quote (approximate; fixed in commits 8-9) - Replace isExcluded: remove hasFreeSpacingFlag(), use none() - Ruby dialect features preserved unchanged (\\A, \\z, \\G, \\h, etc.); dialect shift is commit 5 Test queries: - parse.ql, regexp.ql: replace codeql.ruby.Regexp with semmle.code.cpp.regex.RegexTreeView - .expected cleared (no test.cpp until commit 3) Verified: both queries compile successfully with codeql query compile.
1 parent 90a14d8 commit 52b35ef

6 files changed

Lines changed: 33 additions & 812 deletions

File tree

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

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
/**
22
* Provides a class hierarchy corresponding to a parse tree of C++ regular expressions.
3-
*
4-
* This file is a verbatim copy of the Ruby RegExpTreeView.qll, with only the
5-
* file-header comment updated. It does not yet compile against C++.
63
*/
74

85
private import internal.ParseRegExp
96
private import codeql.util.Numbers
10-
private import codeql.ruby.ast.Literal as Ast
11-
private import codeql.Locations
12-
private import codeql.regex.nfa.NfaUtils as NfaUtils
7+
private import semmle.code.cpp.exprs.Literal
138
private import codeql.regex.RegexTreeView
149
// exporting as RegexTreeView, and in the top-level scope.
1510
import Impl as RegexTreeView
1611
import Impl
1712

1813
/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */
19-
RegExpTerm getParsedRegExp(Ast::RegExpLiteral re) {
14+
RegExpTerm getParsedRegExp(StringLiteral re) {
2015
result.getRegExp() = re and result.isRootTerm()
2116
}
2217

@@ -60,7 +55,7 @@ private newtype TRegExpParent =
6055
re.namedCharacterProperty(start, end, _)
6156
}
6257

63-
/** An implementation that statisfies the RegexTreeView signature. */
58+
/** An implementation that satisfies the RegexTreeView signature. */
6459
private module Impl implements RegexTreeViewSig {
6560
/**
6661
* An element containing a regular expression term, that is, either
@@ -216,24 +211,18 @@ private module Impl implements RegexTreeViewSig {
216211
*/
217212
Location getLocation() { result = re.getLocation() }
218213

219-
pragma[noinline]
220-
private predicate componentHasLocationInfo(
221-
int i, string filepath, int startline, int startcolumn, int endline, int endcolumn
222-
) {
223-
re.getComponent(i)
224-
.getLocation()
225-
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
226-
}
227-
228-
/** Holds if this term is found at the specified location offsets. */
214+
/** Holds if this term is found at the specified location offsets.
215+
*
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.
218+
*/
229219
predicate hasLocationInfo(
230220
string filepath, int startline, int startcolumn, int endline, int endcolumn
231221
) {
232222
exists(int re_start |
233-
this.componentHasLocationInfo(0, filepath, startline, re_start, _, _) and
234-
this.componentHasLocationInfo(re.getNumberOfComponents() - 1, filepath, _, _, endline, _) and
235-
startcolumn = re_start + start and
236-
endcolumn = re_start + end - 1
223+
re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, _) and
224+
startcolumn = re_start + 1 + start and
225+
endcolumn = re_start + 1 + end - 1
237226
)
238227
}
239228

@@ -317,7 +306,7 @@ private module Impl implements RegexTreeViewSig {
317306
result.getEnd() = part_end
318307
}
319308

320-
/** Hols if this term may match an unlimited number of times. */
309+
/** Holds if this term may match an unlimited number of times. */
321310
predicate mayRepeatForever() { may_repeat_forever = true }
322311

323312
/** Gets the qualifier for this term. That is e.g "?" for "a?". */
@@ -665,9 +654,9 @@ private module Impl implements RegexTreeViewSig {
665654
*
666655
* Examples:
667656
*
668-
* ```rb
669-
* /[a-fA-F0-9]/
670-
* /[^abc]/
657+
* ```cpp
658+
* "[a-fA-F0-9]"
659+
* "[^abc]"
671660
* ```
672661
*/
673662
class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass {
@@ -1119,7 +1108,7 @@ private module Impl implements RegexTreeViewSig {
11191108
*
11201109
* ```
11211110
* \1
1122-
* (?P=quote)
1111+
* \k<quote>
11231112
* ```
11241113
*/
11251114
class RegExpBackRef extends RegExpTerm, TRegExpBackRef {
@@ -1200,9 +1189,7 @@ private module Impl implements RegexTreeViewSig {
12001189
/**
12011190
* Holds if the regular expression should not be considered.
12021191
*/
1203-
predicate isExcluded(RegExpParent parent) {
1204-
parent.(RegExpTerm).getRegExp().(Ast::RegExpLiteral).hasFreeSpacingFlag() // exclude free-spacing mode regexes
1205-
}
1192+
predicate isExcluded(RegExpParent parent) { none() }
12061193

12071194
/**
12081195
* Holds if `term` is a possessive quantifier.
@@ -1212,13 +1199,13 @@ private module Impl implements RegexTreeViewSig {
12121199

12131200
/**
12141201
* Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against.
1215-
* Not yet implemented for Ruby.
1202+
* Not yet implemented for C++.
12161203
*/
12171204
predicate matchesAnyPrefix(RegExpTerm term) { any() }
12181205

12191206
/**
12201207
* Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against.
1221-
* Not yet implemented for Ruby.
1208+
* Not yet implemented for C++.
12221209
*/
12231210
predicate matchesAnySuffix(RegExpTerm term) { any() }
12241211

‎cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll‎

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,16 @@
33
*
44
* N.B. does not yet handle stripping whitespace and comments in regexes with
55
* the `x` (free-spacing) flag.
6-
*
7-
* This file is a verbatim copy of the Ruby ParseRegExp.qll, with only the
8-
* file-header comment updated. It does not yet compile against C++.
96
*/
107

11-
private import codeql.ruby.AST as Ast
12-
private import codeql.Locations
8+
private import semmle.code.cpp.exprs.Literal
139

1410
/**
15-
* A `StringlikeLiteral` containing a regular expression term, that is, either
11+
* A C++ string literal that contains a regular expression term, that is, either
1612
* a regular expression literal, or a string literal used in a context where
17-
* it is parsed as regular expression.
13+
* it is parsed as a regular expression.
1814
*/
19-
abstract class RegExp extends Ast::StringlikeLiteral {
15+
abstract class RegExp extends StringLiteral {
2016
/**
2117
* Holds if this `RegExp` has the `s` flag for multi-line matching.
2218
*/
@@ -256,12 +252,8 @@ abstract class RegExp extends Ast::StringlikeLiteral {
256252
this.getChar(pos) != "\\" and result = false
257253
}
258254

259-
/** Gets the text of this regex */
260-
string getText() {
261-
exists(Ast::ConstantValue c | c = this.getConstantValue() |
262-
result = [this.getConstantValue().getString(), this.getConstantValue().getRegExp()]
263-
)
264-
}
255+
/** Gets the text of this regex (the value of the string literal). */
256+
string getText() { result = this.getValue() }
265257

266258
/** Gets the `i`th character of this regex */
267259
string getChar(int i) { result = this.getText().charAt(i) }

0 commit comments

Comments
 (0)