Skip to content

Commit a7f9789

Browse files
authored
Merge pull request github#5863 from erik-krogh/printReg
JS: add printAst.ql support for regular expressions
2 parents 0e5a2c4 + 504c34e commit a7f9789

File tree

5 files changed

+223
-84
lines changed

5 files changed

+223
-84
lines changed

javascript/ql/src/semmle/javascript/PrintAst.qll

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ private newtype TPrintAstNode =
7373
THTMLAttributesNodes(HTML::Element e) { shouldPrint(e, _) and not isNotNeeded(e) } or
7474
THTMLAttributeNode(HTML::Attribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or
7575
THTMLScript(Script script) { shouldPrint(script, _) and not isNotNeeded(script) } or
76-
THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) }
76+
THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or
77+
TRegExpTermNode(RegExpTerm term) {
78+
shouldPrint(term, _) and
79+
term.isUsedAsRegExp() and
80+
any(RegExpLiteral lit).getRoot() = term.getRootTerm()
81+
}
7782

7883
/**
7984
* A node in the output tree.
@@ -282,6 +287,39 @@ private module PrintJavaScript {
282287
}
283288
}
284289

290+
/**
291+
* A print node for regexp literals.
292+
*
293+
* The single child of this node is the root `RegExpTerm`.
294+
*/
295+
class RegexpNode extends ElementNode {
296+
override RegExpLiteral element;
297+
298+
override PrintAstNode getChild(int childIndex) {
299+
childIndex = 0 and
300+
result.(RegExpTermNode).getTerm() = element.getRoot()
301+
}
302+
}
303+
304+
/**
305+
* A print node for regexp terms.
306+
*/
307+
class RegExpTermNode extends PrintAstNode, TRegExpTermNode {
308+
RegExpTerm term;
309+
310+
RegExpTermNode() { this = TRegExpTermNode(term) }
311+
312+
RegExpTerm getTerm() { result = term }
313+
314+
override PrintAstNode getChild(int childIndex) {
315+
result.(RegExpTermNode).getTerm() = term.getChild(childIndex)
316+
}
317+
318+
override string toString() { result = getQlClass(term) + term.toString() }
319+
320+
override Location getLocation() { result = term.getLocation() }
321+
}
322+
285323
/**
286324
* An aggregate node representing all the arguments for an function invocation.
287325
*/

javascript/ql/src/semmle/javascript/Regexp.qll

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,9 @@ class InfiniteRepetitionQuantifier extends RegExpQuantifier {
215215
* \w
216216
* ```
217217
*/
218-
class RegExpEscape extends RegExpTerm, @regexp_escape { }
218+
class RegExpEscape extends RegExpTerm, @regexp_escape {
219+
override string getAPrimaryQlClass() { result = "RegExpEscape" }
220+
}
219221

220222
/**
221223
* A constant regular expression term, that is, a regular expression
@@ -240,6 +242,8 @@ class RegExpConstant extends RegExpTerm, @regexp_constant {
240242
override predicate isNullable() { none() }
241243

242244
override string getConstantValue() { result = getValue() }
245+
246+
override string getAPrimaryQlClass() { result = "RegExpConstant" }
243247
}
244248

245249
/**
@@ -264,6 +268,8 @@ class RegExpCharEscape extends RegExpEscape, RegExpConstant, @regexp_char_escape
264268
)
265269
)
266270
}
271+
272+
override string getAPrimaryQlClass() { result = "RegExpCharEscape" }
267273
}
268274

269275
/**
@@ -285,6 +291,8 @@ class RegExpAlt extends RegExpTerm, @regexp_alt {
285291
override predicate isNullable() { getAlternative().isNullable() }
286292

287293
override string getAMatchedString() { result = getAlternative().getAMatchedString() }
294+
295+
override string getAPrimaryQlClass() { result = "RegExpAlt" }
288296
}
289297

290298
/**
@@ -332,6 +340,8 @@ class RegExpSequence extends RegExpTerm, @regexp_seq {
332340
result = this.getChild(i + 1)
333341
)
334342
}
343+
344+
override string getAPrimaryQlClass() { result = "RegExpSequence" }
335345
}
336346

337347
/**
@@ -346,6 +356,8 @@ class RegExpSequence extends RegExpTerm, @regexp_seq {
346356
*/
347357
class RegExpAnchor extends RegExpTerm, @regexp_anchor {
348358
override predicate isNullable() { any() }
359+
360+
override string getAPrimaryQlClass() { result = "RegExpAnchor" }
349361
}
350362

351363
/**
@@ -357,7 +369,9 @@ class RegExpAnchor extends RegExpTerm, @regexp_anchor {
357369
* ^
358370
* ```
359371
*/
360-
class RegExpCaret extends RegExpAnchor, @regexp_caret { }
372+
class RegExpCaret extends RegExpAnchor, @regexp_caret {
373+
override string getAPrimaryQlClass() { result = "RegExpCaret" }
374+
}
361375

362376
/**
363377
* A dollar assertion `$` matching the end of a line.
@@ -368,7 +382,9 @@ class RegExpCaret extends RegExpAnchor, @regexp_caret { }
368382
* $
369383
* ```
370384
*/
371-
class RegExpDollar extends RegExpAnchor, @regexp_dollar { }
385+
class RegExpDollar extends RegExpAnchor, @regexp_dollar {
386+
override string getAPrimaryQlClass() { result = "RegExpDollar" }
387+
}
372388

373389
/**
374390
* A word boundary assertion.
@@ -381,6 +397,8 @@ class RegExpDollar extends RegExpAnchor, @regexp_dollar { }
381397
*/
382398
class RegExpWordBoundary extends RegExpTerm, @regexp_wordboundary {
383399
override predicate isNullable() { any() }
400+
401+
override string getAPrimaryQlClass() { result = "RegExpWordBoundary" }
384402
}
385403

386404
/**
@@ -394,6 +412,8 @@ class RegExpWordBoundary extends RegExpTerm, @regexp_wordboundary {
394412
*/
395413
class RegExpNonWordBoundary extends RegExpTerm, @regexp_nonwordboundary {
396414
override predicate isNullable() { any() }
415+
416+
override string getAPrimaryQlClass() { result = "RegExpNonWordBoundary" }
397417
}
398418

399419
/**
@@ -425,7 +445,9 @@ class RegExpSubPattern extends RegExpTerm, @regexp_subpattern {
425445
* (?!\n)
426446
* ```
427447
*/
428-
class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { }
448+
class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead {
449+
override string getAPrimaryQlClass() { result = "RegExpLookahead" }
450+
}
429451

430452
/**
431453
* A zero-width lookbehind assertion.
@@ -437,7 +459,9 @@ class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { }
437459
* (?<!\\)
438460
* ```
439461
*/
440-
class RegExpLookbehind extends RegExpSubPattern, @regexp_lookbehind { }
462+
class RegExpLookbehind extends RegExpSubPattern, @regexp_lookbehind {
463+
override string getAPrimaryQlClass() { result = "RegExpLookbehind" }
464+
}
441465

442466
/**
443467
* A positive-lookahead assertion.
@@ -448,7 +472,9 @@ class RegExpLookbehind extends RegExpSubPattern, @regexp_lookbehind { }
448472
* (?=\w)
449473
* ```
450474
*/
451-
class RegExpPositiveLookahead extends RegExpLookahead, @regexp_positive_lookahead { }
475+
class RegExpPositiveLookahead extends RegExpLookahead, @regexp_positive_lookahead {
476+
override string getAPrimaryQlClass() { result = "RegExpPositiveLookahead" }
477+
}
452478

453479
/**
454480
* A negative-lookahead assertion.
@@ -459,7 +485,9 @@ class RegExpPositiveLookahead extends RegExpLookahead, @regexp_positive_lookahea
459485
* (?!\n)
460486
* ```
461487
*/
462-
class RegExpNegativeLookahead extends RegExpLookahead, @regexp_negative_lookahead { }
488+
class RegExpNegativeLookahead extends RegExpLookahead, @regexp_negative_lookahead {
489+
override string getAPrimaryQlClass() { result = "RegExpNegativeLookahead" }
490+
}
463491

464492
/**
465493
* A positive-lookbehind assertion.
@@ -470,7 +498,9 @@ class RegExpNegativeLookahead extends RegExpLookahead, @regexp_negative_lookahea
470498
* (?<=\.)
471499
* ```
472500
*/
473-
class RegExpPositiveLookbehind extends RegExpLookbehind, @regexp_positive_lookbehind { }
501+
class RegExpPositiveLookbehind extends RegExpLookbehind, @regexp_positive_lookbehind {
502+
override string getAPrimaryQlClass() { result = "RegExpPositiveLookbehind" }
503+
}
474504

475505
/**
476506
* A negative-lookbehind assertion.
@@ -481,7 +511,9 @@ class RegExpPositiveLookbehind extends RegExpLookbehind, @regexp_positive_lookbe
481511
* (?<!\\)
482512
* ```
483513
*/
484-
class RegExpNegativeLookbehind extends RegExpLookbehind, @regexp_negative_lookbehind { }
514+
class RegExpNegativeLookbehind extends RegExpLookbehind, @regexp_negative_lookbehind {
515+
override string getAPrimaryQlClass() { result = "RegExpNegativeLookbehind" }
516+
}
485517

486518
/**
487519
* A star-quantified term.
@@ -494,6 +526,8 @@ class RegExpNegativeLookbehind extends RegExpLookbehind, @regexp_negative_lookbe
494526
*/
495527
class RegExpStar extends RegExpQuantifier, @regexp_star {
496528
override predicate isNullable() { any() }
529+
530+
override string getAPrimaryQlClass() { result = "RegExpStar" }
497531
}
498532

499533
/**
@@ -507,6 +541,8 @@ class RegExpStar extends RegExpQuantifier, @regexp_star {
507541
*/
508542
class RegExpPlus extends RegExpQuantifier, @regexp_plus {
509543
override predicate isNullable() { getAChild().isNullable() }
544+
545+
override string getAPrimaryQlClass() { result = "RegExpPlus" }
510546
}
511547

512548
/**
@@ -520,6 +556,8 @@ class RegExpPlus extends RegExpQuantifier, @regexp_plus {
520556
*/
521557
class RegExpOpt extends RegExpQuantifier, @regexp_opt {
522558
override predicate isNullable() { any() }
559+
560+
override string getAPrimaryQlClass() { result = "RegExpOpt" }
523561
}
524562

525563
/**
@@ -550,6 +588,8 @@ class RegExpRange extends RegExpQuantifier, @regexp_range {
550588
getAChild().isNullable() or
551589
getLowerBound() = 0
552590
}
591+
592+
override string getAPrimaryQlClass() { result = "RegExpRange" }
553593
}
554594

555595
/**
@@ -563,6 +603,8 @@ class RegExpRange extends RegExpQuantifier, @regexp_range {
563603
*/
564604
class RegExpDot extends RegExpTerm, @regexp_dot {
565605
override predicate isNullable() { none() }
606+
607+
override string getAPrimaryQlClass() { result = "RegExpDot" }
566608
}
567609

568610
/**
@@ -602,6 +644,8 @@ class RegExpGroup extends RegExpTerm, @regexp_group {
602644
override string getConstantValue() { result = getAChild().getConstantValue() }
603645

604646
override string getAMatchedString() { result = getAChild().getAMatchedString() }
647+
648+
override string getAPrimaryQlClass() { result = "RegExpGroup" }
605649
}
606650

607651
/**
@@ -614,7 +658,9 @@ class RegExpGroup extends RegExpTerm, @regexp_group {
614658
* ;
615659
* ```
616660
*/
617-
class RegExpNormalConstant extends RegExpConstant, @regexp_normal_constant { }
661+
class RegExpNormalConstant extends RegExpConstant, @regexp_normal_constant {
662+
override string getAPrimaryQlClass() { result = "RegExpNormalConstant" }
663+
}
618664

619665
/**
620666
* DEPRECATED. Use `RegExpNormalConstant` instead.
@@ -634,7 +680,9 @@ deprecated class RegExpNormalChar = RegExpNormalConstant;
634680
* \x0a
635681
* ```
636682
*/
637-
class RegExpHexEscape extends RegExpCharEscape, @regexp_hex_escape { }
683+
class RegExpHexEscape extends RegExpCharEscape, @regexp_hex_escape {
684+
override string getAPrimaryQlClass() { result = "RegExpHexEscape" }
685+
}
638686

639687
/**
640688
* A unicode character escape in a regular expression.
@@ -645,7 +693,9 @@ class RegExpHexEscape extends RegExpCharEscape, @regexp_hex_escape { }
645693
* \u000a
646694
* ```
647695
*/
648-
class RegExpUnicodeEscape extends RegExpCharEscape, @regexp_unicode_escape { }
696+
class RegExpUnicodeEscape extends RegExpCharEscape, @regexp_unicode_escape {
697+
override string getAPrimaryQlClass() { result = "RegExpUnicodeEscape" }
698+
}
649699

650700
/**
651701
* A decimal character escape in a regular expression.
@@ -656,7 +706,9 @@ class RegExpUnicodeEscape extends RegExpCharEscape, @regexp_unicode_escape { }
656706
* \0
657707
* ```
658708
*/
659-
class RegExpDecimalEscape extends RegExpCharEscape, @regexp_dec_escape { }
709+
class RegExpDecimalEscape extends RegExpCharEscape, @regexp_dec_escape {
710+
override string getAPrimaryQlClass() { result = "RegExpDecimalEscape" }
711+
}
660712

661713
/**
662714
* An octal character escape in a regular expression.
@@ -667,7 +719,9 @@ class RegExpDecimalEscape extends RegExpCharEscape, @regexp_dec_escape { }
667719
* \0177
668720
* ```
669721
*/
670-
class RegExpOctalEscape extends RegExpCharEscape, @regexp_oct_escape { }
722+
class RegExpOctalEscape extends RegExpCharEscape, @regexp_oct_escape {
723+
override string getAPrimaryQlClass() { result = "RegExpOctalEscape" }
724+
}
671725

672726
/**
673727
* A control character escape in a regular expression.
@@ -678,7 +732,9 @@ class RegExpOctalEscape extends RegExpCharEscape, @regexp_oct_escape { }
678732
* \ca
679733
* ```
680734
*/
681-
class RegExpControlEscape extends RegExpCharEscape, @regexp_ctrl_escape { }
735+
class RegExpControlEscape extends RegExpCharEscape, @regexp_ctrl_escape {
736+
override string getAPrimaryQlClass() { result = "RegExpControlEscape" }
737+
}
682738

683739
/**
684740
* A character class escape in a regular expression.
@@ -695,6 +751,8 @@ class RegExpCharacterClassEscape extends RegExpEscape, @regexp_char_class_escape
695751
string getValue() { char_class_escape(this, result) }
696752

697753
override predicate isNullable() { none() }
754+
755+
override string getAPrimaryQlClass() { result = "RegExpCharacterClassEscape" }
698756
}
699757

700758
/**
@@ -723,6 +781,8 @@ class RegExpUnicodePropertyEscape extends RegExpEscape, @regexp_unicode_property
723781
string getValue() { unicode_property_escapevalue(this, result) }
724782

725783
override predicate isNullable() { none() }
784+
785+
override string getAPrimaryQlClass() { result = "RegExpUnicodePropertyEscape" }
726786
}
727787

728788
/**
@@ -736,7 +796,9 @@ class RegExpUnicodePropertyEscape extends RegExpEscape, @regexp_unicode_property
736796
* \/
737797
* ```
738798
*/
739-
class RegExpIdentityEscape extends RegExpCharEscape, @regexp_id_escape { }
799+
class RegExpIdentityEscape extends RegExpCharEscape, @regexp_id_escape {
800+
override string getAPrimaryQlClass() { result = "RegExpIdentityEscape" }
801+
}
740802

741803
/**
742804
* A back reference, that is, a term of the form `\i` or `\k<name>`
@@ -770,6 +832,8 @@ class RegExpBackRef extends RegExpTerm, @regexp_backref {
770832
}
771833

772834
override predicate isNullable() { getGroup().isNullable() }
835+
836+
override string getAPrimaryQlClass() { result = "RegExpBackRef" }
773837
}
774838

775839
/**
@@ -808,6 +872,8 @@ class RegExpCharacterClass extends RegExpTerm, @regexp_char_class {
808872
cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase()
809873
)
810874
}
875+
876+
override string getAPrimaryQlClass() { result = "RegExpCharacterClass" }
811877
}
812878

813879
/**
@@ -827,6 +893,8 @@ class RegExpCharacterRange extends RegExpTerm, @regexp_char_range {
827893
lo = getChild(0).(RegExpConstant).getValue() and
828894
hi = getChild(1).(RegExpConstant).getValue()
829895
}
896+
897+
override string getAPrimaryQlClass() { result = "RegExpCharacterRange" }
830898
}
831899

832900
/** A parse error encountered while processing a regular expression literal. */
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
// Dummy file to be imported so the other files are seen as modules.
22
export let x = 5;
3+
4+
export let reg = /ab+c/;

0 commit comments

Comments
 (0)