Skip to content

Commit a788bf8

Browse files
author
Max Schaefer
committed
JavaScript: Fix RegExpTerm.getPredecessor and getSuccessor.
These were originally meant to give you the term that is textually matched right before/right after the receiver. When I introduced support for lookbehinds, I changed the behaviour to give you the term that is _operationally_ matched before/after the receiver (remember that lookbehinds are implemented by reverse-matching). However, I think that's rarely ever what you want, and is wrong for the only two uses of these predicates, where it's the textual matching order that we are after, not the operational order. Consequently, I've changed the semantics back and updated the comments to hopefully clarify the intention.
1 parent ec8ced7 commit a788bf8

File tree

5 files changed

+14
-12
lines changed

5 files changed

+14
-12
lines changed

change-notes/1.24/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717

1818
## Changes to libraries
1919

20+
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,21 @@ class RegExpTerm extends Locatable, @regexpterm {
7373
/** Holds if this regular expression term can match the empty string. */
7474
predicate isNullable() { none() } // Overridden in subclasses.
7575

76-
/** Gets the regular expression term that is matched before this one, if any. */
76+
/** Gets the regular expression term that is matched (textually) before this one, if any. */
7777
RegExpTerm getPredecessor() {
7878
exists(RegExpSequence seq, int i |
7979
seq.getChild(i) = this and
80-
seq.getChild(i - getDirection()) = result
80+
seq.getChild(i - 1) = result
8181
)
8282
or
8383
result = getParent().(RegExpTerm).getPredecessor()
8484
}
8585

86-
/** Gets the regular expression term that is matched after this one, if any. */
86+
/** Gets the regular expression term that is matched (textually) after this one, if any. */
8787
RegExpTerm getSuccessor() {
8888
exists(RegExpSequence seq, int i |
8989
seq.getChild(i) = this and
90-
seq.getChild(i + getDirection()) = result
90+
seq.getChild(i + 1) = result
9191
)
9292
or
9393
exists(RegExpTerm parent |
@@ -98,12 +98,6 @@ class RegExpTerm extends Locatable, @regexpterm {
9898
)
9999
}
100100

101-
/**
102-
* Gets the matching direction of this term: `1` if it is in a forward-matching
103-
* context, `-1` if it is in a backward-matching context.
104-
*/
105-
private int getDirection() { if isInBackwardMatchingContext() then result = -1 else result = 1 }
106-
107101
/**
108102
* Holds if this regular term is in a forward-matching context, that is,
109103
* it has no enclosing lookbehind assertions.

javascript/ql/test/query-tests/RegExp/UnmatchableCaret/tst.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@
2626
/^(^y|^z)(u$|v$)$/;
2727

2828
// OK
29-
/x*^y/;
29+
/x*^y/;
30+
31+
// OK
32+
/(?<=(^|\/)(\.|\.\.))$/;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
| tst.js:2:10:2:10 | $ | This assertion can never match. |
22
| tst.js:11:3:11:3 | $ | This assertion can never match. |
33
| tst.js:20:3:20:3 | $ | This assertion can never match. |
4+
| tst.js:38:6:38:6 | $ | This assertion can never match. |

javascript/ql/test/query-tests/RegExp/UnmatchableDollar/tst.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@
3232
/x(?!y+$).*y.*/;
3333

3434
// OK
35-
/x(?=[yz]+$).*yz.*/;
35+
/x(?=[yz]+$).*yz.*/;
36+
37+
// NOT OK
38+
/(?<=$x)yz/;

0 commit comments

Comments
 (0)