@@ -3,21 +3,14 @@ private import codeql.ruby.security.performance.RegExpTreeView as RETV
33private import internal.AST
44private import internal.Scope
55private import internal.TreeSitter
6+ private import codeql.ruby.controlflow.CfgNodes
67
78/**
89 * A literal.
910 *
1011 * This is the QL root class for all literals.
1112 */
12- class Literal extends Expr , TLiteral {
13- /**
14- * Gets the source text for this literal, if this is a simple literal.
15- *
16- * For complex literals, such as arrays, hashes, and strings with
17- * interpolations, this predicate has no result.
18- */
19- override string getValueText ( ) { none ( ) }
20- }
13+ class Literal extends Expr , TLiteral { }
2114
2215/**
2316 * A numeric literal, i.e. an integer, floating-point, rational, or complex
@@ -281,10 +274,10 @@ class StringComponent extends AstNode, TStringComponent {
281274 * "foo#{ bar() } baz"
282275 * ```
283276 */
284- class StringTextComponent extends StringComponent , TStringTextComponent {
277+ class StringTextComponent extends StringComponent , TStringTextComponentNonRegexp {
285278 private Ruby:: Token g ;
286279
287- StringTextComponent ( ) { this = TStringTextComponent ( g ) }
280+ StringTextComponent ( ) { this = TStringTextComponentNonRegexp ( g ) }
288281
289282 final override string toString ( ) { result = g .getValue ( ) }
290283
@@ -296,10 +289,10 @@ class StringTextComponent extends StringComponent, TStringTextComponent {
296289/**
297290 * An escape sequence component of a string or string-like literal.
298291 */
299- class StringEscapeSequenceComponent extends StringComponent , TStringEscapeSequenceComponent {
292+ class StringEscapeSequenceComponent extends StringComponent , TStringEscapeSequenceComponentNonRegexp {
300293 private Ruby:: EscapeSequence g ;
301294
302- StringEscapeSequenceComponent ( ) { this = TStringEscapeSequenceComponent ( g ) }
295+ StringEscapeSequenceComponent ( ) { this = TStringEscapeSequenceComponentNonRegexp ( g ) }
303296
304297 final override string toString ( ) { result = g .getValue ( ) }
305298
@@ -312,10 +305,10 @@ class StringEscapeSequenceComponent extends StringComponent, TStringEscapeSequen
312305 * An interpolation expression component of a string or string-like literal.
313306 */
314307class StringInterpolationComponent extends StringComponent , StmtSequence ,
315- TStringInterpolationComponent {
308+ TStringInterpolationComponentNonRegexp {
316309 private Ruby:: Interpolation g ;
317310
318- StringInterpolationComponent ( ) { this = TStringInterpolationComponent ( g ) }
311+ StringInterpolationComponent ( ) { this = TStringInterpolationComponentNonRegexp ( g ) }
319312
320313 final override string toString ( ) { result = "#{...}" }
321314
@@ -326,6 +319,83 @@ class StringInterpolationComponent extends StringComponent, StmtSequence,
326319 final override string getAPrimaryQlClass ( ) { result = "StringInterpolationComponent" }
327320}
328321
322+ private class TRegExpComponent =
323+ TStringTextComponentRegexp or TStringEscapeSequenceComponentRegexp or
324+ TStringInterpolationComponentRegexp ;
325+
326+ /**
327+ * The base class for a component of a regular expression literal.
328+ */
329+ class RegExpComponent extends AstNode , TRegExpComponent {
330+ /** Gets the source text for this regex component, if any. */
331+ string getValueText ( ) { none ( ) }
332+ }
333+
334+ /**
335+ * A component of a regex literal that is simply text.
336+ *
337+ * For example, the following regex literals all contain `RegExpTextComponent`
338+ * components whose `getValueText()` returns `"foo"`:
339+ *
340+ * ```rb
341+ * 'foo'
342+ * "#{ bar() }foo"
343+ * "foo#{ bar() } baz"
344+ * ```
345+ */
346+ class RegExpTextComponent extends RegExpComponent , TStringTextComponentRegexp {
347+ private Ruby:: Token g ;
348+
349+ RegExpTextComponent ( ) { this = TStringTextComponentRegexp ( g ) }
350+
351+ final override string toString ( ) { result = g .getValue ( ) }
352+
353+ // Exclude components that are children of a free-spacing regex.
354+ // We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
355+ final override string getValueText ( ) {
356+ not this .getParent ( ) .( RegExpLiteral ) .hasFreeSpacingFlag ( ) and result = g .getValue ( )
357+ }
358+
359+ final override string getAPrimaryQlClass ( ) { result = "RegExpTextComponent" }
360+ }
361+
362+ /**
363+ * An escape sequence component of a regex literal.
364+ */
365+ class RegExpEscapeSequenceComponent extends RegExpComponent , TStringEscapeSequenceComponentRegexp {
366+ private Ruby:: EscapeSequence g ;
367+
368+ RegExpEscapeSequenceComponent ( ) { this = TStringEscapeSequenceComponentRegexp ( g ) }
369+
370+ final override string toString ( ) { result = g .getValue ( ) }
371+
372+ // Exclude components that are children of a free-spacing regex.
373+ // We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
374+ final override string getValueText ( ) {
375+ not this .getParent ( ) .( RegExpLiteral ) .hasFreeSpacingFlag ( ) and result = g .getValue ( )
376+ }
377+
378+ final override string getAPrimaryQlClass ( ) { result = "RegExpEscapeSequenceComponent" }
379+ }
380+
381+ /**
382+ * An interpolation expression component of a regex literal.
383+ */
384+ class RegExpInterpolationComponent extends RegExpComponent , StmtSequence ,
385+ TStringInterpolationComponentRegexp {
386+ private Ruby:: Interpolation g ;
387+
388+ RegExpInterpolationComponent ( ) { this = TStringInterpolationComponentRegexp ( g ) }
389+
390+ final override string toString ( ) { result = "#{...}" }
391+
392+ final override Stmt getStmt ( int n ) { toGenerated ( result ) = g .getChild ( n ) }
393+
394+ final override string getValueText ( ) { none ( ) }
395+
396+ final override string getAPrimaryQlClass ( ) { result = "RegExpInterpolationComponent" }
397+ }
398+
329399/**
330400 * A string, symbol, regexp, or subshell literal.
331401 */
@@ -410,17 +480,6 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral {
410480 result = ""
411481 }
412482
413- override string getValueText ( ) {
414- // 0 components should result in the empty string
415- // if there are any interpolations, there should be no result
416- // otherwise, concatenate all the components
417- forall ( StringComponent c | c = this .getComponent ( _) |
418- not c instanceof StringInterpolationComponent
419- ) and
420- result =
421- concat ( StringComponent c , int i | c = this .getComponent ( i ) | c .getValueText ( ) order by i )
422- }
423-
424483 override string toString ( ) {
425484 exists ( string full , string summary |
426485 full =
0 commit comments