@@ -268,6 +268,9 @@ class RegExpTerm extends RegExpParent {
268268
269269 /** Gets the primary QL class for this term. */
270270 override string getAPrimaryQlClass ( ) { result = "RegExpTerm" }
271+
272+ /** Holds if this regular expression term can match the empty string. */
273+ predicate matchesEmptyString ( ) { none ( ) }
271274}
272275
273276/**
@@ -326,6 +329,8 @@ class RegExpStar extends InfiniteRepetitionQuantifier {
326329 RegExpStar ( ) { this .getQualifier ( ) .charAt ( 0 ) = "*" }
327330
328331 override string getAPrimaryQlClass ( ) { result = "RegExpStar" }
332+
333+ override predicate matchesEmptyString ( ) { any ( ) }
329334}
330335
331336/**
@@ -341,6 +346,8 @@ class RegExpPlus extends InfiniteRepetitionQuantifier {
341346 RegExpPlus ( ) { this .getQualifier ( ) .charAt ( 0 ) = "+" }
342347
343348 override string getAPrimaryQlClass ( ) { result = "RegExpPlus" }
349+
350+ override predicate matchesEmptyString ( ) { this .getAChild ( ) .matchesEmptyString ( ) }
344351}
345352
346353/**
@@ -356,6 +363,8 @@ class RegExpOpt extends RegExpQuantifier {
356363 RegExpOpt ( ) { this .getQualifier ( ) .charAt ( 0 ) = "?" }
357364
358365 override string getAPrimaryQlClass ( ) { result = "RegExpOpt" }
366+
367+ override predicate matchesEmptyString ( ) { any ( ) }
359368}
360369
361370/**
@@ -375,6 +384,8 @@ class RegExpRange extends RegExpQuantifier {
375384
376385 RegExpRange ( ) { re .multiples ( part_end , end , lower , upper ) }
377386
387+ override string getAPrimaryQlClass ( ) { result = "RegExpRange" }
388+
378389 /** Gets the string defining the upper bound of this range, if any. */
379390 string getUpper ( ) { result = upper }
380391
@@ -393,7 +404,9 @@ class RegExpRange extends RegExpQuantifier {
393404 /** Gets the lower bound of the range. */
394405 int getLowerBound ( ) { result = this .getLower ( ) .toInt ( ) }
395406
396- override string getAPrimaryQlClass ( ) { result = "RegExpRange" }
407+ override predicate matchesEmptyString ( ) {
408+ this .getAChild ( ) .matchesEmptyString ( ) or this .getLowerBound ( ) = 0
409+ }
397410}
398411
399412/**
@@ -440,6 +453,10 @@ class RegExpSequence extends RegExpTerm, TRegExpSequence {
440453 }
441454
442455 override string getAPrimaryQlClass ( ) { result = "RegExpSequence" }
456+
457+ override predicate matchesEmptyString ( ) {
458+ forall ( RegExpTerm child | child = this .getAChild ( ) | child .matchesEmptyString ( ) )
459+ }
443460}
444461
445462pragma [ nomagic]
@@ -505,6 +522,8 @@ class RegExpAlt extends RegExpTerm, TRegExpAlt {
505522 override string getAMatchedString ( ) { result = this .getAlternative ( ) .getAMatchedString ( ) }
506523
507524 override string getAPrimaryQlClass ( ) { result = "RegExpAlt" }
525+
526+ override predicate matchesEmptyString ( ) { this .getAChild ( ) .matchesEmptyString ( ) }
508527}
509528
510529class RegExpCharEscape = RegExpEscape ;
@@ -579,6 +598,8 @@ class RegExpEscape extends RegExpNormalChar {
579598 */
580599class RegExpWordBoundary extends RegExpSpecialChar {
581600 RegExpWordBoundary ( ) { this .getChar ( ) = "\\b" }
601+
602+ override predicate matchesEmptyString ( ) { none ( ) }
582603}
583604
584605/**
@@ -607,6 +628,8 @@ class RegExpCharacterClassEscape extends RegExpEscape {
607628 override RegExpTerm getChild ( int i ) { none ( ) }
608629
609630 override string getAPrimaryQlClass ( ) { result = "RegExpCharacterClassEscape" }
631+
632+ override predicate matchesEmptyString ( ) { none ( ) }
610633}
611634
612635/**
@@ -663,6 +686,8 @@ class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass {
663686 }
664687
665688 override string getAPrimaryQlClass ( ) { result = "RegExpCharacterClass" }
689+
690+ override predicate matchesEmptyString ( ) { none ( ) }
666691}
667692
668693/**
@@ -702,6 +727,8 @@ class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange {
702727 }
703728
704729 override string getAPrimaryQlClass ( ) { result = "RegExpCharacterRange" }
730+
731+ override predicate matchesEmptyString ( ) { none ( ) }
705732}
706733
707734/**
@@ -773,6 +800,8 @@ class RegExpConstant extends RegExpTerm {
773800 override string getConstantValue ( ) { result = this .getValue ( ) }
774801
775802 override string getAPrimaryQlClass ( ) { result = "RegExpConstant" }
803+
804+ override predicate matchesEmptyString ( ) { none ( ) }
776805}
777806
778807/**
@@ -820,6 +849,8 @@ class RegExpGroup extends RegExpTerm, TRegExpGroup {
820849 override string getAMatchedString ( ) { result = this .getAChild ( ) .getAMatchedString ( ) }
821850
822851 override string getAPrimaryQlClass ( ) { result = "RegExpGroup" }
852+
853+ override predicate matchesEmptyString ( ) { this .getAChild ( ) .matchesEmptyString ( ) }
823854}
824855
825856/**
@@ -867,6 +898,8 @@ class RegExpDot extends RegExpSpecialChar {
867898 RegExpDot ( ) { this .getChar ( ) = "." }
868899
869900 override string getAPrimaryQlClass ( ) { result = "RegExpDot" }
901+
902+ override predicate matchesEmptyString ( ) { none ( ) }
870903}
871904
872905/**
@@ -897,6 +930,8 @@ class RegExpDollar extends RegExpAnchor {
897930 RegExpDollar ( ) { this .getChar ( ) = [ "$" , "\\Z" , "\\z" ] }
898931
899932 override string getAPrimaryQlClass ( ) { result = "RegExpDollar" }
933+
934+ override predicate matchesEmptyString ( ) { any ( ) }
900935}
901936
902937/**
@@ -912,6 +947,8 @@ class RegExpCaret extends RegExpAnchor {
912947 RegExpCaret ( ) { this .getChar ( ) = [ "^" , "\\A" ] }
913948
914949 override string getAPrimaryQlClass ( ) { result = "RegExpCaret" }
950+
951+ override predicate matchesEmptyString ( ) { any ( ) }
915952}
916953
917954/**
@@ -929,6 +966,8 @@ class RegExpZeroWidthMatch extends RegExpGroup {
929966 override RegExpTerm getChild ( int i ) { none ( ) }
930967
931968 override string getAPrimaryQlClass ( ) { result = "RegExpZeroWidthMatch" }
969+
970+ override predicate matchesEmptyString ( ) { any ( ) }
932971}
933972
934973/**
@@ -954,6 +993,8 @@ class RegExpSubPattern extends RegExpZeroWidthMatch {
954993 result .getEnd ( ) = in_end
955994 )
956995 }
996+
997+ override predicate matchesEmptyString ( ) { any ( ) }
957998}
958999
9591000/**
@@ -981,6 +1022,8 @@ class RegExpPositiveLookahead extends RegExpLookahead {
9811022 RegExpPositiveLookahead ( ) { re .positiveLookaheadAssertionGroup ( start , end ) }
9821023
9831024 override string getAPrimaryQlClass ( ) { result = "RegExpPositiveLookahead" }
1025+
1026+ override predicate matchesEmptyString ( ) { any ( ) }
9841027}
9851028
9861029/**
@@ -1076,6 +1119,8 @@ class RegExpBackRef extends RegExpTerm, TRegExpBackRef {
10761119 override RegExpTerm getChild ( int i ) { none ( ) }
10771120
10781121 override string getAPrimaryQlClass ( ) { result = "RegExpBackRef" }
1122+
1123+ override predicate matchesEmptyString ( ) { this .getGroup ( ) .matchesEmptyString ( ) }
10791124}
10801125
10811126/**
0 commit comments