From 7f959bff17c9657cafbd41f947930f8503f67107 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:01:38 +0000 Subject: [PATCH 01/37] C++: Copy Ruby regex library and tests verbatim --- .../semmle/code/cpp/regex/RegexTreeView.qll | 1235 +++++++++++++++++ .../code/cpp/regex/internal/ParseRegExp.qll | 1037 ++++++++++++++ .../test/library-tests/regex/parse.expected | 489 +++++++ cpp/ql/test/library-tests/regex/parse.ql | 27 + .../test/library-tests/regex/regexp.expected | 270 ++++ cpp/ql/test/library-tests/regex/regexp.ql | 11 + cpp/ql/test/library-tests/regex/regexp.rb | 82 ++ 7 files changed, 3151 insertions(+) create mode 100644 cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll create mode 100644 cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll create mode 100644 cpp/ql/test/library-tests/regex/parse.expected create mode 100644 cpp/ql/test/library-tests/regex/parse.ql create mode 100644 cpp/ql/test/library-tests/regex/regexp.expected create mode 100644 cpp/ql/test/library-tests/regex/regexp.ql create mode 100644 cpp/ql/test/library-tests/regex/regexp.rb diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll new file mode 100644 index 000000000000..51df17008817 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -0,0 +1,1235 @@ +/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */ + +private import internal.ParseRegExp +private import codeql.util.Numbers +private import codeql.ruby.ast.Literal as Ast +private import codeql.Locations +private import codeql.regex.nfa.NfaUtils as NfaUtils +private import codeql.regex.RegexTreeView +// exporting as RegexTreeView, and in the top-level scope. +import Impl as RegexTreeView +import Impl + +/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ +RegExpTerm getParsedRegExp(Ast::RegExpLiteral re) { + result.getRegExp() = re and result.isRootTerm() +} + +/** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + * + * For sequences and alternations, we require at least one child. + * Otherwise, we wish to represent the term differently. + * This avoids multiple representations of the same term. + */ +private newtype TRegExpParent = + /** A string literal used as a regular expression */ + TRegExpLiteral(RegExp re) or + /** A quantified term */ + TRegExpQuantifier(RegExp re, int start, int end) { re.qualifiedItem(start, end, _, _) } or + /** A sequence term */ + TRegExpSequence(RegExp re, int start, int end) { re.sequence(start, end) } or + /** An alternation term */ + TRegExpAlt(RegExp re, int start, int end) { re.alternation(start, end) } or + /** A character class term */ + TRegExpCharacterClass(RegExp re, int start, int end) { re.charSet(start, end) } or + /** A character range term */ + TRegExpCharacterRange(RegExp re, int start, int end) { re.charRange(_, start, _, _, end) } or + /** A group term */ + TRegExpGroup(RegExp re, int start, int end) { re.group(start, end) } or + /** A special character */ + TRegExpSpecialChar(RegExp re, int start, int end) { re.specialCharacter(start, end, _) } or + /** A normal character */ + TRegExpNormalChar(RegExp re, int start, int end) { + re.normalCharacterSequence(start, end) + or + re.escapedCharacter(start, end) and + not re.specialCharacter(start, end, _) + } or + /** A back reference */ + TRegExpBackRef(RegExp re, int start, int end) { re.backreference(start, end) } or + /** A named character property */ + TRegExpNamedCharacterProperty(RegExp re, int start, int end) { + re.namedCharacterProperty(start, end, _) + } + +/** An implementation that statisfies the RegexTreeView signature. */ +private module Impl implements RegexTreeViewSig { + /** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + */ + class RegExpParent extends TRegExpParent { + /** Gets a textual representation of this element. */ + string toString() { result = "RegExpParent" } + + /** Gets the `i`th child term. */ + RegExpTerm getChild(int i) { none() } + + /** Gets a child term . */ + final RegExpTerm getAChild() { result = this.getChild(_) } + + /** Gets the number of child terms. */ + int getNumChild() { result = count(this.getAChild()) } + + /** Gets the last child term of this element. */ + RegExpTerm getLastChild() { result = this.getChild(this.getNumChild() - 1) } + + /** + * Gets the name of a primary CodeQL class to which this regular + * expression term belongs. + */ + string getAPrimaryQlClass() { result = "RegExpParent" } + + /** + * Gets a comma-separated list of the names of the primary CodeQL classes to + * which this regular expression term belongs. + */ + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + } + + /** A string literal used as a regular expression */ + class RegExpLiteral extends TRegExpLiteral, RegExpParent { + RegExp re; + + RegExpLiteral() { this = TRegExpLiteral(re) } + + override RegExpTerm getChild(int i) { + i = 0 and result.getRegExp() = re and result.isRootTerm() + } + + /** Holds if dot, `.`, matches all characters, including newlines. */ + predicate isDotAll() { re.isDotAll() } + + /** Holds if this regex matching is case-insensitive for this regex. */ + predicate isIgnoreCase() { re.isIgnoreCase() } + + /** Get a string representing all modes for this regex. */ + string getFlags() { result = re.getFlags() } + + /** Gets the primary QL class for this regex. */ + override string getAPrimaryQlClass() { result = "RegExpLiteral" } + } + + /** + * A regular expression term, that is, a syntactic part of a regular expression. + */ + class RegExpTerm extends RegExpParent { + RegExp re; + int start; + int end; + + RegExpTerm() { + this = TRegExpAlt(re, start, end) + or + this = TRegExpBackRef(re, start, end) + or + this = TRegExpCharacterClass(re, start, end) + or + this = TRegExpCharacterRange(re, start, end) + or + this = TRegExpNormalChar(re, start, end) + or + this = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + or + this = TRegExpSpecialChar(re, start, end) + or + this = TRegExpNamedCharacterProperty(re, start, end) + } + + /** + * Gets the outermost term of this regular expression. + */ + RegExpTerm getRootTerm() { + this.isRootTerm() and result = this + or + result = this.getParent().(RegExpTerm).getRootTerm() + } + + /** + * Holds if this term is part of a string literal + * that is interpreted as a regular expression. + */ + predicate isUsedAsRegExp() { any() } + + /** + * Holds if this is the root term of a regular expression. + */ + predicate isRootTerm() { start = 0 and end = re.getText().length() } + + override RegExpTerm getChild(int i) { + result = this.(RegExpAlt).getChild(i) + or + result = this.(RegExpBackRef).getChild(i) + or + result = this.(RegExpCharacterClass).getChild(i) + or + result = this.(RegExpCharacterRange).getChild(i) + or + result = this.(RegExpNormalChar).getChild(i) + or + result = this.(RegExpGroup).getChild(i) + or + result = this.(RegExpQuantifier).getChild(i) + or + result = this.(RegExpSequence).getChild(i) + or + result = this.(RegExpSpecialChar).getChild(i) + or + result = this.(RegExpNamedCharacterProperty).getChild(i) + } + + /** + * Gets the parent term of this regular expression term, or the + * regular expression literal if this is the root term. + */ + RegExpParent getParent() { result.getAChild() = this } + + /** Gets the associated `RegExp`. */ + RegExp getRegExp() { result = re } + + /** Gets the offset at which this term starts. */ + int getStart() { result = start } + + /** Gets the offset at which this term ends. */ + int getEnd() { result = end } + + override string toString() { result = re.getText().substring(start, end) } + + /** + * Gets the location of the surrounding regex, as locations inside the regex do not exist. + * To get location information corresponding to the term inside the regex, + * use `hasLocationInfo`. + */ + Location getLocation() { result = re.getLocation() } + + pragma[noinline] + private predicate componentHasLocationInfo( + int i, string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + re.getComponent(i) + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Holds if this term is found at the specified location offsets. */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(int re_start | + this.componentHasLocationInfo(0, filepath, startline, re_start, _, _) and + this.componentHasLocationInfo(re.getNumberOfComponents() - 1, filepath, _, _, endline, _) and + startcolumn = re_start + start and + endcolumn = re_start + end - 1 + ) + } + + /** Gets the file in which this term is found. */ + File getFile() { result = this.getLocation().getFile() } + + /** Gets the raw source text of this term. */ + string getRawValue() { result = this.toString() } + + /** Gets the string literal in which this term is found. */ + RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } + + /** Gets the regular expression term that is matched (textually) before this one, if any. */ + RegExpTerm getPredecessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).previousElement(this) + or + not exists(parent.(RegExpSequence).previousElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getPredecessor() + ) + } + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).nextElement(this) + or + not exists(parent.(RegExpSequence).nextElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getSuccessor() + ) + } + + /** + * Gets the single string this regular-expression term matches. + * + * This predicate is only defined for (sequences/groups of) constant regular + * expressions. In particular, terms involving zero-width assertions like `^` + * or `\b` are not considered to have a constant value. + * + * Note that this predicate does not take flags of the enclosing + * regular-expression literal into account. + */ + string getConstantValue() { none() } + + /** + * Gets a string that is matched by this regular-expression term. + */ + string getAMatchedString() { result = this.getConstantValue() } + + /** Gets the primary QL class for this term. */ + override string getAPrimaryQlClass() { result = "RegExpTerm" } + + /** Holds if this regular expression term can match the empty string. */ + predicate isNullable() { none() } + } + + /** + * A quantified regular expression term. + * + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` + */ + class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean may_repeat_forever; + + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.qualifiedPart(start, part_end, end, _, may_repeat_forever) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + result.getEnd() = part_end + } + + /** Hols if this term may match an unlimited number of times. */ + predicate mayRepeatForever() { may_repeat_forever = true } + + /** Gets the qualifier for this term. That is e.g "?" for "a?". */ + string getQualifier() { result = re.getText().substring(part_end, end) } + + override string getAPrimaryQlClass() { result = "RegExpQuantifier" } + } + + /** + * A regular expression term that permits unlimited repetitions. + */ + class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } + + override string getAPrimaryQlClass() { result = "InfiniteRepetitionQuantifier" } + } + + /** + * A star-quantified term. + * + * Example: + * + * ``` + * \w* + * ``` + */ + class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQualifier().charAt(0) = "*" } + + override string getAPrimaryQlClass() { result = "RegExpStar" } + + override predicate isNullable() { any() } + } + + /** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ + class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQualifier().charAt(0) = "+" } + + override string getAPrimaryQlClass() { result = "RegExpPlus" } + + override predicate isNullable() { this.getAChild().isNullable() } + } + + /** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ + class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQualifier().charAt(0) = "?" } + + override string getAPrimaryQlClass() { result = "RegExpOpt" } + + override predicate isNullable() { any() } + } + + /** + * A range-quantified term + * + * Examples: + * + * ``` + * \w{2,4} + * \w{2,} + * \w{2} + * ``` + */ + class RegExpRange extends RegExpQuantifier { + string upper; + string lower; + + RegExpRange() { re.multiples(part_end, end, lower, upper) } + + override string getAPrimaryQlClass() { result = "RegExpRange" } + + /** Gets the string defining the upper bound of this range, if any. */ + string getUpper() { result = upper } + + /** Gets the string defining the lower bound of this range, if any. */ + string getLower() { result = lower } + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound() { result = this.getUpper().toInt() } + + /** Gets the lower bound of the range. */ + int getLowerBound() { result = this.getLower().toInt() } + + override predicate isNullable() { this.getAChild().isNullable() or this.getLowerBound() = 0 } + } + + /** + * A sequence term. + * + * Example: + * + * ``` + * (ECMA|Java)Script + * ``` + * + * This is a sequence with the elements `(ECMA|Java)` and `Script`. + */ + class RegExpSequence extends RegExpTerm, TRegExpSequence { + RegExpSequence() { + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + } + + override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } + + /** Gets the element preceding `element` in this sequence. */ + RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } + + /** Gets the element following `element` in this sequence. */ + RegExpTerm nextElement(RegExpTerm element) { + exists(int i | + element = this.getChild(i) and + result = this.getChild(i + 1) + ) + } + + override string getConstantValue() { result = this.getConstantValue(0) } + + /** + * Gets the single string matched by the `i`th child and all following + * children of this sequence, if any. + */ + private string getConstantValue(int i) { + i = this.getNumChild() and + result = "" + or + result = this.getChild(i).getConstantValue() + this.getConstantValue(i + 1) + } + + override string getAPrimaryQlClass() { result = "RegExpSequence" } + + override predicate isNullable() { + forall(RegExpTerm child | child = this.getAChild() | child.isNullable()) + } + } + + pragma[nomagic] + private int seqChildEnd(RegExp re, int start, int end, int i) { + result = seqChild(re, start, end, i).getEnd() + } + + // moved out so we can use it in the charpred + private RegExpTerm seqChild(RegExp re, int start, int end, int i) { + re.sequence(start, end) and + ( + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + exists(int itemEnd | + re.item(start, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegExp() = re and + exists(int itemStart | itemStart = seqChildEnd(re, start, end, i - 1) | + result.getStart() = itemStart and + re.item(itemStart, result.getEnd()) + ) + ) + } + + /** + * An alternative term, that is, a term of the form `a|b`. + * + * Example: + * + * ``` + * ECMA|Java + * ``` + */ + class RegExpAlt extends RegExpTerm, TRegExpAlt { + RegExpAlt() { this = TRegExpAlt(re, start, end) } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + exists(int part_end | + re.alternationOption(start, end, start, part_end) and + result.getEnd() = part_end + ) + or + i > 0 and + result.getRegExp() = re and + exists(int part_start | + part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | + | + result.getStart() = part_start and + re.alternationOption(start, end, part_start, result.getEnd()) + ) + } + + /** Gets an alternative of this term. */ + RegExpTerm getAlternative() { result = this.getAChild() } + + override string getAMatchedString() { result = this.getAlternative().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpAlt" } + + override predicate isNullable() { this.getAChild().isNullable() } + } + + /** + * A character escape in a regular expression. + * + * Example: + * + * ``` + * \. + * ``` + */ + class RegExpCharEscape = RegExpEscape; + + /** + * An escaped regular expression term, that is, a regular expression + * term starting with a backslash, which is not a backreference. + * + * Example: + * + * ``` + * \. + * \w + * ``` + */ + class RegExpEscape extends RegExpNormalChar { + RegExpEscape() { re.escapedCharacter(start, end) } + + /** + * Gets the name of the escaped; for example, `w` for `\w`. + * TODO: Handle named escapes. + */ + override string getValue() { + not this.isUnicode() and + this.isIdentityEscape() and + result = this.getUnescaped() + or + this.getUnescaped() = "n" and result = "\n" + or + this.getUnescaped() = "r" and result = "\r" + or + this.getUnescaped() = "t" and result = "\t" + or + this.getUnescaped() = "f" and result = 12.toUnicode() + or + this.getUnescaped() = "v" and result = 11.toUnicode() + or + this.isUnicode() and + result = this.getUnicode() + } + + /** Holds if this terms name is given by the part following the escape character. */ + predicate isIdentityEscape() { + not this.getUnescaped() in ["n", "r", "t", "f", "v"] and not this.isUnicode() + } + + override string getAPrimaryQlClass() { result = "RegExpEscape" } + + /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ + string getUnescaped() { result = this.getText().suffix(1) } + + /** + * Gets the text for this escape. That is e.g. "\w". + */ + private string getText() { result = re.getText().substring(start, end) } + + /** + * Holds if this is a unicode escape. + */ + private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] } + + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { + this.isUnicode() and + result = parseHexInt(this.getText().suffix(2)).toUnicode() + } + } + + /** + * A word boundary, that is, a regular expression term of the form `\b`. + */ + class RegExpWordBoundary extends RegExpSpecialChar { + RegExpWordBoundary() { this.getChar() = "\\b" } + + override predicate isNullable() { none() } + } + + /** + * A non-word boundary, that is, a regular expression term of the form `\B`. + */ + class RegExpNonWordBoundary extends RegExpSpecialChar { + RegExpNonWordBoundary() { this.getChar() = "\\B" } + + override string getAPrimaryQlClass() { result = "RegExpNonWordBoundary" } + } + + /** + * A character class escape in a regular expression. + * That is, an escaped character that denotes multiple characters. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ + class RegExpCharacterClassEscape extends RegExpEscape { + RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H"] } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClassEscape" } + + override predicate isNullable() { none() } + } + + /** + * A character class in a regular expression. + * + * Examples: + * + * ```rb + * /[a-fA-F0-9]/ + * /[^abc]/ + * ``` + */ + class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { + RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + + /** Holds if this character class is inverted, matching the opposite of its content. */ + predicate isInverted() { re.getChar(start + 1) = "^" } + + /** Holds if this character class can match anything. */ + predicate isUniversalClass() { + // [^] + this.isInverted() and not exists(this.getAChild()) + or + // [\w\W] and similar + not this.isInverted() and + exists(string cce1, string cce2 | + cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() + | + cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() + ) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + exists(int itemStart, int itemEnd | + result.getStart() = itemStart and + re.charSetStart(start, itemStart) and + re.charSetChild(start, itemStart, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegExp() = re and + exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() | + result.getStart() = itemStart and + re.charSetChild(start, itemStart, result.getEnd()) + ) + } + + override string getAMatchedString() { + not this.isInverted() and result = this.getAChild().getAMatchedString() + } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClass" } + + override predicate isNullable() { none() } + } + + /** + * A character range in a character class in a regular expression. + * + * Example: + * + * ``` + * a-z + * ``` + */ + class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { + int lower_end; + int upper_start; + + RegExpCharacterRange() { + this = TRegExpCharacterRange(re, start, end) and + re.charRange(_, start, lower_end, upper_start, end) + } + + /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ + predicate isRange(string lo, string hi) { + lo = re.getText().substring(start, lower_end) and + hi = re.getText().substring(upper_start, end) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + result.getEnd() = lower_end + or + i = 1 and + result.getRegExp() = re and + result.getStart() = upper_start and + result.getEnd() = end + } + + override string getAPrimaryQlClass() { result = "RegExpCharacterRange" } + + override predicate isNullable() { none() } + } + + /** + * A normal character in a regular expression, that is, a character + * without special meaning. This includes escaped characters. + * + * Examples: + * ``` + * t + * \t + * ``` + */ + additional class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { + RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string representation of the char matched by this term. */ + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpNormalChar" } + } + + /** + * A constant regular expression term, that is, a regular expression + * term matching a single string. Currently, this will always be a single character. + * + * Example: + * + * ``` + * a + * ``` + */ + class RegExpConstant extends RegExpTerm { + string value; + + RegExpConstant() { + this = TRegExpNormalChar(re, start, end) and + not this instanceof RegExpCharacterClassEscape and + // exclude chars in qualifiers + // TODO: push this into regex library + not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) | + qstart <= start and end <= qend + ) and + value = this.(RegExpNormalChar).getValue() + or + this = TRegExpSpecialChar(re, start, end) and + re.inCharSet(start) and + value = this.(RegExpSpecialChar).getChar() + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string matched by this constant term. */ + string getValue() { result = value } + + override RegExpTerm getChild(int i) { none() } + + override string getConstantValue() { result = this.getValue() } + + override string getAPrimaryQlClass() { result = "RegExpConstant" } + + override predicate isNullable() { none() } + } + + /** + * A grouped regular expression. + * + * Examples: + * + * ``` + * (ECMA|Java) + * (?:ECMA|Java) + * (?['"]) + * ``` + */ + class RegExpGroup extends RegExpTerm, TRegExpGroup { + RegExpGroup() { this = TRegExpGroup(re, start, end) } + + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber() { result = re.getGroupNumber(start, end) } + + /** Holds if this is a capture group. */ + predicate isCapture() { exists(this.getNumber()) } + + /** Holds if this is a named capture group. */ + predicate isNamed() { exists(this.getName()) } + + /** Gets the name of this capture group, if any. */ + string getName() { result = re.getGroupName(start, end) } + + override RegExpTerm getChild(int i) { + result.getRegExp() = re and + i = 0 and + re.groupContents(start, end, result.getStart(), result.getEnd()) + } + + override string getConstantValue() { result = this.getAChild().getConstantValue() } + + override string getAMatchedString() { result = this.getAChild().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpGroup" } + + override predicate isNullable() { this.getAChild().isNullable() } + } + + /** + * A special character in a regular expression. + * + * Examples: + * ``` + * ^ + * $ + * . + * ``` + */ + additional class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { + string char; + + RegExpSpecialChar() { + this = TRegExpSpecialChar(re, start, end) and + re.specialCharacter(start, end, char) + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the char for this term. */ + string getChar() { result = char } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpSpecialChar" } + } + + /** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ + class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getAPrimaryQlClass() { result = "RegExpDot" } + + override predicate isNullable() { none() } + } + + /** + * A term that matches a specific position between characters in the string. + * + * Example: + * + * ``` + * \A + * ``` + */ + class RegExpAnchor extends RegExpSpecialChar { + RegExpAnchor() { this.getChar() = ["^", "$", "\\A", "\\Z", "\\z"] } + + override string getAPrimaryQlClass() { result = "RegExpAnchor" } + } + + /** + * A dollar assertion `$` or `\Z` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ + class RegExpDollar extends RegExpAnchor { + RegExpDollar() { this.getChar() = ["$", "\\Z", "\\z"] } + + override string getAPrimaryQlClass() { result = "RegExpDollar" } + + override predicate isNullable() { any() } + } + + /** + * A caret assertion `^` or `\A` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ + class RegExpCaret extends RegExpAnchor { + RegExpCaret() { this.getChar() = ["^", "\\A"] } + + override string getAPrimaryQlClass() { result = "RegExpCaret" } + + override predicate isNullable() { any() } + } + + /** + * A zero-width match, that is, either an empty group or an assertion. + * + * Examples: + * ``` + * () + * (?=\w) + * ``` + */ + additional class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpZeroWidthMatch" } + + override predicate isNullable() { any() } + } + + /** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?` + * in a regular expression. + * + * Examples: + * + * ``` + * \1 + * (?P=quote) + * ``` + */ + class RegExpBackRef extends RegExpTerm, TRegExpBackRef { + RegExpBackRef() { this = TRegExpBackRef(re, start, end) } + + /** + * Gets the number of the capture group this back reference refers to, if any. + */ + int getNumber() { result = re.getBackRefNumber(start, end) } + + /** + * Gets the name of the capture group this back reference refers to, if any. + */ + string getName() { result = re.getBackRefName(start, end) } + + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup() { + result.getLiteral() = this.getLiteral() and + ( + result.getNumber() = this.getNumber() or + result.getName() = this.getName() + ) + } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpBackRef" } + + override predicate isNullable() { this.getGroup().isNullable() } + } + + /** + * A named character property. For example, the POSIX bracket expression + * `[[:digit:]]`. + */ + additional class RegExpNamedCharacterProperty extends RegExpTerm, TRegExpNamedCharacterProperty { + RegExpNamedCharacterProperty() { this = TRegExpNamedCharacterProperty(re, start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpNamedCharacterProperty" } + + /** + * Gets the property name. For example, in `\p{Space}`, the result is + * `"Space"`. + */ + string getName() { result = re.getCharacterPropertyName(start, end) } + + /** + * Holds if the property is inverted. For example, it holds for `\p{^Digit}`, + * which matches non-digits. + */ + predicate isInverted() { re.namedCharacterPropertyIsInverted(start, end) } + } + + class Top = RegExpParent; + + /** + * Holds if `term` is an escape class representing e.g. `\d`. + * `clazz` is which character class it represents, e.g. "d" for `\d`. + */ + predicate isEscapeClass(RegExpTerm term, string clazz) { + exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz) + or + // TODO: expand to cover more properties + exists(RegExpNamedCharacterProperty escape | term = escape | + escape.getName().toLowerCase() = "digit" and + if escape.isInverted() then clazz = "D" else clazz = "d" + or + escape.getName().toLowerCase() = "space" and + if escape.isInverted() then clazz = "S" else clazz = "s" + or + escape.getName().toLowerCase() = "word" and + if escape.isInverted() then clazz = "W" else clazz = "w" + ) + } + + /** + * Holds if the regular expression should not be considered. + */ + predicate isExcluded(RegExpParent parent) { + parent.(RegExpTerm).getRegExp().(Ast::RegExpLiteral).hasFreeSpacingFlag() // exclude free-spacing mode regexes + } + + /** + * Holds if `term` is a possessive quantifier. + * Not currently implemented, but is used by the shared library. + */ + predicate isPossessive(RegExpQuantifier term) { none() } + + /** + * 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. + * Not yet implemented for Ruby. + */ + predicate matchesAnyPrefix(RegExpTerm term) { any() } + + /** + * 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. + * Not yet implemented for Ruby. + */ + predicate matchesAnySuffix(RegExpTerm term) { any() } + + /** + * Holds if `root` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isIgnoreCase() + } + + /** + * Holds if `root` has the `s` flag for multi-line matching. + */ + predicate isDotAll(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isDotAll() + } +} diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll new file mode 100644 index 000000000000..d35d9353bf13 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -0,0 +1,1037 @@ +/** + * Library for parsing for Ruby regular expressions. + * + * N.B. does not yet handle stripping whitespace and comments in regexes with + * the `x` (free-spacing) flag. + */ + +private import codeql.ruby.AST as Ast +private import codeql.Locations + +/** + * A `StringlikeLiteral` containing a regular expression term, that is, either + * a regular expression literal, or a string literal used in a context where + * it is parsed as regular expression. + */ +abstract class RegExp extends Ast::StringlikeLiteral { + /** + * Holds if this `RegExp` has the `s` flag for multi-line matching. + */ + predicate isDotAll() { none() } + + /** + * Holds if this `RegExp` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase() { none() } + + /** + * Gets the flags for this `RegExp`, or the empty string if it has no flags. + */ + string getFlags() { result = "" } + + /** + * Helper predicate for `charSetStart(int start, int end)`. + * + * In order to identify left brackets ('[') which actually start a character class, + * we perform a left to right scan of the string. + * + * To avoid negative recursion we return a boolean. See `escaping`, + * the helper for `escapingChar`, for a clean use of this pattern. + * + * result is true for those start chars that actually mark a start of a char set. + */ + boolean charSetStart(int pos) { + exists(int index | + // is opening bracket + this.charSetDelimiter(index, pos) = true and + ( + // if this is the first bracket, `pos` starts a char set + index = 1 and result = true + or + // if the previous char set delimiter was not a closing bracket, `pos` does + // not start a char set. This is needed to handle cases such as `[[]` (a + // char set that matches the `[` char) + index > 1 and + not this.charSetDelimiter(index - 1, _) = false and + result = false + or + // special handling of cases such as `[][]` (the character-set of the characters `]` and `[`). + exists(int prevClosingBracketPos | + // previous bracket is a closing bracket + this.charSetDelimiter(index - 1, prevClosingBracketPos) = false and + if + // check if the character that comes before the previous closing bracket + // is an opening bracket (taking `^` into account) + // check if the character that comes before the previous closing bracket + // is an opening bracket (taking `^` into account) + exists(int posBeforePrevClosingBracket | + if this.getChar(prevClosingBracketPos - 1) = "^" + then posBeforePrevClosingBracket = prevClosingBracketPos - 2 + else posBeforePrevClosingBracket = prevClosingBracketPos - 1 + | + this.charSetDelimiter(index - 2, posBeforePrevClosingBracket) = true + ) + then + // brackets without anything in between is not valid character ranges, so + // the first closing bracket in `[]]` and `[^]]` does not count, + // + // and we should _not_ mark the second opening bracket in `[][]` and `[^][]` + // as starting a new char set. ^ ^ + exists(int posBeforePrevClosingBracket | + this.charSetDelimiter(index - 2, posBeforePrevClosingBracket) = true + | + result = this.charSetStart(posBeforePrevClosingBracket).booleanNot() + ) + else + // if not, `pos` does in fact mark a real start of a character range + result = true + ) + ) + ) + } + + /** + * Helper predicate for chars that could be character-set delimiters. + * Holds if the (non-escaped) char at `pos` in the string, is the (one-based) `index` occurrence of a bracket (`[` or `]`) in the string. + * Result if `true` is the char is `[`, and `false` if the char is `]`. + */ + boolean charSetDelimiter(int index, int pos) { + pos = + rank[index](int p | + (this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and + // Brackets that art part of POSIX expressions should not count as + // char-set delimiters. + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and pos >= x and pos < y + ) + ) and + ( + this.nonEscapedCharAt(pos) = "[" and result = true + or + this.nonEscapedCharAt(pos) = "]" and result = false + ) + } + + /** Holds if a character set starts between `start` and `end`. */ + predicate charSetStart(int start, int end) { + this.charSetStart(start) = true and + ( + this.getChar(start + 1) = "^" and end = start + 2 + or + not this.getChar(start + 1) = "^" and end = start + 1 + ) + } + + /** Whether there is a character class, between start (inclusive) and end (exclusive) */ + predicate charSet(int start, int end) { + exists(int innerStart, int innerEnd | + this.charSetStart(start, innerStart) and + not this.charSetStart(_, start) + | + end = innerEnd + 1 and + innerEnd = + min(int e | + e > innerStart and + this.nonEscapedCharAt(e) = "]" and + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and e >= x and e < y + ) + | + e + ) + ) + } + + /** + * Holds if the character set starting at `charsetStart` contains either + * a character or a `-` found between `start` and `end`. + */ + private predicate charSetToken(int charsetStart, int index, int tokenStart, int tokenEnd) { + tokenStart = + rank[index](int start, int end | this.charSetToken(charsetStart, start, end) | start) and + this.charSetToken(charsetStart, tokenStart, tokenEnd) + } + + /** + * Holds if the character set starting at `charsetStart` contains either + * a character or a `-` found between `start` and `end`. + */ + private predicate charSetToken(int charsetStart, int start, int end) { + this.charSetStart(charsetStart, start) and + ( + this.escapedCharacter(start, end) + or + this.namedCharacterProperty(start, end, _) + or + exists(this.nonEscapedCharAt(start)) and end = start + 1 + ) + or + this.charSetToken(charsetStart, _, start) and + ( + this.escapedCharacter(start, end) + or + this.namedCharacterProperty(start, end, _) + or + exists(this.nonEscapedCharAt(start)) and + end = start + 1 and + not this.getChar(start) = "]" + ) + } + + /** + * Holds if the character set starting at `charsetStart` contains either + * a character or a range found between `start` and `end`. + */ + predicate charSetChild(int charsetStart, int start, int end) { + this.charSetToken(charsetStart, start, end) and + not exists(int rangeStart, int rangeEnd | + this.charRange(charsetStart, rangeStart, _, _, rangeEnd) and + rangeStart <= start and + rangeEnd >= end + ) + or + this.charRange(charsetStart, start, _, _, end) + } + + /** + * Holds if the character set starting at `charsetStart` contains a character range + * with lower bound found between `start` and `lowerEnd` + * and upper bound found between `upperStart` and `end`. + */ + predicate charRange(int charsetStart, int start, int lowerEnd, int upperStart, int end) { + exists(int index | + this.charRangeEnd(charsetStart, index) = true and + this.charSetToken(charsetStart, index - 2, start, lowerEnd) and + this.charSetToken(charsetStart, index, upperStart, end) + ) + } + + /** + * Helper predicate for `charRange`. + * We can determine where character ranges end by a left to right sweep. + * + * To avoid negative recursion we return a boolean. See `escaping`, + * the helper for `escapingChar`, for a clean use of this pattern. + */ + private boolean charRangeEnd(int charsetStart, int index) { + this.charSetToken(charsetStart, index, _, _) and + ( + index in [1, 2] and result = false + or + index > 2 and + exists(int connectorStart | + this.charSetToken(charsetStart, index - 1, connectorStart, _) and + this.nonEscapedCharAt(connectorStart) = "-" and + result = + this.charRangeEnd(charsetStart, index - 2) + .booleanNot() + .booleanAnd(this.charRangeEnd(charsetStart, index - 1).booleanNot()) + ) + or + not exists(int connectorStart | + this.charSetToken(charsetStart, index - 1, connectorStart, _) and + this.nonEscapedCharAt(connectorStart) = "-" + ) and + result = false + ) + } + + /** Holds if the character at `pos` is a "\" that is actually escaping what comes after. */ + predicate escapingChar(int pos) { this.escaping(pos) = true } + + /** + * Helper predicate for `escapingChar`. + * In order to avoid negative recursion, we return a boolean. + * This way, we can refer to `escaping(pos - 1).booleanNot()` + * rather than to a negated version of `escaping(pos)`. + */ + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** Gets the text of this regex */ + string getText() { + exists(Ast::ConstantValue c | c = this.getConstantValue() | + result = [this.getConstantValue().getString(), this.getConstantValue().getRegExp()] + ) + } + + /** Gets the `i`th character of this regex */ + string getChar(int i) { result = this.getText().charAt(i) } + + /** Gets the `i`th character of this regex, unless it is part of a character escape sequence. */ + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1]) + } + + private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } + + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" and not this.inCharSet(i) } + + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) } + + /** + * Holds if the `i`th character could not be parsed. + */ + predicate failedToParse(int i) { + exists(this.getChar(i)) and + not exists(int start, int end | + this.topLevel(start, end) and + start <= i and + end > i + ) + } + + /** Matches named character properties such as `\p{Word}` and `[[:digit:]]` */ + predicate namedCharacterProperty(int start, int end, string name) { + this.pStyleNamedCharacterProperty(start, end, name) or + this.posixStyleNamedCharacterProperty(start, end, name) + } + + /** Gets the name of the character property in start,end */ + string getCharacterPropertyName(int start, int end) { + this.namedCharacterProperty(start, end, result) + } + + /** Matches a POSIX bracket expression such as `[:alnum:]` within a character class. */ + private predicate posixStyleNamedCharacterProperty(int start, int end, string name) { + this.getChar(start) = "[" and + this.getChar(start + 1) = ":" and + end = + min(int e | + e > start and + this.getChar(e - 2) = ":" and + this.getChar(e - 1) = "]" + | + e + ) and + exists(int nameStart | + this.getChar(start + 2) = "^" and nameStart = start + 3 + or + not this.getChar(start + 2) = "^" and nameStart = start + 2 + | + name = this.getText().substring(nameStart, end - 2) + ) + } + + /** + * Matches named character properties. For example: + * - `\p{Space}` + * - `\P{Digit}` upper-case P means inverted + * - `\p{^Word}` caret also means inverted + * + * These can occur both inside and outside of character classes. + */ + private predicate pStyleNamedCharacterProperty(int start, int end, string name) { + this.escapingChar(start) and + this.getChar(start + 1) in ["p", "P"] and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") and + exists(int nameStart | + this.getChar(start + 3) = "^" and nameStart = start + 4 + or + not this.getChar(start + 3) = "^" and nameStart = start + 3 + | + name = this.getText().substring(nameStart, end - 1) + ) + } + + /** + * Holds if the named character property is inverted. Examples for which it holds: + * - `\P{Digit}` upper-case P means inverted + * - `\p{^Word}` caret also means inverted + * - `[[:^digit:]]` + * + * Examples for which it doesn't hold: + * - `\p{Word}` + * - `\P{^Space}` - upper-case P and caret cancel each other out + * - `[[:alnum:]]` + */ + predicate namedCharacterPropertyIsInverted(int start, int end) { + this.pStyleNamedCharacterProperty(start, end, _) and + exists(boolean upperP, boolean caret | + (if this.getChar(start + 1) = "P" then upperP = true else upperP = false) and + (if this.getChar(start + 3) = "^" then caret = true else caret = false) + | + upperP.booleanXor(caret) = true + ) + or + this.posixStyleNamedCharacterProperty(start, end, _) and + this.getChar(start + 3) = "^" + } + + /** + * Holds if an escaped character is found between `start` and `end`. + * Escaped characters include hex values, octal values and named escapes, + * but excludes backreferences. + */ + predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and + not this.numberedBackreference(start, _, _) and + not this.namedBackreference(start, _, _) and + not this.pStyleNamedCharacterProperty(start, _, _) and + ( + // hex char \xhh + this.getChar(start + 1) = "x" and end = start + 4 + or + // wide hex char \uhhhh + this.getChar(start + 1) = "u" and end = start + 6 + or + // escape not handled above; update when adding a new case + not this.getChar(start + 1) in ["x", "u"] and + not exists(this.getChar(start + 1).toInt()) and + end = start + 2 + ) + } + + /** + * Holds if the character at `index` is inside a character set. + */ + predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) + } + + /** + * Holds if the character at `index` is inside a posix bracket. + */ + predicate inPosixBracket(int index) { + exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and index in [x + 1 .. y - 2] + ) + } + + /** + * 'simple' characters are any that don't alter the parsing of the regex. + */ + private predicate simpleCharacter(int start, int end) { + end = start + 1 and + not this.charSet(start, _) and + not this.charSet(_, start + 1) and + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and + start >= x and + end <= y + ) and + exists(string c | c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.charSetStart(x, y) + | + start = y + or + start = z - 2 + or + start > y and start < z - 2 and not this.charRange(_, _, start, end, _) + ) + or + not this.inCharSet(start) and + not c = "(" and + not c = "[" and + not c = ")" and + not c = "|" and + not this.qualifier(start, _, _, _) + ) + } + + /** + * Holds if a simple or escaped character is found between `start` and `end`. + */ + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) and + not exists(int x, int y | this.groupStart(x, y) and x <= start and y >= end) and + not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end) and + not exists(int x, int y | + this.pStyleNamedCharacterProperty(x, y, _) and x <= start and y >= end + ) and + not exists(int x, int y | this.multiples(x, y, _, _) and x <= start and y >= end) + } + + /** + * Holds if a normal character is found between `start` and `end`. + */ + predicate normalCharacter(int start, int end) { + end = start + 1 and + this.character(start, end) and + not this.specialCharacter(start, end, _) + } + + /** + * Holds if a special character is found between `start` and `end`. + */ + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) and + not this.inCharSet(start) and + ( + end = start + 1 and + char = this.getChar(start) and + (char = "$" or char = "^" or char = ".") + or + end = start + 2 and + this.escapingChar(start) and + char = this.getText().substring(start, end) and + char = ["\\A", "\\Z", "\\z", "\\G", "\\b", "\\B"] + ) + } + + /** + * Holds if the range [start:end) consists of only 'normal' characters. + */ + predicate normalCharacterSequence(int start, int end) { + // a normal character inside a character set is interpreted on its own + this.normalCharacter(start, end) and + this.inCharSet(start) + or + // a maximal run of normal characters is considered as one constant + exists(int s, int e | + e = max(int i | this.normalCharacterRun(s, i)) and + not this.inCharSet(s) + | + // 'abc' can be considered one constant, but + // 'abc+' has to be broken up into 'ab' and 'c+', + // as the qualifier only applies to 'c'. + if this.qualifier(e, _, _, _) + then + end = e and start = e - 1 + or + end = e - 1 and start = s and start < end + else ( + end = e and + start = s + ) + ) + } + + private predicate normalCharacterRun(int start, int end) { + ( + this.normalCharacterRun(start, end - 1) + or + start = end - 1 and not this.normalCharacter(start - 1, start) + ) and + this.normalCharacter(end - 1, end) + } + + private predicate characterItem(int start, int end) { + this.normalCharacterSequence(start, end) or + this.escapedCharacter(start, end) or + this.specialCharacter(start, end, _) + } + + /** Whether the text in the range `start,end` is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + not this.nonCapturingGroupStart(start, _) and + result = + count(int i | this.group(i, _) and i < start and not this.nonCapturingGroupStart(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) and + exists(int nameEnd | + this.namedGroupStart(start, nameEnd) and + result = this.getText().substring(start + 3, nameEnd - 1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + /** Holds if an empty group is found between `start` and `end`. */ + predicate emptyGroup(int start, int end) { + exists(int endm1 | end = endm1 + 1 | + this.groupStart(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int inStart | + this.negativeLookaheadAssertionStart(start, inStart) + or + this.negativeLookbehindAssertionStart(start, inStart) + | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a negative lookahead is found between `start` and `end` */ + predicate negativeLookaheadAssertionGroup(int start, int end) { + exists(int inStart | this.negativeLookaheadAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a negative lookbehind is found between `start` and `end` */ + predicate negativeLookbehindAssertionGroup(int start, int end) { + exists(int inStart | this.negativeLookbehindAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a positive lookahead is found between `start` and `end` */ + predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int inStart | this.lookaheadAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a positive lookbehind is found between `start` and `end` */ + predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int inStart | this.lookbehindAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + private predicate groupStart(int start, int end) { + this.nonCapturingGroupStart(start, end) + or + this.namedGroupStart(start, end) + or + this.lookaheadAssertionStart(start, end) + or + this.negativeLookaheadAssertionStart(start, end) + or + this.lookbehindAssertionStart(start, end) + or + this.negativeLookbehindAssertionStart(start, end) + or + this.commentGroupStart(start, end) + or + this.simpleGroupStart(start, end) + } + + /** Matches the start of a non-capturing group, e.g. `(?:` */ + private predicate nonCapturingGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = [":", "=", "<", "!", "#"] and + end = start + 3 + } + + /** Matches the start of a simple group, e.g. `(a+)`. */ + private predicate simpleGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) != "?" and + end = start + 1 + } + + /** + * Matches the start of a named group, such as: + * - `(?\w+)` + * - `(?'name'\w+)` + */ + private predicate namedGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + ( + this.getChar(start + 2) = "<" and + not this.getChar(start + 3) = "=" and // (?<=foo) is a positive lookbehind assertion + not this.getChar(start + 3) = "!" and // (? start + 3 and this.getChar(i) = ">") and + end = nameEnd + 1 + ) + or + this.getChar(start + 2) = "'" and + exists(int nameEnd | + nameEnd = min(int i | i > start + 2 and this.getChar(i) = "'") and end = nameEnd + 1 + ) + ) + } + + /** Matches the start of a positive lookahead assertion, i.e. `(?=`. */ + private predicate lookaheadAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "=" and + end = start + 3 + } + + /** Matches the start of a negative lookahead assertion, i.e. `(?!`. */ + private predicate negativeLookaheadAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "!" and + end = start + 3 + } + + /** Matches the start of a positive lookbehind assertion, i.e. `(?<=`. */ + private predicate lookbehindAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "=" and + end = start + 4 + } + + /** Matches the start of a negative lookbehind assertion, i.e. `(?`. */ + predicate namedBackreference(int start, int end, string name) { + this.escapingChar(start) and + this.getChar(start + 1) = "k" and + this.getChar(start + 2) = "<" and + exists(int nameEnd | nameEnd = min(int i | i > start + 3 and this.getChar(i) = ">") | + end = nameEnd + 1 and + name = this.getText().substring(start + 3, nameEnd) + ) + } + + /** Matches a numbered backreference, e.g. `\1`. */ + predicate numberedBackreference(int start, int end, int value) { + this.escapingChar(start) and + not this.getChar(start + 1) = "0" and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and + len in [2 .. 3] + | + svalue = text.substring(start + 1, start + len) and + value = svalue.toInt() and + not exists(text.substring(start + 1, start + len + 1).toInt()) and + value > 0 + ) + } + + /** Whether the text in the range `start,end` is a back reference */ + predicate backreference(int start, int end) { + this.numberedBackreference(start, end, _) + or + this.namedBackreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackRefNumber(int start, int end) { this.numberedBackreference(start, end, result) } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackRefName(int start, int end) { this.namedBackreference(start, end, result) } + + private predicate baseItem(int start, int end) { + this.characterItem(start, end) and + not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + or + this.backreference(start, end) + or + this.pStyleNamedCharacterProperty(start, end, _) + } + + private predicate qualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + this.shortQualifier(start, end, maybeEmpty, mayRepeatForever) and + not this.getChar(end) = "?" + or + exists(int shortEnd | this.shortQualifier(start, shortEnd, maybeEmpty, mayRepeatForever) | + if this.getChar(shortEnd) = "?" then end = shortEnd + 1 else end = shortEnd + ) + } + + private predicate shortQualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + ( + this.getChar(start) = "+" and maybeEmpty = false and mayRepeatForever = true + or + this.getChar(start) = "*" and maybeEmpty = true and mayRepeatForever = true + or + this.getChar(start) = "?" and maybeEmpty = true and mayRepeatForever = false + ) and + end = start + 1 + or + exists(string lower, string upper | + this.multiples(start, end, lower, upper) and + (if lower = "" or lower.toInt() = 0 then maybeEmpty = true else maybeEmpty = false) and + if upper = "" then mayRepeatForever = true else mayRepeatForever = false + ) + } + + /** + * Holds if a repetition quantifier is found between `start` and `end`, + * with the given lower and upper bounds. If a bound is omitted, the corresponding + * string is empty. + */ + predicate multiples(int start, int end, string lower, string upper) { + exists(string text, string match, string inner | + text = this.getText() and + end = start + match.length() and + inner = match.substring(1, match.length() - 1) + | + match = text.regexpFind("\\{[0-9]+\\}", _, start) and + lower = inner and + upper = lower + or + match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and + exists(int commaIndex | + commaIndex = inner.indexOf(",") and + lower = inner.prefix(commaIndex) and + upper = inner.suffix(commaIndex + 1) + ) + ) + } + + /** + * Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + this.qualifiedPart(start, _, end, maybeEmpty, mayRepeatForever) + } + + /** + * Holds if a qualified part is found between `start` and `partEnd` and the qualifier is + * found between `partEnd` and `end`. + * + * `maybeEmpty` is true if the part is optional. + * `mayRepeatForever` is true if the part may be repeated unboundedly. + */ + predicate qualifiedPart( + int start, int partEnd, int end, boolean maybeEmpty, boolean mayRepeatForever + ) { + this.baseItem(start, partEnd) and + this.qualifier(partEnd, end, maybeEmpty, mayRepeatForever) + } + + /** Holds if the range `start`, `end` contains a character, a quantifier, a character set or a group. */ + predicate item(int start, int end) { + this.qualifiedItem(start, end, _, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _, _) + } + + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.groupStart(_, start) or + this.isOptionDivider(start - 1) + ) and + this.item(start, end) + or + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + } + + /** + * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _, _) + } + + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.itemStart(end) + } + + private predicate itemStart(int start) { + this.characterItem(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) or + this.backreference(start, _) or + this.namedCharacterProperty(start, _, _) + } + + private predicate itemEnd(int end) { + this.characterItem(_, end) + or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) + or + this.charSet(_, end) + or + this.qualifier(_, end, _, _) + } + + private predicate topLevel(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } + + private predicate subalternation(int start, int end, int itemStart) { + this.sequenceOrQualified(start, end) and + not this.isOptionDivider(start - 1) and + itemStart = start + or + start = end and + not this.itemEnd(start) and + this.isOptionDivider(end) and + itemStart = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + itemStart = mid + 1 + | + this.sequenceOrQualified(itemStart, end) + or + not this.itemStart(end) and end = itemStart + ) + } + + /** + * Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + not this.inCharSet(start) and + this.topLevel(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } + + /** + * Whether the text in the range start,end is an alternation and the text in partStart, partEnd is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int partStart, int partEnd) { + this.alternation(start, end) and + this.subalternation(start, partEnd, partStart) + } + + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) + or + this.qualifiedItem(x, start, true, _) + or + // ^ and \A match the start of the string + this.specialCharacter(x, start, ["^", "\\A"]) + ) + or + exists(int y | this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _, _) + ) + or + exists(int x, int y | this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true, _) + or + // $, \Z, and \z match the end of the string. + this.specialCharacter(end, y, ["$", "\\Z", "\\z"]) + ) + or + this.lastPart(_, end) and + this.item(start, end) + or + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _, _)) + or + exists(int x, int y | this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** + * Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.characterItem(start, end) + or + this.qualifiedItem(start, end, _, _) + or + this.charSet(start, end) + ) and + this.firstPart(start, end) + } + + /** + * Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.characterItem(start, end) + or + this.qualifiedItem(start, end, _, _) + or + this.charSet(start, end) + ) and + this.lastPart(start, end) + } +} diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected new file mode 100644 index 000000000000..3df78cdb4fbf --- /dev/null +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -0,0 +1,489 @@ +regexp.rb: +# 5| [RegExpConstant, RegExpNormalChar] abc + +# 8| [RegExpConstant, RegExpNormalChar] a + +# 8| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 8| [RegExpSequence] a*b+c?d +#-----| 0 -> [RegExpStar] a* +#-----| 1 -> [RegExpPlus] b+ +#-----| 2 -> [RegExpOpt] c? +#-----| 3 -> [RegExpConstant, RegExpNormalChar] d + +# 8| [RegExpConstant, RegExpNormalChar] b + +# 8| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 8| [RegExpConstant, RegExpNormalChar] c + +# 8| [RegExpOpt] c? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c + +# 8| [RegExpConstant, RegExpNormalChar] d + +# 9| [RegExpConstant, RegExpNormalChar] a + +# 9| [RegExpRange] a{4,8} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 10| [RegExpConstant, RegExpNormalChar] a + +# 10| [RegExpRange] a{,8} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 11| [RegExpConstant, RegExpNormalChar] a + +# 11| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 12| [RegExpConstant, RegExpNormalChar] a + +# 12| [RegExpRange] a{7} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 15| [RegExpConstant, RegExpNormalChar] foo + +# 15| [RegExpAlt] foo|bar +#-----| 0 -> [RegExpConstant, RegExpNormalChar] foo +#-----| 1 -> [RegExpConstant, RegExpNormalChar] bar + +# 15| [RegExpConstant, RegExpNormalChar] bar + +# 18| [RegExpCharacterClass] [abc] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 18| [RegExpConstant, RegExpNormalChar] a + +# 18| [RegExpConstant, RegExpNormalChar] b + +# 18| [RegExpConstant, RegExpNormalChar] c + +# 19| [RegExpCharacterClass] [a-fA-F0-9_] +#-----| 0 -> [RegExpCharacterRange] a-f +#-----| 1 -> [RegExpCharacterRange] A-F +#-----| 2 -> [RegExpCharacterRange] 0-9 +#-----| 3 -> [RegExpConstant, RegExpNormalChar] _ + +# 19| [RegExpConstant, RegExpNormalChar] a + +# 19| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 19| [RegExpConstant, RegExpNormalChar] f + +# 19| [RegExpConstant, RegExpNormalChar] A + +# 19| [RegExpCharacterRange] A-F +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] F + +# 19| [RegExpConstant, RegExpNormalChar] F + +# 19| [RegExpConstant, RegExpNormalChar] 0 + +# 19| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 19| [RegExpConstant, RegExpNormalChar] 9 + +# 19| [RegExpConstant, RegExpNormalChar] _ + +# 20| [RegExpCaret] \A + +# 20| [RegExpSequence] \A[+-]?\d+ +#-----| 0 -> [RegExpCaret] \A +#-----| 1 -> [RegExpOpt] [+-]? +#-----| 2 -> [RegExpPlus] \d+ + +# 20| [RegExpCharacterClass] [+-] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] + +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 20| [RegExpOpt] [+-]? +#-----| 0 -> [RegExpCharacterClass] [+-] + +# 20| [RegExpConstant, RegExpNormalChar] + + +# 20| [RegExpConstant, RegExpNormalChar] - + +# 20| [RegExpCharacterClassEscape] \d + +# 20| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 21| [RegExpCharacterClass] [\w] +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 21| [RegExpPlus] [\w]+ +#-----| 0 -> [RegExpCharacterClass] [\w] + +# 21| [RegExpCharacterClassEscape] \w + +# 22| [RegExpConstant, RegExpEscape] \[ + +# 22| [RegExpSequence] \[\][123] +#-----| 0 -> [RegExpConstant, RegExpEscape] \[ +#-----| 1 -> [RegExpConstant, RegExpEscape] \] +#-----| 2 -> [RegExpCharacterClass] [123] + +# 22| [RegExpConstant, RegExpEscape] \] + +# 22| [RegExpCharacterClass] [123] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 1 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 +#-----| 2 -> [RegExpConstant, RegExpNormalChar] 3 + +# 22| [RegExpConstant, RegExpNormalChar] 1 + +# 22| [RegExpConstant, RegExpNormalChar] 2 + +# 22| [RegExpConstant, RegExpNormalChar] 3 + +# 23| [RegExpCharacterClass] [^A-Z] +#-----| 0 -> [RegExpCharacterRange] A-Z + +# 23| [RegExpConstant, RegExpNormalChar] A + +# 23| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 23| [RegExpConstant, RegExpNormalChar] Z + +# 24| [RegExpCharacterClass] []] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ] + +# 24| [RegExpConstant, RegExpNormalChar] ] + +# 25| [RegExpCharacterClass] [^]] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ] + +# 25| [RegExpConstant, RegExpNormalChar] ] + +# 26| [RegExpCharacterClass] [^-] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] - + +# 26| [RegExpConstant, RegExpNormalChar] - + +# 27| [RegExpCharacterClass] [|] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] | + +# 27| [RegExpConstant, RegExpNormalChar] | + +# 30| [RegExpCharacterClass] [[a-f] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] [ +#-----| 1 -> [RegExpCharacterRange] a-f + +# 30| [RegExpSequence] [[a-f]A-F] +#-----| 0 -> [RegExpCharacterClass] [[a-f] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] A-F] + +# 30| [RegExpConstant, RegExpNormalChar] [ + +# 30| [RegExpConstant, RegExpNormalChar] a + +# 30| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 30| [RegExpConstant, RegExpNormalChar] f + +# 30| [RegExpConstant, RegExpNormalChar] A-F] + +# 33| [RegExpDot] . + +# 33| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 34| [RegExpDot] . + +# 34| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 35| [RegExpCharacterClassEscape] \w + +# 35| [RegExpPlus] \w+ +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 35| [RegExpSequence] \w+\W +#-----| 0 -> [RegExpPlus] \w+ +#-----| 1 -> [RegExpCharacterClassEscape] \W + +# 35| [RegExpCharacterClassEscape] \W + +# 36| [RegExpCharacterClassEscape] \s + +# 36| [RegExpSequence] \s\S +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 36| [RegExpCharacterClassEscape] \S + +# 37| [RegExpCharacterClassEscape] \d + +# 37| [RegExpSequence] \d\D +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpCharacterClassEscape] \D + +# 37| [RegExpCharacterClassEscape] \D + +# 38| [RegExpCharacterClassEscape] \h + +# 38| [RegExpSequence] \h\H +#-----| 0 -> [RegExpCharacterClassEscape] \h +#-----| 1 -> [RegExpCharacterClassEscape] \H + +# 38| [RegExpCharacterClassEscape] \H + +# 39| [RegExpConstant, RegExpEscape] \n + +# 39| [RegExpSequence] \n\r\t +#-----| 0 -> [RegExpConstant, RegExpEscape] \n +#-----| 1 -> [RegExpConstant, RegExpEscape] \r +#-----| 2 -> [RegExpConstant, RegExpEscape] \t + +# 39| [RegExpConstant, RegExpEscape] \r + +# 39| [RegExpConstant, RegExpEscape] \t + +# 42| [RegExpSpecialChar] \G + +# 42| [RegExpSequence] \Gabc +#-----| 0 -> [RegExpSpecialChar] \G +#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc + +# 42| [RegExpConstant, RegExpNormalChar] abc + +# 43| [RegExpSpecialChar] \b + +# 43| [RegExpSequence] \b!a\B +#-----| 0 -> [RegExpSpecialChar] \b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] !a +#-----| 2 -> [RegExpNonWordBoundary] \B + +# 43| [RegExpConstant, RegExpNormalChar] !a + +# 43| [RegExpNonWordBoundary] \B + +# 46| [RegExpGroup] (foo) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] foo + +# 46| [RegExpStar] (foo)* +#-----| 0 -> [RegExpGroup] (foo) + +# 46| [RegExpSequence] (foo)*bar +#-----| 0 -> [RegExpStar] (foo)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] bar + +# 46| [RegExpConstant, RegExpNormalChar] foo + +# 46| [RegExpConstant, RegExpNormalChar] bar + +# 47| [RegExpConstant, RegExpNormalChar] fo + +# 47| [RegExpSequence] fo(o|b)ar +#-----| 0 -> [RegExpConstant, RegExpNormalChar] fo +#-----| 1 -> [RegExpGroup] (o|b) +#-----| 2 -> [RegExpConstant, RegExpNormalChar] ar + +# 47| [RegExpGroup] (o|b) +#-----| 0 -> [RegExpAlt] o|b + +# 47| [RegExpConstant, RegExpNormalChar] o + +# 47| [RegExpAlt] o|b +#-----| 0 -> [RegExpConstant, RegExpNormalChar] o +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 47| [RegExpConstant, RegExpNormalChar] b + +# 47| [RegExpConstant, RegExpNormalChar] ar + +# 48| [RegExpGroup] (a|b|cd) +#-----| 0 -> [RegExpAlt] a|b|cd + +# 48| [RegExpSequence] (a|b|cd)e +#-----| 0 -> [RegExpGroup] (a|b|cd) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] e + +# 48| [RegExpConstant, RegExpNormalChar] a + +# 48| [RegExpAlt] a|b|cd +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] cd + +# 48| [RegExpConstant, RegExpNormalChar] b + +# 48| [RegExpConstant, RegExpNormalChar] cd + +# 48| [RegExpConstant, RegExpNormalChar] e + +# 49| [RegExpGroup] (?::+) +#-----| 0 -> [RegExpPlus] :+ + +# 49| [RegExpSequence] (?::+)\w +#-----| 0 -> [RegExpGroup] (?::+) +#-----| 1 -> [RegExpCharacterClassEscape] \w + +# 49| [RegExpConstant, RegExpNormalChar] : + +# 49| [RegExpPlus] :+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : + +# 49| [RegExpCharacterClassEscape] \w + +# 52| [RegExpGroup] (?\w+) +#-----| 0 -> [RegExpPlus] \w+ + +# 52| [RegExpCharacterClassEscape] \w + +# 52| [RegExpPlus] \w+ +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 53| [RegExpGroup] (?'foo'fo+) +#-----| 0 -> [RegExpSequence] fo+ + +# 53| [RegExpConstant, RegExpNormalChar] f + +# 53| [RegExpSequence] fo+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] f +#-----| 1 -> [RegExpPlus] o+ + +# 53| [RegExpConstant, RegExpNormalChar] o + +# 53| [RegExpPlus] o+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] o + +# 56| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 56| [RegExpSequence] (a+)b+\1 +#-----| 0 -> [RegExpGroup] (a+) +#-----| 1 -> [RegExpPlus] b+ +#-----| 2 -> [RegExpBackRef] \1 + +# 56| [RegExpConstant, RegExpNormalChar] a + +# 56| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 56| [RegExpConstant, RegExpNormalChar] b + +# 56| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 56| [RegExpBackRef] \1 + +# 57| [RegExpGroup] (?q+) +#-----| 0 -> [RegExpPlus] q+ + +# 57| [RegExpSequence] (?q+)\s+\k+ +#-----| 0 -> [RegExpGroup] (?q+) +#-----| 1 -> [RegExpPlus] \s+ +#-----| 2 -> [RegExpPlus] \k+ + +# 57| [RegExpConstant, RegExpNormalChar] q + +# 57| [RegExpPlus] q+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] q + +# 57| [RegExpCharacterClassEscape] \s + +# 57| [RegExpPlus] \s+ +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 57| [RegExpBackRef] \k + +# 57| [RegExpPlus] \k+ +#-----| 0 -> [RegExpBackRef] \k + +# 60| [RegExpNamedCharacterProperty] \p{Word} + +# 60| [RegExpStar] \p{Word}* +#-----| 0 -> [RegExpNamedCharacterProperty] \p{Word} + +# 61| [RegExpNamedCharacterProperty] \P{Digit} + +# 61| [RegExpPlus] \P{Digit}+ +#-----| 0 -> [RegExpNamedCharacterProperty] \P{Digit} + +# 62| [RegExpNamedCharacterProperty] \p{^Alnum} + +# 62| [RegExpRange] \p{^Alnum}{2,3} +#-----| 0 -> [RegExpNamedCharacterProperty] \p{^Alnum} + +# 63| [RegExpCharacterClass] [a-f\p{Digit}] +#-----| 0 -> [RegExpCharacterRange] a-f +#-----| 1 -> [RegExpNamedCharacterProperty] \p{Digit} + +# 63| [RegExpPlus] [a-f\p{Digit}]+ +#-----| 0 -> [RegExpCharacterClass] [a-f\p{Digit}] + +# 63| [RegExpConstant, RegExpNormalChar] a + +# 63| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 63| [RegExpConstant, RegExpNormalChar] f + +# 63| [RegExpNamedCharacterProperty] \p{Digit} + +# 66| [RegExpCharacterClass] [[:alpha:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] + +# 66| [RegExpSequence] [[:alpha:]][[:digit:]] +#-----| 0 -> [RegExpCharacterClass] [[:alpha:]] +#-----| 1 -> [RegExpCharacterClass] [[:digit:]] + +# 66| [RegExpNamedCharacterProperty] [:alpha:] + +# 66| [RegExpCharacterClass] [[:digit:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] + +# 66| [RegExpNamedCharacterProperty] [:digit:] + +# 69| [RegExpCharacterClass] [[:alpha:][:digit:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] +#-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] + +# 69| [RegExpNamedCharacterProperty] [:alpha:] + +# 69| [RegExpNamedCharacterProperty] [:digit:] + +# 72| [RegExpCharacterClass] [A-F[:digit:]a-f] +#-----| 0 -> [RegExpCharacterRange] A-F +#-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] +#-----| 2 -> [RegExpCharacterRange] a-f + +# 72| [RegExpConstant, RegExpNormalChar] A + +# 72| [RegExpCharacterRange] A-F +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] F + +# 72| [RegExpConstant, RegExpNormalChar] F + +# 72| [RegExpNamedCharacterProperty] [:digit:] + +# 72| [RegExpConstant, RegExpNormalChar] a + +# 72| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 72| [RegExpConstant, RegExpNormalChar] f + +# 75| [RegExpNamedCharacterProperty] [:digit:] + +# 79| [RegExpConstant, RegExpNormalChar] abc + +# 82| [RegExpConstant, RegExpEscape] \u{987 diff --git a/cpp/ql/test/library-tests/regex/parse.ql b/cpp/ql/test/library-tests/regex/parse.ql new file mode 100644 index 000000000000..9bc804ada4de --- /dev/null +++ b/cpp/ql/test/library-tests/regex/parse.ql @@ -0,0 +1,27 @@ +/** + * @kind graph + */ + +import codeql.Locations +import codeql.ruby.Regexp as RE + +query predicate nodes(RE::RegExpTerm n, string attr, string val) { + attr = "semmle.label" and + val = "[" + concat(n.getAPrimaryQlClass(), ", ") + "] " + n.toString() + or + attr = "semmle.order" and + val = + any(int i | + n = + rank[i](RE::RegExpTerm t, string fp, int sl, int sc, int el, int ec | + t.hasLocationInfo(fp, sl, sc, el, ec) + | + t order by fp, sl, sc, el, ec, t.toString() + ) + ).toString() +} + +query predicate edges(RE::RegExpTerm pred, RE::RegExpTerm succ, string attr, string val) { + attr in ["semmle.label", "semmle.order"] and + val = any(int i | succ = pred.getChild(i)).toString() +} diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected new file mode 100644 index 000000000000..e464b5ce5ea5 --- /dev/null +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -0,0 +1,270 @@ +groupName +| regexp.rb:52:2:52:11 | (?\\w+) | id | +| regexp.rb:53:2:53:12 | (?'foo'fo+) | foo | +| regexp.rb:57:2:57:11 | (?q+) | qux | +groupNumber +| regexp.rb:46:2:46:6 | (foo) | 1 | +| regexp.rb:47:4:47:8 | (o\|b) | 1 | +| regexp.rb:48:2:48:9 | (a\|b\|cd) | 1 | +| regexp.rb:53:2:53:12 | (?'foo'fo+) | 1 | +| regexp.rb:56:2:56:5 | (a+) | 1 | +term +| regexp.rb:5:2:5:4 | abc | RegExpConstant,RegExpNormalChar | +| regexp.rb:8:2:8:2 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:8:2:8:3 | a* | RegExpStar | +| regexp.rb:8:2:8:8 | a*b+c?d | RegExpSequence | +| regexp.rb:8:4:8:4 | b | RegExpConstant,RegExpNormalChar | +| regexp.rb:8:4:8:5 | b+ | RegExpPlus | +| regexp.rb:8:6:8:6 | c | RegExpConstant,RegExpNormalChar | +| regexp.rb:8:6:8:7 | c? | RegExpOpt | +| regexp.rb:8:8:8:8 | d | RegExpConstant,RegExpNormalChar | +| regexp.rb:9:2:9:2 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:9:2:9:7 | a{4,8} | RegExpRange | +| regexp.rb:10:2:10:2 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:10:2:10:6 | a{,8} | RegExpRange | +| regexp.rb:11:2:11:2 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:11:2:11:6 | a{3,} | InfiniteRepetitionQuantifier,RegExpRange | +| regexp.rb:12:2:12:2 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:12:2:12:5 | a{7} | RegExpRange | +| regexp.rb:15:2:15:4 | foo | RegExpConstant,RegExpNormalChar | +| regexp.rb:15:2:15:8 | foo\|bar | RegExpAlt | +| regexp.rb:15:6:15:8 | bar | RegExpConstant,RegExpNormalChar | +| regexp.rb:18:2:18:6 | [abc] | RegExpCharacterClass | +| regexp.rb:18:3:18:3 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:18:4:18:4 | b | RegExpConstant,RegExpNormalChar | +| regexp.rb:18:5:18:5 | c | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:2:19:13 | [a-fA-F0-9_] | RegExpCharacterClass | +| regexp.rb:19:3:19:3 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:3:19:5 | a-f | RegExpCharacterRange | +| regexp.rb:19:5:19:5 | f | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:6:19:6 | A | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:6:19:8 | A-F | RegExpCharacterRange | +| regexp.rb:19:8:19:8 | F | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:9:19:9 | 0 | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:9:19:11 | 0-9 | RegExpCharacterRange | +| regexp.rb:19:11:19:11 | 9 | RegExpConstant,RegExpNormalChar | +| regexp.rb:19:12:19:12 | _ | RegExpConstant,RegExpNormalChar | +| regexp.rb:20:2:20:3 | \\A | RegExpCaret | +| regexp.rb:20:2:20:11 | \\A[+-]?\\d+ | RegExpSequence | +| regexp.rb:20:4:20:7 | [+-] | RegExpCharacterClass | +| regexp.rb:20:4:20:8 | [+-]? | RegExpOpt | +| regexp.rb:20:5:20:5 | + | RegExpConstant,RegExpNormalChar | +| regexp.rb:20:6:20:6 | - | RegExpConstant,RegExpNormalChar | +| regexp.rb:20:9:20:10 | \\d | RegExpCharacterClassEscape | +| regexp.rb:20:9:20:11 | \\d+ | RegExpPlus | +| regexp.rb:21:2:21:5 | [\\w] | RegExpCharacterClass | +| regexp.rb:21:2:21:6 | [\\w]+ | RegExpPlus | +| regexp.rb:21:3:21:4 | \\w | RegExpCharacterClassEscape | +| regexp.rb:22:2:22:3 | \\[ | RegExpConstant,RegExpEscape | +| regexp.rb:22:2:22:10 | \\[\\][123] | RegExpSequence | +| regexp.rb:22:4:22:5 | \\] | RegExpConstant,RegExpEscape | +| regexp.rb:22:6:22:10 | [123] | RegExpCharacterClass | +| regexp.rb:22:7:22:7 | 1 | RegExpConstant,RegExpNormalChar | +| regexp.rb:22:8:22:8 | 2 | RegExpConstant,RegExpNormalChar | +| regexp.rb:22:9:22:9 | 3 | RegExpConstant,RegExpNormalChar | +| regexp.rb:23:2:23:7 | [^A-Z] | RegExpCharacterClass | +| regexp.rb:23:4:23:4 | A | RegExpConstant,RegExpNormalChar | +| regexp.rb:23:4:23:6 | A-Z | RegExpCharacterRange | +| regexp.rb:23:6:23:6 | Z | RegExpConstant,RegExpNormalChar | +| regexp.rb:24:2:24:4 | []] | RegExpCharacterClass | +| regexp.rb:24:3:24:3 | ] | RegExpConstant,RegExpNormalChar | +| regexp.rb:25:2:25:5 | [^]] | RegExpCharacterClass | +| regexp.rb:25:4:25:4 | ] | RegExpConstant,RegExpNormalChar | +| regexp.rb:26:2:26:5 | [^-] | RegExpCharacterClass | +| regexp.rb:26:4:26:4 | - | RegExpConstant,RegExpNormalChar | +| regexp.rb:27:2:27:4 | [\|] | RegExpCharacterClass | +| regexp.rb:27:3:27:3 | \| | RegExpConstant,RegExpNormalChar | +| regexp.rb:30:2:30:7 | [[a-f] | RegExpCharacterClass | +| regexp.rb:30:2:30:11 | [[a-f]A-F] | RegExpSequence | +| regexp.rb:30:3:30:3 | [ | RegExpConstant,RegExpNormalChar | +| regexp.rb:30:4:30:4 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:30:4:30:6 | a-f | RegExpCharacterRange | +| regexp.rb:30:6:30:6 | f | RegExpConstant,RegExpNormalChar | +| regexp.rb:30:8:30:11 | A-F] | RegExpConstant,RegExpNormalChar | +| regexp.rb:33:2:33:2 | . | RegExpDot | +| regexp.rb:33:2:33:3 | .* | RegExpStar | +| regexp.rb:34:2:34:2 | . | RegExpDot | +| regexp.rb:34:2:34:3 | .* | RegExpStar | +| regexp.rb:35:2:35:3 | \\w | RegExpCharacterClassEscape | +| regexp.rb:35:2:35:4 | \\w+ | RegExpPlus | +| regexp.rb:35:2:35:6 | \\w+\\W | RegExpSequence | +| regexp.rb:35:5:35:6 | \\W | RegExpCharacterClassEscape | +| regexp.rb:36:2:36:3 | \\s | RegExpCharacterClassEscape | +| regexp.rb:36:2:36:5 | \\s\\S | RegExpSequence | +| regexp.rb:36:4:36:5 | \\S | RegExpCharacterClassEscape | +| regexp.rb:37:2:37:3 | \\d | RegExpCharacterClassEscape | +| regexp.rb:37:2:37:5 | \\d\\D | RegExpSequence | +| regexp.rb:37:4:37:5 | \\D | RegExpCharacterClassEscape | +| regexp.rb:38:2:38:3 | \\h | RegExpCharacterClassEscape | +| regexp.rb:38:2:38:5 | \\h\\H | RegExpSequence | +| regexp.rb:38:4:38:5 | \\H | RegExpCharacterClassEscape | +| regexp.rb:39:2:39:3 | \\n | RegExpConstant,RegExpEscape | +| regexp.rb:39:2:39:7 | \\n\\r\\t | RegExpSequence | +| regexp.rb:39:4:39:5 | \\r | RegExpConstant,RegExpEscape | +| regexp.rb:39:6:39:7 | \\t | RegExpConstant,RegExpEscape | +| regexp.rb:42:2:42:3 | \\G | RegExpSpecialChar | +| regexp.rb:42:2:42:6 | \\Gabc | RegExpSequence | +| regexp.rb:42:4:42:6 | abc | RegExpConstant,RegExpNormalChar | +| regexp.rb:43:2:43:3 | \\b | RegExpSpecialChar | +| regexp.rb:43:2:43:7 | \\b!a\\B | RegExpSequence | +| regexp.rb:43:4:43:5 | !a | RegExpConstant,RegExpNormalChar | +| regexp.rb:43:6:43:7 | \\B | RegExpNonWordBoundary | +| regexp.rb:46:2:46:6 | (foo) | RegExpGroup | +| regexp.rb:46:2:46:7 | (foo)* | RegExpStar | +| regexp.rb:46:2:46:10 | (foo)*bar | RegExpSequence | +| regexp.rb:46:3:46:5 | foo | RegExpConstant,RegExpNormalChar | +| regexp.rb:46:8:46:10 | bar | RegExpConstant,RegExpNormalChar | +| regexp.rb:47:2:47:3 | fo | RegExpConstant,RegExpNormalChar | +| regexp.rb:47:2:47:10 | fo(o\|b)ar | RegExpSequence | +| regexp.rb:47:4:47:8 | (o\|b) | RegExpGroup | +| regexp.rb:47:5:47:5 | o | RegExpConstant,RegExpNormalChar | +| regexp.rb:47:5:47:7 | o\|b | RegExpAlt | +| regexp.rb:47:7:47:7 | b | RegExpConstant,RegExpNormalChar | +| regexp.rb:47:9:47:10 | ar | RegExpConstant,RegExpNormalChar | +| regexp.rb:48:2:48:9 | (a\|b\|cd) | RegExpGroup | +| regexp.rb:48:2:48:10 | (a\|b\|cd)e | RegExpSequence | +| regexp.rb:48:3:48:3 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:48:3:48:8 | a\|b\|cd | RegExpAlt | +| regexp.rb:48:5:48:5 | b | RegExpConstant,RegExpNormalChar | +| regexp.rb:48:7:48:8 | cd | RegExpConstant,RegExpNormalChar | +| regexp.rb:48:10:48:10 | e | RegExpConstant,RegExpNormalChar | +| regexp.rb:49:2:49:7 | (?::+) | RegExpGroup | +| regexp.rb:49:2:49:9 | (?::+)\\w | RegExpSequence | +| regexp.rb:49:5:49:5 | : | RegExpConstant,RegExpNormalChar | +| regexp.rb:49:5:49:6 | :+ | RegExpPlus | +| regexp.rb:49:8:49:9 | \\w | RegExpCharacterClassEscape | +| regexp.rb:52:2:52:11 | (?\\w+) | RegExpGroup | +| regexp.rb:52:8:52:9 | \\w | RegExpCharacterClassEscape | +| regexp.rb:52:8:52:10 | \\w+ | RegExpPlus | +| regexp.rb:53:2:53:12 | (?'foo'fo+) | RegExpGroup | +| regexp.rb:53:9:53:9 | f | RegExpConstant,RegExpNormalChar | +| regexp.rb:53:9:53:11 | fo+ | RegExpSequence | +| regexp.rb:53:10:53:10 | o | RegExpConstant,RegExpNormalChar | +| regexp.rb:53:10:53:11 | o+ | RegExpPlus | +| regexp.rb:56:2:56:5 | (a+) | RegExpGroup | +| regexp.rb:56:2:56:9 | (a+)b+\\1 | RegExpSequence | +| regexp.rb:56:3:56:3 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:56:3:56:4 | a+ | RegExpPlus | +| regexp.rb:56:6:56:6 | b | RegExpConstant,RegExpNormalChar | +| regexp.rb:56:6:56:7 | b+ | RegExpPlus | +| regexp.rb:56:8:56:9 | \\1 | RegExpBackRef | +| regexp.rb:57:2:57:11 | (?q+) | RegExpGroup | +| regexp.rb:57:2:57:22 | (?q+)\\s+\\k+ | RegExpSequence | +| regexp.rb:57:9:57:9 | q | RegExpConstant,RegExpNormalChar | +| regexp.rb:57:9:57:10 | q+ | RegExpPlus | +| regexp.rb:57:12:57:13 | \\s | RegExpCharacterClassEscape | +| regexp.rb:57:12:57:14 | \\s+ | RegExpPlus | +| regexp.rb:57:15:57:21 | \\k | RegExpBackRef | +| regexp.rb:57:15:57:22 | \\k+ | RegExpPlus | +| regexp.rb:60:2:60:9 | \\p{Word} | RegExpNamedCharacterProperty | +| regexp.rb:60:2:60:10 | \\p{Word}* | RegExpStar | +| regexp.rb:61:2:61:10 | \\P{Digit} | RegExpNamedCharacterProperty | +| regexp.rb:61:2:61:11 | \\P{Digit}+ | RegExpPlus | +| regexp.rb:62:2:62:11 | \\p{^Alnum} | RegExpNamedCharacterProperty | +| regexp.rb:62:2:62:16 | \\p{^Alnum}{2,3} | RegExpRange | +| regexp.rb:63:2:63:15 | [a-f\\p{Digit}] | RegExpCharacterClass | +| regexp.rb:63:2:63:16 | [a-f\\p{Digit}]+ | RegExpPlus | +| regexp.rb:63:3:63:3 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:63:3:63:5 | a-f | RegExpCharacterRange | +| regexp.rb:63:5:63:5 | f | RegExpConstant,RegExpNormalChar | +| regexp.rb:63:6:63:14 | \\p{Digit} | RegExpNamedCharacterProperty | +| regexp.rb:66:2:66:12 | [[:alpha:]] | RegExpCharacterClass | +| regexp.rb:66:2:66:23 | [[:alpha:]][[:digit:]] | RegExpSequence | +| regexp.rb:66:3:66:11 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.rb:66:13:66:23 | [[:digit:]] | RegExpCharacterClass | +| regexp.rb:66:14:66:22 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.rb:69:2:69:21 | [[:alpha:][:digit:]] | RegExpCharacterClass | +| regexp.rb:69:3:69:11 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.rb:69:12:69:20 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.rb:72:2:72:18 | [A-F[:digit:]a-f] | RegExpCharacterClass | +| regexp.rb:72:3:72:3 | A | RegExpConstant,RegExpNormalChar | +| regexp.rb:72:3:72:5 | A-F | RegExpCharacterRange | +| regexp.rb:72:5:72:5 | F | RegExpConstant,RegExpNormalChar | +| regexp.rb:72:6:72:14 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.rb:72:15:72:15 | a | RegExpConstant,RegExpNormalChar | +| regexp.rb:72:15:72:17 | a-f | RegExpCharacterRange | +| regexp.rb:72:17:72:17 | f | RegExpConstant,RegExpNormalChar | +| regexp.rb:75:2:75:10 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.rb:79:2:79:4 | abc | RegExpConstant,RegExpNormalChar | +| regexp.rb:82:2:82:7 | \\u{987 | RegExpConstant,RegExpEscape | +regExpNormalCharValue +| regexp.rb:5:2:5:4 | abc | abc | +| regexp.rb:8:2:8:2 | a | a | +| regexp.rb:8:4:8:4 | b | b | +| regexp.rb:8:6:8:6 | c | c | +| regexp.rb:8:8:8:8 | d | d | +| regexp.rb:9:2:9:2 | a | a | +| regexp.rb:10:2:10:2 | a | a | +| regexp.rb:11:2:11:2 | a | a | +| regexp.rb:12:2:12:2 | a | a | +| regexp.rb:15:2:15:4 | foo | foo | +| regexp.rb:15:6:15:8 | bar | bar | +| regexp.rb:18:3:18:3 | a | a | +| regexp.rb:18:4:18:4 | b | b | +| regexp.rb:18:5:18:5 | c | c | +| regexp.rb:19:3:19:3 | a | a | +| regexp.rb:19:5:19:5 | f | f | +| regexp.rb:19:6:19:6 | A | A | +| regexp.rb:19:8:19:8 | F | F | +| regexp.rb:19:9:19:9 | 0 | 0 | +| regexp.rb:19:11:19:11 | 9 | 9 | +| regexp.rb:19:12:19:12 | _ | _ | +| regexp.rb:20:5:20:5 | + | + | +| regexp.rb:20:6:20:6 | - | - | +| regexp.rb:20:9:20:10 | \\d | d | +| regexp.rb:21:3:21:4 | \\w | w | +| regexp.rb:22:2:22:3 | \\[ | [ | +| regexp.rb:22:4:22:5 | \\] | ] | +| regexp.rb:22:7:22:7 | 1 | 1 | +| regexp.rb:22:8:22:8 | 2 | 2 | +| regexp.rb:22:9:22:9 | 3 | 3 | +| regexp.rb:23:4:23:4 | A | A | +| regexp.rb:23:6:23:6 | Z | Z | +| regexp.rb:24:3:24:3 | ] | ] | +| regexp.rb:25:4:25:4 | ] | ] | +| regexp.rb:26:4:26:4 | - | - | +| regexp.rb:27:3:27:3 | \| | \| | +| regexp.rb:30:3:30:3 | [ | [ | +| regexp.rb:30:4:30:4 | a | a | +| regexp.rb:30:6:30:6 | f | f | +| regexp.rb:30:8:30:11 | A-F] | A-F] | +| regexp.rb:35:2:35:3 | \\w | w | +| regexp.rb:35:5:35:6 | \\W | W | +| regexp.rb:36:2:36:3 | \\s | s | +| regexp.rb:36:4:36:5 | \\S | S | +| regexp.rb:37:2:37:3 | \\d | d | +| regexp.rb:37:4:37:5 | \\D | D | +| regexp.rb:38:2:38:3 | \\h | h | +| regexp.rb:38:4:38:5 | \\H | H | +| regexp.rb:39:2:39:3 | \\n | \n | +| regexp.rb:39:4:39:5 | \\r | \r | +| regexp.rb:39:6:39:7 | \\t | \t | +| regexp.rb:42:4:42:6 | abc | abc | +| regexp.rb:43:4:43:5 | !a | !a | +| regexp.rb:46:3:46:5 | foo | foo | +| regexp.rb:46:8:46:10 | bar | bar | +| regexp.rb:47:2:47:3 | fo | fo | +| regexp.rb:47:5:47:5 | o | o | +| regexp.rb:47:7:47:7 | b | b | +| regexp.rb:47:9:47:10 | ar | ar | +| regexp.rb:48:3:48:3 | a | a | +| regexp.rb:48:5:48:5 | b | b | +| regexp.rb:48:7:48:8 | cd | cd | +| regexp.rb:48:10:48:10 | e | e | +| regexp.rb:49:5:49:5 | : | : | +| regexp.rb:49:8:49:9 | \\w | w | +| regexp.rb:52:8:52:9 | \\w | w | +| regexp.rb:53:9:53:9 | f | f | +| regexp.rb:53:10:53:10 | o | o | +| regexp.rb:56:3:56:3 | a | a | +| regexp.rb:56:6:56:6 | b | b | +| regexp.rb:57:9:57:9 | q | q | +| regexp.rb:57:12:57:13 | \\s | s | +| regexp.rb:63:3:63:3 | a | a | +| regexp.rb:63:5:63:5 | f | f | +| regexp.rb:72:3:72:3 | A | A | +| regexp.rb:72:5:72:5 | F | F | +| regexp.rb:72:15:72:15 | a | a | +| regexp.rb:72:17:72:17 | f | f | +| regexp.rb:79:2:79:4 | abc | abc | +| regexp.rb:82:2:82:7 | \\u{987 | \u0987 | diff --git a/cpp/ql/test/library-tests/regex/regexp.ql b/cpp/ql/test/library-tests/regex/regexp.ql new file mode 100644 index 000000000000..90fd09ab1be8 --- /dev/null +++ b/cpp/ql/test/library-tests/regex/regexp.ql @@ -0,0 +1,11 @@ +import codeql.ruby.Regexp + +query predicate groupName(RegExpGroup g, string name) { name = g.getName() } + +query predicate groupNumber(RegExpGroup g, int number) { number = g.getNumber() } + +query predicate term(RegExpTerm term, string c) { c = term.getPrimaryQlClasses() } + +query predicate regExpNormalCharValue(RegExpNormalChar term, string value) { + value = term.getValue() +} diff --git a/cpp/ql/test/library-tests/regex/regexp.rb b/cpp/ql/test/library-tests/regex/regexp.rb new file mode 100644 index 000000000000..a68c4878094f --- /dev/null +++ b/cpp/ql/test/library-tests/regex/regexp.rb @@ -0,0 +1,82 @@ +# Empty +// + +# Basic sequence +/abc/ + +# Repetition +/a*b+c?d/ +/a{4,8}/ +/a{,8}/ +/a{3,}/ +/a{7}/ + +# Alternation +/foo|bar/ + +# Character classes +/[abc]/ +/[a-fA-F0-9_]/ +/\A[+-]?\d+/ +/[\w]+/ +/\[\][123]/ +/[^A-Z]/ +/[]]/ # MRI gives a warning, but accepts this as matching ']' +/[^]]/ # MRI gives a warning, but accepts this as matching anything except ']' +/[^-]/ +/[|]/ + +# Nested character classes +/[[a-f]A-F]/ # BAD - not parsed correctly + +# Meta-character classes +/.*/ +/.*/m +/\w+\W/ +/\s\S/ +/\d\D/ +/\h\H/ +/\n\r\t/ + +# Anchors +/\Gabc/ +/\b!a\B/ + +# Groups +/(foo)*bar/ +/fo(o|b)ar/ +/(a|b|cd)e/ +/(?::+)\w/ # Non-capturing group matching colons + +# Named groups +/(?\w+)/ +/(?'foo'fo+)/ + +# Backreferences +/(a+)b+\1/ +/(?q+)\s+\k+/ + +# Named character properties using the p-style syntax +/\p{Word}*/ +/\P{Digit}+/ +/\p{^Alnum}{2,3}/ +/[a-f\p{Digit}]+/ # Also valid inside character classes + +# Two separate character classes, each containing a single POSIX bracket expression +/[[:alpha:]][[:digit:]]/ + +# A single character class containing two POSIX bracket expressions +/[[:alpha:][:digit:]]/ + +# A single character class containing two ranges and one POSIX bracket expression +/[A-F[:digit:]a-f]/ + +# *Not* a POSIX bracket expression; just a regular character class. +/[:digit:]/ + +# Simple constant interpolation +A = "a" +/#{A}bc/ + +# unicode +/\u{9879}/ \ No newline at end of file From 40967258559bb0ff34ef462687e5b1f16bf137bc Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 21 Jul 2026 16:10:30 +0200 Subject: [PATCH 02/37] C++: Make regex library compile --- cpp/ql/lib/qlpack.yml | 1 + .../semmle/code/cpp/regex/RegexTreeView.qll | 44 +++++++------------ .../code/cpp/regex/internal/ParseRegExp.qll | 17 +++---- cpp/ql/test/library-tests/regex/parse.ql | 9 ++-- .../regex/{regexp.rb => regexp.cpp} | 0 cpp/ql/test/library-tests/regex/regexp.ql | 2 +- 6 files changed, 28 insertions(+), 45 deletions(-) rename cpp/ql/test/library-tests/regex/{regexp.rb => regexp.cpp} (100%) diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 0d952a0c1bf9..04f48ffa72c5 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -10,6 +10,7 @@ dependencies: codeql/mad: ${workspace} codeql/quantum: ${workspace} codeql/rangeanalysis: ${workspace} + codeql/regex: ${workspace} codeql/ssa: ${workspace} codeql/typeflow: ${workspace} codeql/tutorial: ${workspace} diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 51df17008817..a0a116f2fe01 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -1,17 +1,17 @@ -/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */ +/** + * Provides a class hierarchy corresponding to a parse tree of C++ regular expressions. + */ private import internal.ParseRegExp private import codeql.util.Numbers -private import codeql.ruby.ast.Literal as Ast -private import codeql.Locations -private import codeql.regex.nfa.NfaUtils as NfaUtils +private import semmle.code.cpp.exprs.Literal private import codeql.regex.RegexTreeView // exporting as RegexTreeView, and in the top-level scope. import Impl as RegexTreeView import Impl /** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ -RegExpTerm getParsedRegExp(Ast::RegExpLiteral re) { +RegExpTerm getParsedRegExp(StringLiteral re) { result.getRegExp() = re and result.isRootTerm() } @@ -55,7 +55,7 @@ private newtype TRegExpParent = re.namedCharacterProperty(start, end, _) } -/** An implementation that statisfies the RegexTreeView signature. */ +/** An implementation that satisfies the RegexTreeView signature. */ private module Impl implements RegexTreeViewSig { /** * An element containing a regular expression term, that is, either @@ -211,24 +211,14 @@ private module Impl implements RegexTreeViewSig { */ Location getLocation() { result = re.getLocation() } - pragma[noinline] - private predicate componentHasLocationInfo( - int i, string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - re.getComponent(i) - .getLocation() - .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - } - /** Holds if this term is found at the specified location offsets. */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { exists(int re_start | - this.componentHasLocationInfo(0, filepath, startline, re_start, _, _) and - this.componentHasLocationInfo(re.getNumberOfComponents() - 1, filepath, _, _, endline, _) and - startcolumn = re_start + start and - endcolumn = re_start + end - 1 + re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, _) and + startcolumn = re_start + 1 + start and + endcolumn = re_start + 1 + end - 1 ) } @@ -312,7 +302,7 @@ private module Impl implements RegexTreeViewSig { result.getEnd() = part_end } - /** Hols if this term may match an unlimited number of times. */ + /** Hodls if this term may match an unlimited number of times. */ predicate mayRepeatForever() { may_repeat_forever = true } /** Gets the qualifier for this term. That is e.g "?" for "a?". */ @@ -660,9 +650,9 @@ private module Impl implements RegexTreeViewSig { * * Examples: * - * ```rb - * /[a-fA-F0-9]/ - * /[^abc]/ + * ```cpp + * "[a-fA-F0-9]" + * "[^abc]" * ``` */ class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { @@ -1195,9 +1185,7 @@ private module Impl implements RegexTreeViewSig { /** * Holds if the regular expression should not be considered. */ - predicate isExcluded(RegExpParent parent) { - parent.(RegExpTerm).getRegExp().(Ast::RegExpLiteral).hasFreeSpacingFlag() // exclude free-spacing mode regexes - } + predicate isExcluded(RegExpParent parent) { none() } /** * Holds if `term` is a possessive quantifier. @@ -1207,13 +1195,13 @@ private module Impl implements RegexTreeViewSig { /** * 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. - * Not yet implemented for Ruby. + * Not yet implemented for C++. */ predicate matchesAnyPrefix(RegExpTerm term) { any() } /** * 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. - * Not yet implemented for Ruby. + * Not yet implemented for C++. */ predicate matchesAnySuffix(RegExpTerm term) { any() } diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index d35d9353bf13..d9163bb3af05 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -1,19 +1,18 @@ /** - * Library for parsing for Ruby regular expressions. + * Library for parsing for C++ regular expressions. * * N.B. does not yet handle stripping whitespace and comments in regexes with * the `x` (free-spacing) flag. */ -private import codeql.ruby.AST as Ast -private import codeql.Locations +private import semmle.code.cpp.exprs.Literal /** - * A `StringlikeLiteral` containing a regular expression term, that is, either + * A C++ string literal containing a regular expression term, that is, either * a regular expression literal, or a string literal used in a context where - * it is parsed as regular expression. + * it is parsed as a regular expression. */ -abstract class RegExp extends Ast::StringlikeLiteral { +abstract class RegExp extends StringLiteral { /** * Holds if this `RegExp` has the `s` flag for multi-line matching. */ @@ -254,11 +253,7 @@ abstract class RegExp extends Ast::StringlikeLiteral { } /** Gets the text of this regex */ - string getText() { - exists(Ast::ConstantValue c | c = this.getConstantValue() | - result = [this.getConstantValue().getString(), this.getConstantValue().getRegExp()] - ) - } + string getText() { result = this.getValue() } /** Gets the `i`th character of this regex */ string getChar(int i) { result = this.getText().charAt(i) } diff --git a/cpp/ql/test/library-tests/regex/parse.ql b/cpp/ql/test/library-tests/regex/parse.ql index 9bc804ada4de..ba9a04ab9bd7 100644 --- a/cpp/ql/test/library-tests/regex/parse.ql +++ b/cpp/ql/test/library-tests/regex/parse.ql @@ -2,10 +2,9 @@ * @kind graph */ -import codeql.Locations -import codeql.ruby.Regexp as RE +import semmle.code.cpp.regex.RegexTreeView -query predicate nodes(RE::RegExpTerm n, string attr, string val) { +query predicate nodes(RegExpTerm n, string attr, string val) { attr = "semmle.label" and val = "[" + concat(n.getAPrimaryQlClass(), ", ") + "] " + n.toString() or @@ -13,7 +12,7 @@ query predicate nodes(RE::RegExpTerm n, string attr, string val) { val = any(int i | n = - rank[i](RE::RegExpTerm t, string fp, int sl, int sc, int el, int ec | + rank[i](RegExpTerm t, string fp, int sl, int sc, int el, int ec | t.hasLocationInfo(fp, sl, sc, el, ec) | t order by fp, sl, sc, el, ec, t.toString() @@ -21,7 +20,7 @@ query predicate nodes(RE::RegExpTerm n, string attr, string val) { ).toString() } -query predicate edges(RE::RegExpTerm pred, RE::RegExpTerm succ, string attr, string val) { +query predicate edges(RegExpTerm pred, RegExpTerm succ, string attr, string val) { attr in ["semmle.label", "semmle.order"] and val = any(int i | succ = pred.getChild(i)).toString() } diff --git a/cpp/ql/test/library-tests/regex/regexp.rb b/cpp/ql/test/library-tests/regex/regexp.cpp similarity index 100% rename from cpp/ql/test/library-tests/regex/regexp.rb rename to cpp/ql/test/library-tests/regex/regexp.cpp diff --git a/cpp/ql/test/library-tests/regex/regexp.ql b/cpp/ql/test/library-tests/regex/regexp.ql index 90fd09ab1be8..e167cb1a7bb0 100644 --- a/cpp/ql/test/library-tests/regex/regexp.ql +++ b/cpp/ql/test/library-tests/regex/regexp.ql @@ -1,4 +1,4 @@ -import codeql.ruby.Regexp +import semmle.code.cpp.regex.RegexTreeView query predicate groupName(RegExpGroup g, string name) { name = g.getName() } From cc4f5bd6bde5a0cc8d97c6c1c6992a89ed3e3086 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 21 Jul 2026 16:29:30 +0200 Subject: [PATCH 03/37] C++: Make regex test work Note that we are currently still implementing what Ruby thinks a regex is. This is not correct as C++ by default uses a variant of ECMAScript regexes. We will address this in the follow-up commits. --- .../test/library-tests/regex/parse.expected | 358 ++++++------ cpp/ql/test/library-tests/regex/parse.ql | 6 + cpp/ql/test/library-tests/regex/regexp.cpp | 173 +++--- .../test/library-tests/regex/regexp.expected | 530 +++++++++--------- cpp/ql/test/library-tests/regex/regexp.ql | 6 + 5 files changed, 545 insertions(+), 528 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 3df78cdb4fbf..13d74f0e5616 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -1,489 +1,487 @@ -regexp.rb: -# 5| [RegExpConstant, RegExpNormalChar] abc +regexp.cpp: +# 18| [RegExpConstant, RegExpNormalChar] abc -# 8| [RegExpConstant, RegExpNormalChar] a +# 21| [RegExpConstant, RegExpNormalChar] a -# 8| [RegExpStar] a* +# 21| [RegExpStar] a* #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 8| [RegExpSequence] a*b+c?d +# 21| [RegExpSequence] a*b+c?d #-----| 0 -> [RegExpStar] a* #-----| 1 -> [RegExpPlus] b+ #-----| 2 -> [RegExpOpt] c? #-----| 3 -> [RegExpConstant, RegExpNormalChar] d -# 8| [RegExpConstant, RegExpNormalChar] b +# 21| [RegExpConstant, RegExpNormalChar] b -# 8| [RegExpPlus] b+ +# 21| [RegExpPlus] b+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] b -# 8| [RegExpConstant, RegExpNormalChar] c +# 21| [RegExpConstant, RegExpNormalChar] c -# 8| [RegExpOpt] c? +# 21| [RegExpOpt] c? #-----| 0 -> [RegExpConstant, RegExpNormalChar] c -# 8| [RegExpConstant, RegExpNormalChar] d +# 21| [RegExpConstant, RegExpNormalChar] d -# 9| [RegExpConstant, RegExpNormalChar] a +# 22| [RegExpConstant, RegExpNormalChar] a -# 9| [RegExpRange] a{4,8} +# 22| [RegExpRange] a{4,8} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 10| [RegExpConstant, RegExpNormalChar] a +# 23| [RegExpConstant, RegExpNormalChar] a -# 10| [RegExpRange] a{,8} +# 23| [RegExpRange] a{,8} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 11| [RegExpConstant, RegExpNormalChar] a +# 24| [RegExpConstant, RegExpNormalChar] a -# 11| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} +# 24| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 12| [RegExpConstant, RegExpNormalChar] a +# 25| [RegExpConstant, RegExpNormalChar] a -# 12| [RegExpRange] a{7} +# 25| [RegExpRange] a{7} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 15| [RegExpConstant, RegExpNormalChar] foo +# 28| [RegExpConstant, RegExpNormalChar] foo -# 15| [RegExpAlt] foo|bar +# 28| [RegExpAlt] foo|bar #-----| 0 -> [RegExpConstant, RegExpNormalChar] foo #-----| 1 -> [RegExpConstant, RegExpNormalChar] bar -# 15| [RegExpConstant, RegExpNormalChar] bar +# 28| [RegExpConstant, RegExpNormalChar] bar -# 18| [RegExpCharacterClass] [abc] +# 31| [RegExpCharacterClass] [abc] #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] b #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 18| [RegExpConstant, RegExpNormalChar] a +# 31| [RegExpConstant, RegExpNormalChar] a -# 18| [RegExpConstant, RegExpNormalChar] b +# 31| [RegExpConstant, RegExpNormalChar] b -# 18| [RegExpConstant, RegExpNormalChar] c +# 31| [RegExpConstant, RegExpNormalChar] c -# 19| [RegExpCharacterClass] [a-fA-F0-9_] +# 32| [RegExpCharacterClass] [a-fA-F0-9_] #-----| 0 -> [RegExpCharacterRange] a-f #-----| 1 -> [RegExpCharacterRange] A-F #-----| 2 -> [RegExpCharacterRange] 0-9 #-----| 3 -> [RegExpConstant, RegExpNormalChar] _ -# 19| [RegExpConstant, RegExpNormalChar] a +# 32| [RegExpConstant, RegExpNormalChar] a -# 19| [RegExpCharacterRange] a-f +# 32| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 19| [RegExpConstant, RegExpNormalChar] f +# 32| [RegExpConstant, RegExpNormalChar] f -# 19| [RegExpConstant, RegExpNormalChar] A +# 32| [RegExpConstant, RegExpNormalChar] A -# 19| [RegExpCharacterRange] A-F +# 32| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 19| [RegExpConstant, RegExpNormalChar] F +# 32| [RegExpConstant, RegExpNormalChar] F -# 19| [RegExpConstant, RegExpNormalChar] 0 +# 32| [RegExpConstant, RegExpNormalChar] 0 -# 19| [RegExpCharacterRange] 0-9 +# 32| [RegExpCharacterRange] 0-9 #-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 #-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 -# 19| [RegExpConstant, RegExpNormalChar] 9 +# 32| [RegExpConstant, RegExpNormalChar] 9 -# 19| [RegExpConstant, RegExpNormalChar] _ +# 32| [RegExpConstant, RegExpNormalChar] _ -# 20| [RegExpCaret] \A +# 33| [RegExpCaret] \A -# 20| [RegExpSequence] \A[+-]?\d+ +# 33| [RegExpSequence] \A[+-]?\d+ #-----| 0 -> [RegExpCaret] \A #-----| 1 -> [RegExpOpt] [+-]? #-----| 2 -> [RegExpPlus] \d+ -# 20| [RegExpCharacterClass] [+-] +# 33| [RegExpCharacterClass] [+-] #-----| 0 -> [RegExpConstant, RegExpNormalChar] + #-----| 1 -> [RegExpConstant, RegExpNormalChar] - -# 20| [RegExpOpt] [+-]? +# 33| [RegExpOpt] [+-]? #-----| 0 -> [RegExpCharacterClass] [+-] -# 20| [RegExpConstant, RegExpNormalChar] + +# 33| [RegExpConstant, RegExpNormalChar] + -# 20| [RegExpConstant, RegExpNormalChar] - +# 33| [RegExpConstant, RegExpNormalChar] - -# 20| [RegExpCharacterClassEscape] \d +# 33| [RegExpCharacterClassEscape] \d -# 20| [RegExpPlus] \d+ +# 33| [RegExpPlus] \d+ #-----| 0 -> [RegExpCharacterClassEscape] \d -# 21| [RegExpCharacterClass] [\w] +# 34| [RegExpCharacterClass] [\w] #-----| 0 -> [RegExpCharacterClassEscape] \w -# 21| [RegExpPlus] [\w]+ +# 34| [RegExpPlus] [\w]+ #-----| 0 -> [RegExpCharacterClass] [\w] -# 21| [RegExpCharacterClassEscape] \w +# 34| [RegExpCharacterClassEscape] \w -# 22| [RegExpConstant, RegExpEscape] \[ +# 35| [RegExpConstant, RegExpEscape] \[ -# 22| [RegExpSequence] \[\][123] +# 35| [RegExpSequence] \[\][123] #-----| 0 -> [RegExpConstant, RegExpEscape] \[ #-----| 1 -> [RegExpConstant, RegExpEscape] \] #-----| 2 -> [RegExpCharacterClass] [123] -# 22| [RegExpConstant, RegExpEscape] \] +# 35| [RegExpConstant, RegExpEscape] \] -# 22| [RegExpCharacterClass] [123] +# 35| [RegExpCharacterClass] [123] #-----| 0 -> [RegExpConstant, RegExpNormalChar] 1 #-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 #-----| 2 -> [RegExpConstant, RegExpNormalChar] 3 -# 22| [RegExpConstant, RegExpNormalChar] 1 +# 35| [RegExpConstant, RegExpNormalChar] 1 -# 22| [RegExpConstant, RegExpNormalChar] 2 +# 35| [RegExpConstant, RegExpNormalChar] 2 -# 22| [RegExpConstant, RegExpNormalChar] 3 +# 35| [RegExpConstant, RegExpNormalChar] 3 -# 23| [RegExpCharacterClass] [^A-Z] +# 36| [RegExpCharacterClass] [^A-Z] #-----| 0 -> [RegExpCharacterRange] A-Z -# 23| [RegExpConstant, RegExpNormalChar] A +# 36| [RegExpConstant, RegExpNormalChar] A -# 23| [RegExpCharacterRange] A-Z +# 36| [RegExpCharacterRange] A-Z #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] Z -# 23| [RegExpConstant, RegExpNormalChar] Z +# 36| [RegExpConstant, RegExpNormalChar] Z -# 24| [RegExpCharacterClass] []] +# 37| [RegExpCharacterClass] []] #-----| 0 -> [RegExpConstant, RegExpNormalChar] ] -# 24| [RegExpConstant, RegExpNormalChar] ] +# 37| [RegExpConstant, RegExpNormalChar] ] -# 25| [RegExpCharacterClass] [^]] +# 38| [RegExpCharacterClass] [^]] #-----| 0 -> [RegExpConstant, RegExpNormalChar] ] -# 25| [RegExpConstant, RegExpNormalChar] ] +# 38| [RegExpConstant, RegExpNormalChar] ] -# 26| [RegExpCharacterClass] [^-] +# 39| [RegExpCharacterClass] [^-] #-----| 0 -> [RegExpConstant, RegExpNormalChar] - -# 26| [RegExpConstant, RegExpNormalChar] - +# 39| [RegExpConstant, RegExpNormalChar] - -# 27| [RegExpCharacterClass] [|] +# 40| [RegExpCharacterClass] [|] #-----| 0 -> [RegExpConstant, RegExpNormalChar] | -# 27| [RegExpConstant, RegExpNormalChar] | +# 40| [RegExpConstant, RegExpNormalChar] | -# 30| [RegExpCharacterClass] [[a-f] +# 43| [RegExpCharacterClass] [[a-f] #-----| 0 -> [RegExpConstant, RegExpNormalChar] [ #-----| 1 -> [RegExpCharacterRange] a-f -# 30| [RegExpSequence] [[a-f]A-F] +# 43| [RegExpSequence] [[a-f]A-F] #-----| 0 -> [RegExpCharacterClass] [[a-f] #-----| 1 -> [RegExpConstant, RegExpNormalChar] A-F] -# 30| [RegExpConstant, RegExpNormalChar] [ +# 43| [RegExpConstant, RegExpNormalChar] [ -# 30| [RegExpConstant, RegExpNormalChar] a +# 43| [RegExpConstant, RegExpNormalChar] a -# 30| [RegExpCharacterRange] a-f +# 43| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 30| [RegExpConstant, RegExpNormalChar] f +# 43| [RegExpConstant, RegExpNormalChar] f -# 30| [RegExpConstant, RegExpNormalChar] A-F] +# 43| [RegExpConstant, RegExpNormalChar] A-F] -# 33| [RegExpDot] . +# 46| [RegExpDot] . -# 33| [RegExpStar] .* +# 46| [RegExpStar] .* #-----| 0 -> [RegExpDot] . -# 34| [RegExpDot] . +# 47| [RegExpDot] . -# 34| [RegExpStar] .* +# 47| [RegExpStar] .* #-----| 0 -> [RegExpDot] . -# 35| [RegExpCharacterClassEscape] \w +# 48| [RegExpCharacterClassEscape] \w -# 35| [RegExpPlus] \w+ +# 48| [RegExpPlus] \w+ #-----| 0 -> [RegExpCharacterClassEscape] \w -# 35| [RegExpSequence] \w+\W +# 48| [RegExpSequence] \w+\W #-----| 0 -> [RegExpPlus] \w+ #-----| 1 -> [RegExpCharacterClassEscape] \W -# 35| [RegExpCharacterClassEscape] \W +# 48| [RegExpCharacterClassEscape] \W -# 36| [RegExpCharacterClassEscape] \s +# 49| [RegExpCharacterClassEscape] \s -# 36| [RegExpSequence] \s\S +# 49| [RegExpSequence] \s\S #-----| 0 -> [RegExpCharacterClassEscape] \s #-----| 1 -> [RegExpCharacterClassEscape] \S -# 36| [RegExpCharacterClassEscape] \S +# 49| [RegExpCharacterClassEscape] \S -# 37| [RegExpCharacterClassEscape] \d +# 50| [RegExpCharacterClassEscape] \d -# 37| [RegExpSequence] \d\D +# 50| [RegExpSequence] \d\D #-----| 0 -> [RegExpCharacterClassEscape] \d #-----| 1 -> [RegExpCharacterClassEscape] \D -# 37| [RegExpCharacterClassEscape] \D +# 50| [RegExpCharacterClassEscape] \D -# 38| [RegExpCharacterClassEscape] \h +# 51| [RegExpCharacterClassEscape] \h -# 38| [RegExpSequence] \h\H +# 51| [RegExpSequence] \h\H #-----| 0 -> [RegExpCharacterClassEscape] \h #-----| 1 -> [RegExpCharacterClassEscape] \H -# 38| [RegExpCharacterClassEscape] \H +# 51| [RegExpCharacterClassEscape] \H -# 39| [RegExpConstant, RegExpEscape] \n +# 52| [RegExpConstant, RegExpEscape] \n -# 39| [RegExpSequence] \n\r\t +# 52| [RegExpSequence] \n\r\t #-----| 0 -> [RegExpConstant, RegExpEscape] \n #-----| 1 -> [RegExpConstant, RegExpEscape] \r #-----| 2 -> [RegExpConstant, RegExpEscape] \t -# 39| [RegExpConstant, RegExpEscape] \r +# 52| [RegExpConstant, RegExpEscape] \r -# 39| [RegExpConstant, RegExpEscape] \t +# 52| [RegExpConstant, RegExpEscape] \t -# 42| [RegExpSpecialChar] \G +# 55| [RegExpSpecialChar] \G -# 42| [RegExpSequence] \Gabc +# 55| [RegExpSequence] \Gabc #-----| 0 -> [RegExpSpecialChar] \G #-----| 1 -> [RegExpConstant, RegExpNormalChar] abc -# 42| [RegExpConstant, RegExpNormalChar] abc +# 55| [RegExpConstant, RegExpNormalChar] abc -# 43| [RegExpSpecialChar] \b +# 56| [RegExpSpecialChar] \b -# 43| [RegExpSequence] \b!a\B +# 56| [RegExpSequence] \b!a\B #-----| 0 -> [RegExpSpecialChar] \b #-----| 1 -> [RegExpConstant, RegExpNormalChar] !a #-----| 2 -> [RegExpNonWordBoundary] \B -# 43| [RegExpConstant, RegExpNormalChar] !a +# 56| [RegExpConstant, RegExpNormalChar] !a -# 43| [RegExpNonWordBoundary] \B +# 56| [RegExpNonWordBoundary] \B -# 46| [RegExpGroup] (foo) +# 59| [RegExpGroup] (foo) #-----| 0 -> [RegExpConstant, RegExpNormalChar] foo -# 46| [RegExpStar] (foo)* +# 59| [RegExpStar] (foo)* #-----| 0 -> [RegExpGroup] (foo) -# 46| [RegExpSequence] (foo)*bar +# 59| [RegExpSequence] (foo)*bar #-----| 0 -> [RegExpStar] (foo)* #-----| 1 -> [RegExpConstant, RegExpNormalChar] bar -# 46| [RegExpConstant, RegExpNormalChar] foo +# 59| [RegExpConstant, RegExpNormalChar] foo -# 46| [RegExpConstant, RegExpNormalChar] bar +# 59| [RegExpConstant, RegExpNormalChar] bar -# 47| [RegExpConstant, RegExpNormalChar] fo +# 60| [RegExpConstant, RegExpNormalChar] fo -# 47| [RegExpSequence] fo(o|b)ar +# 60| [RegExpSequence] fo(o|b)ar #-----| 0 -> [RegExpConstant, RegExpNormalChar] fo #-----| 1 -> [RegExpGroup] (o|b) #-----| 2 -> [RegExpConstant, RegExpNormalChar] ar -# 47| [RegExpGroup] (o|b) +# 60| [RegExpGroup] (o|b) #-----| 0 -> [RegExpAlt] o|b -# 47| [RegExpConstant, RegExpNormalChar] o +# 60| [RegExpConstant, RegExpNormalChar] o -# 47| [RegExpAlt] o|b +# 60| [RegExpAlt] o|b #-----| 0 -> [RegExpConstant, RegExpNormalChar] o #-----| 1 -> [RegExpConstant, RegExpNormalChar] b -# 47| [RegExpConstant, RegExpNormalChar] b +# 60| [RegExpConstant, RegExpNormalChar] b -# 47| [RegExpConstant, RegExpNormalChar] ar +# 60| [RegExpConstant, RegExpNormalChar] ar -# 48| [RegExpGroup] (a|b|cd) +# 61| [RegExpGroup] (a|b|cd) #-----| 0 -> [RegExpAlt] a|b|cd -# 48| [RegExpSequence] (a|b|cd)e +# 61| [RegExpSequence] (a|b|cd)e #-----| 0 -> [RegExpGroup] (a|b|cd) #-----| 1 -> [RegExpConstant, RegExpNormalChar] e -# 48| [RegExpConstant, RegExpNormalChar] a +# 61| [RegExpConstant, RegExpNormalChar] a -# 48| [RegExpAlt] a|b|cd +# 61| [RegExpAlt] a|b|cd #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] b #-----| 2 -> [RegExpConstant, RegExpNormalChar] cd -# 48| [RegExpConstant, RegExpNormalChar] b +# 61| [RegExpConstant, RegExpNormalChar] b -# 48| [RegExpConstant, RegExpNormalChar] cd +# 61| [RegExpConstant, RegExpNormalChar] cd -# 48| [RegExpConstant, RegExpNormalChar] e +# 61| [RegExpConstant, RegExpNormalChar] e -# 49| [RegExpGroup] (?::+) +# 62| [RegExpGroup] (?::+) #-----| 0 -> [RegExpPlus] :+ -# 49| [RegExpSequence] (?::+)\w +# 62| [RegExpSequence] (?::+)\w #-----| 0 -> [RegExpGroup] (?::+) #-----| 1 -> [RegExpCharacterClassEscape] \w -# 49| [RegExpConstant, RegExpNormalChar] : +# 62| [RegExpConstant, RegExpNormalChar] : -# 49| [RegExpPlus] :+ +# 62| [RegExpPlus] :+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] : -# 49| [RegExpCharacterClassEscape] \w +# 62| [RegExpCharacterClassEscape] \w -# 52| [RegExpGroup] (?\w+) +# 65| [RegExpGroup] (?\w+) #-----| 0 -> [RegExpPlus] \w+ -# 52| [RegExpCharacterClassEscape] \w +# 65| [RegExpCharacterClassEscape] \w -# 52| [RegExpPlus] \w+ +# 65| [RegExpPlus] \w+ #-----| 0 -> [RegExpCharacterClassEscape] \w -# 53| [RegExpGroup] (?'foo'fo+) +# 66| [RegExpGroup] (?'foo'fo+) #-----| 0 -> [RegExpSequence] fo+ -# 53| [RegExpConstant, RegExpNormalChar] f +# 66| [RegExpConstant, RegExpNormalChar] f -# 53| [RegExpSequence] fo+ +# 66| [RegExpSequence] fo+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] f #-----| 1 -> [RegExpPlus] o+ -# 53| [RegExpConstant, RegExpNormalChar] o +# 66| [RegExpConstant, RegExpNormalChar] o -# 53| [RegExpPlus] o+ +# 66| [RegExpPlus] o+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] o -# 56| [RegExpGroup] (a+) +# 69| [RegExpGroup] (a+) #-----| 0 -> [RegExpPlus] a+ -# 56| [RegExpSequence] (a+)b+\1 +# 69| [RegExpSequence] (a+)b+\1 #-----| 0 -> [RegExpGroup] (a+) #-----| 1 -> [RegExpPlus] b+ #-----| 2 -> [RegExpBackRef] \1 -# 56| [RegExpConstant, RegExpNormalChar] a +# 69| [RegExpConstant, RegExpNormalChar] a -# 56| [RegExpPlus] a+ +# 69| [RegExpPlus] a+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 56| [RegExpConstant, RegExpNormalChar] b +# 69| [RegExpConstant, RegExpNormalChar] b -# 56| [RegExpPlus] b+ +# 69| [RegExpPlus] b+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] b -# 56| [RegExpBackRef] \1 +# 69| [RegExpBackRef] \1 -# 57| [RegExpGroup] (?q+) +# 70| [RegExpGroup] (?q+) #-----| 0 -> [RegExpPlus] q+ -# 57| [RegExpSequence] (?q+)\s+\k+ +# 70| [RegExpSequence] (?q+)\s+\k+ #-----| 0 -> [RegExpGroup] (?q+) #-----| 1 -> [RegExpPlus] \s+ #-----| 2 -> [RegExpPlus] \k+ -# 57| [RegExpConstant, RegExpNormalChar] q +# 70| [RegExpConstant, RegExpNormalChar] q -# 57| [RegExpPlus] q+ +# 70| [RegExpPlus] q+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] q -# 57| [RegExpCharacterClassEscape] \s +# 70| [RegExpCharacterClassEscape] \s -# 57| [RegExpPlus] \s+ +# 70| [RegExpPlus] \s+ #-----| 0 -> [RegExpCharacterClassEscape] \s -# 57| [RegExpBackRef] \k +# 70| [RegExpBackRef] \k -# 57| [RegExpPlus] \k+ +# 70| [RegExpPlus] \k+ #-----| 0 -> [RegExpBackRef] \k -# 60| [RegExpNamedCharacterProperty] \p{Word} +# 73| [RegExpNamedCharacterProperty] \p{Word} -# 60| [RegExpStar] \p{Word}* +# 73| [RegExpStar] \p{Word}* #-----| 0 -> [RegExpNamedCharacterProperty] \p{Word} -# 61| [RegExpNamedCharacterProperty] \P{Digit} +# 74| [RegExpNamedCharacterProperty] \P{Digit} -# 61| [RegExpPlus] \P{Digit}+ +# 74| [RegExpPlus] \P{Digit}+ #-----| 0 -> [RegExpNamedCharacterProperty] \P{Digit} -# 62| [RegExpNamedCharacterProperty] \p{^Alnum} +# 75| [RegExpNamedCharacterProperty] \p{^Alnum} -# 62| [RegExpRange] \p{^Alnum}{2,3} +# 75| [RegExpRange] \p{^Alnum}{2,3} #-----| 0 -> [RegExpNamedCharacterProperty] \p{^Alnum} -# 63| [RegExpCharacterClass] [a-f\p{Digit}] +# 76| [RegExpCharacterClass] [a-f\p{Digit}] #-----| 0 -> [RegExpCharacterRange] a-f #-----| 1 -> [RegExpNamedCharacterProperty] \p{Digit} -# 63| [RegExpPlus] [a-f\p{Digit}]+ +# 76| [RegExpPlus] [a-f\p{Digit}]+ #-----| 0 -> [RegExpCharacterClass] [a-f\p{Digit}] -# 63| [RegExpConstant, RegExpNormalChar] a +# 76| [RegExpConstant, RegExpNormalChar] a -# 63| [RegExpCharacterRange] a-f +# 76| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 63| [RegExpConstant, RegExpNormalChar] f +# 76| [RegExpConstant, RegExpNormalChar] f -# 63| [RegExpNamedCharacterProperty] \p{Digit} +# 76| [RegExpNamedCharacterProperty] \p{Digit} -# 66| [RegExpCharacterClass] [[:alpha:]] +# 79| [RegExpCharacterClass] [[:alpha:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] -# 66| [RegExpSequence] [[:alpha:]][[:digit:]] +# 79| [RegExpSequence] [[:alpha:]][[:digit:]] #-----| 0 -> [RegExpCharacterClass] [[:alpha:]] #-----| 1 -> [RegExpCharacterClass] [[:digit:]] -# 66| [RegExpNamedCharacterProperty] [:alpha:] +# 79| [RegExpNamedCharacterProperty] [:alpha:] -# 66| [RegExpCharacterClass] [[:digit:]] +# 79| [RegExpCharacterClass] [[:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] -# 66| [RegExpNamedCharacterProperty] [:digit:] +# 79| [RegExpNamedCharacterProperty] [:digit:] -# 69| [RegExpCharacterClass] [[:alpha:][:digit:]] +# 82| [RegExpCharacterClass] [[:alpha:][:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] -# 69| [RegExpNamedCharacterProperty] [:alpha:] +# 82| [RegExpNamedCharacterProperty] [:alpha:] -# 69| [RegExpNamedCharacterProperty] [:digit:] +# 82| [RegExpNamedCharacterProperty] [:digit:] -# 72| [RegExpCharacterClass] [A-F[:digit:]a-f] +# 85| [RegExpCharacterClass] [A-F[:digit:]a-f] #-----| 0 -> [RegExpCharacterRange] A-F #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] #-----| 2 -> [RegExpCharacterRange] a-f -# 72| [RegExpConstant, RegExpNormalChar] A +# 85| [RegExpConstant, RegExpNormalChar] A -# 72| [RegExpCharacterRange] A-F +# 85| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 72| [RegExpConstant, RegExpNormalChar] F +# 85| [RegExpConstant, RegExpNormalChar] F -# 72| [RegExpNamedCharacterProperty] [:digit:] +# 85| [RegExpNamedCharacterProperty] [:digit:] -# 72| [RegExpConstant, RegExpNormalChar] a +# 85| [RegExpConstant, RegExpNormalChar] a -# 72| [RegExpCharacterRange] a-f +# 85| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 72| [RegExpConstant, RegExpNormalChar] f +# 85| [RegExpConstant, RegExpNormalChar] f -# 75| [RegExpNamedCharacterProperty] [:digit:] +# 88| [RegExpNamedCharacterProperty] [:digit:] -# 79| [RegExpConstant, RegExpNormalChar] abc - -# 82| [RegExpConstant, RegExpEscape] \u{987 +# 91| [RegExpConstant, RegExpEscape] \u{987 diff --git a/cpp/ql/test/library-tests/regex/parse.ql b/cpp/ql/test/library-tests/regex/parse.ql index ba9a04ab9bd7..2e9debf3784a 100644 --- a/cpp/ql/test/library-tests/regex/parse.ql +++ b/cpp/ql/test/library-tests/regex/parse.ql @@ -2,8 +2,14 @@ * @kind graph */ +import semmle.code.cpp.regex.internal.ParseRegExp import semmle.code.cpp.regex.RegexTreeView +// Stop gap for missing flow configs +class RegExpTest extends RegExp { + RegExpTest() { any() } +} + query predicate nodes(RegExpTerm n, string attr, string val) { attr = "semmle.label" and val = "[" + concat(n.getAPrimaryQlClass(), ", ") + "] " + n.toString() diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index a68c4878094f..944cf3aa2dbb 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -1,82 +1,91 @@ -# Empty -// - -# Basic sequence -/abc/ - -# Repetition -/a*b+c?d/ -/a{4,8}/ -/a{,8}/ -/a{3,}/ -/a{7}/ - -# Alternation -/foo|bar/ - -# Character classes -/[abc]/ -/[a-fA-F0-9_]/ -/\A[+-]?\d+/ -/[\w]+/ -/\[\][123]/ -/[^A-Z]/ -/[]]/ # MRI gives a warning, but accepts this as matching ']' -/[^]]/ # MRI gives a warning, but accepts this as matching anything except ']' -/[^-]/ -/[|]/ - -# Nested character classes -/[[a-f]A-F]/ # BAD - not parsed correctly - -# Meta-character classes -/.*/ -/.*/m -/\w+\W/ -/\s\S/ -/\d\D/ -/\h\H/ -/\n\r\t/ - -# Anchors -/\Gabc/ -/\b!a\B/ - -# Groups -/(foo)*bar/ -/fo(o|b)ar/ -/(a|b|cd)e/ -/(?::+)\w/ # Non-capturing group matching colons - -# Named groups -/(?\w+)/ -/(?'foo'fo+)/ - -# Backreferences -/(a+)b+\1/ -/(?q+)\s+\k+/ - -# Named character properties using the p-style syntax -/\p{Word}*/ -/\P{Digit}+/ -/\p{^Alnum}{2,3}/ -/[a-f\p{Digit}]+/ # Also valid inside character classes - -# Two separate character classes, each containing a single POSIX bracket expression -/[[:alpha:]][[:digit:]]/ - -# A single character class containing two POSIX bracket expressions -/[[:alpha:][:digit:]]/ - -# A single character class containing two ranges and one POSIX bracket expression -/[A-F[:digit:]a-f]/ - -# *Not* a POSIX bracket expression; just a regular character class. -/[:digit:]/ - -# Simple constant interpolation -A = "a" -/#{A}bc/ - -# unicode -/\u{9879}/ \ No newline at end of file +// semmle-extractor-options: -std=c++17 + +namespace std { + template + class basic_regex { + public: + basic_regex(const CharT *s) {} + basic_regex(const CharT *s, int flags) {} + basic_regex &assign(const CharT *s) { return *this; } + }; + typedef basic_regex regex; +} // namespace std + +// Empty +std::regex r_empty(""); + +// Basic sequence +std::regex r_abc("abc"); + +// Repetition +std::regex r_rep1("a*b+c?d"); +std::regex r_rep2("a{4,8}"); +std::regex r_rep3("a{,8}"); +std::regex r_rep4("a{3,}"); +std::regex r_rep5("a{7}"); + +// Alternation +std::regex r_alt("foo|bar"); + +// Character classes +std::regex r_cc1("[abc]"); +std::regex r_cc2("[a-fA-F0-9_]"); +std::regex r_cc3("\\A[+-]?\\d+"); +std::regex r_cc4("[\\w]+"); +std::regex r_cc5("\\[\\][123]"); +std::regex r_cc6("[^A-Z]"); +std::regex r_cc7("[]]"); // MRI gives a warning, but accepts this as matching ']' +std::regex r_cc8("[^]]"); // MRI gives a warning, but accepts this as matching anything except ']' +std::regex r_cc9("[^-]"); +std::regex r_cc10("[|]"); + +// Nested character classes (BAD - not parsed correctly) +std::regex r_nested("[[a-f]A-F]"); + +// Meta-character classes +std::regex r_meta1(".*"); +std::regex r_meta1m(".*"); // /.*/m mode variant — mode flags are constructor args in C++ +std::regex r_meta2("\\w+\\W"); +std::regex r_meta3("\\s\\S"); +std::regex r_meta4("\\d\\D"); +std::regex r_meta5("\\h\\H"); +std::regex r_meta6("\\n\\r\\t"); + +// Anchors +std::regex r_anc1("\\Gabc"); +std::regex r_anc2("\\b!a\\B"); + +// Groups +std::regex r_grp1("(foo)*bar"); +std::regex r_grp2("fo(o|b)ar"); +std::regex r_grp3("(a|b|cd)e"); +std::regex r_grp4("(?::+)\\w"); // Non-capturing group matching colons + +// Named groups +std::regex r_ng1("(?\\w+)"); +std::regex r_ng2("(?'foo'fo+)"); // single-quote named-group form + +// Backreferences +std::regex r_bref1("(a+)b+\\1"); +std::regex r_bref2("(?q+)\\s+\\k+"); + +// Named character properties using the p-style syntax +std::regex r_prop1("\\p{Word}*"); +std::regex r_prop2("\\P{Digit}+"); +std::regex r_prop3("\\p{^Alnum}{2,3}"); +std::regex r_prop4("[a-f\\p{Digit}]+"); // Also valid inside character classes + +// Two separate character classes, each containing a single POSIX bracket expression +std::regex r_posix1("[[:alpha:]][[:digit:]]"); + +// A single character class containing two POSIX bracket expressions +std::regex r_posix2("[[:alpha:][:digit:]]"); + +// A single character class containing two ranges and one POSIX bracket expression +std::regex r_posix3("[A-F[:digit:]a-f]"); + +// *Not* a POSIX bracket expression; just a regular character class. +std::regex r_posix4("[:digit:]"); + +// unicode +std::regex r_uni("\\u{9879}"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index e464b5ce5ea5..6b2409e7c7c0 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -1,270 +1,268 @@ groupName -| regexp.rb:52:2:52:11 | (?\\w+) | id | -| regexp.rb:53:2:53:12 | (?'foo'fo+) | foo | -| regexp.rb:57:2:57:11 | (?q+) | qux | +| regexp.cpp:65:19:65:28 | (?\\w+) | id | +| regexp.cpp:66:19:66:29 | (?'foo'fo+) | foo | +| regexp.cpp:70:21:70:30 | (?q+) | qux | groupNumber -| regexp.rb:46:2:46:6 | (foo) | 1 | -| regexp.rb:47:4:47:8 | (o\|b) | 1 | -| regexp.rb:48:2:48:9 | (a\|b\|cd) | 1 | -| regexp.rb:53:2:53:12 | (?'foo'fo+) | 1 | -| regexp.rb:56:2:56:5 | (a+) | 1 | +| regexp.cpp:59:20:59:24 | (foo) | 1 | +| regexp.cpp:60:22:60:26 | (o\|b) | 1 | +| regexp.cpp:61:20:61:27 | (a\|b\|cd) | 1 | +| regexp.cpp:66:19:66:29 | (?'foo'fo+) | 1 | +| regexp.cpp:69:21:69:24 | (a+) | 1 | term -| regexp.rb:5:2:5:4 | abc | RegExpConstant,RegExpNormalChar | -| regexp.rb:8:2:8:2 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:8:2:8:3 | a* | RegExpStar | -| regexp.rb:8:2:8:8 | a*b+c?d | RegExpSequence | -| regexp.rb:8:4:8:4 | b | RegExpConstant,RegExpNormalChar | -| regexp.rb:8:4:8:5 | b+ | RegExpPlus | -| regexp.rb:8:6:8:6 | c | RegExpConstant,RegExpNormalChar | -| regexp.rb:8:6:8:7 | c? | RegExpOpt | -| regexp.rb:8:8:8:8 | d | RegExpConstant,RegExpNormalChar | -| regexp.rb:9:2:9:2 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:9:2:9:7 | a{4,8} | RegExpRange | -| regexp.rb:10:2:10:2 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:10:2:10:6 | a{,8} | RegExpRange | -| regexp.rb:11:2:11:2 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:11:2:11:6 | a{3,} | InfiniteRepetitionQuantifier,RegExpRange | -| regexp.rb:12:2:12:2 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:12:2:12:5 | a{7} | RegExpRange | -| regexp.rb:15:2:15:4 | foo | RegExpConstant,RegExpNormalChar | -| regexp.rb:15:2:15:8 | foo\|bar | RegExpAlt | -| regexp.rb:15:6:15:8 | bar | RegExpConstant,RegExpNormalChar | -| regexp.rb:18:2:18:6 | [abc] | RegExpCharacterClass | -| regexp.rb:18:3:18:3 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:18:4:18:4 | b | RegExpConstant,RegExpNormalChar | -| regexp.rb:18:5:18:5 | c | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:2:19:13 | [a-fA-F0-9_] | RegExpCharacterClass | -| regexp.rb:19:3:19:3 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:3:19:5 | a-f | RegExpCharacterRange | -| regexp.rb:19:5:19:5 | f | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:6:19:6 | A | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:6:19:8 | A-F | RegExpCharacterRange | -| regexp.rb:19:8:19:8 | F | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:9:19:9 | 0 | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:9:19:11 | 0-9 | RegExpCharacterRange | -| regexp.rb:19:11:19:11 | 9 | RegExpConstant,RegExpNormalChar | -| regexp.rb:19:12:19:12 | _ | RegExpConstant,RegExpNormalChar | -| regexp.rb:20:2:20:3 | \\A | RegExpCaret | -| regexp.rb:20:2:20:11 | \\A[+-]?\\d+ | RegExpSequence | -| regexp.rb:20:4:20:7 | [+-] | RegExpCharacterClass | -| regexp.rb:20:4:20:8 | [+-]? | RegExpOpt | -| regexp.rb:20:5:20:5 | + | RegExpConstant,RegExpNormalChar | -| regexp.rb:20:6:20:6 | - | RegExpConstant,RegExpNormalChar | -| regexp.rb:20:9:20:10 | \\d | RegExpCharacterClassEscape | -| regexp.rb:20:9:20:11 | \\d+ | RegExpPlus | -| regexp.rb:21:2:21:5 | [\\w] | RegExpCharacterClass | -| regexp.rb:21:2:21:6 | [\\w]+ | RegExpPlus | -| regexp.rb:21:3:21:4 | \\w | RegExpCharacterClassEscape | -| regexp.rb:22:2:22:3 | \\[ | RegExpConstant,RegExpEscape | -| regexp.rb:22:2:22:10 | \\[\\][123] | RegExpSequence | -| regexp.rb:22:4:22:5 | \\] | RegExpConstant,RegExpEscape | -| regexp.rb:22:6:22:10 | [123] | RegExpCharacterClass | -| regexp.rb:22:7:22:7 | 1 | RegExpConstant,RegExpNormalChar | -| regexp.rb:22:8:22:8 | 2 | RegExpConstant,RegExpNormalChar | -| regexp.rb:22:9:22:9 | 3 | RegExpConstant,RegExpNormalChar | -| regexp.rb:23:2:23:7 | [^A-Z] | RegExpCharacterClass | -| regexp.rb:23:4:23:4 | A | RegExpConstant,RegExpNormalChar | -| regexp.rb:23:4:23:6 | A-Z | RegExpCharacterRange | -| regexp.rb:23:6:23:6 | Z | RegExpConstant,RegExpNormalChar | -| regexp.rb:24:2:24:4 | []] | RegExpCharacterClass | -| regexp.rb:24:3:24:3 | ] | RegExpConstant,RegExpNormalChar | -| regexp.rb:25:2:25:5 | [^]] | RegExpCharacterClass | -| regexp.rb:25:4:25:4 | ] | RegExpConstant,RegExpNormalChar | -| regexp.rb:26:2:26:5 | [^-] | RegExpCharacterClass | -| regexp.rb:26:4:26:4 | - | RegExpConstant,RegExpNormalChar | -| regexp.rb:27:2:27:4 | [\|] | RegExpCharacterClass | -| regexp.rb:27:3:27:3 | \| | RegExpConstant,RegExpNormalChar | -| regexp.rb:30:2:30:7 | [[a-f] | RegExpCharacterClass | -| regexp.rb:30:2:30:11 | [[a-f]A-F] | RegExpSequence | -| regexp.rb:30:3:30:3 | [ | RegExpConstant,RegExpNormalChar | -| regexp.rb:30:4:30:4 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:30:4:30:6 | a-f | RegExpCharacterRange | -| regexp.rb:30:6:30:6 | f | RegExpConstant,RegExpNormalChar | -| regexp.rb:30:8:30:11 | A-F] | RegExpConstant,RegExpNormalChar | -| regexp.rb:33:2:33:2 | . | RegExpDot | -| regexp.rb:33:2:33:3 | .* | RegExpStar | -| regexp.rb:34:2:34:2 | . | RegExpDot | -| regexp.rb:34:2:34:3 | .* | RegExpStar | -| regexp.rb:35:2:35:3 | \\w | RegExpCharacterClassEscape | -| regexp.rb:35:2:35:4 | \\w+ | RegExpPlus | -| regexp.rb:35:2:35:6 | \\w+\\W | RegExpSequence | -| regexp.rb:35:5:35:6 | \\W | RegExpCharacterClassEscape | -| regexp.rb:36:2:36:3 | \\s | RegExpCharacterClassEscape | -| regexp.rb:36:2:36:5 | \\s\\S | RegExpSequence | -| regexp.rb:36:4:36:5 | \\S | RegExpCharacterClassEscape | -| regexp.rb:37:2:37:3 | \\d | RegExpCharacterClassEscape | -| regexp.rb:37:2:37:5 | \\d\\D | RegExpSequence | -| regexp.rb:37:4:37:5 | \\D | RegExpCharacterClassEscape | -| regexp.rb:38:2:38:3 | \\h | RegExpCharacterClassEscape | -| regexp.rb:38:2:38:5 | \\h\\H | RegExpSequence | -| regexp.rb:38:4:38:5 | \\H | RegExpCharacterClassEscape | -| regexp.rb:39:2:39:3 | \\n | RegExpConstant,RegExpEscape | -| regexp.rb:39:2:39:7 | \\n\\r\\t | RegExpSequence | -| regexp.rb:39:4:39:5 | \\r | RegExpConstant,RegExpEscape | -| regexp.rb:39:6:39:7 | \\t | RegExpConstant,RegExpEscape | -| regexp.rb:42:2:42:3 | \\G | RegExpSpecialChar | -| regexp.rb:42:2:42:6 | \\Gabc | RegExpSequence | -| regexp.rb:42:4:42:6 | abc | RegExpConstant,RegExpNormalChar | -| regexp.rb:43:2:43:3 | \\b | RegExpSpecialChar | -| regexp.rb:43:2:43:7 | \\b!a\\B | RegExpSequence | -| regexp.rb:43:4:43:5 | !a | RegExpConstant,RegExpNormalChar | -| regexp.rb:43:6:43:7 | \\B | RegExpNonWordBoundary | -| regexp.rb:46:2:46:6 | (foo) | RegExpGroup | -| regexp.rb:46:2:46:7 | (foo)* | RegExpStar | -| regexp.rb:46:2:46:10 | (foo)*bar | RegExpSequence | -| regexp.rb:46:3:46:5 | foo | RegExpConstant,RegExpNormalChar | -| regexp.rb:46:8:46:10 | bar | RegExpConstant,RegExpNormalChar | -| regexp.rb:47:2:47:3 | fo | RegExpConstant,RegExpNormalChar | -| regexp.rb:47:2:47:10 | fo(o\|b)ar | RegExpSequence | -| regexp.rb:47:4:47:8 | (o\|b) | RegExpGroup | -| regexp.rb:47:5:47:5 | o | RegExpConstant,RegExpNormalChar | -| regexp.rb:47:5:47:7 | o\|b | RegExpAlt | -| regexp.rb:47:7:47:7 | b | RegExpConstant,RegExpNormalChar | -| regexp.rb:47:9:47:10 | ar | RegExpConstant,RegExpNormalChar | -| regexp.rb:48:2:48:9 | (a\|b\|cd) | RegExpGroup | -| regexp.rb:48:2:48:10 | (a\|b\|cd)e | RegExpSequence | -| regexp.rb:48:3:48:3 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:48:3:48:8 | a\|b\|cd | RegExpAlt | -| regexp.rb:48:5:48:5 | b | RegExpConstant,RegExpNormalChar | -| regexp.rb:48:7:48:8 | cd | RegExpConstant,RegExpNormalChar | -| regexp.rb:48:10:48:10 | e | RegExpConstant,RegExpNormalChar | -| regexp.rb:49:2:49:7 | (?::+) | RegExpGroup | -| regexp.rb:49:2:49:9 | (?::+)\\w | RegExpSequence | -| regexp.rb:49:5:49:5 | : | RegExpConstant,RegExpNormalChar | -| regexp.rb:49:5:49:6 | :+ | RegExpPlus | -| regexp.rb:49:8:49:9 | \\w | RegExpCharacterClassEscape | -| regexp.rb:52:2:52:11 | (?\\w+) | RegExpGroup | -| regexp.rb:52:8:52:9 | \\w | RegExpCharacterClassEscape | -| regexp.rb:52:8:52:10 | \\w+ | RegExpPlus | -| regexp.rb:53:2:53:12 | (?'foo'fo+) | RegExpGroup | -| regexp.rb:53:9:53:9 | f | RegExpConstant,RegExpNormalChar | -| regexp.rb:53:9:53:11 | fo+ | RegExpSequence | -| regexp.rb:53:10:53:10 | o | RegExpConstant,RegExpNormalChar | -| regexp.rb:53:10:53:11 | o+ | RegExpPlus | -| regexp.rb:56:2:56:5 | (a+) | RegExpGroup | -| regexp.rb:56:2:56:9 | (a+)b+\\1 | RegExpSequence | -| regexp.rb:56:3:56:3 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:56:3:56:4 | a+ | RegExpPlus | -| regexp.rb:56:6:56:6 | b | RegExpConstant,RegExpNormalChar | -| regexp.rb:56:6:56:7 | b+ | RegExpPlus | -| regexp.rb:56:8:56:9 | \\1 | RegExpBackRef | -| regexp.rb:57:2:57:11 | (?q+) | RegExpGroup | -| regexp.rb:57:2:57:22 | (?q+)\\s+\\k+ | RegExpSequence | -| regexp.rb:57:9:57:9 | q | RegExpConstant,RegExpNormalChar | -| regexp.rb:57:9:57:10 | q+ | RegExpPlus | -| regexp.rb:57:12:57:13 | \\s | RegExpCharacterClassEscape | -| regexp.rb:57:12:57:14 | \\s+ | RegExpPlus | -| regexp.rb:57:15:57:21 | \\k | RegExpBackRef | -| regexp.rb:57:15:57:22 | \\k+ | RegExpPlus | -| regexp.rb:60:2:60:9 | \\p{Word} | RegExpNamedCharacterProperty | -| regexp.rb:60:2:60:10 | \\p{Word}* | RegExpStar | -| regexp.rb:61:2:61:10 | \\P{Digit} | RegExpNamedCharacterProperty | -| regexp.rb:61:2:61:11 | \\P{Digit}+ | RegExpPlus | -| regexp.rb:62:2:62:11 | \\p{^Alnum} | RegExpNamedCharacterProperty | -| regexp.rb:62:2:62:16 | \\p{^Alnum}{2,3} | RegExpRange | -| regexp.rb:63:2:63:15 | [a-f\\p{Digit}] | RegExpCharacterClass | -| regexp.rb:63:2:63:16 | [a-f\\p{Digit}]+ | RegExpPlus | -| regexp.rb:63:3:63:3 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:63:3:63:5 | a-f | RegExpCharacterRange | -| regexp.rb:63:5:63:5 | f | RegExpConstant,RegExpNormalChar | -| regexp.rb:63:6:63:14 | \\p{Digit} | RegExpNamedCharacterProperty | -| regexp.rb:66:2:66:12 | [[:alpha:]] | RegExpCharacterClass | -| regexp.rb:66:2:66:23 | [[:alpha:]][[:digit:]] | RegExpSequence | -| regexp.rb:66:3:66:11 | [:alpha:] | RegExpNamedCharacterProperty | -| regexp.rb:66:13:66:23 | [[:digit:]] | RegExpCharacterClass | -| regexp.rb:66:14:66:22 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.rb:69:2:69:21 | [[:alpha:][:digit:]] | RegExpCharacterClass | -| regexp.rb:69:3:69:11 | [:alpha:] | RegExpNamedCharacterProperty | -| regexp.rb:69:12:69:20 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.rb:72:2:72:18 | [A-F[:digit:]a-f] | RegExpCharacterClass | -| regexp.rb:72:3:72:3 | A | RegExpConstant,RegExpNormalChar | -| regexp.rb:72:3:72:5 | A-F | RegExpCharacterRange | -| regexp.rb:72:5:72:5 | F | RegExpConstant,RegExpNormalChar | -| regexp.rb:72:6:72:14 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.rb:72:15:72:15 | a | RegExpConstant,RegExpNormalChar | -| regexp.rb:72:15:72:17 | a-f | RegExpCharacterRange | -| regexp.rb:72:17:72:17 | f | RegExpConstant,RegExpNormalChar | -| regexp.rb:75:2:75:10 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.rb:79:2:79:4 | abc | RegExpConstant,RegExpNormalChar | -| regexp.rb:82:2:82:7 | \\u{987 | RegExpConstant,RegExpEscape | +| regexp.cpp:18:19:18:21 | abc | RegExpConstant,RegExpNormalChar | +| regexp.cpp:21:20:21:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:21:20:21:21 | a* | RegExpStar | +| regexp.cpp:21:20:21:26 | a*b+c?d | RegExpSequence | +| regexp.cpp:21:22:21:22 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:21:22:21:23 | b+ | RegExpPlus | +| regexp.cpp:21:24:21:24 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:21:24:21:25 | c? | RegExpOpt | +| regexp.cpp:21:26:21:26 | d | RegExpConstant,RegExpNormalChar | +| regexp.cpp:22:20:22:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:22:20:22:25 | a{4,8} | RegExpRange | +| regexp.cpp:23:20:23:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:23:20:23:24 | a{,8} | RegExpRange | +| regexp.cpp:24:20:24:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:24:20:24:24 | a{3,} | InfiniteRepetitionQuantifier,RegExpRange | +| regexp.cpp:25:20:25:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:25:20:25:23 | a{7} | RegExpRange | +| regexp.cpp:28:19:28:21 | foo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:28:19:28:25 | foo\|bar | RegExpAlt | +| regexp.cpp:28:23:28:25 | bar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:19:31:23 | [abc] | RegExpCharacterClass | +| regexp.cpp:31:20:31:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:21:31:21 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:22:31:22 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:19:32:30 | [a-fA-F0-9_] | RegExpCharacterClass | +| regexp.cpp:32:20:32:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:20:32:22 | a-f | RegExpCharacterRange | +| regexp.cpp:32:22:32:22 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:23:32:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:23:32:25 | A-F | RegExpCharacterRange | +| regexp.cpp:32:25:32:25 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:26:32:26 | 0 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:26:32:28 | 0-9 | RegExpCharacterRange | +| regexp.cpp:32:28:32:28 | 9 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:29:32:29 | _ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:33:19:33:20 | \\A | RegExpCaret | +| regexp.cpp:33:19:33:28 | \\A[+-]?\\d+ | RegExpSequence | +| regexp.cpp:33:21:33:24 | [+-] | RegExpCharacterClass | +| regexp.cpp:33:21:33:25 | [+-]? | RegExpOpt | +| regexp.cpp:33:22:33:22 | + | RegExpConstant,RegExpNormalChar | +| regexp.cpp:33:23:33:23 | - | RegExpConstant,RegExpNormalChar | +| regexp.cpp:33:26:33:27 | \\d | RegExpCharacterClassEscape | +| regexp.cpp:33:26:33:28 | \\d+ | RegExpPlus | +| regexp.cpp:34:19:34:22 | [\\w] | RegExpCharacterClass | +| regexp.cpp:34:19:34:23 | [\\w]+ | RegExpPlus | +| regexp.cpp:34:20:34:21 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:35:19:35:20 | \\[ | RegExpConstant,RegExpEscape | +| regexp.cpp:35:19:35:27 | \\[\\][123] | RegExpSequence | +| regexp.cpp:35:21:35:22 | \\] | RegExpConstant,RegExpEscape | +| regexp.cpp:35:23:35:27 | [123] | RegExpCharacterClass | +| regexp.cpp:35:24:35:24 | 1 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:35:25:35:25 | 2 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:35:26:35:26 | 3 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:36:19:36:24 | [^A-Z] | RegExpCharacterClass | +| regexp.cpp:36:21:36:21 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:36:21:36:23 | A-Z | RegExpCharacterRange | +| regexp.cpp:36:23:36:23 | Z | RegExpConstant,RegExpNormalChar | +| regexp.cpp:37:19:37:21 | []] | RegExpCharacterClass | +| regexp.cpp:37:20:37:20 | ] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:38:19:38:22 | [^]] | RegExpCharacterClass | +| regexp.cpp:38:21:38:21 | ] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:39:19:39:22 | [^-] | RegExpCharacterClass | +| regexp.cpp:39:21:39:21 | - | RegExpConstant,RegExpNormalChar | +| regexp.cpp:40:20:40:22 | [\|] | RegExpCharacterClass | +| regexp.cpp:40:21:40:21 | \| | RegExpConstant,RegExpNormalChar | +| regexp.cpp:43:22:43:27 | [[a-f] | RegExpCharacterClass | +| regexp.cpp:43:22:43:31 | [[a-f]A-F] | RegExpSequence | +| regexp.cpp:43:23:43:23 | [ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:43:24:43:24 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:43:24:43:26 | a-f | RegExpCharacterRange | +| regexp.cpp:43:26:43:26 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:43:28:43:31 | A-F] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:46:21:46:21 | . | RegExpDot | +| regexp.cpp:46:21:46:22 | .* | RegExpStar | +| regexp.cpp:47:22:47:22 | . | RegExpDot | +| regexp.cpp:47:22:47:23 | .* | RegExpStar | +| regexp.cpp:48:21:48:22 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:48:21:48:23 | \\w+ | RegExpPlus | +| regexp.cpp:48:21:48:25 | \\w+\\W | RegExpSequence | +| regexp.cpp:48:24:48:25 | \\W | RegExpCharacterClassEscape | +| regexp.cpp:49:21:49:22 | \\s | RegExpCharacterClassEscape | +| regexp.cpp:49:21:49:24 | \\s\\S | RegExpSequence | +| regexp.cpp:49:23:49:24 | \\S | RegExpCharacterClassEscape | +| regexp.cpp:50:21:50:22 | \\d | RegExpCharacterClassEscape | +| regexp.cpp:50:21:50:24 | \\d\\D | RegExpSequence | +| regexp.cpp:50:23:50:24 | \\D | RegExpCharacterClassEscape | +| regexp.cpp:51:21:51:22 | \\h | RegExpCharacterClassEscape | +| regexp.cpp:51:21:51:24 | \\h\\H | RegExpSequence | +| regexp.cpp:51:23:51:24 | \\H | RegExpCharacterClassEscape | +| regexp.cpp:52:21:52:22 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:52:21:52:26 | \\n\\r\\t | RegExpSequence | +| regexp.cpp:52:23:52:24 | \\r | RegExpConstant,RegExpEscape | +| regexp.cpp:52:25:52:26 | \\t | RegExpConstant,RegExpEscape | +| regexp.cpp:55:20:55:21 | \\G | RegExpSpecialChar | +| regexp.cpp:55:20:55:24 | \\Gabc | RegExpSequence | +| regexp.cpp:55:22:55:24 | abc | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:20:56:21 | \\b | RegExpSpecialChar | +| regexp.cpp:56:20:56:25 | \\b!a\\B | RegExpSequence | +| regexp.cpp:56:22:56:23 | !a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:24:56:25 | \\B | RegExpNonWordBoundary | +| regexp.cpp:59:20:59:24 | (foo) | RegExpGroup | +| regexp.cpp:59:20:59:25 | (foo)* | RegExpStar | +| regexp.cpp:59:20:59:28 | (foo)*bar | RegExpSequence | +| regexp.cpp:59:21:59:23 | foo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:59:26:59:28 | bar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:60:20:60:21 | fo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:60:20:60:28 | fo(o\|b)ar | RegExpSequence | +| regexp.cpp:60:22:60:26 | (o\|b) | RegExpGroup | +| regexp.cpp:60:23:60:23 | o | RegExpConstant,RegExpNormalChar | +| regexp.cpp:60:23:60:25 | o\|b | RegExpAlt | +| regexp.cpp:60:25:60:25 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:60:27:60:28 | ar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:61:20:61:27 | (a\|b\|cd) | RegExpGroup | +| regexp.cpp:61:20:61:28 | (a\|b\|cd)e | RegExpSequence | +| regexp.cpp:61:21:61:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:61:21:61:26 | a\|b\|cd | RegExpAlt | +| regexp.cpp:61:23:61:23 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:61:25:61:26 | cd | RegExpConstant,RegExpNormalChar | +| regexp.cpp:61:28:61:28 | e | RegExpConstant,RegExpNormalChar | +| regexp.cpp:62:20:62:25 | (?::+) | RegExpGroup | +| regexp.cpp:62:20:62:27 | (?::+)\\w | RegExpSequence | +| regexp.cpp:62:23:62:23 | : | RegExpConstant,RegExpNormalChar | +| regexp.cpp:62:23:62:24 | :+ | RegExpPlus | +| regexp.cpp:62:26:62:27 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:65:19:65:28 | (?\\w+) | RegExpGroup | +| regexp.cpp:65:25:65:26 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:65:25:65:27 | \\w+ | RegExpPlus | +| regexp.cpp:66:19:66:29 | (?'foo'fo+) | RegExpGroup | +| regexp.cpp:66:26:66:26 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:66:26:66:28 | fo+ | RegExpSequence | +| regexp.cpp:66:27:66:27 | o | RegExpConstant,RegExpNormalChar | +| regexp.cpp:66:27:66:28 | o+ | RegExpPlus | +| regexp.cpp:69:21:69:24 | (a+) | RegExpGroup | +| regexp.cpp:69:21:69:28 | (a+)b+\\1 | RegExpSequence | +| regexp.cpp:69:22:69:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:69:22:69:23 | a+ | RegExpPlus | +| regexp.cpp:69:25:69:25 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:69:25:69:26 | b+ | RegExpPlus | +| regexp.cpp:69:27:69:28 | \\1 | RegExpBackRef | +| regexp.cpp:70:21:70:30 | (?q+) | RegExpGroup | +| regexp.cpp:70:21:70:41 | (?q+)\\s+\\k+ | RegExpSequence | +| regexp.cpp:70:28:70:28 | q | RegExpConstant,RegExpNormalChar | +| regexp.cpp:70:28:70:29 | q+ | RegExpPlus | +| regexp.cpp:70:31:70:32 | \\s | RegExpCharacterClassEscape | +| regexp.cpp:70:31:70:33 | \\s+ | RegExpPlus | +| regexp.cpp:70:34:70:40 | \\k | RegExpBackRef | +| regexp.cpp:70:34:70:41 | \\k+ | RegExpPlus | +| regexp.cpp:73:21:73:28 | \\p{Word} | RegExpNamedCharacterProperty | +| regexp.cpp:73:21:73:29 | \\p{Word}* | RegExpStar | +| regexp.cpp:74:21:74:29 | \\P{Digit} | RegExpNamedCharacterProperty | +| regexp.cpp:74:21:74:30 | \\P{Digit}+ | RegExpPlus | +| regexp.cpp:75:21:75:30 | \\p{^Alnum} | RegExpNamedCharacterProperty | +| regexp.cpp:75:21:75:35 | \\p{^Alnum}{2,3} | RegExpRange | +| regexp.cpp:76:21:76:34 | [a-f\\p{Digit}] | RegExpCharacterClass | +| regexp.cpp:76:21:76:35 | [a-f\\p{Digit}]+ | RegExpPlus | +| regexp.cpp:76:22:76:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:76:22:76:24 | a-f | RegExpCharacterRange | +| regexp.cpp:76:24:76:24 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:76:25:76:33 | \\p{Digit} | RegExpNamedCharacterProperty | +| regexp.cpp:79:22:79:32 | [[:alpha:]] | RegExpCharacterClass | +| regexp.cpp:79:22:79:43 | [[:alpha:]][[:digit:]] | RegExpSequence | +| regexp.cpp:79:23:79:31 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.cpp:79:33:79:43 | [[:digit:]] | RegExpCharacterClass | +| regexp.cpp:79:34:79:42 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:82:22:82:41 | [[:alpha:][:digit:]] | RegExpCharacterClass | +| regexp.cpp:82:23:82:31 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.cpp:82:32:82:40 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:85:22:85:38 | [A-F[:digit:]a-f] | RegExpCharacterClass | +| regexp.cpp:85:23:85:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:85:23:85:25 | A-F | RegExpCharacterRange | +| regexp.cpp:85:25:85:25 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:85:26:85:34 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:85:35:85:35 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:85:35:85:37 | a-f | RegExpCharacterRange | +| regexp.cpp:85:37:85:37 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:88:22:88:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:91:19:91:24 | \\u{987 | RegExpConstant,RegExpEscape | regExpNormalCharValue -| regexp.rb:5:2:5:4 | abc | abc | -| regexp.rb:8:2:8:2 | a | a | -| regexp.rb:8:4:8:4 | b | b | -| regexp.rb:8:6:8:6 | c | c | -| regexp.rb:8:8:8:8 | d | d | -| regexp.rb:9:2:9:2 | a | a | -| regexp.rb:10:2:10:2 | a | a | -| regexp.rb:11:2:11:2 | a | a | -| regexp.rb:12:2:12:2 | a | a | -| regexp.rb:15:2:15:4 | foo | foo | -| regexp.rb:15:6:15:8 | bar | bar | -| regexp.rb:18:3:18:3 | a | a | -| regexp.rb:18:4:18:4 | b | b | -| regexp.rb:18:5:18:5 | c | c | -| regexp.rb:19:3:19:3 | a | a | -| regexp.rb:19:5:19:5 | f | f | -| regexp.rb:19:6:19:6 | A | A | -| regexp.rb:19:8:19:8 | F | F | -| regexp.rb:19:9:19:9 | 0 | 0 | -| regexp.rb:19:11:19:11 | 9 | 9 | -| regexp.rb:19:12:19:12 | _ | _ | -| regexp.rb:20:5:20:5 | + | + | -| regexp.rb:20:6:20:6 | - | - | -| regexp.rb:20:9:20:10 | \\d | d | -| regexp.rb:21:3:21:4 | \\w | w | -| regexp.rb:22:2:22:3 | \\[ | [ | -| regexp.rb:22:4:22:5 | \\] | ] | -| regexp.rb:22:7:22:7 | 1 | 1 | -| regexp.rb:22:8:22:8 | 2 | 2 | -| regexp.rb:22:9:22:9 | 3 | 3 | -| regexp.rb:23:4:23:4 | A | A | -| regexp.rb:23:6:23:6 | Z | Z | -| regexp.rb:24:3:24:3 | ] | ] | -| regexp.rb:25:4:25:4 | ] | ] | -| regexp.rb:26:4:26:4 | - | - | -| regexp.rb:27:3:27:3 | \| | \| | -| regexp.rb:30:3:30:3 | [ | [ | -| regexp.rb:30:4:30:4 | a | a | -| regexp.rb:30:6:30:6 | f | f | -| regexp.rb:30:8:30:11 | A-F] | A-F] | -| regexp.rb:35:2:35:3 | \\w | w | -| regexp.rb:35:5:35:6 | \\W | W | -| regexp.rb:36:2:36:3 | \\s | s | -| regexp.rb:36:4:36:5 | \\S | S | -| regexp.rb:37:2:37:3 | \\d | d | -| regexp.rb:37:4:37:5 | \\D | D | -| regexp.rb:38:2:38:3 | \\h | h | -| regexp.rb:38:4:38:5 | \\H | H | -| regexp.rb:39:2:39:3 | \\n | \n | -| regexp.rb:39:4:39:5 | \\r | \r | -| regexp.rb:39:6:39:7 | \\t | \t | -| regexp.rb:42:4:42:6 | abc | abc | -| regexp.rb:43:4:43:5 | !a | !a | -| regexp.rb:46:3:46:5 | foo | foo | -| regexp.rb:46:8:46:10 | bar | bar | -| regexp.rb:47:2:47:3 | fo | fo | -| regexp.rb:47:5:47:5 | o | o | -| regexp.rb:47:7:47:7 | b | b | -| regexp.rb:47:9:47:10 | ar | ar | -| regexp.rb:48:3:48:3 | a | a | -| regexp.rb:48:5:48:5 | b | b | -| regexp.rb:48:7:48:8 | cd | cd | -| regexp.rb:48:10:48:10 | e | e | -| regexp.rb:49:5:49:5 | : | : | -| regexp.rb:49:8:49:9 | \\w | w | -| regexp.rb:52:8:52:9 | \\w | w | -| regexp.rb:53:9:53:9 | f | f | -| regexp.rb:53:10:53:10 | o | o | -| regexp.rb:56:3:56:3 | a | a | -| regexp.rb:56:6:56:6 | b | b | -| regexp.rb:57:9:57:9 | q | q | -| regexp.rb:57:12:57:13 | \\s | s | -| regexp.rb:63:3:63:3 | a | a | -| regexp.rb:63:5:63:5 | f | f | -| regexp.rb:72:3:72:3 | A | A | -| regexp.rb:72:5:72:5 | F | F | -| regexp.rb:72:15:72:15 | a | a | -| regexp.rb:72:17:72:17 | f | f | -| regexp.rb:79:2:79:4 | abc | abc | -| regexp.rb:82:2:82:7 | \\u{987 | \u0987 | +| regexp.cpp:18:19:18:21 | abc | abc | +| regexp.cpp:21:20:21:20 | a | a | +| regexp.cpp:21:22:21:22 | b | b | +| regexp.cpp:21:24:21:24 | c | c | +| regexp.cpp:21:26:21:26 | d | d | +| regexp.cpp:22:20:22:20 | a | a | +| regexp.cpp:23:20:23:20 | a | a | +| regexp.cpp:24:20:24:20 | a | a | +| regexp.cpp:25:20:25:20 | a | a | +| regexp.cpp:28:19:28:21 | foo | foo | +| regexp.cpp:28:23:28:25 | bar | bar | +| regexp.cpp:31:20:31:20 | a | a | +| regexp.cpp:31:21:31:21 | b | b | +| regexp.cpp:31:22:31:22 | c | c | +| regexp.cpp:32:20:32:20 | a | a | +| regexp.cpp:32:22:32:22 | f | f | +| regexp.cpp:32:23:32:23 | A | A | +| regexp.cpp:32:25:32:25 | F | F | +| regexp.cpp:32:26:32:26 | 0 | 0 | +| regexp.cpp:32:28:32:28 | 9 | 9 | +| regexp.cpp:32:29:32:29 | _ | _ | +| regexp.cpp:33:22:33:22 | + | + | +| regexp.cpp:33:23:33:23 | - | - | +| regexp.cpp:33:26:33:27 | \\d | d | +| regexp.cpp:34:20:34:21 | \\w | w | +| regexp.cpp:35:19:35:20 | \\[ | [ | +| regexp.cpp:35:21:35:22 | \\] | ] | +| regexp.cpp:35:24:35:24 | 1 | 1 | +| regexp.cpp:35:25:35:25 | 2 | 2 | +| regexp.cpp:35:26:35:26 | 3 | 3 | +| regexp.cpp:36:21:36:21 | A | A | +| regexp.cpp:36:23:36:23 | Z | Z | +| regexp.cpp:37:20:37:20 | ] | ] | +| regexp.cpp:38:21:38:21 | ] | ] | +| regexp.cpp:39:21:39:21 | - | - | +| regexp.cpp:40:21:40:21 | \| | \| | +| regexp.cpp:43:23:43:23 | [ | [ | +| regexp.cpp:43:24:43:24 | a | a | +| regexp.cpp:43:26:43:26 | f | f | +| regexp.cpp:43:28:43:31 | A-F] | A-F] | +| regexp.cpp:48:21:48:22 | \\w | w | +| regexp.cpp:48:24:48:25 | \\W | W | +| regexp.cpp:49:21:49:22 | \\s | s | +| regexp.cpp:49:23:49:24 | \\S | S | +| regexp.cpp:50:21:50:22 | \\d | d | +| regexp.cpp:50:23:50:24 | \\D | D | +| regexp.cpp:51:21:51:22 | \\h | h | +| regexp.cpp:51:23:51:24 | \\H | H | +| regexp.cpp:52:21:52:22 | \\n | \n | +| regexp.cpp:52:23:52:24 | \\r | \r | +| regexp.cpp:52:25:52:26 | \\t | \t | +| regexp.cpp:55:22:55:24 | abc | abc | +| regexp.cpp:56:22:56:23 | !a | !a | +| regexp.cpp:59:21:59:23 | foo | foo | +| regexp.cpp:59:26:59:28 | bar | bar | +| regexp.cpp:60:20:60:21 | fo | fo | +| regexp.cpp:60:23:60:23 | o | o | +| regexp.cpp:60:25:60:25 | b | b | +| regexp.cpp:60:27:60:28 | ar | ar | +| regexp.cpp:61:21:61:21 | a | a | +| regexp.cpp:61:23:61:23 | b | b | +| regexp.cpp:61:25:61:26 | cd | cd | +| regexp.cpp:61:28:61:28 | e | e | +| regexp.cpp:62:23:62:23 | : | : | +| regexp.cpp:62:26:62:27 | \\w | w | +| regexp.cpp:65:25:65:26 | \\w | w | +| regexp.cpp:66:26:66:26 | f | f | +| regexp.cpp:66:27:66:27 | o | o | +| regexp.cpp:69:22:69:22 | a | a | +| regexp.cpp:69:25:69:25 | b | b | +| regexp.cpp:70:28:70:28 | q | q | +| regexp.cpp:70:31:70:32 | \\s | s | +| regexp.cpp:76:22:76:22 | a | a | +| regexp.cpp:76:24:76:24 | f | f | +| regexp.cpp:85:23:85:23 | A | A | +| regexp.cpp:85:25:85:25 | F | F | +| regexp.cpp:85:35:85:35 | a | a | +| regexp.cpp:85:37:85:37 | f | f | +| regexp.cpp:91:19:91:24 | \\u{987 | \u0987 | diff --git a/cpp/ql/test/library-tests/regex/regexp.ql b/cpp/ql/test/library-tests/regex/regexp.ql index e167cb1a7bb0..a29717199e24 100644 --- a/cpp/ql/test/library-tests/regex/regexp.ql +++ b/cpp/ql/test/library-tests/regex/regexp.ql @@ -1,5 +1,11 @@ +import semmle.code.cpp.regex.internal.ParseRegExp import semmle.code.cpp.regex.RegexTreeView +// Stop gap for missing flow configs +class RegExpTest extends RegExp { + RegExpTest() { any() } +} + query predicate groupName(RegExpGroup g, string name) { name = g.getName() } query predicate groupNumber(RegExpGroup g, int number) { number = g.getNumber() } From d5d73f675024b88f67c704cc044a6f73c60401ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:01:18 +0000 Subject: [PATCH 04/37] C++: Add location tests for all literal prefixes --- .../library-tests/regex/locations.expected | 55 ++++++++ cpp/ql/test/library-tests/regex/locations.ql | 11 ++ .../test/library-tests/regex/parse.expected | 132 ++++++++++++++++++ cpp/ql/test/library-tests/regex/regexp.cpp | 16 ++- .../test/library-tests/regex/regexp.expected | 84 +++++++++++ 5 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 cpp/ql/test/library-tests/regex/locations.expected create mode 100644 cpp/ql/test/library-tests/regex/locations.ql diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected new file mode 100644 index 000000000000..ea409e1dab94 --- /dev/null +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -0,0 +1,55 @@ +| regexp.cpp:18:19:18:21 | abc | regexp.cpp | 18 | 19 | 18 | 21 | +| regexp.cpp:21:20:21:26 | a*b+c?d | regexp.cpp | 21 | 20 | 21 | 26 | +| regexp.cpp:22:20:22:25 | a{4,8} | regexp.cpp | 22 | 20 | 22 | 25 | +| regexp.cpp:23:20:23:24 | a{,8} | regexp.cpp | 23 | 20 | 23 | 24 | +| regexp.cpp:24:20:24:24 | a{3,} | regexp.cpp | 24 | 20 | 24 | 24 | +| regexp.cpp:25:20:25:23 | a{7} | regexp.cpp | 25 | 20 | 25 | 23 | +| regexp.cpp:28:19:28:25 | foo\|bar | regexp.cpp | 28 | 19 | 28 | 25 | +| regexp.cpp:31:19:31:23 | [abc] | regexp.cpp | 31 | 19 | 31 | 23 | +| regexp.cpp:32:19:32:30 | [a-fA-F0-9_] | regexp.cpp | 32 | 19 | 32 | 30 | +| regexp.cpp:33:19:33:28 | \\A[+-]?\\d+ | regexp.cpp | 33 | 19 | 33 | 28 | +| regexp.cpp:34:19:34:23 | [\\w]+ | regexp.cpp | 34 | 19 | 34 | 23 | +| regexp.cpp:35:19:35:27 | \\[\\][123] | regexp.cpp | 35 | 19 | 35 | 27 | +| regexp.cpp:36:19:36:24 | [^A-Z] | regexp.cpp | 36 | 19 | 36 | 24 | +| regexp.cpp:37:19:37:21 | []] | regexp.cpp | 37 | 19 | 37 | 21 | +| regexp.cpp:38:19:38:22 | [^]] | regexp.cpp | 38 | 19 | 38 | 22 | +| regexp.cpp:39:19:39:22 | [^-] | regexp.cpp | 39 | 19 | 39 | 22 | +| regexp.cpp:40:20:40:22 | [\|] | regexp.cpp | 40 | 20 | 40 | 22 | +| regexp.cpp:43:22:43:31 | [[a-f]A-F] | regexp.cpp | 43 | 22 | 43 | 31 | +| regexp.cpp:46:21:46:22 | .* | regexp.cpp | 46 | 21 | 46 | 22 | +| regexp.cpp:47:22:47:23 | .* | regexp.cpp | 47 | 22 | 47 | 23 | +| regexp.cpp:48:21:48:25 | \\w+\\W | regexp.cpp | 48 | 21 | 48 | 25 | +| regexp.cpp:49:21:49:24 | \\s\\S | regexp.cpp | 49 | 21 | 49 | 24 | +| regexp.cpp:50:21:50:24 | \\d\\D | regexp.cpp | 50 | 21 | 50 | 24 | +| regexp.cpp:51:21:51:24 | \\h\\H | regexp.cpp | 51 | 21 | 51 | 24 | +| regexp.cpp:52:21:52:26 | \\n\\r\\t | regexp.cpp | 52 | 21 | 52 | 26 | +| regexp.cpp:55:20:55:24 | \\Gabc | regexp.cpp | 55 | 20 | 55 | 24 | +| regexp.cpp:56:20:56:25 | \\b!a\\B | regexp.cpp | 56 | 20 | 56 | 25 | +| regexp.cpp:59:20:59:28 | (foo)*bar | regexp.cpp | 59 | 20 | 59 | 28 | +| regexp.cpp:60:20:60:28 | fo(o\|b)ar | regexp.cpp | 60 | 20 | 60 | 28 | +| regexp.cpp:61:20:61:28 | (a\|b\|cd)e | regexp.cpp | 61 | 20 | 61 | 28 | +| regexp.cpp:62:20:62:27 | (?::+)\\w | regexp.cpp | 62 | 20 | 62 | 27 | +| regexp.cpp:65:19:65:28 | (?\\w+) | regexp.cpp | 65 | 19 | 65 | 28 | +| regexp.cpp:66:19:66:29 | (?'foo'fo+) | regexp.cpp | 66 | 19 | 66 | 29 | +| regexp.cpp:69:21:69:28 | (a+)b+\\1 | regexp.cpp | 69 | 21 | 69 | 28 | +| regexp.cpp:70:21:70:41 | (?q+)\\s+\\k+ | regexp.cpp | 70 | 21 | 70 | 41 | +| regexp.cpp:73:21:73:29 | \\p{Word}* | regexp.cpp | 73 | 21 | 73 | 29 | +| regexp.cpp:74:21:74:30 | \\P{Digit}+ | regexp.cpp | 74 | 21 | 74 | 30 | +| regexp.cpp:75:21:75:35 | \\p{^Alnum}{2,3} | regexp.cpp | 75 | 21 | 75 | 35 | +| regexp.cpp:76:21:76:35 | [a-f\\p{Digit}]+ | regexp.cpp | 76 | 21 | 76 | 35 | +| regexp.cpp:79:22:79:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 79 | 22 | 79 | 43 | +| regexp.cpp:82:22:82:41 | [[:alpha:][:digit:]] | regexp.cpp | 82 | 22 | 82 | 41 | +| regexp.cpp:85:22:85:38 | [A-F[:digit:]a-f] | regexp.cpp | 85 | 22 | 85 | 38 | +| regexp.cpp:88:22:88:30 | [:digit:] | regexp.cpp | 88 | 22 | 88 | 30 | +| regexp.cpp:94:25:94:28 | a\\nb | regexp.cpp | 94 | 25 | 94 | 28 | +| regexp.cpp:95:36:95:39 | a\\nc | regexp.cpp | 95 | 36 | 95 | 39 | +| regexp.cpp:96:37:96:40 | a\\nc | regexp.cpp | 96 | 37 | 96 | 40 | +| regexp.cpp:97:37:97:40 | a\\nc | regexp.cpp | 97 | 37 | 97 | 40 | +| regexp.cpp:98:37:98:40 | a\\nc | regexp.cpp | 98 | 37 | 98 | 40 | +| regexp.cpp:99:21:99:24 | a\\nc | regexp.cpp | 99 | 21 | 99 | 24 | +| regexp.cpp:100:37:100:40 | a\\nc | regexp.cpp | 100 | 37 | 100 | 40 | +| regexp.cpp:101:38:101:41 | a\\nc | regexp.cpp | 101 | 38 | 101 | 41 | +| regexp.cpp:102:38:102:41 | a\\nc | regexp.cpp | 102 | 38 | 102 | 41 | +| regexp.cpp:103:38:103:41 | a\\nc | regexp.cpp | 103 | 38 | 103 | 41 | +| regexp.cpp:104:22:104:25 | a\\nc | regexp.cpp | 104 | 22 | 104 | 25 | +| regexp.cpp:105:24:105:27 | a\\nc | regexp.cpp | 105 | 24 | 105 | 27 | diff --git a/cpp/ql/test/library-tests/regex/locations.ql b/cpp/ql/test/library-tests/regex/locations.ql new file mode 100644 index 000000000000..0b6e23f2b79a --- /dev/null +++ b/cpp/ql/test/library-tests/regex/locations.ql @@ -0,0 +1,11 @@ +import semmle.code.cpp.regex.internal.ParseRegExp +import semmle.code.cpp.regex.RegexTreeView + +// Stop gap for missing flow configs +class RegExpTest extends RegExp { + RegExpTest() { any() } +} + +from RegExpTerm t, string file, int sl, int sc, int el, int ec +where t.isRootTerm() and t.hasLocationInfo(file, sl, sc, el, ec) +select t, file, sl, sc, el, ec diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 13d74f0e5616..ebbd892e145f 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -485,3 +485,135 @@ regexp.cpp: # 88| [RegExpNamedCharacterProperty] [:digit:] # 91| [RegExpConstant, RegExpEscape] \u{987 + +# 94| [RegExpConstant, RegExpNormalChar] a + +# 94| [RegExpSequence] a\nb +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] b + +# 94| [RegExpConstant, RegExpEscape] \n + +# 94| [RegExpConstant, RegExpNormalChar] b + +# 95| [RegExpConstant, RegExpNormalChar] a + +# 95| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 95| [RegExpConstant, RegExpEscape] \n + +# 95| [RegExpConstant, RegExpNormalChar] c + +# 96| [RegExpConstant, RegExpNormalChar] a + +# 96| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 96| [RegExpConstant, RegExpEscape] \n + +# 96| [RegExpConstant, RegExpNormalChar] c + +# 97| [RegExpConstant, RegExpNormalChar] a + +# 97| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 97| [RegExpConstant, RegExpEscape] \n + +# 97| [RegExpConstant, RegExpNormalChar] c + +# 98| [RegExpConstant, RegExpNormalChar] a + +# 98| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 98| [RegExpConstant, RegExpEscape] \n + +# 98| [RegExpConstant, RegExpNormalChar] c + +# 99| [RegExpConstant, RegExpNormalChar] a + +# 99| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 99| [RegExpConstant, RegExpEscape] \n + +# 99| [RegExpConstant, RegExpNormalChar] c + +# 100| [RegExpConstant, RegExpNormalChar] a + +# 100| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 100| [RegExpConstant, RegExpEscape] \n + +# 100| [RegExpConstant, RegExpNormalChar] c + +# 101| [RegExpConstant, RegExpNormalChar] a + +# 101| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 101| [RegExpConstant, RegExpEscape] \n + +# 101| [RegExpConstant, RegExpNormalChar] c + +# 102| [RegExpConstant, RegExpNormalChar] a + +# 102| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 102| [RegExpConstant, RegExpEscape] \n + +# 102| [RegExpConstant, RegExpNormalChar] c + +# 103| [RegExpConstant, RegExpNormalChar] a + +# 103| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 103| [RegExpConstant, RegExpEscape] \n + +# 103| [RegExpConstant, RegExpNormalChar] c + +# 104| [RegExpConstant, RegExpNormalChar] a + +# 104| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 104| [RegExpConstant, RegExpEscape] \n + +# 104| [RegExpConstant, RegExpNormalChar] c + +# 105| [RegExpConstant, RegExpNormalChar] a + +# 105| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 105| [RegExpConstant, RegExpEscape] \n + +# 105| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 944cf3aa2dbb..6561a265e22e 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -1,4 +1,4 @@ -// semmle-extractor-options: -std=c++17 +// semmle-extractor-options: -std=c++20 namespace std { template @@ -89,3 +89,17 @@ std::regex r_posix4("[:digit:]"); // unicode std::regex r_uni("\\u{9879}"); + +// String literal spellings for location tests +std::regex r_loc_plain("a\\nb"); +std::basic_regex r_loc_L(L"a\\nc"); +std::basic_regex r_loc_u8(u8"a\\nc"); +std::basic_regex r_loc_u(u"a\\nc"); +std::basic_regex r_loc_U(U"a\\nc"); +std::regex r_loc_R(R"(a\nc)"); +std::basic_regex r_loc_LR(LR"(a\nc)"); +std::basic_regex r_loc_u8R(u8R"(a\nc)"); +std::basic_regex r_loc_uR(uR"(a\nc)"); +std::basic_regex r_loc_UR(UR"(a\nc)"); +std::regex r_loc_Rx(R"x(a\nc)x"); +std::regex r_loc_Rfoo(R"foo(a\nc)foo"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 6b2409e7c7c0..120815749f38 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -186,6 +186,54 @@ term | regexp.cpp:85:37:85:37 | f | RegExpConstant,RegExpNormalChar | | regexp.cpp:88:22:88:30 | [:digit:] | RegExpNamedCharacterProperty | | regexp.cpp:91:19:91:24 | \\u{987 | RegExpConstant,RegExpEscape | +| regexp.cpp:94:25:94:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:94:25:94:28 | a\\nb | RegExpSequence | +| regexp.cpp:94:26:94:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:94:28:94:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:36:95:36 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:36:95:39 | a\\nc | RegExpSequence | +| regexp.cpp:95:37:95:38 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:95:39:95:39 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:37:96:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:37:96:40 | a\\nc | RegExpSequence | +| regexp.cpp:96:38:96:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:96:40:96:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:37:97:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:37:97:40 | a\\nc | RegExpSequence | +| regexp.cpp:97:38:97:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:97:40:97:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:37:98:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:37:98:40 | a\\nc | RegExpSequence | +| regexp.cpp:98:38:98:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:98:40:98:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:21:99:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:21:99:24 | a\\nc | RegExpSequence | +| regexp.cpp:99:22:99:23 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:99:24:99:24 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:37:100:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:37:100:40 | a\\nc | RegExpSequence | +| regexp.cpp:100:38:100:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:100:40:100:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:38:101:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:38:101:41 | a\\nc | RegExpSequence | +| regexp.cpp:101:39:101:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:101:41:101:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:38:102:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:38:102:41 | a\\nc | RegExpSequence | +| regexp.cpp:102:39:102:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:102:41:102:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:38:103:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:38:103:41 | a\\nc | RegExpSequence | +| regexp.cpp:103:39:103:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:103:41:103:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:22:104:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:22:104:25 | a\\nc | RegExpSequence | +| regexp.cpp:104:23:104:24 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:104:25:104:25 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:24:105:24 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:24:105:27 | a\\nc | RegExpSequence | +| regexp.cpp:105:25:105:26 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:105:27:105:27 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -266,3 +314,39 @@ regExpNormalCharValue | regexp.cpp:85:35:85:35 | a | a | | regexp.cpp:85:37:85:37 | f | f | | regexp.cpp:91:19:91:24 | \\u{987 | \u0987 | +| regexp.cpp:94:25:94:25 | a | a | +| regexp.cpp:94:26:94:27 | \\n | \n | +| regexp.cpp:94:28:94:28 | b | b | +| regexp.cpp:95:36:95:36 | a | a | +| regexp.cpp:95:37:95:38 | \\n | \n | +| regexp.cpp:95:39:95:39 | c | c | +| regexp.cpp:96:37:96:37 | a | a | +| regexp.cpp:96:38:96:39 | \\n | \n | +| regexp.cpp:96:40:96:40 | c | c | +| regexp.cpp:97:37:97:37 | a | a | +| regexp.cpp:97:38:97:39 | \\n | \n | +| regexp.cpp:97:40:97:40 | c | c | +| regexp.cpp:98:37:98:37 | a | a | +| regexp.cpp:98:38:98:39 | \\n | \n | +| regexp.cpp:98:40:98:40 | c | c | +| regexp.cpp:99:21:99:21 | a | a | +| regexp.cpp:99:22:99:23 | \\n | \n | +| regexp.cpp:99:24:99:24 | c | c | +| regexp.cpp:100:37:100:37 | a | a | +| regexp.cpp:100:38:100:39 | \\n | \n | +| regexp.cpp:100:40:100:40 | c | c | +| regexp.cpp:101:38:101:38 | a | a | +| regexp.cpp:101:39:101:40 | \\n | \n | +| regexp.cpp:101:41:101:41 | c | c | +| regexp.cpp:102:38:102:38 | a | a | +| regexp.cpp:102:39:102:40 | \\n | \n | +| regexp.cpp:102:41:102:41 | c | c | +| regexp.cpp:103:38:103:38 | a | a | +| regexp.cpp:103:39:103:40 | \\n | \n | +| regexp.cpp:103:41:103:41 | c | c | +| regexp.cpp:104:22:104:22 | a | a | +| regexp.cpp:104:23:104:24 | \\n | \n | +| regexp.cpp:104:25:104:25 | c | c | +| regexp.cpp:105:24:105:24 | a | a | +| regexp.cpp:105:25:105:26 | \\n | \n | +| regexp.cpp:105:27:105:27 | c | c | From 322c1c02f3d73d284fd283c5f507c57abc7bb47a Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 23 Jul 2026 15:07:45 +0200 Subject: [PATCH 05/37] C++: Add `RawStringLiteral` class --- cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll index 31e2b5135b42..7238ea2a35ed 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll @@ -133,6 +133,19 @@ class StringLiteral extends TextLiteral { override string getAPrimaryQlClass() { result = "StringLiteral" } } +/** + * A C++ raw string literal. For example: + * ``` + * const char *s1 = R"(abcdef)"; + * const wchar_t *s2 = LR"x(123456)x"; + * ``` + */ +class RawStringLiteral extends StringLiteral { + RawStringLiteral() { this.getValueText().regexpMatch("[^\"]*R\".*\\(.*") } + + override string getAPrimaryQlClass() { result = "RawStringLiteral" } +} + /** * An octal literal. For example: * ``` From fd929e2d1fa3190b21d0d1987e6eba6be0f44bc3 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 23 Jul 2026 15:08:48 +0200 Subject: [PATCH 06/37] C++: Fix regex starting location Strings can have a prefix in C++, which affects the location. --- .../semmle/code/cpp/regex/RegexTreeView.qll | 27 ++- .../library-tests/regex/locations.expected | 22 +-- .../test/library-tests/regex/regexp.expected | 154 +++++++++--------- 3 files changed, 109 insertions(+), 94 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index a0a116f2fe01..23b621e66f17 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -11,9 +11,7 @@ import Impl as RegexTreeView import Impl /** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ -RegExpTerm getParsedRegExp(StringLiteral re) { - result.getRegExp() = re and result.isRootTerm() -} +RegExpTerm getParsedRegExp(StringLiteral re) { result.getRegExp() = re and result.isRootTerm() } /** * An element containing a regular expression term, that is, either @@ -211,14 +209,31 @@ private module Impl implements RegexTreeViewSig { */ Location getLocation() { result = re.getLocation() } + /** + * Gets the number of source characters from the start of the string literal + * to the first content character (i.e., past the opening delimiter). + */ + private int regexpContentOffset() { + exists(string vt | vt = re.getValueText() | + // Find the '(' that opens the raw content. + re instanceof RawStringLiteral and + result = 1 + min(int i | vt.charAt(i) = "(") + or + // Find the opening '"'. + not re instanceof RawStringLiteral and + result = 1 + min(int i | vt.charAt(i) = "\"") + ) + } + /** Holds if this term is found at the specified location offsets. */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - exists(int re_start | + exists(int re_start, int offset | re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, _) and - startcolumn = re_start + 1 + start and - endcolumn = re_start + 1 + end - 1 + offset = this.regexpContentOffset() and + startcolumn = re_start + offset + start and + endcolumn = re_start + offset + end - 1 ) } diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index ea409e1dab94..d3cfc7d61c73 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -42,14 +42,14 @@ | regexp.cpp:85:22:85:38 | [A-F[:digit:]a-f] | regexp.cpp | 85 | 22 | 85 | 38 | | regexp.cpp:88:22:88:30 | [:digit:] | regexp.cpp | 88 | 22 | 88 | 30 | | regexp.cpp:94:25:94:28 | a\\nb | regexp.cpp | 94 | 25 | 94 | 28 | -| regexp.cpp:95:36:95:39 | a\\nc | regexp.cpp | 95 | 36 | 95 | 39 | -| regexp.cpp:96:37:96:40 | a\\nc | regexp.cpp | 96 | 37 | 96 | 40 | -| regexp.cpp:97:37:97:40 | a\\nc | regexp.cpp | 97 | 37 | 97 | 40 | -| regexp.cpp:98:37:98:40 | a\\nc | regexp.cpp | 98 | 37 | 98 | 40 | -| regexp.cpp:99:21:99:24 | a\\nc | regexp.cpp | 99 | 21 | 99 | 24 | -| regexp.cpp:100:37:100:40 | a\\nc | regexp.cpp | 100 | 37 | 100 | 40 | -| regexp.cpp:101:38:101:41 | a\\nc | regexp.cpp | 101 | 38 | 101 | 41 | -| regexp.cpp:102:38:102:41 | a\\nc | regexp.cpp | 102 | 38 | 102 | 41 | -| regexp.cpp:103:38:103:41 | a\\nc | regexp.cpp | 103 | 38 | 103 | 41 | -| regexp.cpp:104:22:104:25 | a\\nc | regexp.cpp | 104 | 22 | 104 | 25 | -| regexp.cpp:105:24:105:27 | a\\nc | regexp.cpp | 105 | 24 | 105 | 27 | +| regexp.cpp:95:37:95:40 | a\\nc | regexp.cpp | 95 | 37 | 95 | 40 | +| regexp.cpp:96:39:96:42 | a\\nc | regexp.cpp | 96 | 39 | 96 | 42 | +| regexp.cpp:97:38:97:41 | a\\nc | regexp.cpp | 97 | 38 | 97 | 41 | +| regexp.cpp:98:38:98:41 | a\\nc | regexp.cpp | 98 | 38 | 98 | 41 | +| regexp.cpp:99:23:99:26 | a\\nc | regexp.cpp | 99 | 23 | 99 | 26 | +| regexp.cpp:100:40:100:43 | a\\nc | regexp.cpp | 100 | 40 | 100 | 43 | +| regexp.cpp:101:42:101:45 | a\\nc | regexp.cpp | 101 | 42 | 101 | 45 | +| regexp.cpp:102:41:102:44 | a\\nc | regexp.cpp | 102 | 41 | 102 | 44 | +| regexp.cpp:103:41:103:44 | a\\nc | regexp.cpp | 103 | 41 | 103 | 44 | +| regexp.cpp:104:25:104:28 | a\\nc | regexp.cpp | 104 | 25 | 104 | 28 | +| regexp.cpp:105:29:105:32 | a\\nc | regexp.cpp | 105 | 29 | 105 | 32 | diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 120815749f38..e11a7cfea99e 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -190,50 +190,50 @@ term | regexp.cpp:94:25:94:28 | a\\nb | RegExpSequence | | regexp.cpp:94:26:94:27 | \\n | RegExpConstant,RegExpEscape | | regexp.cpp:94:28:94:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:36:95:36 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:36:95:39 | a\\nc | RegExpSequence | -| regexp.cpp:95:37:95:38 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:95:39:95:39 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:37:96:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:37:96:40 | a\\nc | RegExpSequence | -| regexp.cpp:96:38:96:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:96:40:96:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:37:97:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:37:97:40 | a\\nc | RegExpSequence | -| regexp.cpp:97:38:97:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:97:40:97:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:37:98:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:37:98:40 | a\\nc | RegExpSequence | -| regexp.cpp:98:38:98:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:98:40:98:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:21:99:21 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:21:99:24 | a\\nc | RegExpSequence | -| regexp.cpp:99:22:99:23 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:99:24:99:24 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:37:100:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:37:100:40 | a\\nc | RegExpSequence | -| regexp.cpp:100:38:100:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:100:40:100:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:38:101:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:38:101:41 | a\\nc | RegExpSequence | -| regexp.cpp:101:39:101:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:101:41:101:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:38:102:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:38:102:41 | a\\nc | RegExpSequence | -| regexp.cpp:102:39:102:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:102:41:102:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:38:103:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:38:103:41 | a\\nc | RegExpSequence | -| regexp.cpp:103:39:103:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:103:41:103:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:22:104:22 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:22:104:25 | a\\nc | RegExpSequence | -| regexp.cpp:104:23:104:24 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:104:25:104:25 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:24:105:24 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:24:105:27 | a\\nc | RegExpSequence | -| regexp.cpp:105:25:105:26 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:105:27:105:27 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:37:95:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:37:95:40 | a\\nc | RegExpSequence | +| regexp.cpp:95:38:95:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:95:40:95:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:39:96:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:39:96:42 | a\\nc | RegExpSequence | +| regexp.cpp:96:40:96:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:96:42:96:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:38:97:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:38:97:41 | a\\nc | RegExpSequence | +| regexp.cpp:97:39:97:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:97:41:97:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:38:98:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:38:98:41 | a\\nc | RegExpSequence | +| regexp.cpp:98:39:98:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:98:41:98:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:23:99:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:23:99:26 | a\\nc | RegExpSequence | +| regexp.cpp:99:24:99:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:99:26:99:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:40:100:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:40:100:43 | a\\nc | RegExpSequence | +| regexp.cpp:100:41:100:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:100:43:100:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:42:101:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:42:101:45 | a\\nc | RegExpSequence | +| regexp.cpp:101:43:101:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:101:45:101:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:41:102:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:41:102:44 | a\\nc | RegExpSequence | +| regexp.cpp:102:42:102:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:102:44:102:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:41:103:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:41:103:44 | a\\nc | RegExpSequence | +| regexp.cpp:103:42:103:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:103:44:103:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:25:104:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:25:104:28 | a\\nc | RegExpSequence | +| regexp.cpp:104:26:104:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:104:28:104:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:29:105:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:29:105:32 | a\\nc | RegExpSequence | +| regexp.cpp:105:30:105:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:105:32:105:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -317,36 +317,36 @@ regExpNormalCharValue | regexp.cpp:94:25:94:25 | a | a | | regexp.cpp:94:26:94:27 | \\n | \n | | regexp.cpp:94:28:94:28 | b | b | -| regexp.cpp:95:36:95:36 | a | a | -| regexp.cpp:95:37:95:38 | \\n | \n | -| regexp.cpp:95:39:95:39 | c | c | -| regexp.cpp:96:37:96:37 | a | a | -| regexp.cpp:96:38:96:39 | \\n | \n | -| regexp.cpp:96:40:96:40 | c | c | -| regexp.cpp:97:37:97:37 | a | a | -| regexp.cpp:97:38:97:39 | \\n | \n | -| regexp.cpp:97:40:97:40 | c | c | -| regexp.cpp:98:37:98:37 | a | a | -| regexp.cpp:98:38:98:39 | \\n | \n | -| regexp.cpp:98:40:98:40 | c | c | -| regexp.cpp:99:21:99:21 | a | a | -| regexp.cpp:99:22:99:23 | \\n | \n | -| regexp.cpp:99:24:99:24 | c | c | -| regexp.cpp:100:37:100:37 | a | a | -| regexp.cpp:100:38:100:39 | \\n | \n | -| regexp.cpp:100:40:100:40 | c | c | -| regexp.cpp:101:38:101:38 | a | a | -| regexp.cpp:101:39:101:40 | \\n | \n | -| regexp.cpp:101:41:101:41 | c | c | -| regexp.cpp:102:38:102:38 | a | a | -| regexp.cpp:102:39:102:40 | \\n | \n | -| regexp.cpp:102:41:102:41 | c | c | -| regexp.cpp:103:38:103:38 | a | a | -| regexp.cpp:103:39:103:40 | \\n | \n | -| regexp.cpp:103:41:103:41 | c | c | -| regexp.cpp:104:22:104:22 | a | a | -| regexp.cpp:104:23:104:24 | \\n | \n | -| regexp.cpp:104:25:104:25 | c | c | -| regexp.cpp:105:24:105:24 | a | a | -| regexp.cpp:105:25:105:26 | \\n | \n | -| regexp.cpp:105:27:105:27 | c | c | +| regexp.cpp:95:37:95:37 | a | a | +| regexp.cpp:95:38:95:39 | \\n | \n | +| regexp.cpp:95:40:95:40 | c | c | +| regexp.cpp:96:39:96:39 | a | a | +| regexp.cpp:96:40:96:41 | \\n | \n | +| regexp.cpp:96:42:96:42 | c | c | +| regexp.cpp:97:38:97:38 | a | a | +| regexp.cpp:97:39:97:40 | \\n | \n | +| regexp.cpp:97:41:97:41 | c | c | +| regexp.cpp:98:38:98:38 | a | a | +| regexp.cpp:98:39:98:40 | \\n | \n | +| regexp.cpp:98:41:98:41 | c | c | +| regexp.cpp:99:23:99:23 | a | a | +| regexp.cpp:99:24:99:25 | \\n | \n | +| regexp.cpp:99:26:99:26 | c | c | +| regexp.cpp:100:40:100:40 | a | a | +| regexp.cpp:100:41:100:42 | \\n | \n | +| regexp.cpp:100:43:100:43 | c | c | +| regexp.cpp:101:42:101:42 | a | a | +| regexp.cpp:101:43:101:44 | \\n | \n | +| regexp.cpp:101:45:101:45 | c | c | +| regexp.cpp:102:41:102:41 | a | a | +| regexp.cpp:102:42:102:43 | \\n | \n | +| regexp.cpp:102:44:102:44 | c | c | +| regexp.cpp:103:41:103:41 | a | a | +| regexp.cpp:103:42:103:43 | \\n | \n | +| regexp.cpp:103:44:103:44 | c | c | +| regexp.cpp:104:25:104:25 | a | a | +| regexp.cpp:104:26:104:27 | \\n | \n | +| regexp.cpp:104:28:104:28 | c | c | +| regexp.cpp:105:29:105:29 | a | a | +| regexp.cpp:105:30:105:31 | \\n | \n | +| regexp.cpp:105:32:105:32 | c | c | From eb53e5655aeea71402fcfaea2caaf02b07799c56 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:04:43 +0000 Subject: [PATCH 07/37] C++: Remove Ruby-only \A/\Z/\z/\G anchors from regex grammar --- .../semmle/code/cpp/regex/RegexTreeView.qll | 12 +++---- .../code/cpp/regex/internal/ParseRegExp.qll | 10 +++--- .../library-tests/regex/locations.expected | 3 -- .../test/library-tests/regex/parse.expected | 36 ------------------- cpp/ql/test/library-tests/regex/regexp.cpp | 6 ++-- .../test/library-tests/regex/regexp.expected | 17 --------- 6 files changed, 14 insertions(+), 70 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 23b621e66f17..1c42fb80709e 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -936,17 +936,17 @@ private module Impl implements RegexTreeViewSig { * Example: * * ``` - * \A + * ^ * ``` */ class RegExpAnchor extends RegExpSpecialChar { - RegExpAnchor() { this.getChar() = ["^", "$", "\\A", "\\Z", "\\z"] } + RegExpAnchor() { this.getChar() = ["^", "$"] } override string getAPrimaryQlClass() { result = "RegExpAnchor" } } /** - * A dollar assertion `$` or `\Z` matching the end of a line. + * A dollar assertion `$` matching the end of a line. * * Example: * @@ -955,7 +955,7 @@ private module Impl implements RegexTreeViewSig { * ``` */ class RegExpDollar extends RegExpAnchor { - RegExpDollar() { this.getChar() = ["$", "\\Z", "\\z"] } + RegExpDollar() { this.getChar() = "$" } override string getAPrimaryQlClass() { result = "RegExpDollar" } @@ -963,7 +963,7 @@ private module Impl implements RegexTreeViewSig { } /** - * A caret assertion `^` or `\A` matching the beginning of a line. + * A caret assertion `^` matching the beginning of a line. * * Example: * @@ -972,7 +972,7 @@ private module Impl implements RegexTreeViewSig { * ``` */ class RegExpCaret extends RegExpAnchor { - RegExpCaret() { this.getChar() = ["^", "\\A"] } + RegExpCaret() { this.getChar() = "^" } override string getAPrimaryQlClass() { result = "RegExpCaret" } diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index d9163bb3af05..a7d0d38919a1 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -476,7 +476,7 @@ abstract class RegExp extends StringLiteral { end = start + 2 and this.escapingChar(start) and char = this.getText().substring(start, end) and - char = ["\\A", "\\Z", "\\z", "\\G", "\\b", "\\B"] + char = ["\\b", "\\B"] ) } @@ -958,8 +958,8 @@ abstract class RegExp extends StringLiteral { or this.qualifiedItem(x, start, true, _) or - // ^ and \A match the start of the string - this.specialCharacter(x, start, ["^", "\\A"]) + // ^ matches the start of the string + this.specialCharacter(x, start, "^") ) or exists(int y | this.firstPart(start, y) | @@ -984,8 +984,8 @@ abstract class RegExp extends StringLiteral { or this.qualifiedItem(end, y, true, _) or - // $, \Z, and \z match the end of the string. - this.specialCharacter(end, y, ["$", "\\Z", "\\z"]) + // $ matches the end of the string. + this.specialCharacter(end, y, "$") ) or this.lastPart(_, end) and diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index d3cfc7d61c73..d7912bdf1677 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -7,7 +7,6 @@ | regexp.cpp:28:19:28:25 | foo\|bar | regexp.cpp | 28 | 19 | 28 | 25 | | regexp.cpp:31:19:31:23 | [abc] | regexp.cpp | 31 | 19 | 31 | 23 | | regexp.cpp:32:19:32:30 | [a-fA-F0-9_] | regexp.cpp | 32 | 19 | 32 | 30 | -| regexp.cpp:33:19:33:28 | \\A[+-]?\\d+ | regexp.cpp | 33 | 19 | 33 | 28 | | regexp.cpp:34:19:34:23 | [\\w]+ | regexp.cpp | 34 | 19 | 34 | 23 | | regexp.cpp:35:19:35:27 | \\[\\][123] | regexp.cpp | 35 | 19 | 35 | 27 | | regexp.cpp:36:19:36:24 | [^A-Z] | regexp.cpp | 36 | 19 | 36 | 24 | @@ -17,13 +16,11 @@ | regexp.cpp:40:20:40:22 | [\|] | regexp.cpp | 40 | 20 | 40 | 22 | | regexp.cpp:43:22:43:31 | [[a-f]A-F] | regexp.cpp | 43 | 22 | 43 | 31 | | regexp.cpp:46:21:46:22 | .* | regexp.cpp | 46 | 21 | 46 | 22 | -| regexp.cpp:47:22:47:23 | .* | regexp.cpp | 47 | 22 | 47 | 23 | | regexp.cpp:48:21:48:25 | \\w+\\W | regexp.cpp | 48 | 21 | 48 | 25 | | regexp.cpp:49:21:49:24 | \\s\\S | regexp.cpp | 49 | 21 | 49 | 24 | | regexp.cpp:50:21:50:24 | \\d\\D | regexp.cpp | 50 | 21 | 50 | 24 | | regexp.cpp:51:21:51:24 | \\h\\H | regexp.cpp | 51 | 21 | 51 | 24 | | regexp.cpp:52:21:52:26 | \\n\\r\\t | regexp.cpp | 52 | 21 | 52 | 26 | -| regexp.cpp:55:20:55:24 | \\Gabc | regexp.cpp | 55 | 20 | 55 | 24 | | regexp.cpp:56:20:56:25 | \\b!a\\B | regexp.cpp | 56 | 20 | 56 | 25 | | regexp.cpp:59:20:59:28 | (foo)*bar | regexp.cpp | 59 | 20 | 59 | 28 | | regexp.cpp:60:20:60:28 | fo(o\|b)ar | regexp.cpp | 60 | 20 | 60 | 28 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index ebbd892e145f..e2a783d2c702 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -95,29 +95,6 @@ regexp.cpp: # 32| [RegExpConstant, RegExpNormalChar] _ -# 33| [RegExpCaret] \A - -# 33| [RegExpSequence] \A[+-]?\d+ -#-----| 0 -> [RegExpCaret] \A -#-----| 1 -> [RegExpOpt] [+-]? -#-----| 2 -> [RegExpPlus] \d+ - -# 33| [RegExpCharacterClass] [+-] -#-----| 0 -> [RegExpConstant, RegExpNormalChar] + -#-----| 1 -> [RegExpConstant, RegExpNormalChar] - - -# 33| [RegExpOpt] [+-]? -#-----| 0 -> [RegExpCharacterClass] [+-] - -# 33| [RegExpConstant, RegExpNormalChar] + - -# 33| [RegExpConstant, RegExpNormalChar] - - -# 33| [RegExpCharacterClassEscape] \d - -# 33| [RegExpPlus] \d+ -#-----| 0 -> [RegExpCharacterClassEscape] \d - # 34| [RegExpCharacterClass] [\w] #-----| 0 -> [RegExpCharacterClassEscape] \w @@ -202,11 +179,6 @@ regexp.cpp: # 46| [RegExpStar] .* #-----| 0 -> [RegExpDot] . -# 47| [RegExpDot] . - -# 47| [RegExpStar] .* -#-----| 0 -> [RegExpDot] . - # 48| [RegExpCharacterClassEscape] \w # 48| [RegExpPlus] \w+ @@ -253,14 +225,6 @@ regexp.cpp: # 52| [RegExpConstant, RegExpEscape] \t -# 55| [RegExpSpecialChar] \G - -# 55| [RegExpSequence] \Gabc -#-----| 0 -> [RegExpSpecialChar] \G -#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc - -# 55| [RegExpConstant, RegExpNormalChar] abc - # 56| [RegExpSpecialChar] \b # 56| [RegExpSequence] \b!a\B diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 6561a265e22e..dcbc6b0f5841 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -30,7 +30,7 @@ std::regex r_alt("foo|bar"); // Character classes std::regex r_cc1("[abc]"); std::regex r_cc2("[a-fA-F0-9_]"); -std::regex r_cc3("\\A[+-]?\\d+"); + std::regex r_cc4("[\\w]+"); std::regex r_cc5("\\[\\][123]"); std::regex r_cc6("[^A-Z]"); @@ -44,7 +44,7 @@ std::regex r_nested("[[a-f]A-F]"); // Meta-character classes std::regex r_meta1(".*"); -std::regex r_meta1m(".*"); // /.*/m mode variant — mode flags are constructor args in C++ + std::regex r_meta2("\\w+\\W"); std::regex r_meta3("\\s\\S"); std::regex r_meta4("\\d\\D"); @@ -52,7 +52,7 @@ std::regex r_meta5("\\h\\H"); std::regex r_meta6("\\n\\r\\t"); // Anchors -std::regex r_anc1("\\Gabc"); + std::regex r_anc2("\\b!a\\B"); // Groups diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index e11a7cfea99e..8a3af59875e9 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -44,14 +44,6 @@ term | regexp.cpp:32:26:32:28 | 0-9 | RegExpCharacterRange | | regexp.cpp:32:28:32:28 | 9 | RegExpConstant,RegExpNormalChar | | regexp.cpp:32:29:32:29 | _ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:33:19:33:20 | \\A | RegExpCaret | -| regexp.cpp:33:19:33:28 | \\A[+-]?\\d+ | RegExpSequence | -| regexp.cpp:33:21:33:24 | [+-] | RegExpCharacterClass | -| regexp.cpp:33:21:33:25 | [+-]? | RegExpOpt | -| regexp.cpp:33:22:33:22 | + | RegExpConstant,RegExpNormalChar | -| regexp.cpp:33:23:33:23 | - | RegExpConstant,RegExpNormalChar | -| regexp.cpp:33:26:33:27 | \\d | RegExpCharacterClassEscape | -| regexp.cpp:33:26:33:28 | \\d+ | RegExpPlus | | regexp.cpp:34:19:34:22 | [\\w] | RegExpCharacterClass | | regexp.cpp:34:19:34:23 | [\\w]+ | RegExpPlus | | regexp.cpp:34:20:34:21 | \\w | RegExpCharacterClassEscape | @@ -83,8 +75,6 @@ term | regexp.cpp:43:28:43:31 | A-F] | RegExpConstant,RegExpNormalChar | | regexp.cpp:46:21:46:21 | . | RegExpDot | | regexp.cpp:46:21:46:22 | .* | RegExpStar | -| regexp.cpp:47:22:47:22 | . | RegExpDot | -| regexp.cpp:47:22:47:23 | .* | RegExpStar | | regexp.cpp:48:21:48:22 | \\w | RegExpCharacterClassEscape | | regexp.cpp:48:21:48:23 | \\w+ | RegExpPlus | | regexp.cpp:48:21:48:25 | \\w+\\W | RegExpSequence | @@ -102,9 +92,6 @@ term | regexp.cpp:52:21:52:26 | \\n\\r\\t | RegExpSequence | | regexp.cpp:52:23:52:24 | \\r | RegExpConstant,RegExpEscape | | regexp.cpp:52:25:52:26 | \\t | RegExpConstant,RegExpEscape | -| regexp.cpp:55:20:55:21 | \\G | RegExpSpecialChar | -| regexp.cpp:55:20:55:24 | \\Gabc | RegExpSequence | -| regexp.cpp:55:22:55:24 | abc | RegExpConstant,RegExpNormalChar | | regexp.cpp:56:20:56:21 | \\b | RegExpSpecialChar | | regexp.cpp:56:20:56:25 | \\b!a\\B | RegExpSequence | | regexp.cpp:56:22:56:23 | !a | RegExpConstant,RegExpNormalChar | @@ -256,9 +243,6 @@ regExpNormalCharValue | regexp.cpp:32:26:32:26 | 0 | 0 | | regexp.cpp:32:28:32:28 | 9 | 9 | | regexp.cpp:32:29:32:29 | _ | _ | -| regexp.cpp:33:22:33:22 | + | + | -| regexp.cpp:33:23:33:23 | - | - | -| regexp.cpp:33:26:33:27 | \\d | d | | regexp.cpp:34:20:34:21 | \\w | w | | regexp.cpp:35:19:35:20 | \\[ | [ | | regexp.cpp:35:21:35:22 | \\] | ] | @@ -286,7 +270,6 @@ regExpNormalCharValue | regexp.cpp:52:21:52:22 | \\n | \n | | regexp.cpp:52:23:52:24 | \\r | \r | | regexp.cpp:52:25:52:26 | \\t | \t | -| regexp.cpp:55:22:55:24 | abc | abc | | regexp.cpp:56:22:56:23 | !a | !a | | regexp.cpp:59:21:59:23 | foo | foo | | regexp.cpp:59:26:59:28 | bar | bar | From 6e71f00bbc6f2874591c53bedb0a1d7a0850dd3f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:06:02 +0000 Subject: [PATCH 08/37] C++: Remove Ruby-only \h and \H character class escapes --- cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll | 2 +- cpp/ql/test/library-tests/regex/locations.expected | 1 - cpp/ql/test/library-tests/regex/parse.expected | 8 -------- cpp/ql/test/library-tests/regex/regexp.cpp | 2 +- cpp/ql/test/library-tests/regex/regexp.expected | 5 ----- 5 files changed, 2 insertions(+), 16 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 1c42fb80709e..4ed66a945a32 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -651,7 +651,7 @@ private module Impl implements RegexTreeViewSig { * ``` */ class RegExpCharacterClassEscape extends RegExpEscape { - RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H"] } + RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W"] } override RegExpTerm getChild(int i) { none() } diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index d7912bdf1677..c85866cef176 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -19,7 +19,6 @@ | regexp.cpp:48:21:48:25 | \\w+\\W | regexp.cpp | 48 | 21 | 48 | 25 | | regexp.cpp:49:21:49:24 | \\s\\S | regexp.cpp | 49 | 21 | 49 | 24 | | regexp.cpp:50:21:50:24 | \\d\\D | regexp.cpp | 50 | 21 | 50 | 24 | -| regexp.cpp:51:21:51:24 | \\h\\H | regexp.cpp | 51 | 21 | 51 | 24 | | regexp.cpp:52:21:52:26 | \\n\\r\\t | regexp.cpp | 52 | 21 | 52 | 26 | | regexp.cpp:56:20:56:25 | \\b!a\\B | regexp.cpp | 56 | 20 | 56 | 25 | | regexp.cpp:59:20:59:28 | (foo)*bar | regexp.cpp | 59 | 20 | 59 | 28 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index e2a783d2c702..7296e9d3b0ec 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -206,14 +206,6 @@ regexp.cpp: # 50| [RegExpCharacterClassEscape] \D -# 51| [RegExpCharacterClassEscape] \h - -# 51| [RegExpSequence] \h\H -#-----| 0 -> [RegExpCharacterClassEscape] \h -#-----| 1 -> [RegExpCharacterClassEscape] \H - -# 51| [RegExpCharacterClassEscape] \H - # 52| [RegExpConstant, RegExpEscape] \n # 52| [RegExpSequence] \n\r\t diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index dcbc6b0f5841..7386b8ec5c6a 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -48,7 +48,7 @@ std::regex r_meta1(".*"); std::regex r_meta2("\\w+\\W"); std::regex r_meta3("\\s\\S"); std::regex r_meta4("\\d\\D"); -std::regex r_meta5("\\h\\H"); + std::regex r_meta6("\\n\\r\\t"); // Anchors diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 8a3af59875e9..69a757ff4147 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -85,9 +85,6 @@ term | regexp.cpp:50:21:50:22 | \\d | RegExpCharacterClassEscape | | regexp.cpp:50:21:50:24 | \\d\\D | RegExpSequence | | regexp.cpp:50:23:50:24 | \\D | RegExpCharacterClassEscape | -| regexp.cpp:51:21:51:22 | \\h | RegExpCharacterClassEscape | -| regexp.cpp:51:21:51:24 | \\h\\H | RegExpSequence | -| regexp.cpp:51:23:51:24 | \\H | RegExpCharacterClassEscape | | regexp.cpp:52:21:52:22 | \\n | RegExpConstant,RegExpEscape | | regexp.cpp:52:21:52:26 | \\n\\r\\t | RegExpSequence | | regexp.cpp:52:23:52:24 | \\r | RegExpConstant,RegExpEscape | @@ -265,8 +262,6 @@ regExpNormalCharValue | regexp.cpp:49:23:49:24 | \\S | S | | regexp.cpp:50:21:50:22 | \\d | d | | regexp.cpp:50:23:50:24 | \\D | D | -| regexp.cpp:51:21:51:22 | \\h | h | -| regexp.cpp:51:23:51:24 | \\H | H | | regexp.cpp:52:21:52:22 | \\n | \n | | regexp.cpp:52:23:52:24 | \\r | \r | | regexp.cpp:52:25:52:26 | \\t | \t | From a471af97cadbbc398651bed5017d24730fcc15f6 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 23 Jul 2026 16:07:08 +0200 Subject: [PATCH 09/37] C++: Remove Ruby-style \p and \P named character properties --- .../semmle/code/cpp/regex/RegexTreeView.qll | 6 +-- .../code/cpp/regex/internal/ParseRegExp.qll | 51 ++----------------- .../library-tests/regex/locations.expected | 8 +-- .../test/library-tests/regex/parse.expected | 37 +++++++++----- cpp/ql/test/library-tests/regex/regexp.cpp | 10 ++-- .../test/library-tests/regex/regexp.expected | 21 ++++---- 6 files changed, 50 insertions(+), 83 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 4ed66a945a32..86214f910972 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -1163,13 +1163,13 @@ private module Impl implements RegexTreeViewSig { override string getAPrimaryQlClass() { result = "RegExpNamedCharacterProperty" } /** - * Gets the property name. For example, in `\p{Space}`, the result is - * `"Space"`. + * Gets the property name. For example, in `[[:digit:]]`, the result is + * `"digit"`. */ string getName() { result = re.getCharacterPropertyName(start, end) } /** - * Holds if the property is inverted. For example, it holds for `\p{^Digit}`, + * Holds if the property is inverted. For example, it holds for `[[:^digit:]]`, * which matches non-digits. */ predicate isInverted() { re.namedCharacterPropertyIsInverted(start, end) } diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index a7d0d38919a1..cdb196f55cda 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -282,9 +282,8 @@ abstract class RegExp extends StringLiteral { ) } - /** Matches named character properties such as `\p{Word}` and `[[:digit:]]` */ + /** Matches named character properties such as `[[:digit:]]`. */ predicate namedCharacterProperty(int start, int end, string name) { - this.pStyleNamedCharacterProperty(start, end, name) or this.posixStyleNamedCharacterProperty(start, end, name) } @@ -315,49 +314,11 @@ abstract class RegExp extends StringLiteral { } /** - * Matches named character properties. For example: - * - `\p{Space}` - * - `\P{Digit}` upper-case P means inverted - * - `\p{^Word}` caret also means inverted + * Holds if the named character property is inverted. This, e.g., holds for `[[:^digit:]]`. * - * These can occur both inside and outside of character classes. - */ - private predicate pStyleNamedCharacterProperty(int start, int end, string name) { - this.escapingChar(start) and - this.getChar(start + 1) in ["p", "P"] and - this.getChar(start + 2) = "{" and - this.getChar(end - 1) = "}" and - end > start and - not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") and - exists(int nameStart | - this.getChar(start + 3) = "^" and nameStart = start + 4 - or - not this.getChar(start + 3) = "^" and nameStart = start + 3 - | - name = this.getText().substring(nameStart, end - 1) - ) - } - - /** - * Holds if the named character property is inverted. Examples for which it holds: - * - `\P{Digit}` upper-case P means inverted - * - `\p{^Word}` caret also means inverted - * - `[[:^digit:]]` - * - * Examples for which it doesn't hold: - * - `\p{Word}` - * - `\P{^Space}` - upper-case P and caret cancel each other out - * - `[[:alnum:]]` + * This does not hold for, e.g., `[[:alnum:]]`. */ predicate namedCharacterPropertyIsInverted(int start, int end) { - this.pStyleNamedCharacterProperty(start, end, _) and - exists(boolean upperP, boolean caret | - (if this.getChar(start + 1) = "P" then upperP = true else upperP = false) and - (if this.getChar(start + 3) = "^" then caret = true else caret = false) - | - upperP.booleanXor(caret) = true - ) - or this.posixStyleNamedCharacterProperty(start, end, _) and this.getChar(start + 3) = "^" } @@ -371,7 +332,6 @@ abstract class RegExp extends StringLiteral { this.escapingChar(start) and not this.numberedBackreference(start, _, _) and not this.namedBackreference(start, _, _) and - not this.pStyleNamedCharacterProperty(start, _, _) and ( // hex char \xhh this.getChar(start + 1) = "x" and end = start + 4 @@ -447,9 +407,6 @@ abstract class RegExp extends StringLiteral { ) and not exists(int x, int y | this.groupStart(x, y) and x <= start and y >= end) and not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end) and - not exists(int x, int y | - this.pStyleNamedCharacterProperty(x, y, _) and x <= start and y >= end - ) and not exists(int x, int y | this.multiples(x, y, _, _) and x <= start and y >= end) } @@ -776,8 +733,6 @@ abstract class RegExp extends StringLiteral { this.charSet(start, end) or this.backreference(start, end) - or - this.pStyleNamedCharacterProperty(start, end, _) } private predicate qualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index c85866cef176..f6b6303ee427 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -29,10 +29,10 @@ | regexp.cpp:66:19:66:29 | (?'foo'fo+) | regexp.cpp | 66 | 19 | 66 | 29 | | regexp.cpp:69:21:69:28 | (a+)b+\\1 | regexp.cpp | 69 | 21 | 69 | 28 | | regexp.cpp:70:21:70:41 | (?q+)\\s+\\k+ | regexp.cpp | 70 | 21 | 70 | 41 | -| regexp.cpp:73:21:73:29 | \\p{Word}* | regexp.cpp | 73 | 21 | 73 | 29 | -| regexp.cpp:74:21:74:30 | \\P{Digit}+ | regexp.cpp | 74 | 21 | 74 | 30 | -| regexp.cpp:75:21:75:35 | \\p{^Alnum}{2,3} | regexp.cpp | 75 | 21 | 75 | 35 | -| regexp.cpp:76:21:76:35 | [a-f\\p{Digit}]+ | regexp.cpp | 76 | 21 | 76 | 35 | +| regexp.cpp:73:21:73:32 | [[:alnum:]]* | regexp.cpp | 73 | 21 | 73 | 32 | +| regexp.cpp:74:21:74:32 | [[:digit:]]+ | regexp.cpp | 74 | 21 | 74 | 32 | +| regexp.cpp:75:21:75:36 | [[:alnum:]]{2,3} | regexp.cpp | 75 | 21 | 75 | 36 | +| regexp.cpp:76:21:76:35 | [a-f[:digit:]]+ | regexp.cpp | 76 | 21 | 76 | 35 | | regexp.cpp:79:22:79:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 79 | 22 | 79 | 43 | | regexp.cpp:82:22:82:41 | [[:alpha:][:digit:]] | regexp.cpp | 82 | 22 | 82 | 41 | | regexp.cpp:85:22:85:38 | [A-F[:digit:]a-f] | regexp.cpp | 85 | 22 | 85 | 38 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 7296e9d3b0ec..024f588889b0 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -361,27 +361,36 @@ regexp.cpp: # 70| [RegExpPlus] \k+ #-----| 0 -> [RegExpBackRef] \k -# 73| [RegExpNamedCharacterProperty] \p{Word} +# 73| [RegExpCharacterClass] [[:alnum:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] -# 73| [RegExpStar] \p{Word}* -#-----| 0 -> [RegExpNamedCharacterProperty] \p{Word} +# 73| [RegExpStar] [[:alnum:]]* +#-----| 0 -> [RegExpCharacterClass] [[:alnum:]] -# 74| [RegExpNamedCharacterProperty] \P{Digit} +# 73| [RegExpNamedCharacterProperty] [:alnum:] -# 74| [RegExpPlus] \P{Digit}+ -#-----| 0 -> [RegExpNamedCharacterProperty] \P{Digit} +# 74| [RegExpCharacterClass] [[:digit:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] + +# 74| [RegExpPlus] [[:digit:]]+ +#-----| 0 -> [RegExpCharacterClass] [[:digit:]] + +# 74| [RegExpNamedCharacterProperty] [:digit:] -# 75| [RegExpNamedCharacterProperty] \p{^Alnum} +# 75| [RegExpCharacterClass] [[:alnum:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] -# 75| [RegExpRange] \p{^Alnum}{2,3} -#-----| 0 -> [RegExpNamedCharacterProperty] \p{^Alnum} +# 75| [RegExpRange] [[:alnum:]]{2,3} +#-----| 0 -> [RegExpCharacterClass] [[:alnum:]] -# 76| [RegExpCharacterClass] [a-f\p{Digit}] +# 75| [RegExpNamedCharacterProperty] [:alnum:] + +# 76| [RegExpCharacterClass] [a-f[:digit:]] #-----| 0 -> [RegExpCharacterRange] a-f -#-----| 1 -> [RegExpNamedCharacterProperty] \p{Digit} +#-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] -# 76| [RegExpPlus] [a-f\p{Digit}]+ -#-----| 0 -> [RegExpCharacterClass] [a-f\p{Digit}] +# 76| [RegExpPlus] [a-f[:digit:]]+ +#-----| 0 -> [RegExpCharacterClass] [a-f[:digit:]] # 76| [RegExpConstant, RegExpNormalChar] a @@ -391,7 +400,7 @@ regexp.cpp: # 76| [RegExpConstant, RegExpNormalChar] f -# 76| [RegExpNamedCharacterProperty] \p{Digit} +# 76| [RegExpNamedCharacterProperty] [:digit:] # 79| [RegExpCharacterClass] [[:alpha:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 7386b8ec5c6a..c5f8780ce9d8 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -69,11 +69,11 @@ std::regex r_ng2("(?'foo'fo+)"); // single-quote named-group form std::regex r_bref1("(a+)b+\\1"); std::regex r_bref2("(?q+)\\s+\\k+"); -// Named character properties using the p-style syntax -std::regex r_prop1("\\p{Word}*"); -std::regex r_prop2("\\P{Digit}+"); -std::regex r_prop3("\\p{^Alnum}{2,3}"); -std::regex r_prop4("[a-f\\p{Digit}]+"); // Also valid inside character classes +// Named character properties using a single POSIX bracket expression +std::regex r_prop1("[[:alnum:]]*"); +std::regex r_prop2("[[:digit:]]+"); +std::regex r_prop3("[[:alnum:]]{2,3}"); +std::regex r_prop4("[a-f[:digit:]]+"); // Also valid inside character classes // Two separate character classes, each containing a single POSIX bracket expression std::regex r_posix1("[[:alpha:]][[:digit:]]"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 69a757ff4147..c1da19559675 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -140,18 +140,21 @@ term | regexp.cpp:70:31:70:33 | \\s+ | RegExpPlus | | regexp.cpp:70:34:70:40 | \\k | RegExpBackRef | | regexp.cpp:70:34:70:41 | \\k+ | RegExpPlus | -| regexp.cpp:73:21:73:28 | \\p{Word} | RegExpNamedCharacterProperty | -| regexp.cpp:73:21:73:29 | \\p{Word}* | RegExpStar | -| regexp.cpp:74:21:74:29 | \\P{Digit} | RegExpNamedCharacterProperty | -| regexp.cpp:74:21:74:30 | \\P{Digit}+ | RegExpPlus | -| regexp.cpp:75:21:75:30 | \\p{^Alnum} | RegExpNamedCharacterProperty | -| regexp.cpp:75:21:75:35 | \\p{^Alnum}{2,3} | RegExpRange | -| regexp.cpp:76:21:76:34 | [a-f\\p{Digit}] | RegExpCharacterClass | -| regexp.cpp:76:21:76:35 | [a-f\\p{Digit}]+ | RegExpPlus | +| regexp.cpp:73:21:73:31 | [[:alnum:]] | RegExpCharacterClass | +| regexp.cpp:73:21:73:32 | [[:alnum:]]* | RegExpStar | +| regexp.cpp:73:22:73:30 | [:alnum:] | RegExpNamedCharacterProperty | +| regexp.cpp:74:21:74:31 | [[:digit:]] | RegExpCharacterClass | +| regexp.cpp:74:21:74:32 | [[:digit:]]+ | RegExpPlus | +| regexp.cpp:74:22:74:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:75:21:75:31 | [[:alnum:]] | RegExpCharacterClass | +| regexp.cpp:75:21:75:36 | [[:alnum:]]{2,3} | RegExpRange | +| regexp.cpp:75:22:75:30 | [:alnum:] | RegExpNamedCharacterProperty | +| regexp.cpp:76:21:76:34 | [a-f[:digit:]] | RegExpCharacterClass | +| regexp.cpp:76:21:76:35 | [a-f[:digit:]]+ | RegExpPlus | | regexp.cpp:76:22:76:22 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:76:22:76:24 | a-f | RegExpCharacterRange | | regexp.cpp:76:24:76:24 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:76:25:76:33 | \\p{Digit} | RegExpNamedCharacterProperty | +| regexp.cpp:76:25:76:33 | [:digit:] | RegExpNamedCharacterProperty | | regexp.cpp:79:22:79:32 | [[:alpha:]] | RegExpCharacterClass | | regexp.cpp:79:22:79:43 | [[:alpha:]][[:digit:]] | RegExpSequence | | regexp.cpp:79:23:79:31 | [:alpha:] | RegExpNamedCharacterProperty | From c3fd9660085ef71083804936d4683ace11f8b9f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:09:02 +0000 Subject: [PATCH 10/37] C++: Remove Ruby-style (?#...) comment groups --- .../semmle/code/cpp/regex/internal/ParseRegExp.qll | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index cdb196f55cda..810ffdc5f5b9 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -590,8 +590,6 @@ abstract class RegExp extends StringLiteral { or this.negativeLookbehindAssertionStart(start, end) or - this.commentGroupStart(start, end) - or this.simpleGroupStart(start, end) } @@ -599,7 +597,7 @@ abstract class RegExp extends StringLiteral { private predicate nonCapturingGroupStart(int start, int end) { this.isGroupStart(start) and this.getChar(start + 1) = "?" and - this.getChar(start + 2) = [":", "=", "<", "!", "#"] and + this.getChar(start + 2) = [":", "=", "<", "!"] and end = start + 3 } @@ -668,14 +666,6 @@ abstract class RegExp extends StringLiteral { end = start + 4 } - /** Matches the start of a comment group, i.e. `(?#`. */ - private predicate commentGroupStart(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - this.getChar(start + 2) = "#" and - end = start + 3 - } - /** Matches the contents of a group. */ predicate groupContents(int start, int end, int inStart, int inEnd) { this.groupStart(start, inStart) and From b2d076671d1d22afe010f82aa78a5e625adf53a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:10:15 +0000 Subject: [PATCH 11/37] C++: Remove single-quote named capture groups --- .../semmle/code/cpp/regex/internal/ParseRegExp.qll | 6 ------ cpp/ql/test/library-tests/regex/locations.expected | 1 - cpp/ql/test/library-tests/regex/parse.expected | 14 -------------- cpp/ql/test/library-tests/regex/regexp.cpp | 2 +- cpp/ql/test/library-tests/regex/regexp.expected | 9 --------- 5 files changed, 1 insertion(+), 31 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 810ffdc5f5b9..18b7459f2d85 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -611,7 +611,6 @@ abstract class RegExp extends StringLiteral { /** * Matches the start of a named group, such as: * - `(?\w+)` - * - `(?'name'\w+)` */ private predicate namedGroupStart(int start, int end) { this.isGroupStart(start) and @@ -624,11 +623,6 @@ abstract class RegExp extends StringLiteral { nameEnd = min(int i | i > start + 3 and this.getChar(i) = ">") and end = nameEnd + 1 ) - or - this.getChar(start + 2) = "'" and - exists(int nameEnd | - nameEnd = min(int i | i > start + 2 and this.getChar(i) = "'") and end = nameEnd + 1 - ) ) } diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index f6b6303ee427..9dc8a639c4af 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -26,7 +26,6 @@ | regexp.cpp:61:20:61:28 | (a\|b\|cd)e | regexp.cpp | 61 | 20 | 61 | 28 | | regexp.cpp:62:20:62:27 | (?::+)\\w | regexp.cpp | 62 | 20 | 62 | 27 | | regexp.cpp:65:19:65:28 | (?\\w+) | regexp.cpp | 65 | 19 | 65 | 28 | -| regexp.cpp:66:19:66:29 | (?'foo'fo+) | regexp.cpp | 66 | 19 | 66 | 29 | | regexp.cpp:69:21:69:28 | (a+)b+\\1 | regexp.cpp | 69 | 21 | 69 | 28 | | regexp.cpp:70:21:70:41 | (?q+)\\s+\\k+ | regexp.cpp | 70 | 21 | 70 | 41 | | regexp.cpp:73:21:73:32 | [[:alnum:]]* | regexp.cpp | 73 | 21 | 73 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 024f588889b0..fcc4149fcd65 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -304,20 +304,6 @@ regexp.cpp: # 65| [RegExpPlus] \w+ #-----| 0 -> [RegExpCharacterClassEscape] \w -# 66| [RegExpGroup] (?'foo'fo+) -#-----| 0 -> [RegExpSequence] fo+ - -# 66| [RegExpConstant, RegExpNormalChar] f - -# 66| [RegExpSequence] fo+ -#-----| 0 -> [RegExpConstant, RegExpNormalChar] f -#-----| 1 -> [RegExpPlus] o+ - -# 66| [RegExpConstant, RegExpNormalChar] o - -# 66| [RegExpPlus] o+ -#-----| 0 -> [RegExpConstant, RegExpNormalChar] o - # 69| [RegExpGroup] (a+) #-----| 0 -> [RegExpPlus] a+ diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index c5f8780ce9d8..b696f2f7fd3d 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -63,7 +63,7 @@ std::regex r_grp4("(?::+)\\w"); // Non-capturing group matching colons // Named groups std::regex r_ng1("(?\\w+)"); -std::regex r_ng2("(?'foo'fo+)"); // single-quote named-group form + // Backreferences std::regex r_bref1("(a+)b+\\1"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index c1da19559675..8df6563f06ea 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -1,12 +1,10 @@ groupName | regexp.cpp:65:19:65:28 | (?\\w+) | id | -| regexp.cpp:66:19:66:29 | (?'foo'fo+) | foo | | regexp.cpp:70:21:70:30 | (?q+) | qux | groupNumber | regexp.cpp:59:20:59:24 | (foo) | 1 | | regexp.cpp:60:22:60:26 | (o\|b) | 1 | | regexp.cpp:61:20:61:27 | (a\|b\|cd) | 1 | -| regexp.cpp:66:19:66:29 | (?'foo'fo+) | 1 | | regexp.cpp:69:21:69:24 | (a+) | 1 | term | regexp.cpp:18:19:18:21 | abc | RegExpConstant,RegExpNormalChar | @@ -120,11 +118,6 @@ term | regexp.cpp:65:19:65:28 | (?\\w+) | RegExpGroup | | regexp.cpp:65:25:65:26 | \\w | RegExpCharacterClassEscape | | regexp.cpp:65:25:65:27 | \\w+ | RegExpPlus | -| regexp.cpp:66:19:66:29 | (?'foo'fo+) | RegExpGroup | -| regexp.cpp:66:26:66:26 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:66:26:66:28 | fo+ | RegExpSequence | -| regexp.cpp:66:27:66:27 | o | RegExpConstant,RegExpNormalChar | -| regexp.cpp:66:27:66:28 | o+ | RegExpPlus | | regexp.cpp:69:21:69:24 | (a+) | RegExpGroup | | regexp.cpp:69:21:69:28 | (a+)b+\\1 | RegExpSequence | | regexp.cpp:69:22:69:22 | a | RegExpConstant,RegExpNormalChar | @@ -282,8 +275,6 @@ regExpNormalCharValue | regexp.cpp:62:23:62:23 | : | : | | regexp.cpp:62:26:62:27 | \\w | w | | regexp.cpp:65:25:65:26 | \\w | w | -| regexp.cpp:66:26:66:26 | f | f | -| regexp.cpp:66:27:66:27 | o | o | | regexp.cpp:69:22:69:22 | a | a | | regexp.cpp:69:25:69:25 | b | b | | regexp.cpp:70:28:70:28 | q | q | From 985a2ba6be458e6a0d55658fa03eda733abed04b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:12:01 +0000 Subject: [PATCH 12/37] C++: Remove named capture groups --- .../semmle/code/cpp/regex/RegexTreeView.qll | 11 +------ .../code/cpp/regex/internal/ParseRegExp.qll | 31 +------------------ .../library-tests/regex/locations.expected | 2 -- .../test/library-tests/regex/parse.expected | 31 ------------------- cpp/ql/test/library-tests/regex/regexp.cpp | 6 ++-- .../test/library-tests/regex/regexp.expected | 17 ---------- cpp/ql/test/library-tests/regex/regexp.ql | 2 -- 7 files changed, 5 insertions(+), 95 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 86214f910972..21e7ec428d40 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -860,12 +860,6 @@ private module Impl implements RegexTreeViewSig { /** Holds if this is a capture group. */ predicate isCapture() { exists(this.getNumber()) } - /** Holds if this is a named capture group. */ - predicate isNamed() { exists(this.getName()) } - - /** Gets the name of this capture group, if any. */ - string getName() { result = re.getGroupName(start, end) } - override RegExpTerm getChild(int i) { result.getRegExp() = re and i = 0 and @@ -1138,10 +1132,7 @@ private module Impl implements RegexTreeViewSig { /** Gets the capture group this back reference refers to. */ RegExpGroup getGroup() { result.getLiteral() = this.getLiteral() and - ( - result.getNumber() = this.getNumber() or - result.getName() = this.getName() - ) + result.getNumber() = this.getNumber() } override RegExpTerm getChild(int i) { none() } diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 18b7459f2d85..47011350b131 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -495,15 +495,6 @@ abstract class RegExp extends StringLiteral { count(int i | this.group(i, _) and i < start and not this.nonCapturingGroupStart(i, _)) + 1 } - /** Gets the name, if it has one, of the group in start,end */ - string getGroupName(int start, int end) { - this.group(start, end) and - exists(int nameEnd | - this.namedGroupStart(start, nameEnd) and - result = this.getText().substring(start + 3, nameEnd - 1) - ) - } - /** Whether the text in the range start, end is a group and can match the empty string. */ predicate zeroWidthMatch(int start, int end) { this.emptyGroup(start, end) @@ -580,8 +571,6 @@ abstract class RegExp extends StringLiteral { private predicate groupStart(int start, int end) { this.nonCapturingGroupStart(start, end) or - this.namedGroupStart(start, end) - or this.lookaheadAssertionStart(start, end) or this.negativeLookaheadAssertionStart(start, end) @@ -597,7 +586,7 @@ abstract class RegExp extends StringLiteral { private predicate nonCapturingGroupStart(int start, int end) { this.isGroupStart(start) and this.getChar(start + 1) = "?" and - this.getChar(start + 2) = [":", "=", "<", "!"] and + this.getChar(start + 2) = [":", "=", "!"] and end = start + 3 } @@ -608,24 +597,6 @@ abstract class RegExp extends StringLiteral { end = start + 1 } - /** - * Matches the start of a named group, such as: - * - `(?\w+)` - */ - private predicate namedGroupStart(int start, int end) { - this.isGroupStart(start) and - this.getChar(start + 1) = "?" and - ( - this.getChar(start + 2) = "<" and - not this.getChar(start + 3) = "=" and // (?<=foo) is a positive lookbehind assertion - not this.getChar(start + 3) = "!" and // (? start + 3 and this.getChar(i) = ">") and - end = nameEnd + 1 - ) - ) - } - /** Matches the start of a positive lookahead assertion, i.e. `(?=`. */ private predicate lookaheadAssertionStart(int start, int end) { this.isGroupStart(start) and diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 9dc8a639c4af..74433c987cdb 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -25,9 +25,7 @@ | regexp.cpp:60:20:60:28 | fo(o\|b)ar | regexp.cpp | 60 | 20 | 60 | 28 | | regexp.cpp:61:20:61:28 | (a\|b\|cd)e | regexp.cpp | 61 | 20 | 61 | 28 | | regexp.cpp:62:20:62:27 | (?::+)\\w | regexp.cpp | 62 | 20 | 62 | 27 | -| regexp.cpp:65:19:65:28 | (?\\w+) | regexp.cpp | 65 | 19 | 65 | 28 | | regexp.cpp:69:21:69:28 | (a+)b+\\1 | regexp.cpp | 69 | 21 | 69 | 28 | -| regexp.cpp:70:21:70:41 | (?q+)\\s+\\k+ | regexp.cpp | 70 | 21 | 70 | 41 | | regexp.cpp:73:21:73:32 | [[:alnum:]]* | regexp.cpp | 73 | 21 | 73 | 32 | | regexp.cpp:74:21:74:32 | [[:digit:]]+ | regexp.cpp | 74 | 21 | 74 | 32 | | regexp.cpp:75:21:75:36 | [[:alnum:]]{2,3} | regexp.cpp | 75 | 21 | 75 | 36 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index fcc4149fcd65..f4430d70df43 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -296,14 +296,6 @@ regexp.cpp: # 62| [RegExpCharacterClassEscape] \w -# 65| [RegExpGroup] (?\w+) -#-----| 0 -> [RegExpPlus] \w+ - -# 65| [RegExpCharacterClassEscape] \w - -# 65| [RegExpPlus] \w+ -#-----| 0 -> [RegExpCharacterClassEscape] \w - # 69| [RegExpGroup] (a+) #-----| 0 -> [RegExpPlus] a+ @@ -324,29 +316,6 @@ regexp.cpp: # 69| [RegExpBackRef] \1 -# 70| [RegExpGroup] (?q+) -#-----| 0 -> [RegExpPlus] q+ - -# 70| [RegExpSequence] (?q+)\s+\k+ -#-----| 0 -> [RegExpGroup] (?q+) -#-----| 1 -> [RegExpPlus] \s+ -#-----| 2 -> [RegExpPlus] \k+ - -# 70| [RegExpConstant, RegExpNormalChar] q - -# 70| [RegExpPlus] q+ -#-----| 0 -> [RegExpConstant, RegExpNormalChar] q - -# 70| [RegExpCharacterClassEscape] \s - -# 70| [RegExpPlus] \s+ -#-----| 0 -> [RegExpCharacterClassEscape] \s - -# 70| [RegExpBackRef] \k - -# 70| [RegExpPlus] \k+ -#-----| 0 -> [RegExpBackRef] \k - # 73| [RegExpCharacterClass] [[:alnum:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index b696f2f7fd3d..6fed37ac70d2 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -61,13 +61,13 @@ std::regex r_grp2("fo(o|b)ar"); std::regex r_grp3("(a|b|cd)e"); std::regex r_grp4("(?::+)\\w"); // Non-capturing group matching colons -// Named groups -std::regex r_ng1("(?\\w+)"); + + // Backreferences std::regex r_bref1("(a+)b+\\1"); -std::regex r_bref2("(?q+)\\s+\\k+"); + // Named character properties using a single POSIX bracket expression std::regex r_prop1("[[:alnum:]]*"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 8df6563f06ea..413ac7ebe2bc 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -1,6 +1,3 @@ -groupName -| regexp.cpp:65:19:65:28 | (?\\w+) | id | -| regexp.cpp:70:21:70:30 | (?q+) | qux | groupNumber | regexp.cpp:59:20:59:24 | (foo) | 1 | | regexp.cpp:60:22:60:26 | (o\|b) | 1 | @@ -115,9 +112,6 @@ term | regexp.cpp:62:23:62:23 | : | RegExpConstant,RegExpNormalChar | | regexp.cpp:62:23:62:24 | :+ | RegExpPlus | | regexp.cpp:62:26:62:27 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:65:19:65:28 | (?\\w+) | RegExpGroup | -| regexp.cpp:65:25:65:26 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:65:25:65:27 | \\w+ | RegExpPlus | | regexp.cpp:69:21:69:24 | (a+) | RegExpGroup | | regexp.cpp:69:21:69:28 | (a+)b+\\1 | RegExpSequence | | regexp.cpp:69:22:69:22 | a | RegExpConstant,RegExpNormalChar | @@ -125,14 +119,6 @@ term | regexp.cpp:69:25:69:25 | b | RegExpConstant,RegExpNormalChar | | regexp.cpp:69:25:69:26 | b+ | RegExpPlus | | regexp.cpp:69:27:69:28 | \\1 | RegExpBackRef | -| regexp.cpp:70:21:70:30 | (?q+) | RegExpGroup | -| regexp.cpp:70:21:70:41 | (?q+)\\s+\\k+ | RegExpSequence | -| regexp.cpp:70:28:70:28 | q | RegExpConstant,RegExpNormalChar | -| regexp.cpp:70:28:70:29 | q+ | RegExpPlus | -| regexp.cpp:70:31:70:32 | \\s | RegExpCharacterClassEscape | -| regexp.cpp:70:31:70:33 | \\s+ | RegExpPlus | -| regexp.cpp:70:34:70:40 | \\k | RegExpBackRef | -| regexp.cpp:70:34:70:41 | \\k+ | RegExpPlus | | regexp.cpp:73:21:73:31 | [[:alnum:]] | RegExpCharacterClass | | regexp.cpp:73:21:73:32 | [[:alnum:]]* | RegExpStar | | regexp.cpp:73:22:73:30 | [:alnum:] | RegExpNamedCharacterProperty | @@ -274,11 +260,8 @@ regExpNormalCharValue | regexp.cpp:61:28:61:28 | e | e | | regexp.cpp:62:23:62:23 | : | : | | regexp.cpp:62:26:62:27 | \\w | w | -| regexp.cpp:65:25:65:26 | \\w | w | | regexp.cpp:69:22:69:22 | a | a | | regexp.cpp:69:25:69:25 | b | b | -| regexp.cpp:70:28:70:28 | q | q | -| regexp.cpp:70:31:70:32 | \\s | s | | regexp.cpp:76:22:76:22 | a | a | | regexp.cpp:76:24:76:24 | f | f | | regexp.cpp:85:23:85:23 | A | A | diff --git a/cpp/ql/test/library-tests/regex/regexp.ql b/cpp/ql/test/library-tests/regex/regexp.ql index a29717199e24..b8eb41e12dbb 100644 --- a/cpp/ql/test/library-tests/regex/regexp.ql +++ b/cpp/ql/test/library-tests/regex/regexp.ql @@ -6,8 +6,6 @@ class RegExpTest extends RegExp { RegExpTest() { any() } } -query predicate groupName(RegExpGroup g, string name) { name = g.getName() } - query predicate groupNumber(RegExpGroup g, int number) { number = g.getNumber() } query predicate term(RegExpTerm term, string c) { c = term.getPrimaryQlClasses() } From bb96e9029646eb9d2f8957be622871b030507879 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:13:23 +0000 Subject: [PATCH 13/37] C++: Remove named backreferences --- .../lib/semmle/code/cpp/regex/RegexTreeView.qll | 10 ++-------- .../code/cpp/regex/internal/ParseRegExp.qll | 17 ----------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 21e7ec428d40..7e42c2b16943 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -1106,14 +1106,13 @@ private module Impl implements RegexTreeViewSig { } /** - * A back reference, that is, a term of the form `\i` or `\k` - * in a regular expression. + * A back reference, that is, a term of the form `\i` in a regular + * expression. * * Examples: * * ``` * \1 - * (?P=quote) * ``` */ class RegExpBackRef extends RegExpTerm, TRegExpBackRef { @@ -1124,11 +1123,6 @@ private module Impl implements RegexTreeViewSig { */ int getNumber() { result = re.getBackRefNumber(start, end) } - /** - * Gets the name of the capture group this back reference refers to, if any. - */ - string getName() { result = re.getBackRefName(start, end) } - /** Gets the capture group this back reference refers to. */ RegExpGroup getGroup() { result.getLiteral() = this.getLiteral() and diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 47011350b131..3b7596fb17e9 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -331,7 +331,6 @@ abstract class RegExp extends StringLiteral { predicate escapedCharacter(int start, int end) { this.escapingChar(start) and not this.numberedBackreference(start, _, _) and - not this.namedBackreference(start, _, _) and ( // hex char \xhh this.getChar(start + 1) = "x" and end = start + 4 @@ -639,17 +638,6 @@ abstract class RegExp extends StringLiteral { this.isGroupEnd(inEnd) } - /** Matches a named backreference, e.g. `\k`. */ - predicate namedBackreference(int start, int end, string name) { - this.escapingChar(start) and - this.getChar(start + 1) = "k" and - this.getChar(start + 2) = "<" and - exists(int nameEnd | nameEnd = min(int i | i > start + 3 and this.getChar(i) = ">") | - end = nameEnd + 1 and - name = this.getText().substring(start + 3, nameEnd) - ) - } - /** Matches a numbered backreference, e.g. `\1`. */ predicate numberedBackreference(int start, int end, int value) { this.escapingChar(start) and @@ -669,16 +657,11 @@ abstract class RegExp extends StringLiteral { /** Whether the text in the range `start,end` is a back reference */ predicate backreference(int start, int end) { this.numberedBackreference(start, end, _) - or - this.namedBackreference(start, end, _) } /** Gets the number of the back reference in start,end */ int getBackRefNumber(int start, int end) { this.numberedBackreference(start, end, result) } - /** Gets the name, if it has one, of the back reference in start,end */ - string getBackRefName(int start, int end) { this.namedBackreference(start, end, result) } - private predicate baseItem(int start, int end) { this.characterItem(start, end) and not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) From da931e4ba304cfaf045f324d6de290ba7284bc76 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:15:43 +0000 Subject: [PATCH 14/37] C++: Remove lookbehind assertions --- .../semmle/code/cpp/regex/RegexTreeView.qll | 44 +----------------- .../code/cpp/regex/internal/ParseRegExp.qll | 46 +------------------ 2 files changed, 3 insertions(+), 87 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 7e42c2b16943..313fe32f1a7b 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -993,15 +993,13 @@ private module Impl implements RegexTreeViewSig { } /** - * A zero-width lookahead or lookbehind assertion. + * A zero-width lookahead assertion. * * Examples: * * ``` * (?=\w) * (?!\n) - * (?<=\.) - * (? Date: Fri, 24 Jul 2026 11:14:41 +0200 Subject: [PATCH 15/37] C++: Remove {,m} quantifier form --- cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll | 2 +- cpp/ql/test/library-tests/regex/locations.expected | 1 - cpp/ql/test/library-tests/regex/parse.expected | 5 ----- cpp/ql/test/library-tests/regex/regexp.cpp | 2 +- cpp/ql/test/library-tests/regex/regexp.expected | 3 --- 5 files changed, 2 insertions(+), 11 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 5a0569b8594a..84590db2172c 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -670,7 +670,7 @@ abstract class RegExp extends StringLiteral { lower = inner and upper = lower or - match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and + match = text.regexpFind("\\{[0-9]+,[0-9]*\\}", _, start) and exists(int commaIndex | commaIndex = inner.indexOf(",") and lower = inner.prefix(commaIndex) and diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 74433c987cdb..6da60c274849 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -1,7 +1,6 @@ | regexp.cpp:18:19:18:21 | abc | regexp.cpp | 18 | 19 | 18 | 21 | | regexp.cpp:21:20:21:26 | a*b+c?d | regexp.cpp | 21 | 20 | 21 | 26 | | regexp.cpp:22:20:22:25 | a{4,8} | regexp.cpp | 22 | 20 | 22 | 25 | -| regexp.cpp:23:20:23:24 | a{,8} | regexp.cpp | 23 | 20 | 23 | 24 | | regexp.cpp:24:20:24:24 | a{3,} | regexp.cpp | 24 | 20 | 24 | 24 | | regexp.cpp:25:20:25:23 | a{7} | regexp.cpp | 25 | 20 | 25 | 23 | | regexp.cpp:28:19:28:25 | foo\|bar | regexp.cpp | 28 | 19 | 28 | 25 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index f4430d70df43..62fd2c5b60cb 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -29,11 +29,6 @@ regexp.cpp: # 22| [RegExpRange] a{4,8} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 23| [RegExpConstant, RegExpNormalChar] a - -# 23| [RegExpRange] a{,8} -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a - # 24| [RegExpConstant, RegExpNormalChar] a # 24| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 6fed37ac70d2..ede36252b6f6 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -20,7 +20,7 @@ std::regex r_abc("abc"); // Repetition std::regex r_rep1("a*b+c?d"); std::regex r_rep2("a{4,8}"); -std::regex r_rep3("a{,8}"); + std::regex r_rep4("a{3,}"); std::regex r_rep5("a{7}"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 413ac7ebe2bc..1eb1b1e5ff02 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -15,8 +15,6 @@ term | regexp.cpp:21:26:21:26 | d | RegExpConstant,RegExpNormalChar | | regexp.cpp:22:20:22:20 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:22:20:22:25 | a{4,8} | RegExpRange | -| regexp.cpp:23:20:23:20 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:23:20:23:24 | a{,8} | RegExpRange | | regexp.cpp:24:20:24:20 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:24:20:24:24 | a{3,} | InfiniteRepetitionQuantifier,RegExpRange | | regexp.cpp:25:20:25:20 | a | RegExpConstant,RegExpNormalChar | @@ -207,7 +205,6 @@ regExpNormalCharValue | regexp.cpp:21:24:21:24 | c | c | | regexp.cpp:21:26:21:26 | d | d | | regexp.cpp:22:20:22:20 | a | a | -| regexp.cpp:23:20:23:20 | a | a | | regexp.cpp:24:20:24:20 | a | a | | regexp.cpp:25:20:25:20 | a | a | | regexp.cpp:28:19:28:21 | foo | foo | From 64a9a754a47a1f49d7bc2611e3fbb80c093933e5 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 16:12:23 +0200 Subject: [PATCH 16/37] C++: Remove inverted named character properties --- .../semmle/code/cpp/regex/RegexTreeView.qll | 12 +++--------- .../code/cpp/regex/internal/ParseRegExp.qll | 18 +----------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 313fe32f1a7b..e9b27d27a79d 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -1112,12 +1112,6 @@ private module Impl implements RegexTreeViewSig { * `"digit"`. */ string getName() { result = re.getCharacterPropertyName(start, end) } - - /** - * Holds if the property is inverted. For example, it holds for `[[:^digit:]]`, - * which matches non-digits. - */ - predicate isInverted() { re.namedCharacterPropertyIsInverted(start, end) } } class Top = RegExpParent; @@ -1132,13 +1126,13 @@ private module Impl implements RegexTreeViewSig { // TODO: expand to cover more properties exists(RegExpNamedCharacterProperty escape | term = escape | escape.getName().toLowerCase() = "digit" and - if escape.isInverted() then clazz = "D" else clazz = "d" + clazz = "d" or escape.getName().toLowerCase() = "space" and - if escape.isInverted() then clazz = "S" else clazz = "s" + clazz = "s" or escape.getName().toLowerCase() = "word" and - if escape.isInverted() then clazz = "W" else clazz = "w" + clazz = "w" ) } diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 84590db2172c..b6029c187a7a 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -304,23 +304,7 @@ abstract class RegExp extends StringLiteral { | e ) and - exists(int nameStart | - this.getChar(start + 2) = "^" and nameStart = start + 3 - or - not this.getChar(start + 2) = "^" and nameStart = start + 2 - | - name = this.getText().substring(nameStart, end - 2) - ) - } - - /** - * Holds if the named character property is inverted. This, e.g., holds for `[[:^digit:]]`. - * - * This does not hold for, e.g., `[[:alnum:]]`. - */ - predicate namedCharacterPropertyIsInverted(int start, int end) { - this.posixStyleNamedCharacterProperty(start, end, _) and - this.getChar(start + 3) = "^" + name = this.getText().substring(start + 2, end - 2) } /** From 24251de5ea4b8a6729a4b9ed267c0bffb67713f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:16:18 +0000 Subject: [PATCH 17/37] C++: Add control-escape tests (pre-fix) Clean up test source while here --- .../library-tests/regex/locations.expected | 90 +-- .../test/library-tests/regex/parse.expected | 382 ++++++------ cpp/ql/test/library-tests/regex/regexp.cpp | 36 +- .../test/library-tests/regex/regexp.expected | 566 +++++++++--------- 4 files changed, 548 insertions(+), 526 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 6da60c274849..8bcd383853d0 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -1,47 +1,49 @@ | regexp.cpp:18:19:18:21 | abc | regexp.cpp | 18 | 19 | 18 | 21 | | regexp.cpp:21:20:21:26 | a*b+c?d | regexp.cpp | 21 | 20 | 21 | 26 | | regexp.cpp:22:20:22:25 | a{4,8} | regexp.cpp | 22 | 20 | 22 | 25 | -| regexp.cpp:24:20:24:24 | a{3,} | regexp.cpp | 24 | 20 | 24 | 24 | -| regexp.cpp:25:20:25:23 | a{7} | regexp.cpp | 25 | 20 | 25 | 23 | -| regexp.cpp:28:19:28:25 | foo\|bar | regexp.cpp | 28 | 19 | 28 | 25 | -| regexp.cpp:31:19:31:23 | [abc] | regexp.cpp | 31 | 19 | 31 | 23 | -| regexp.cpp:32:19:32:30 | [a-fA-F0-9_] | regexp.cpp | 32 | 19 | 32 | 30 | -| regexp.cpp:34:19:34:23 | [\\w]+ | regexp.cpp | 34 | 19 | 34 | 23 | -| regexp.cpp:35:19:35:27 | \\[\\][123] | regexp.cpp | 35 | 19 | 35 | 27 | -| regexp.cpp:36:19:36:24 | [^A-Z] | regexp.cpp | 36 | 19 | 36 | 24 | -| regexp.cpp:37:19:37:21 | []] | regexp.cpp | 37 | 19 | 37 | 21 | -| regexp.cpp:38:19:38:22 | [^]] | regexp.cpp | 38 | 19 | 38 | 22 | -| regexp.cpp:39:19:39:22 | [^-] | regexp.cpp | 39 | 19 | 39 | 22 | -| regexp.cpp:40:20:40:22 | [\|] | regexp.cpp | 40 | 20 | 40 | 22 | -| regexp.cpp:43:22:43:31 | [[a-f]A-F] | regexp.cpp | 43 | 22 | 43 | 31 | -| regexp.cpp:46:21:46:22 | .* | regexp.cpp | 46 | 21 | 46 | 22 | -| regexp.cpp:48:21:48:25 | \\w+\\W | regexp.cpp | 48 | 21 | 48 | 25 | -| regexp.cpp:49:21:49:24 | \\s\\S | regexp.cpp | 49 | 21 | 49 | 24 | -| regexp.cpp:50:21:50:24 | \\d\\D | regexp.cpp | 50 | 21 | 50 | 24 | -| regexp.cpp:52:21:52:26 | \\n\\r\\t | regexp.cpp | 52 | 21 | 52 | 26 | -| regexp.cpp:56:20:56:25 | \\b!a\\B | regexp.cpp | 56 | 20 | 56 | 25 | -| regexp.cpp:59:20:59:28 | (foo)*bar | regexp.cpp | 59 | 20 | 59 | 28 | -| regexp.cpp:60:20:60:28 | fo(o\|b)ar | regexp.cpp | 60 | 20 | 60 | 28 | -| regexp.cpp:61:20:61:28 | (a\|b\|cd)e | regexp.cpp | 61 | 20 | 61 | 28 | -| regexp.cpp:62:20:62:27 | (?::+)\\w | regexp.cpp | 62 | 20 | 62 | 27 | -| regexp.cpp:69:21:69:28 | (a+)b+\\1 | regexp.cpp | 69 | 21 | 69 | 28 | -| regexp.cpp:73:21:73:32 | [[:alnum:]]* | regexp.cpp | 73 | 21 | 73 | 32 | -| regexp.cpp:74:21:74:32 | [[:digit:]]+ | regexp.cpp | 74 | 21 | 74 | 32 | -| regexp.cpp:75:21:75:36 | [[:alnum:]]{2,3} | regexp.cpp | 75 | 21 | 75 | 36 | -| regexp.cpp:76:21:76:35 | [a-f[:digit:]]+ | regexp.cpp | 76 | 21 | 76 | 35 | -| regexp.cpp:79:22:79:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 79 | 22 | 79 | 43 | -| regexp.cpp:82:22:82:41 | [[:alpha:][:digit:]] | regexp.cpp | 82 | 22 | 82 | 41 | -| regexp.cpp:85:22:85:38 | [A-F[:digit:]a-f] | regexp.cpp | 85 | 22 | 85 | 38 | -| regexp.cpp:88:22:88:30 | [:digit:] | regexp.cpp | 88 | 22 | 88 | 30 | -| regexp.cpp:94:25:94:28 | a\\nb | regexp.cpp | 94 | 25 | 94 | 28 | -| regexp.cpp:95:37:95:40 | a\\nc | regexp.cpp | 95 | 37 | 95 | 40 | -| regexp.cpp:96:39:96:42 | a\\nc | regexp.cpp | 96 | 39 | 96 | 42 | -| regexp.cpp:97:38:97:41 | a\\nc | regexp.cpp | 97 | 38 | 97 | 41 | -| regexp.cpp:98:38:98:41 | a\\nc | regexp.cpp | 98 | 38 | 98 | 41 | -| regexp.cpp:99:23:99:26 | a\\nc | regexp.cpp | 99 | 23 | 99 | 26 | -| regexp.cpp:100:40:100:43 | a\\nc | regexp.cpp | 100 | 40 | 100 | 43 | -| regexp.cpp:101:42:101:45 | a\\nc | regexp.cpp | 101 | 42 | 101 | 45 | -| regexp.cpp:102:41:102:44 | a\\nc | regexp.cpp | 102 | 41 | 102 | 44 | -| regexp.cpp:103:41:103:44 | a\\nc | regexp.cpp | 103 | 41 | 103 | 44 | -| regexp.cpp:104:25:104:28 | a\\nc | regexp.cpp | 104 | 25 | 104 | 28 | -| regexp.cpp:105:29:105:32 | a\\nc | regexp.cpp | 105 | 29 | 105 | 32 | +| regexp.cpp:23:20:23:24 | a{3,} | regexp.cpp | 23 | 20 | 23 | 24 | +| regexp.cpp:24:20:24:23 | a{7} | regexp.cpp | 24 | 20 | 24 | 23 | +| regexp.cpp:27:19:27:25 | foo\|bar | regexp.cpp | 27 | 19 | 27 | 25 | +| regexp.cpp:30:19:30:23 | [abc] | regexp.cpp | 30 | 19 | 30 | 23 | +| regexp.cpp:31:19:31:30 | [a-fA-F0-9_] | regexp.cpp | 31 | 19 | 31 | 30 | +| regexp.cpp:32:19:32:23 | [\\w]+ | regexp.cpp | 32 | 19 | 32 | 23 | +| regexp.cpp:33:19:33:27 | \\[\\][123] | regexp.cpp | 33 | 19 | 33 | 27 | +| regexp.cpp:34:19:34:24 | [^A-Z] | regexp.cpp | 34 | 19 | 34 | 24 | +| regexp.cpp:35:19:35:21 | []] | regexp.cpp | 35 | 19 | 35 | 21 | +| regexp.cpp:36:19:36:22 | [^]] | regexp.cpp | 36 | 19 | 36 | 22 | +| regexp.cpp:37:19:37:22 | [^-] | regexp.cpp | 37 | 19 | 37 | 22 | +| regexp.cpp:38:19:38:21 | [\|] | regexp.cpp | 38 | 19 | 38 | 21 | +| regexp.cpp:41:22:41:31 | [[a-f]A-F] | regexp.cpp | 41 | 22 | 41 | 31 | +| regexp.cpp:44:21:44:22 | .* | regexp.cpp | 44 | 21 | 44 | 22 | +| regexp.cpp:45:21:45:25 | \\w+\\W | regexp.cpp | 45 | 21 | 45 | 25 | +| regexp.cpp:46:21:46:24 | \\s\\S | regexp.cpp | 46 | 21 | 46 | 24 | +| regexp.cpp:47:21:47:24 | \\d\\D | regexp.cpp | 47 | 21 | 47 | 24 | +| regexp.cpp:48:21:48:26 | \\n\\r\\t | regexp.cpp | 48 | 21 | 48 | 26 | +| regexp.cpp:51:20:51:25 | \\b!a\\B | regexp.cpp | 51 | 20 | 51 | 25 | +| regexp.cpp:54:20:54:28 | (foo)*bar | regexp.cpp | 54 | 20 | 54 | 28 | +| regexp.cpp:55:20:55:28 | fo(o\|b)ar | regexp.cpp | 55 | 20 | 55 | 28 | +| regexp.cpp:56:20:56:28 | (a\|b\|cd)e | regexp.cpp | 56 | 20 | 56 | 28 | +| regexp.cpp:57:20:57:27 | (?::+)\\w | regexp.cpp | 57 | 20 | 57 | 27 | +| regexp.cpp:60:21:60:28 | (a+)b+\\1 | regexp.cpp | 60 | 21 | 60 | 28 | +| regexp.cpp:63:21:63:32 | [[:alnum:]]* | regexp.cpp | 63 | 21 | 63 | 32 | +| regexp.cpp:64:21:64:32 | [[:digit:]]+ | regexp.cpp | 64 | 21 | 64 | 32 | +| regexp.cpp:65:21:65:36 | [[:alnum:]]{2,3} | regexp.cpp | 65 | 21 | 65 | 36 | +| regexp.cpp:66:21:66:35 | [a-f[:digit:]]+ | regexp.cpp | 66 | 21 | 66 | 35 | +| regexp.cpp:69:22:69:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 69 | 22 | 69 | 43 | +| regexp.cpp:72:22:72:41 | [[:alpha:][:digit:]] | regexp.cpp | 72 | 22 | 72 | 41 | +| regexp.cpp:75:22:75:38 | [A-F[:digit:]a-f] | regexp.cpp | 75 | 22 | 75 | 38 | +| regexp.cpp:78:22:78:30 | [:digit:] | regexp.cpp | 78 | 22 | 78 | 30 | +| regexp.cpp:84:21:84:23 | \\cA | regexp.cpp | 84 | 21 | 84 | 23 | +| regexp.cpp:85:21:85:25 | [\\cZ] | regexp.cpp | 85 | 21 | 85 | 25 | +| regexp.cpp:88:25:88:28 | a\\nb | regexp.cpp | 88 | 25 | 88 | 28 | +| regexp.cpp:89:37:89:40 | a\\nc | regexp.cpp | 89 | 37 | 89 | 40 | +| regexp.cpp:90:39:90:42 | a\\nc | regexp.cpp | 90 | 39 | 90 | 42 | +| regexp.cpp:91:38:91:41 | a\\nc | regexp.cpp | 91 | 38 | 91 | 41 | +| regexp.cpp:92:38:92:41 | a\\nc | regexp.cpp | 92 | 38 | 92 | 41 | +| regexp.cpp:93:23:93:26 | a\\nc | regexp.cpp | 93 | 23 | 93 | 26 | +| regexp.cpp:94:40:94:43 | a\\nc | regexp.cpp | 94 | 40 | 94 | 43 | +| regexp.cpp:95:42:95:45 | a\\nc | regexp.cpp | 95 | 42 | 95 | 45 | +| regexp.cpp:96:41:96:44 | a\\nc | regexp.cpp | 96 | 41 | 96 | 44 | +| regexp.cpp:97:41:97:44 | a\\nc | regexp.cpp | 97 | 41 | 97 | 44 | +| regexp.cpp:98:25:98:28 | a\\nc | regexp.cpp | 98 | 25 | 98 | 28 | +| regexp.cpp:99:29:99:32 | a\\nc | regexp.cpp | 99 | 29 | 99 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 62fd2c5b60cb..49498ec0d230 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -29,506 +29,522 @@ regexp.cpp: # 22| [RegExpRange] a{4,8} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 24| [RegExpConstant, RegExpNormalChar] a +# 23| [RegExpConstant, RegExpNormalChar] a -# 24| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} +# 23| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 25| [RegExpConstant, RegExpNormalChar] a +# 24| [RegExpConstant, RegExpNormalChar] a -# 25| [RegExpRange] a{7} +# 24| [RegExpRange] a{7} #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 28| [RegExpConstant, RegExpNormalChar] foo +# 27| [RegExpConstant, RegExpNormalChar] foo -# 28| [RegExpAlt] foo|bar +# 27| [RegExpAlt] foo|bar #-----| 0 -> [RegExpConstant, RegExpNormalChar] foo #-----| 1 -> [RegExpConstant, RegExpNormalChar] bar -# 28| [RegExpConstant, RegExpNormalChar] bar +# 27| [RegExpConstant, RegExpNormalChar] bar -# 31| [RegExpCharacterClass] [abc] +# 30| [RegExpCharacterClass] [abc] #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] b #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 31| [RegExpConstant, RegExpNormalChar] a +# 30| [RegExpConstant, RegExpNormalChar] a -# 31| [RegExpConstant, RegExpNormalChar] b +# 30| [RegExpConstant, RegExpNormalChar] b -# 31| [RegExpConstant, RegExpNormalChar] c +# 30| [RegExpConstant, RegExpNormalChar] c -# 32| [RegExpCharacterClass] [a-fA-F0-9_] +# 31| [RegExpCharacterClass] [a-fA-F0-9_] #-----| 0 -> [RegExpCharacterRange] a-f #-----| 1 -> [RegExpCharacterRange] A-F #-----| 2 -> [RegExpCharacterRange] 0-9 #-----| 3 -> [RegExpConstant, RegExpNormalChar] _ -# 32| [RegExpConstant, RegExpNormalChar] a +# 31| [RegExpConstant, RegExpNormalChar] a -# 32| [RegExpCharacterRange] a-f +# 31| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 32| [RegExpConstant, RegExpNormalChar] f +# 31| [RegExpConstant, RegExpNormalChar] f -# 32| [RegExpConstant, RegExpNormalChar] A +# 31| [RegExpConstant, RegExpNormalChar] A -# 32| [RegExpCharacterRange] A-F +# 31| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 32| [RegExpConstant, RegExpNormalChar] F +# 31| [RegExpConstant, RegExpNormalChar] F -# 32| [RegExpConstant, RegExpNormalChar] 0 +# 31| [RegExpConstant, RegExpNormalChar] 0 -# 32| [RegExpCharacterRange] 0-9 +# 31| [RegExpCharacterRange] 0-9 #-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 #-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 -# 32| [RegExpConstant, RegExpNormalChar] 9 +# 31| [RegExpConstant, RegExpNormalChar] 9 -# 32| [RegExpConstant, RegExpNormalChar] _ +# 31| [RegExpConstant, RegExpNormalChar] _ -# 34| [RegExpCharacterClass] [\w] +# 32| [RegExpCharacterClass] [\w] #-----| 0 -> [RegExpCharacterClassEscape] \w -# 34| [RegExpPlus] [\w]+ +# 32| [RegExpPlus] [\w]+ #-----| 0 -> [RegExpCharacterClass] [\w] -# 34| [RegExpCharacterClassEscape] \w +# 32| [RegExpCharacterClassEscape] \w -# 35| [RegExpConstant, RegExpEscape] \[ +# 33| [RegExpConstant, RegExpEscape] \[ -# 35| [RegExpSequence] \[\][123] +# 33| [RegExpSequence] \[\][123] #-----| 0 -> [RegExpConstant, RegExpEscape] \[ #-----| 1 -> [RegExpConstant, RegExpEscape] \] #-----| 2 -> [RegExpCharacterClass] [123] -# 35| [RegExpConstant, RegExpEscape] \] +# 33| [RegExpConstant, RegExpEscape] \] -# 35| [RegExpCharacterClass] [123] +# 33| [RegExpCharacterClass] [123] #-----| 0 -> [RegExpConstant, RegExpNormalChar] 1 #-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 #-----| 2 -> [RegExpConstant, RegExpNormalChar] 3 -# 35| [RegExpConstant, RegExpNormalChar] 1 +# 33| [RegExpConstant, RegExpNormalChar] 1 -# 35| [RegExpConstant, RegExpNormalChar] 2 +# 33| [RegExpConstant, RegExpNormalChar] 2 -# 35| [RegExpConstant, RegExpNormalChar] 3 +# 33| [RegExpConstant, RegExpNormalChar] 3 -# 36| [RegExpCharacterClass] [^A-Z] +# 34| [RegExpCharacterClass] [^A-Z] #-----| 0 -> [RegExpCharacterRange] A-Z -# 36| [RegExpConstant, RegExpNormalChar] A +# 34| [RegExpConstant, RegExpNormalChar] A -# 36| [RegExpCharacterRange] A-Z +# 34| [RegExpCharacterRange] A-Z #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] Z -# 36| [RegExpConstant, RegExpNormalChar] Z +# 34| [RegExpConstant, RegExpNormalChar] Z -# 37| [RegExpCharacterClass] []] +# 35| [RegExpCharacterClass] []] #-----| 0 -> [RegExpConstant, RegExpNormalChar] ] -# 37| [RegExpConstant, RegExpNormalChar] ] +# 35| [RegExpConstant, RegExpNormalChar] ] -# 38| [RegExpCharacterClass] [^]] +# 36| [RegExpCharacterClass] [^]] #-----| 0 -> [RegExpConstant, RegExpNormalChar] ] -# 38| [RegExpConstant, RegExpNormalChar] ] +# 36| [RegExpConstant, RegExpNormalChar] ] -# 39| [RegExpCharacterClass] [^-] +# 37| [RegExpCharacterClass] [^-] #-----| 0 -> [RegExpConstant, RegExpNormalChar] - -# 39| [RegExpConstant, RegExpNormalChar] - +# 37| [RegExpConstant, RegExpNormalChar] - -# 40| [RegExpCharacterClass] [|] +# 38| [RegExpCharacterClass] [|] #-----| 0 -> [RegExpConstant, RegExpNormalChar] | -# 40| [RegExpConstant, RegExpNormalChar] | +# 38| [RegExpConstant, RegExpNormalChar] | -# 43| [RegExpCharacterClass] [[a-f] +# 41| [RegExpCharacterClass] [[a-f] #-----| 0 -> [RegExpConstant, RegExpNormalChar] [ #-----| 1 -> [RegExpCharacterRange] a-f -# 43| [RegExpSequence] [[a-f]A-F] +# 41| [RegExpSequence] [[a-f]A-F] #-----| 0 -> [RegExpCharacterClass] [[a-f] #-----| 1 -> [RegExpConstant, RegExpNormalChar] A-F] -# 43| [RegExpConstant, RegExpNormalChar] [ +# 41| [RegExpConstant, RegExpNormalChar] [ -# 43| [RegExpConstant, RegExpNormalChar] a +# 41| [RegExpConstant, RegExpNormalChar] a -# 43| [RegExpCharacterRange] a-f +# 41| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 43| [RegExpConstant, RegExpNormalChar] f +# 41| [RegExpConstant, RegExpNormalChar] f -# 43| [RegExpConstant, RegExpNormalChar] A-F] +# 41| [RegExpConstant, RegExpNormalChar] A-F] -# 46| [RegExpDot] . +# 44| [RegExpDot] . -# 46| [RegExpStar] .* +# 44| [RegExpStar] .* #-----| 0 -> [RegExpDot] . -# 48| [RegExpCharacterClassEscape] \w +# 45| [RegExpCharacterClassEscape] \w -# 48| [RegExpPlus] \w+ +# 45| [RegExpPlus] \w+ #-----| 0 -> [RegExpCharacterClassEscape] \w -# 48| [RegExpSequence] \w+\W +# 45| [RegExpSequence] \w+\W #-----| 0 -> [RegExpPlus] \w+ #-----| 1 -> [RegExpCharacterClassEscape] \W -# 48| [RegExpCharacterClassEscape] \W +# 45| [RegExpCharacterClassEscape] \W -# 49| [RegExpCharacterClassEscape] \s +# 46| [RegExpCharacterClassEscape] \s -# 49| [RegExpSequence] \s\S +# 46| [RegExpSequence] \s\S #-----| 0 -> [RegExpCharacterClassEscape] \s #-----| 1 -> [RegExpCharacterClassEscape] \S -# 49| [RegExpCharacterClassEscape] \S +# 46| [RegExpCharacterClassEscape] \S -# 50| [RegExpCharacterClassEscape] \d +# 47| [RegExpCharacterClassEscape] \d -# 50| [RegExpSequence] \d\D +# 47| [RegExpSequence] \d\D #-----| 0 -> [RegExpCharacterClassEscape] \d #-----| 1 -> [RegExpCharacterClassEscape] \D -# 50| [RegExpCharacterClassEscape] \D +# 47| [RegExpCharacterClassEscape] \D -# 52| [RegExpConstant, RegExpEscape] \n +# 48| [RegExpConstant, RegExpEscape] \n -# 52| [RegExpSequence] \n\r\t +# 48| [RegExpSequence] \n\r\t #-----| 0 -> [RegExpConstant, RegExpEscape] \n #-----| 1 -> [RegExpConstant, RegExpEscape] \r #-----| 2 -> [RegExpConstant, RegExpEscape] \t -# 52| [RegExpConstant, RegExpEscape] \r +# 48| [RegExpConstant, RegExpEscape] \r -# 52| [RegExpConstant, RegExpEscape] \t +# 48| [RegExpConstant, RegExpEscape] \t -# 56| [RegExpSpecialChar] \b +# 51| [RegExpSpecialChar] \b -# 56| [RegExpSequence] \b!a\B +# 51| [RegExpSequence] \b!a\B #-----| 0 -> [RegExpSpecialChar] \b #-----| 1 -> [RegExpConstant, RegExpNormalChar] !a #-----| 2 -> [RegExpNonWordBoundary] \B -# 56| [RegExpConstant, RegExpNormalChar] !a +# 51| [RegExpConstant, RegExpNormalChar] !a -# 56| [RegExpNonWordBoundary] \B +# 51| [RegExpNonWordBoundary] \B -# 59| [RegExpGroup] (foo) +# 54| [RegExpGroup] (foo) #-----| 0 -> [RegExpConstant, RegExpNormalChar] foo -# 59| [RegExpStar] (foo)* +# 54| [RegExpStar] (foo)* #-----| 0 -> [RegExpGroup] (foo) -# 59| [RegExpSequence] (foo)*bar +# 54| [RegExpSequence] (foo)*bar #-----| 0 -> [RegExpStar] (foo)* #-----| 1 -> [RegExpConstant, RegExpNormalChar] bar -# 59| [RegExpConstant, RegExpNormalChar] foo +# 54| [RegExpConstant, RegExpNormalChar] foo -# 59| [RegExpConstant, RegExpNormalChar] bar +# 54| [RegExpConstant, RegExpNormalChar] bar -# 60| [RegExpConstant, RegExpNormalChar] fo +# 55| [RegExpConstant, RegExpNormalChar] fo -# 60| [RegExpSequence] fo(o|b)ar +# 55| [RegExpSequence] fo(o|b)ar #-----| 0 -> [RegExpConstant, RegExpNormalChar] fo #-----| 1 -> [RegExpGroup] (o|b) #-----| 2 -> [RegExpConstant, RegExpNormalChar] ar -# 60| [RegExpGroup] (o|b) +# 55| [RegExpGroup] (o|b) #-----| 0 -> [RegExpAlt] o|b -# 60| [RegExpConstant, RegExpNormalChar] o +# 55| [RegExpConstant, RegExpNormalChar] o -# 60| [RegExpAlt] o|b +# 55| [RegExpAlt] o|b #-----| 0 -> [RegExpConstant, RegExpNormalChar] o #-----| 1 -> [RegExpConstant, RegExpNormalChar] b -# 60| [RegExpConstant, RegExpNormalChar] b +# 55| [RegExpConstant, RegExpNormalChar] b -# 60| [RegExpConstant, RegExpNormalChar] ar +# 55| [RegExpConstant, RegExpNormalChar] ar -# 61| [RegExpGroup] (a|b|cd) +# 56| [RegExpGroup] (a|b|cd) #-----| 0 -> [RegExpAlt] a|b|cd -# 61| [RegExpSequence] (a|b|cd)e +# 56| [RegExpSequence] (a|b|cd)e #-----| 0 -> [RegExpGroup] (a|b|cd) #-----| 1 -> [RegExpConstant, RegExpNormalChar] e -# 61| [RegExpConstant, RegExpNormalChar] a +# 56| [RegExpConstant, RegExpNormalChar] a -# 61| [RegExpAlt] a|b|cd +# 56| [RegExpAlt] a|b|cd #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] b #-----| 2 -> [RegExpConstant, RegExpNormalChar] cd -# 61| [RegExpConstant, RegExpNormalChar] b +# 56| [RegExpConstant, RegExpNormalChar] b -# 61| [RegExpConstant, RegExpNormalChar] cd +# 56| [RegExpConstant, RegExpNormalChar] cd -# 61| [RegExpConstant, RegExpNormalChar] e +# 56| [RegExpConstant, RegExpNormalChar] e -# 62| [RegExpGroup] (?::+) +# 57| [RegExpGroup] (?::+) #-----| 0 -> [RegExpPlus] :+ -# 62| [RegExpSequence] (?::+)\w +# 57| [RegExpSequence] (?::+)\w #-----| 0 -> [RegExpGroup] (?::+) #-----| 1 -> [RegExpCharacterClassEscape] \w -# 62| [RegExpConstant, RegExpNormalChar] : +# 57| [RegExpConstant, RegExpNormalChar] : -# 62| [RegExpPlus] :+ +# 57| [RegExpPlus] :+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] : -# 62| [RegExpCharacterClassEscape] \w +# 57| [RegExpCharacterClassEscape] \w -# 69| [RegExpGroup] (a+) +# 60| [RegExpGroup] (a+) #-----| 0 -> [RegExpPlus] a+ -# 69| [RegExpSequence] (a+)b+\1 +# 60| [RegExpSequence] (a+)b+\1 #-----| 0 -> [RegExpGroup] (a+) #-----| 1 -> [RegExpPlus] b+ #-----| 2 -> [RegExpBackRef] \1 -# 69| [RegExpConstant, RegExpNormalChar] a +# 60| [RegExpConstant, RegExpNormalChar] a -# 69| [RegExpPlus] a+ +# 60| [RegExpPlus] a+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 69| [RegExpConstant, RegExpNormalChar] b +# 60| [RegExpConstant, RegExpNormalChar] b -# 69| [RegExpPlus] b+ +# 60| [RegExpPlus] b+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] b -# 69| [RegExpBackRef] \1 +# 60| [RegExpBackRef] \1 -# 73| [RegExpCharacterClass] [[:alnum:]] +# 63| [RegExpCharacterClass] [[:alnum:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] -# 73| [RegExpStar] [[:alnum:]]* +# 63| [RegExpStar] [[:alnum:]]* #-----| 0 -> [RegExpCharacterClass] [[:alnum:]] -# 73| [RegExpNamedCharacterProperty] [:alnum:] +# 63| [RegExpNamedCharacterProperty] [:alnum:] -# 74| [RegExpCharacterClass] [[:digit:]] +# 64| [RegExpCharacterClass] [[:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] -# 74| [RegExpPlus] [[:digit:]]+ +# 64| [RegExpPlus] [[:digit:]]+ #-----| 0 -> [RegExpCharacterClass] [[:digit:]] -# 74| [RegExpNamedCharacterProperty] [:digit:] +# 64| [RegExpNamedCharacterProperty] [:digit:] -# 75| [RegExpCharacterClass] [[:alnum:]] +# 65| [RegExpCharacterClass] [[:alnum:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] -# 75| [RegExpRange] [[:alnum:]]{2,3} +# 65| [RegExpRange] [[:alnum:]]{2,3} #-----| 0 -> [RegExpCharacterClass] [[:alnum:]] -# 75| [RegExpNamedCharacterProperty] [:alnum:] +# 65| [RegExpNamedCharacterProperty] [:alnum:] -# 76| [RegExpCharacterClass] [a-f[:digit:]] +# 66| [RegExpCharacterClass] [a-f[:digit:]] #-----| 0 -> [RegExpCharacterRange] a-f #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] -# 76| [RegExpPlus] [a-f[:digit:]]+ +# 66| [RegExpPlus] [a-f[:digit:]]+ #-----| 0 -> [RegExpCharacterClass] [a-f[:digit:]] -# 76| [RegExpConstant, RegExpNormalChar] a +# 66| [RegExpConstant, RegExpNormalChar] a -# 76| [RegExpCharacterRange] a-f +# 66| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 76| [RegExpConstant, RegExpNormalChar] f +# 66| [RegExpConstant, RegExpNormalChar] f -# 76| [RegExpNamedCharacterProperty] [:digit:] +# 66| [RegExpNamedCharacterProperty] [:digit:] -# 79| [RegExpCharacterClass] [[:alpha:]] +# 69| [RegExpCharacterClass] [[:alpha:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] -# 79| [RegExpSequence] [[:alpha:]][[:digit:]] +# 69| [RegExpSequence] [[:alpha:]][[:digit:]] #-----| 0 -> [RegExpCharacterClass] [[:alpha:]] #-----| 1 -> [RegExpCharacterClass] [[:digit:]] -# 79| [RegExpNamedCharacterProperty] [:alpha:] +# 69| [RegExpNamedCharacterProperty] [:alpha:] -# 79| [RegExpCharacterClass] [[:digit:]] +# 69| [RegExpCharacterClass] [[:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] -# 79| [RegExpNamedCharacterProperty] [:digit:] +# 69| [RegExpNamedCharacterProperty] [:digit:] -# 82| [RegExpCharacterClass] [[:alpha:][:digit:]] +# 72| [RegExpCharacterClass] [[:alpha:][:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] -# 82| [RegExpNamedCharacterProperty] [:alpha:] +# 72| [RegExpNamedCharacterProperty] [:alpha:] -# 82| [RegExpNamedCharacterProperty] [:digit:] +# 72| [RegExpNamedCharacterProperty] [:digit:] -# 85| [RegExpCharacterClass] [A-F[:digit:]a-f] +# 75| [RegExpCharacterClass] [A-F[:digit:]a-f] #-----| 0 -> [RegExpCharacterRange] A-F #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] #-----| 2 -> [RegExpCharacterRange] a-f -# 85| [RegExpConstant, RegExpNormalChar] A +# 75| [RegExpConstant, RegExpNormalChar] A -# 85| [RegExpCharacterRange] A-F +# 75| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 85| [RegExpConstant, RegExpNormalChar] F +# 75| [RegExpConstant, RegExpNormalChar] F -# 85| [RegExpNamedCharacterProperty] [:digit:] +# 75| [RegExpNamedCharacterProperty] [:digit:] -# 85| [RegExpConstant, RegExpNormalChar] a +# 75| [RegExpConstant, RegExpNormalChar] a -# 85| [RegExpCharacterRange] a-f +# 75| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 85| [RegExpConstant, RegExpNormalChar] f +# 75| [RegExpConstant, RegExpNormalChar] f -# 88| [RegExpNamedCharacterProperty] [:digit:] +# 78| [RegExpNamedCharacterProperty] [:digit:] -# 91| [RegExpConstant, RegExpEscape] \u{987 +# 81| [RegExpConstant, RegExpEscape] \u{987 -# 94| [RegExpConstant, RegExpNormalChar] a +# 84| [RegExpConstant, RegExpEscape] \c + +# 84| [RegExpSequence] \cA +#-----| 0 -> [RegExpConstant, RegExpEscape] \c +#-----| 1 -> [RegExpConstant, RegExpNormalChar] A -# 94| [RegExpSequence] a\nb +# 84| [RegExpConstant, RegExpNormalChar] A + +# 85| [RegExpCharacterClass] [\cZ] +#-----| 0 -> [RegExpConstant, RegExpEscape] \c +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 85| [RegExpConstant, RegExpEscape] \c + +# 85| [RegExpConstant, RegExpNormalChar] Z + +# 88| [RegExpConstant, RegExpNormalChar] a + +# 88| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] b -# 94| [RegExpConstant, RegExpEscape] \n +# 88| [RegExpConstant, RegExpEscape] \n -# 94| [RegExpConstant, RegExpNormalChar] b +# 88| [RegExpConstant, RegExpNormalChar] b -# 95| [RegExpConstant, RegExpNormalChar] a +# 89| [RegExpConstant, RegExpNormalChar] a -# 95| [RegExpSequence] a\nc +# 89| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 95| [RegExpConstant, RegExpEscape] \n +# 89| [RegExpConstant, RegExpEscape] \n -# 95| [RegExpConstant, RegExpNormalChar] c +# 89| [RegExpConstant, RegExpNormalChar] c -# 96| [RegExpConstant, RegExpNormalChar] a +# 90| [RegExpConstant, RegExpNormalChar] a -# 96| [RegExpSequence] a\nc +# 90| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 96| [RegExpConstant, RegExpEscape] \n +# 90| [RegExpConstant, RegExpEscape] \n -# 96| [RegExpConstant, RegExpNormalChar] c +# 90| [RegExpConstant, RegExpNormalChar] c -# 97| [RegExpConstant, RegExpNormalChar] a +# 91| [RegExpConstant, RegExpNormalChar] a -# 97| [RegExpSequence] a\nc +# 91| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 97| [RegExpConstant, RegExpEscape] \n +# 91| [RegExpConstant, RegExpEscape] \n -# 97| [RegExpConstant, RegExpNormalChar] c +# 91| [RegExpConstant, RegExpNormalChar] c -# 98| [RegExpConstant, RegExpNormalChar] a +# 92| [RegExpConstant, RegExpNormalChar] a -# 98| [RegExpSequence] a\nc +# 92| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 98| [RegExpConstant, RegExpEscape] \n +# 92| [RegExpConstant, RegExpEscape] \n -# 98| [RegExpConstant, RegExpNormalChar] c +# 92| [RegExpConstant, RegExpNormalChar] c -# 99| [RegExpConstant, RegExpNormalChar] a +# 93| [RegExpConstant, RegExpNormalChar] a -# 99| [RegExpSequence] a\nc +# 93| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 99| [RegExpConstant, RegExpEscape] \n +# 93| [RegExpConstant, RegExpEscape] \n -# 99| [RegExpConstant, RegExpNormalChar] c +# 93| [RegExpConstant, RegExpNormalChar] c -# 100| [RegExpConstant, RegExpNormalChar] a +# 94| [RegExpConstant, RegExpNormalChar] a -# 100| [RegExpSequence] a\nc +# 94| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 100| [RegExpConstant, RegExpEscape] \n +# 94| [RegExpConstant, RegExpEscape] \n -# 100| [RegExpConstant, RegExpNormalChar] c +# 94| [RegExpConstant, RegExpNormalChar] c -# 101| [RegExpConstant, RegExpNormalChar] a +# 95| [RegExpConstant, RegExpNormalChar] a -# 101| [RegExpSequence] a\nc +# 95| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 101| [RegExpConstant, RegExpEscape] \n +# 95| [RegExpConstant, RegExpEscape] \n -# 101| [RegExpConstant, RegExpNormalChar] c +# 95| [RegExpConstant, RegExpNormalChar] c -# 102| [RegExpConstant, RegExpNormalChar] a +# 96| [RegExpConstant, RegExpNormalChar] a -# 102| [RegExpSequence] a\nc +# 96| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 102| [RegExpConstant, RegExpEscape] \n +# 96| [RegExpConstant, RegExpEscape] \n -# 102| [RegExpConstant, RegExpNormalChar] c +# 96| [RegExpConstant, RegExpNormalChar] c -# 103| [RegExpConstant, RegExpNormalChar] a +# 97| [RegExpConstant, RegExpNormalChar] a -# 103| [RegExpSequence] a\nc +# 97| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 103| [RegExpConstant, RegExpEscape] \n +# 97| [RegExpConstant, RegExpEscape] \n -# 103| [RegExpConstant, RegExpNormalChar] c +# 97| [RegExpConstant, RegExpNormalChar] c -# 104| [RegExpConstant, RegExpNormalChar] a +# 98| [RegExpConstant, RegExpNormalChar] a -# 104| [RegExpSequence] a\nc +# 98| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 104| [RegExpConstant, RegExpEscape] \n +# 98| [RegExpConstant, RegExpEscape] \n -# 104| [RegExpConstant, RegExpNormalChar] c +# 98| [RegExpConstant, RegExpNormalChar] c -# 105| [RegExpConstant, RegExpNormalChar] a +# 99| [RegExpConstant, RegExpNormalChar] a -# 105| [RegExpSequence] a\nc +# 99| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 105| [RegExpConstant, RegExpEscape] \n +# 99| [RegExpConstant, RegExpEscape] \n -# 105| [RegExpConstant, RegExpNormalChar] c +# 99| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index ede36252b6f6..462f5ecbc293 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -20,9 +20,8 @@ std::regex r_abc("abc"); // Repetition std::regex r_rep1("a*b+c?d"); std::regex r_rep2("a{4,8}"); - -std::regex r_rep4("a{3,}"); -std::regex r_rep5("a{7}"); +std::regex r_rep3("a{3,}"); +std::regex r_rep4("a{7}"); // Alternation std::regex r_alt("foo|bar"); @@ -30,30 +29,26 @@ std::regex r_alt("foo|bar"); // Character classes std::regex r_cc1("[abc]"); std::regex r_cc2("[a-fA-F0-9_]"); - -std::regex r_cc4("[\\w]+"); -std::regex r_cc5("\\[\\][123]"); -std::regex r_cc6("[^A-Z]"); -std::regex r_cc7("[]]"); // MRI gives a warning, but accepts this as matching ']' -std::regex r_cc8("[^]]"); // MRI gives a warning, but accepts this as matching anything except ']' -std::regex r_cc9("[^-]"); -std::regex r_cc10("[|]"); +std::regex r_cc3("[\\w]+"); +std::regex r_cc4("\\[\\][123]"); +std::regex r_cc5("[^A-Z]"); +std::regex r_cc6("[]]"); // MRI gives a warning, but accepts this as matching ']' +std::regex r_cc7("[^]]"); // MRI gives a warning, but accepts this as matching anything except ']' +std::regex r_cc8("[^-]"); +std::regex r_cc9("[|]"); // Nested character classes (BAD - not parsed correctly) std::regex r_nested("[[a-f]A-F]"); // Meta-character classes std::regex r_meta1(".*"); - std::regex r_meta2("\\w+\\W"); std::regex r_meta3("\\s\\S"); std::regex r_meta4("\\d\\D"); - -std::regex r_meta6("\\n\\r\\t"); +std::regex r_meta5("\\n\\r\\t"); // Anchors - -std::regex r_anc2("\\b!a\\B"); +std::regex r_anc1("\\b!a\\B"); // Groups std::regex r_grp1("(foo)*bar"); @@ -61,14 +56,9 @@ std::regex r_grp2("fo(o|b)ar"); std::regex r_grp3("(a|b|cd)e"); std::regex r_grp4("(?::+)\\w"); // Non-capturing group matching colons - - - - // Backreferences std::regex r_bref1("(a+)b+\\1"); - // Named character properties using a single POSIX bracket expression std::regex r_prop1("[[:alnum:]]*"); std::regex r_prop2("[[:digit:]]+"); @@ -90,6 +80,10 @@ std::regex r_posix4("[:digit:]"); // unicode std::regex r_uni("\\u{9879}"); +// control escapes +std::regex r_ctrl1("\\cA"); +std::regex r_ctrl2("[\\cZ]"); + // String literal spellings for location tests std::regex r_loc_plain("a\\nb"); std::basic_regex r_loc_L(L"a\\nc"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 1eb1b1e5ff02..caede9ebdf9f 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -1,8 +1,8 @@ groupNumber -| regexp.cpp:59:20:59:24 | (foo) | 1 | -| regexp.cpp:60:22:60:26 | (o\|b) | 1 | -| regexp.cpp:61:20:61:27 | (a\|b\|cd) | 1 | -| regexp.cpp:69:21:69:24 | (a+) | 1 | +| regexp.cpp:54:20:54:24 | (foo) | 1 | +| regexp.cpp:55:22:55:26 | (o\|b) | 1 | +| regexp.cpp:56:20:56:27 | (a\|b\|cd) | 1 | +| regexp.cpp:60:21:60:24 | (a+) | 1 | term | regexp.cpp:18:19:18:21 | abc | RegExpConstant,RegExpNormalChar | | regexp.cpp:21:20:21:20 | a | RegExpConstant,RegExpNormalChar | @@ -15,189 +15,195 @@ term | regexp.cpp:21:26:21:26 | d | RegExpConstant,RegExpNormalChar | | regexp.cpp:22:20:22:20 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:22:20:22:25 | a{4,8} | RegExpRange | +| regexp.cpp:23:20:23:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:23:20:23:24 | a{3,} | InfiniteRepetitionQuantifier,RegExpRange | | regexp.cpp:24:20:24:20 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:24:20:24:24 | a{3,} | InfiniteRepetitionQuantifier,RegExpRange | -| regexp.cpp:25:20:25:20 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:25:20:25:23 | a{7} | RegExpRange | -| regexp.cpp:28:19:28:21 | foo | RegExpConstant,RegExpNormalChar | -| regexp.cpp:28:19:28:25 | foo\|bar | RegExpAlt | -| regexp.cpp:28:23:28:25 | bar | RegExpConstant,RegExpNormalChar | -| regexp.cpp:31:19:31:23 | [abc] | RegExpCharacterClass | +| regexp.cpp:24:20:24:23 | a{7} | RegExpRange | +| regexp.cpp:27:19:27:21 | foo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:27:19:27:25 | foo\|bar | RegExpAlt | +| regexp.cpp:27:23:27:25 | bar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:30:19:30:23 | [abc] | RegExpCharacterClass | +| regexp.cpp:30:20:30:20 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:30:21:30:21 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:30:22:30:22 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:19:31:30 | [a-fA-F0-9_] | RegExpCharacterClass | | regexp.cpp:31:20:31:20 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:31:21:31:21 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:31:22:31:22 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:19:32:30 | [a-fA-F0-9_] | RegExpCharacterClass | -| regexp.cpp:32:20:32:20 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:20:32:22 | a-f | RegExpCharacterRange | -| regexp.cpp:32:22:32:22 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:23:32:23 | A | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:23:32:25 | A-F | RegExpCharacterRange | -| regexp.cpp:32:25:32:25 | F | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:26:32:26 | 0 | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:26:32:28 | 0-9 | RegExpCharacterRange | -| regexp.cpp:32:28:32:28 | 9 | RegExpConstant,RegExpNormalChar | -| regexp.cpp:32:29:32:29 | _ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:34:19:34:22 | [\\w] | RegExpCharacterClass | -| regexp.cpp:34:19:34:23 | [\\w]+ | RegExpPlus | -| regexp.cpp:34:20:34:21 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:35:19:35:20 | \\[ | RegExpConstant,RegExpEscape | -| regexp.cpp:35:19:35:27 | \\[\\][123] | RegExpSequence | -| regexp.cpp:35:21:35:22 | \\] | RegExpConstant,RegExpEscape | -| regexp.cpp:35:23:35:27 | [123] | RegExpCharacterClass | -| regexp.cpp:35:24:35:24 | 1 | RegExpConstant,RegExpNormalChar | -| regexp.cpp:35:25:35:25 | 2 | RegExpConstant,RegExpNormalChar | -| regexp.cpp:35:26:35:26 | 3 | RegExpConstant,RegExpNormalChar | -| regexp.cpp:36:19:36:24 | [^A-Z] | RegExpCharacterClass | -| regexp.cpp:36:21:36:21 | A | RegExpConstant,RegExpNormalChar | -| regexp.cpp:36:21:36:23 | A-Z | RegExpCharacterRange | -| regexp.cpp:36:23:36:23 | Z | RegExpConstant,RegExpNormalChar | -| regexp.cpp:37:19:37:21 | []] | RegExpCharacterClass | -| regexp.cpp:37:20:37:20 | ] | RegExpConstant,RegExpNormalChar | -| regexp.cpp:38:19:38:22 | [^]] | RegExpCharacterClass | -| regexp.cpp:38:21:38:21 | ] | RegExpConstant,RegExpNormalChar | -| regexp.cpp:39:19:39:22 | [^-] | RegExpCharacterClass | -| regexp.cpp:39:21:39:21 | - | RegExpConstant,RegExpNormalChar | -| regexp.cpp:40:20:40:22 | [\|] | RegExpCharacterClass | -| regexp.cpp:40:21:40:21 | \| | RegExpConstant,RegExpNormalChar | -| regexp.cpp:43:22:43:27 | [[a-f] | RegExpCharacterClass | -| regexp.cpp:43:22:43:31 | [[a-f]A-F] | RegExpSequence | -| regexp.cpp:43:23:43:23 | [ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:43:24:43:24 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:43:24:43:26 | a-f | RegExpCharacterRange | -| regexp.cpp:43:26:43:26 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:43:28:43:31 | A-F] | RegExpConstant,RegExpNormalChar | -| regexp.cpp:46:21:46:21 | . | RegExpDot | -| regexp.cpp:46:21:46:22 | .* | RegExpStar | -| regexp.cpp:48:21:48:22 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:48:21:48:23 | \\w+ | RegExpPlus | -| regexp.cpp:48:21:48:25 | \\w+\\W | RegExpSequence | -| regexp.cpp:48:24:48:25 | \\W | RegExpCharacterClassEscape | -| regexp.cpp:49:21:49:22 | \\s | RegExpCharacterClassEscape | -| regexp.cpp:49:21:49:24 | \\s\\S | RegExpSequence | -| regexp.cpp:49:23:49:24 | \\S | RegExpCharacterClassEscape | -| regexp.cpp:50:21:50:22 | \\d | RegExpCharacterClassEscape | -| regexp.cpp:50:21:50:24 | \\d\\D | RegExpSequence | -| regexp.cpp:50:23:50:24 | \\D | RegExpCharacterClassEscape | -| regexp.cpp:52:21:52:22 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:52:21:52:26 | \\n\\r\\t | RegExpSequence | -| regexp.cpp:52:23:52:24 | \\r | RegExpConstant,RegExpEscape | -| regexp.cpp:52:25:52:26 | \\t | RegExpConstant,RegExpEscape | -| regexp.cpp:56:20:56:21 | \\b | RegExpSpecialChar | -| regexp.cpp:56:20:56:25 | \\b!a\\B | RegExpSequence | -| regexp.cpp:56:22:56:23 | !a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:56:24:56:25 | \\B | RegExpNonWordBoundary | -| regexp.cpp:59:20:59:24 | (foo) | RegExpGroup | -| regexp.cpp:59:20:59:25 | (foo)* | RegExpStar | -| regexp.cpp:59:20:59:28 | (foo)*bar | RegExpSequence | -| regexp.cpp:59:21:59:23 | foo | RegExpConstant,RegExpNormalChar | -| regexp.cpp:59:26:59:28 | bar | RegExpConstant,RegExpNormalChar | -| regexp.cpp:60:20:60:21 | fo | RegExpConstant,RegExpNormalChar | -| regexp.cpp:60:20:60:28 | fo(o\|b)ar | RegExpSequence | -| regexp.cpp:60:22:60:26 | (o\|b) | RegExpGroup | -| regexp.cpp:60:23:60:23 | o | RegExpConstant,RegExpNormalChar | -| regexp.cpp:60:23:60:25 | o\|b | RegExpAlt | +| regexp.cpp:31:20:31:22 | a-f | RegExpCharacterRange | +| regexp.cpp:31:22:31:22 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:23:31:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:23:31:25 | A-F | RegExpCharacterRange | +| regexp.cpp:31:25:31:25 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:26:31:26 | 0 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:26:31:28 | 0-9 | RegExpCharacterRange | +| regexp.cpp:31:28:31:28 | 9 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:31:29:31:29 | _ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:32:19:32:22 | [\\w] | RegExpCharacterClass | +| regexp.cpp:32:19:32:23 | [\\w]+ | RegExpPlus | +| regexp.cpp:32:20:32:21 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:33:19:33:20 | \\[ | RegExpConstant,RegExpEscape | +| regexp.cpp:33:19:33:27 | \\[\\][123] | RegExpSequence | +| regexp.cpp:33:21:33:22 | \\] | RegExpConstant,RegExpEscape | +| regexp.cpp:33:23:33:27 | [123] | RegExpCharacterClass | +| regexp.cpp:33:24:33:24 | 1 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:33:25:33:25 | 2 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:33:26:33:26 | 3 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:34:19:34:24 | [^A-Z] | RegExpCharacterClass | +| regexp.cpp:34:21:34:21 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:34:21:34:23 | A-Z | RegExpCharacterRange | +| regexp.cpp:34:23:34:23 | Z | RegExpConstant,RegExpNormalChar | +| regexp.cpp:35:19:35:21 | []] | RegExpCharacterClass | +| regexp.cpp:35:20:35:20 | ] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:36:19:36:22 | [^]] | RegExpCharacterClass | +| regexp.cpp:36:21:36:21 | ] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:37:19:37:22 | [^-] | RegExpCharacterClass | +| regexp.cpp:37:21:37:21 | - | RegExpConstant,RegExpNormalChar | +| regexp.cpp:38:19:38:21 | [\|] | RegExpCharacterClass | +| regexp.cpp:38:20:38:20 | \| | RegExpConstant,RegExpNormalChar | +| regexp.cpp:41:22:41:27 | [[a-f] | RegExpCharacterClass | +| regexp.cpp:41:22:41:31 | [[a-f]A-F] | RegExpSequence | +| regexp.cpp:41:23:41:23 | [ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:41:24:41:24 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:41:24:41:26 | a-f | RegExpCharacterRange | +| regexp.cpp:41:26:41:26 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:41:28:41:31 | A-F] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:44:21:44:21 | . | RegExpDot | +| regexp.cpp:44:21:44:22 | .* | RegExpStar | +| regexp.cpp:45:21:45:22 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:45:21:45:23 | \\w+ | RegExpPlus | +| regexp.cpp:45:21:45:25 | \\w+\\W | RegExpSequence | +| regexp.cpp:45:24:45:25 | \\W | RegExpCharacterClassEscape | +| regexp.cpp:46:21:46:22 | \\s | RegExpCharacterClassEscape | +| regexp.cpp:46:21:46:24 | \\s\\S | RegExpSequence | +| regexp.cpp:46:23:46:24 | \\S | RegExpCharacterClassEscape | +| regexp.cpp:47:21:47:22 | \\d | RegExpCharacterClassEscape | +| regexp.cpp:47:21:47:24 | \\d\\D | RegExpSequence | +| regexp.cpp:47:23:47:24 | \\D | RegExpCharacterClassEscape | +| regexp.cpp:48:21:48:22 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:48:21:48:26 | \\n\\r\\t | RegExpSequence | +| regexp.cpp:48:23:48:24 | \\r | RegExpConstant,RegExpEscape | +| regexp.cpp:48:25:48:26 | \\t | RegExpConstant,RegExpEscape | +| regexp.cpp:51:20:51:21 | \\b | RegExpSpecialChar | +| regexp.cpp:51:20:51:25 | \\b!a\\B | RegExpSequence | +| regexp.cpp:51:22:51:23 | !a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:51:24:51:25 | \\B | RegExpNonWordBoundary | +| regexp.cpp:54:20:54:24 | (foo) | RegExpGroup | +| regexp.cpp:54:20:54:25 | (foo)* | RegExpStar | +| regexp.cpp:54:20:54:28 | (foo)*bar | RegExpSequence | +| regexp.cpp:54:21:54:23 | foo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:54:26:54:28 | bar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:55:20:55:21 | fo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:55:20:55:28 | fo(o\|b)ar | RegExpSequence | +| regexp.cpp:55:22:55:26 | (o\|b) | RegExpGroup | +| regexp.cpp:55:23:55:23 | o | RegExpConstant,RegExpNormalChar | +| regexp.cpp:55:23:55:25 | o\|b | RegExpAlt | +| regexp.cpp:55:25:55:25 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:55:27:55:28 | ar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:20:56:27 | (a\|b\|cd) | RegExpGroup | +| regexp.cpp:56:20:56:28 | (a\|b\|cd)e | RegExpSequence | +| regexp.cpp:56:21:56:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:21:56:26 | a\|b\|cd | RegExpAlt | +| regexp.cpp:56:23:56:23 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:25:56:26 | cd | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:28:56:28 | e | RegExpConstant,RegExpNormalChar | +| regexp.cpp:57:20:57:25 | (?::+) | RegExpGroup | +| regexp.cpp:57:20:57:27 | (?::+)\\w | RegExpSequence | +| regexp.cpp:57:23:57:23 | : | RegExpConstant,RegExpNormalChar | +| regexp.cpp:57:23:57:24 | :+ | RegExpPlus | +| regexp.cpp:57:26:57:27 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:60:21:60:24 | (a+) | RegExpGroup | +| regexp.cpp:60:21:60:28 | (a+)b+\\1 | RegExpSequence | +| regexp.cpp:60:22:60:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:60:22:60:23 | a+ | RegExpPlus | | regexp.cpp:60:25:60:25 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:60:27:60:28 | ar | RegExpConstant,RegExpNormalChar | -| regexp.cpp:61:20:61:27 | (a\|b\|cd) | RegExpGroup | -| regexp.cpp:61:20:61:28 | (a\|b\|cd)e | RegExpSequence | -| regexp.cpp:61:21:61:21 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:61:21:61:26 | a\|b\|cd | RegExpAlt | -| regexp.cpp:61:23:61:23 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:61:25:61:26 | cd | RegExpConstant,RegExpNormalChar | -| regexp.cpp:61:28:61:28 | e | RegExpConstant,RegExpNormalChar | -| regexp.cpp:62:20:62:25 | (?::+) | RegExpGroup | -| regexp.cpp:62:20:62:27 | (?::+)\\w | RegExpSequence | -| regexp.cpp:62:23:62:23 | : | RegExpConstant,RegExpNormalChar | -| regexp.cpp:62:23:62:24 | :+ | RegExpPlus | -| regexp.cpp:62:26:62:27 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:69:21:69:24 | (a+) | RegExpGroup | -| regexp.cpp:69:21:69:28 | (a+)b+\\1 | RegExpSequence | -| regexp.cpp:69:22:69:22 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:69:22:69:23 | a+ | RegExpPlus | -| regexp.cpp:69:25:69:25 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:69:25:69:26 | b+ | RegExpPlus | -| regexp.cpp:69:27:69:28 | \\1 | RegExpBackRef | -| regexp.cpp:73:21:73:31 | [[:alnum:]] | RegExpCharacterClass | -| regexp.cpp:73:21:73:32 | [[:alnum:]]* | RegExpStar | -| regexp.cpp:73:22:73:30 | [:alnum:] | RegExpNamedCharacterProperty | -| regexp.cpp:74:21:74:31 | [[:digit:]] | RegExpCharacterClass | -| regexp.cpp:74:21:74:32 | [[:digit:]]+ | RegExpPlus | -| regexp.cpp:74:22:74:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:75:21:75:31 | [[:alnum:]] | RegExpCharacterClass | -| regexp.cpp:75:21:75:36 | [[:alnum:]]{2,3} | RegExpRange | -| regexp.cpp:75:22:75:30 | [:alnum:] | RegExpNamedCharacterProperty | -| regexp.cpp:76:21:76:34 | [a-f[:digit:]] | RegExpCharacterClass | -| regexp.cpp:76:21:76:35 | [a-f[:digit:]]+ | RegExpPlus | -| regexp.cpp:76:22:76:22 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:76:22:76:24 | a-f | RegExpCharacterRange | -| regexp.cpp:76:24:76:24 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:76:25:76:33 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:79:22:79:32 | [[:alpha:]] | RegExpCharacterClass | -| regexp.cpp:79:22:79:43 | [[:alpha:]][[:digit:]] | RegExpSequence | -| regexp.cpp:79:23:79:31 | [:alpha:] | RegExpNamedCharacterProperty | -| regexp.cpp:79:33:79:43 | [[:digit:]] | RegExpCharacterClass | -| regexp.cpp:79:34:79:42 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:82:22:82:41 | [[:alpha:][:digit:]] | RegExpCharacterClass | -| regexp.cpp:82:23:82:31 | [:alpha:] | RegExpNamedCharacterProperty | -| regexp.cpp:82:32:82:40 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:85:22:85:38 | [A-F[:digit:]a-f] | RegExpCharacterClass | -| regexp.cpp:85:23:85:23 | A | RegExpConstant,RegExpNormalChar | -| regexp.cpp:85:23:85:25 | A-F | RegExpCharacterRange | -| regexp.cpp:85:25:85:25 | F | RegExpConstant,RegExpNormalChar | -| regexp.cpp:85:26:85:34 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:85:35:85:35 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:85:35:85:37 | a-f | RegExpCharacterRange | -| regexp.cpp:85:37:85:37 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:88:22:88:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:91:19:91:24 | \\u{987 | RegExpConstant,RegExpEscape | -| regexp.cpp:94:25:94:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:94:25:94:28 | a\\nb | RegExpSequence | -| regexp.cpp:94:26:94:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:94:28:94:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:37:95:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:37:95:40 | a\\nc | RegExpSequence | -| regexp.cpp:95:38:95:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:95:40:95:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:39:96:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:39:96:42 | a\\nc | RegExpSequence | -| regexp.cpp:96:40:96:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:96:42:96:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:38:97:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:38:97:41 | a\\nc | RegExpSequence | -| regexp.cpp:97:39:97:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:97:41:97:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:38:98:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:38:98:41 | a\\nc | RegExpSequence | -| regexp.cpp:98:39:98:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:98:41:98:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:23:99:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:23:99:26 | a\\nc | RegExpSequence | -| regexp.cpp:99:24:99:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:99:26:99:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:40:100:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:40:100:43 | a\\nc | RegExpSequence | -| regexp.cpp:100:41:100:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:100:43:100:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:42:101:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:42:101:45 | a\\nc | RegExpSequence | -| regexp.cpp:101:43:101:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:101:45:101:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:41:102:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:41:102:44 | a\\nc | RegExpSequence | -| regexp.cpp:102:42:102:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:102:44:102:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:41:103:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:41:103:44 | a\\nc | RegExpSequence | -| regexp.cpp:103:42:103:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:103:44:103:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:25:104:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:25:104:28 | a\\nc | RegExpSequence | -| regexp.cpp:104:26:104:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:104:28:104:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:29:105:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:29:105:32 | a\\nc | RegExpSequence | -| regexp.cpp:105:30:105:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:105:32:105:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:60:25:60:26 | b+ | RegExpPlus | +| regexp.cpp:60:27:60:28 | \\1 | RegExpBackRef | +| regexp.cpp:63:21:63:31 | [[:alnum:]] | RegExpCharacterClass | +| regexp.cpp:63:21:63:32 | [[:alnum:]]* | RegExpStar | +| regexp.cpp:63:22:63:30 | [:alnum:] | RegExpNamedCharacterProperty | +| regexp.cpp:64:21:64:31 | [[:digit:]] | RegExpCharacterClass | +| regexp.cpp:64:21:64:32 | [[:digit:]]+ | RegExpPlus | +| regexp.cpp:64:22:64:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:65:21:65:31 | [[:alnum:]] | RegExpCharacterClass | +| regexp.cpp:65:21:65:36 | [[:alnum:]]{2,3} | RegExpRange | +| regexp.cpp:65:22:65:30 | [:alnum:] | RegExpNamedCharacterProperty | +| regexp.cpp:66:21:66:34 | [a-f[:digit:]] | RegExpCharacterClass | +| regexp.cpp:66:21:66:35 | [a-f[:digit:]]+ | RegExpPlus | +| regexp.cpp:66:22:66:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:66:22:66:24 | a-f | RegExpCharacterRange | +| regexp.cpp:66:24:66:24 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:66:25:66:33 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:69:22:69:32 | [[:alpha:]] | RegExpCharacterClass | +| regexp.cpp:69:22:69:43 | [[:alpha:]][[:digit:]] | RegExpSequence | +| regexp.cpp:69:23:69:31 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.cpp:69:33:69:43 | [[:digit:]] | RegExpCharacterClass | +| regexp.cpp:69:34:69:42 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:72:22:72:41 | [[:alpha:][:digit:]] | RegExpCharacterClass | +| regexp.cpp:72:23:72:31 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.cpp:72:32:72:40 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:75:22:75:38 | [A-F[:digit:]a-f] | RegExpCharacterClass | +| regexp.cpp:75:23:75:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:75:23:75:25 | A-F | RegExpCharacterRange | +| regexp.cpp:75:25:75:25 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:75:26:75:34 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:75:35:75:35 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:75:35:75:37 | a-f | RegExpCharacterRange | +| regexp.cpp:75:37:75:37 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:78:22:78:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:81:19:81:24 | \\u{987 | RegExpConstant,RegExpEscape | +| regexp.cpp:84:21:84:22 | \\c | RegExpConstant,RegExpEscape | +| regexp.cpp:84:21:84:23 | \\cA | RegExpSequence | +| regexp.cpp:84:23:84:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:85:21:85:25 | [\\cZ] | RegExpCharacterClass | +| regexp.cpp:85:22:85:23 | \\c | RegExpConstant,RegExpEscape | +| regexp.cpp:85:24:85:24 | Z | RegExpConstant,RegExpNormalChar | +| regexp.cpp:88:25:88:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:88:25:88:28 | a\\nb | RegExpSequence | +| regexp.cpp:88:26:88:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:88:28:88:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:89:37:89:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:89:37:89:40 | a\\nc | RegExpSequence | +| regexp.cpp:89:38:89:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:89:40:89:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:90:39:90:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:90:39:90:42 | a\\nc | RegExpSequence | +| regexp.cpp:90:40:90:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:90:42:90:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:91:38:91:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:91:38:91:41 | a\\nc | RegExpSequence | +| regexp.cpp:91:39:91:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:91:41:91:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:92:38:92:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:92:38:92:41 | a\\nc | RegExpSequence | +| regexp.cpp:92:39:92:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:92:41:92:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:93:23:93:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:93:23:93:26 | a\\nc | RegExpSequence | +| regexp.cpp:93:24:93:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:93:26:93:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:94:40:94:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:94:40:94:43 | a\\nc | RegExpSequence | +| regexp.cpp:94:41:94:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:94:43:94:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:42:95:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:42:95:45 | a\\nc | RegExpSequence | +| regexp.cpp:95:43:95:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:95:45:95:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:41:96:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:41:96:44 | a\\nc | RegExpSequence | +| regexp.cpp:96:42:96:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:96:44:96:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:41:97:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:41:97:44 | a\\nc | RegExpSequence | +| regexp.cpp:97:42:97:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:97:44:97:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:25:98:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:25:98:28 | a\\nc | RegExpSequence | +| regexp.cpp:98:26:98:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:98:28:98:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:29:99:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:29:99:32 | a\\nc | RegExpSequence | +| regexp.cpp:99:30:99:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:99:32:99:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -205,100 +211,104 @@ regExpNormalCharValue | regexp.cpp:21:24:21:24 | c | c | | regexp.cpp:21:26:21:26 | d | d | | regexp.cpp:22:20:22:20 | a | a | +| regexp.cpp:23:20:23:20 | a | a | | regexp.cpp:24:20:24:20 | a | a | -| regexp.cpp:25:20:25:20 | a | a | -| regexp.cpp:28:19:28:21 | foo | foo | -| regexp.cpp:28:23:28:25 | bar | bar | +| regexp.cpp:27:19:27:21 | foo | foo | +| regexp.cpp:27:23:27:25 | bar | bar | +| regexp.cpp:30:20:30:20 | a | a | +| regexp.cpp:30:21:30:21 | b | b | +| regexp.cpp:30:22:30:22 | c | c | | regexp.cpp:31:20:31:20 | a | a | -| regexp.cpp:31:21:31:21 | b | b | -| regexp.cpp:31:22:31:22 | c | c | -| regexp.cpp:32:20:32:20 | a | a | -| regexp.cpp:32:22:32:22 | f | f | -| regexp.cpp:32:23:32:23 | A | A | -| regexp.cpp:32:25:32:25 | F | F | -| regexp.cpp:32:26:32:26 | 0 | 0 | -| regexp.cpp:32:28:32:28 | 9 | 9 | -| regexp.cpp:32:29:32:29 | _ | _ | -| regexp.cpp:34:20:34:21 | \\w | w | -| regexp.cpp:35:19:35:20 | \\[ | [ | -| regexp.cpp:35:21:35:22 | \\] | ] | -| regexp.cpp:35:24:35:24 | 1 | 1 | -| regexp.cpp:35:25:35:25 | 2 | 2 | -| regexp.cpp:35:26:35:26 | 3 | 3 | -| regexp.cpp:36:21:36:21 | A | A | -| regexp.cpp:36:23:36:23 | Z | Z | -| regexp.cpp:37:20:37:20 | ] | ] | -| regexp.cpp:38:21:38:21 | ] | ] | -| regexp.cpp:39:21:39:21 | - | - | -| regexp.cpp:40:21:40:21 | \| | \| | -| regexp.cpp:43:23:43:23 | [ | [ | -| regexp.cpp:43:24:43:24 | a | a | -| regexp.cpp:43:26:43:26 | f | f | -| regexp.cpp:43:28:43:31 | A-F] | A-F] | -| regexp.cpp:48:21:48:22 | \\w | w | -| regexp.cpp:48:24:48:25 | \\W | W | -| regexp.cpp:49:21:49:22 | \\s | s | -| regexp.cpp:49:23:49:24 | \\S | S | -| regexp.cpp:50:21:50:22 | \\d | d | -| regexp.cpp:50:23:50:24 | \\D | D | -| regexp.cpp:52:21:52:22 | \\n | \n | -| regexp.cpp:52:23:52:24 | \\r | \r | -| regexp.cpp:52:25:52:26 | \\t | \t | -| regexp.cpp:56:22:56:23 | !a | !a | -| regexp.cpp:59:21:59:23 | foo | foo | -| regexp.cpp:59:26:59:28 | bar | bar | -| regexp.cpp:60:20:60:21 | fo | fo | -| regexp.cpp:60:23:60:23 | o | o | +| regexp.cpp:31:22:31:22 | f | f | +| regexp.cpp:31:23:31:23 | A | A | +| regexp.cpp:31:25:31:25 | F | F | +| regexp.cpp:31:26:31:26 | 0 | 0 | +| regexp.cpp:31:28:31:28 | 9 | 9 | +| regexp.cpp:31:29:31:29 | _ | _ | +| regexp.cpp:32:20:32:21 | \\w | w | +| regexp.cpp:33:19:33:20 | \\[ | [ | +| regexp.cpp:33:21:33:22 | \\] | ] | +| regexp.cpp:33:24:33:24 | 1 | 1 | +| regexp.cpp:33:25:33:25 | 2 | 2 | +| regexp.cpp:33:26:33:26 | 3 | 3 | +| regexp.cpp:34:21:34:21 | A | A | +| regexp.cpp:34:23:34:23 | Z | Z | +| regexp.cpp:35:20:35:20 | ] | ] | +| regexp.cpp:36:21:36:21 | ] | ] | +| regexp.cpp:37:21:37:21 | - | - | +| regexp.cpp:38:20:38:20 | \| | \| | +| regexp.cpp:41:23:41:23 | [ | [ | +| regexp.cpp:41:24:41:24 | a | a | +| regexp.cpp:41:26:41:26 | f | f | +| regexp.cpp:41:28:41:31 | A-F] | A-F] | +| regexp.cpp:45:21:45:22 | \\w | w | +| regexp.cpp:45:24:45:25 | \\W | W | +| regexp.cpp:46:21:46:22 | \\s | s | +| regexp.cpp:46:23:46:24 | \\S | S | +| regexp.cpp:47:21:47:22 | \\d | d | +| regexp.cpp:47:23:47:24 | \\D | D | +| regexp.cpp:48:21:48:22 | \\n | \n | +| regexp.cpp:48:23:48:24 | \\r | \r | +| regexp.cpp:48:25:48:26 | \\t | \t | +| regexp.cpp:51:22:51:23 | !a | !a | +| regexp.cpp:54:21:54:23 | foo | foo | +| regexp.cpp:54:26:54:28 | bar | bar | +| regexp.cpp:55:20:55:21 | fo | fo | +| regexp.cpp:55:23:55:23 | o | o | +| regexp.cpp:55:25:55:25 | b | b | +| regexp.cpp:55:27:55:28 | ar | ar | +| regexp.cpp:56:21:56:21 | a | a | +| regexp.cpp:56:23:56:23 | b | b | +| regexp.cpp:56:25:56:26 | cd | cd | +| regexp.cpp:56:28:56:28 | e | e | +| regexp.cpp:57:23:57:23 | : | : | +| regexp.cpp:57:26:57:27 | \\w | w | +| regexp.cpp:60:22:60:22 | a | a | | regexp.cpp:60:25:60:25 | b | b | -| regexp.cpp:60:27:60:28 | ar | ar | -| regexp.cpp:61:21:61:21 | a | a | -| regexp.cpp:61:23:61:23 | b | b | -| regexp.cpp:61:25:61:26 | cd | cd | -| regexp.cpp:61:28:61:28 | e | e | -| regexp.cpp:62:23:62:23 | : | : | -| regexp.cpp:62:26:62:27 | \\w | w | -| regexp.cpp:69:22:69:22 | a | a | -| regexp.cpp:69:25:69:25 | b | b | -| regexp.cpp:76:22:76:22 | a | a | -| regexp.cpp:76:24:76:24 | f | f | -| regexp.cpp:85:23:85:23 | A | A | -| regexp.cpp:85:25:85:25 | F | F | -| regexp.cpp:85:35:85:35 | a | a | -| regexp.cpp:85:37:85:37 | f | f | -| regexp.cpp:91:19:91:24 | \\u{987 | \u0987 | -| regexp.cpp:94:25:94:25 | a | a | -| regexp.cpp:94:26:94:27 | \\n | \n | -| regexp.cpp:94:28:94:28 | b | b | -| regexp.cpp:95:37:95:37 | a | a | -| regexp.cpp:95:38:95:39 | \\n | \n | -| regexp.cpp:95:40:95:40 | c | c | -| regexp.cpp:96:39:96:39 | a | a | -| regexp.cpp:96:40:96:41 | \\n | \n | -| regexp.cpp:96:42:96:42 | c | c | -| regexp.cpp:97:38:97:38 | a | a | -| regexp.cpp:97:39:97:40 | \\n | \n | -| regexp.cpp:97:41:97:41 | c | c | -| regexp.cpp:98:38:98:38 | a | a | -| regexp.cpp:98:39:98:40 | \\n | \n | -| regexp.cpp:98:41:98:41 | c | c | -| regexp.cpp:99:23:99:23 | a | a | -| regexp.cpp:99:24:99:25 | \\n | \n | -| regexp.cpp:99:26:99:26 | c | c | -| regexp.cpp:100:40:100:40 | a | a | -| regexp.cpp:100:41:100:42 | \\n | \n | -| regexp.cpp:100:43:100:43 | c | c | -| regexp.cpp:101:42:101:42 | a | a | -| regexp.cpp:101:43:101:44 | \\n | \n | -| regexp.cpp:101:45:101:45 | c | c | -| regexp.cpp:102:41:102:41 | a | a | -| regexp.cpp:102:42:102:43 | \\n | \n | -| regexp.cpp:102:44:102:44 | c | c | -| regexp.cpp:103:41:103:41 | a | a | -| regexp.cpp:103:42:103:43 | \\n | \n | -| regexp.cpp:103:44:103:44 | c | c | -| regexp.cpp:104:25:104:25 | a | a | -| regexp.cpp:104:26:104:27 | \\n | \n | -| regexp.cpp:104:28:104:28 | c | c | -| regexp.cpp:105:29:105:29 | a | a | -| regexp.cpp:105:30:105:31 | \\n | \n | -| regexp.cpp:105:32:105:32 | c | c | +| regexp.cpp:66:22:66:22 | a | a | +| regexp.cpp:66:24:66:24 | f | f | +| regexp.cpp:75:23:75:23 | A | A | +| regexp.cpp:75:25:75:25 | F | F | +| regexp.cpp:75:35:75:35 | a | a | +| regexp.cpp:75:37:75:37 | f | f | +| regexp.cpp:81:19:81:24 | \\u{987 | \u0987 | +| regexp.cpp:84:21:84:22 | \\c | c | +| regexp.cpp:84:23:84:23 | A | A | +| regexp.cpp:85:22:85:23 | \\c | c | +| regexp.cpp:85:24:85:24 | Z | Z | +| regexp.cpp:88:25:88:25 | a | a | +| regexp.cpp:88:26:88:27 | \\n | \n | +| regexp.cpp:88:28:88:28 | b | b | +| regexp.cpp:89:37:89:37 | a | a | +| regexp.cpp:89:38:89:39 | \\n | \n | +| regexp.cpp:89:40:89:40 | c | c | +| regexp.cpp:90:39:90:39 | a | a | +| regexp.cpp:90:40:90:41 | \\n | \n | +| regexp.cpp:90:42:90:42 | c | c | +| regexp.cpp:91:38:91:38 | a | a | +| regexp.cpp:91:39:91:40 | \\n | \n | +| regexp.cpp:91:41:91:41 | c | c | +| regexp.cpp:92:38:92:38 | a | a | +| regexp.cpp:92:39:92:40 | \\n | \n | +| regexp.cpp:92:41:92:41 | c | c | +| regexp.cpp:93:23:93:23 | a | a | +| regexp.cpp:93:24:93:25 | \\n | \n | +| regexp.cpp:93:26:93:26 | c | c | +| regexp.cpp:94:40:94:40 | a | a | +| regexp.cpp:94:41:94:42 | \\n | \n | +| regexp.cpp:94:43:94:43 | c | c | +| regexp.cpp:95:42:95:42 | a | a | +| regexp.cpp:95:43:95:44 | \\n | \n | +| regexp.cpp:95:45:95:45 | c | c | +| regexp.cpp:96:41:96:41 | a | a | +| regexp.cpp:96:42:96:43 | \\n | \n | +| regexp.cpp:96:44:96:44 | c | c | +| regexp.cpp:97:41:97:41 | a | a | +| regexp.cpp:97:42:97:43 | \\n | \n | +| regexp.cpp:97:44:97:44 | c | c | +| regexp.cpp:98:25:98:25 | a | a | +| regexp.cpp:98:26:98:27 | \\n | \n | +| regexp.cpp:98:28:98:28 | c | c | +| regexp.cpp:99:29:99:29 | a | a | +| regexp.cpp:99:30:99:31 | \\n | \n | +| regexp.cpp:99:32:99:32 | c | c | From 8d451996a5026de74bc99ea1780ce39bdff11c5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:17:31 +0000 Subject: [PATCH 18/37] C++: Implement \cX control escapes --- .../code/cpp/regex/internal/ParseRegExp.qll | 5 ++++- cpp/ql/test/library-tests/regex/parse.expected | 15 +++------------ cpp/ql/test/library-tests/regex/regexp.expected | 13 ++++--------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index b6029c187a7a..8e9e4992d595 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -322,8 +322,11 @@ abstract class RegExp extends StringLiteral { // wide hex char \uhhhh this.getChar(start + 1) = "u" and end = start + 6 or + // control char \cX + this.getChar(start + 1) = "c" and end = start + 3 + or // escape not handled above; update when adding a new case - not this.getChar(start + 1) in ["x", "u"] and + not this.getChar(start + 1) in ["x", "u", "c"] and not exists(this.getChar(start + 1).toInt()) and end = start + 2 ) diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 49498ec0d230..44c664fa165d 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -401,21 +401,12 @@ regexp.cpp: # 81| [RegExpConstant, RegExpEscape] \u{987 -# 84| [RegExpConstant, RegExpEscape] \c - -# 84| [RegExpSequence] \cA -#-----| 0 -> [RegExpConstant, RegExpEscape] \c -#-----| 1 -> [RegExpConstant, RegExpNormalChar] A - -# 84| [RegExpConstant, RegExpNormalChar] A +# 84| [RegExpConstant, RegExpEscape] \cA # 85| [RegExpCharacterClass] [\cZ] -#-----| 0 -> [RegExpConstant, RegExpEscape] \c -#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z - -# 85| [RegExpConstant, RegExpEscape] \c +#-----| 0 -> [RegExpConstant, RegExpEscape] \cZ -# 85| [RegExpConstant, RegExpNormalChar] Z +# 85| [RegExpConstant, RegExpEscape] \cZ # 88| [RegExpConstant, RegExpNormalChar] a diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index caede9ebdf9f..fad6510a3ad8 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -150,12 +150,9 @@ term | regexp.cpp:75:37:75:37 | f | RegExpConstant,RegExpNormalChar | | regexp.cpp:78:22:78:30 | [:digit:] | RegExpNamedCharacterProperty | | regexp.cpp:81:19:81:24 | \\u{987 | RegExpConstant,RegExpEscape | -| regexp.cpp:84:21:84:22 | \\c | RegExpConstant,RegExpEscape | -| regexp.cpp:84:21:84:23 | \\cA | RegExpSequence | -| regexp.cpp:84:23:84:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:84:21:84:23 | \\cA | RegExpConstant,RegExpEscape | | regexp.cpp:85:21:85:25 | [\\cZ] | RegExpCharacterClass | -| regexp.cpp:85:22:85:23 | \\c | RegExpConstant,RegExpEscape | -| regexp.cpp:85:24:85:24 | Z | RegExpConstant,RegExpNormalChar | +| regexp.cpp:85:22:85:24 | \\cZ | RegExpConstant,RegExpEscape | | regexp.cpp:88:25:88:25 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:88:25:88:28 | a\\nb | RegExpSequence | | regexp.cpp:88:26:88:27 | \\n | RegExpConstant,RegExpEscape | @@ -272,10 +269,8 @@ regExpNormalCharValue | regexp.cpp:75:35:75:35 | a | a | | regexp.cpp:75:37:75:37 | f | f | | regexp.cpp:81:19:81:24 | \\u{987 | \u0987 | -| regexp.cpp:84:21:84:22 | \\c | c | -| regexp.cpp:84:23:84:23 | A | A | -| regexp.cpp:85:22:85:23 | \\c | c | -| regexp.cpp:85:24:85:24 | Z | Z | +| regexp.cpp:84:21:84:23 | \\cA | cA | +| regexp.cpp:85:22:85:24 | \\cZ | cZ | | regexp.cpp:88:25:88:25 | a | a | | regexp.cpp:88:26:88:27 | \\n | \n | | regexp.cpp:88:28:88:28 | b | b | From 87cdedd57d0d09d348b77f7e64549675bbb565a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:18:01 +0000 Subject: [PATCH 19/37] C++: Add NUL-escape tests (pre-fix) --- .../library-tests/regex/locations.expected | 26 +-- .../test/library-tests/regex/parse.expected | 96 +++++----- cpp/ql/test/library-tests/regex/regexp.cpp | 4 + .../test/library-tests/regex/regexp.expected | 175 +++++++++--------- 4 files changed, 162 insertions(+), 139 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 8bcd383853d0..40cd68cf9eb0 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -35,15 +35,17 @@ | regexp.cpp:78:22:78:30 | [:digit:] | regexp.cpp | 78 | 22 | 78 | 30 | | regexp.cpp:84:21:84:23 | \\cA | regexp.cpp | 84 | 21 | 84 | 23 | | regexp.cpp:85:21:85:25 | [\\cZ] | regexp.cpp | 85 | 21 | 85 | 25 | -| regexp.cpp:88:25:88:28 | a\\nb | regexp.cpp | 88 | 25 | 88 | 28 | -| regexp.cpp:89:37:89:40 | a\\nc | regexp.cpp | 89 | 37 | 89 | 40 | -| regexp.cpp:90:39:90:42 | a\\nc | regexp.cpp | 90 | 39 | 90 | 42 | -| regexp.cpp:91:38:91:41 | a\\nc | regexp.cpp | 91 | 38 | 91 | 41 | -| regexp.cpp:92:38:92:41 | a\\nc | regexp.cpp | 92 | 38 | 92 | 41 | -| regexp.cpp:93:23:93:26 | a\\nc | regexp.cpp | 93 | 23 | 93 | 26 | -| regexp.cpp:94:40:94:43 | a\\nc | regexp.cpp | 94 | 40 | 94 | 43 | -| regexp.cpp:95:42:95:45 | a\\nc | regexp.cpp | 95 | 42 | 95 | 45 | -| regexp.cpp:96:41:96:44 | a\\nc | regexp.cpp | 96 | 41 | 96 | 44 | -| regexp.cpp:97:41:97:44 | a\\nc | regexp.cpp | 97 | 41 | 97 | 44 | -| regexp.cpp:98:25:98:28 | a\\nc | regexp.cpp | 98 | 25 | 98 | 28 | -| regexp.cpp:99:29:99:32 | a\\nc | regexp.cpp | 99 | 29 | 99 | 32 | +| regexp.cpp:88:20:88:21 | \\0 | regexp.cpp | 88 | 20 | 88 | 21 | +| regexp.cpp:89:20:89:23 | [\\0] | regexp.cpp | 89 | 20 | 89 | 23 | +| regexp.cpp:92:25:92:28 | a\\nb | regexp.cpp | 92 | 25 | 92 | 28 | +| regexp.cpp:93:37:93:40 | a\\nc | regexp.cpp | 93 | 37 | 93 | 40 | +| regexp.cpp:94:39:94:42 | a\\nc | regexp.cpp | 94 | 39 | 94 | 42 | +| regexp.cpp:95:38:95:41 | a\\nc | regexp.cpp | 95 | 38 | 95 | 41 | +| regexp.cpp:96:38:96:41 | a\\nc | regexp.cpp | 96 | 38 | 96 | 41 | +| regexp.cpp:97:23:97:26 | a\\nc | regexp.cpp | 97 | 23 | 97 | 26 | +| regexp.cpp:98:40:98:43 | a\\nc | regexp.cpp | 98 | 40 | 98 | 43 | +| regexp.cpp:99:42:99:45 | a\\nc | regexp.cpp | 99 | 42 | 99 | 45 | +| regexp.cpp:100:41:100:44 | a\\nc | regexp.cpp | 100 | 41 | 100 | 44 | +| regexp.cpp:101:41:101:44 | a\\nc | regexp.cpp | 101 | 41 | 101 | 44 | +| regexp.cpp:102:25:102:28 | a\\nc | regexp.cpp | 102 | 25 | 102 | 28 | +| regexp.cpp:103:29:103:32 | a\\nc | regexp.cpp | 103 | 29 | 103 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 44c664fa165d..418b245c5140 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -408,60 +408,26 @@ regexp.cpp: # 85| [RegExpConstant, RegExpEscape] \cZ -# 88| [RegExpConstant, RegExpNormalChar] a +# 88| [RegExpConstant, RegExpNormalChar] \0 -# 88| [RegExpSequence] a\nb -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] b - -# 88| [RegExpConstant, RegExpEscape] \n - -# 88| [RegExpConstant, RegExpNormalChar] b - -# 89| [RegExpConstant, RegExpNormalChar] a - -# 89| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c - -# 89| [RegExpConstant, RegExpEscape] \n +# 89| [RegExpCharacterClass] [\0] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] \ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 0 -# 89| [RegExpConstant, RegExpNormalChar] c +# 89| [RegExpConstant, RegExpNormalChar] \ -# 90| [RegExpConstant, RegExpNormalChar] a - -# 90| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c - -# 90| [RegExpConstant, RegExpEscape] \n - -# 90| [RegExpConstant, RegExpNormalChar] c - -# 91| [RegExpConstant, RegExpNormalChar] a - -# 91| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c - -# 91| [RegExpConstant, RegExpEscape] \n - -# 91| [RegExpConstant, RegExpNormalChar] c +# 89| [RegExpConstant, RegExpNormalChar] 0 # 92| [RegExpConstant, RegExpNormalChar] a -# 92| [RegExpSequence] a\nc +# 92| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +#-----| 2 -> [RegExpConstant, RegExpNormalChar] b # 92| [RegExpConstant, RegExpEscape] \n -# 92| [RegExpConstant, RegExpNormalChar] c +# 92| [RegExpConstant, RegExpNormalChar] b # 93| [RegExpConstant, RegExpNormalChar] a @@ -539,3 +505,47 @@ regexp.cpp: # 99| [RegExpConstant, RegExpEscape] \n # 99| [RegExpConstant, RegExpNormalChar] c + +# 100| [RegExpConstant, RegExpNormalChar] a + +# 100| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 100| [RegExpConstant, RegExpEscape] \n + +# 100| [RegExpConstant, RegExpNormalChar] c + +# 101| [RegExpConstant, RegExpNormalChar] a + +# 101| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 101| [RegExpConstant, RegExpEscape] \n + +# 101| [RegExpConstant, RegExpNormalChar] c + +# 102| [RegExpConstant, RegExpNormalChar] a + +# 102| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 102| [RegExpConstant, RegExpEscape] \n + +# 102| [RegExpConstant, RegExpNormalChar] c + +# 103| [RegExpConstant, RegExpNormalChar] a + +# 103| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 103| [RegExpConstant, RegExpEscape] \n + +# 103| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 462f5ecbc293..57dc43bd4714 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -84,6 +84,10 @@ std::regex r_uni("\\u{9879}"); std::regex r_ctrl1("\\cA"); std::regex r_ctrl2("[\\cZ]"); +// NUL escape +std::regex r_nul1("\\0"); +std::regex r_nul2("[\\0]"); + // String literal spellings for location tests std::regex r_loc_plain("a\\nb"); std::basic_regex r_loc_L(L"a\\nc"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index fad6510a3ad8..e2d6abe48a39 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -153,54 +153,58 @@ term | regexp.cpp:84:21:84:23 | \\cA | RegExpConstant,RegExpEscape | | regexp.cpp:85:21:85:25 | [\\cZ] | RegExpCharacterClass | | regexp.cpp:85:22:85:24 | \\cZ | RegExpConstant,RegExpEscape | -| regexp.cpp:88:25:88:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:88:25:88:28 | a\\nb | RegExpSequence | -| regexp.cpp:88:26:88:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:88:28:88:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:89:37:89:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:89:37:89:40 | a\\nc | RegExpSequence | -| regexp.cpp:89:38:89:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:89:40:89:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:90:39:90:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:90:39:90:42 | a\\nc | RegExpSequence | -| regexp.cpp:90:40:90:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:90:42:90:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:91:38:91:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:91:38:91:41 | a\\nc | RegExpSequence | -| regexp.cpp:91:39:91:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:91:41:91:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:92:38:92:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:92:38:92:41 | a\\nc | RegExpSequence | -| regexp.cpp:92:39:92:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:92:41:92:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:93:23:93:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:93:23:93:26 | a\\nc | RegExpSequence | -| regexp.cpp:93:24:93:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:93:26:93:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:94:40:94:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:94:40:94:43 | a\\nc | RegExpSequence | -| regexp.cpp:94:41:94:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:94:43:94:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:42:95:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:42:95:45 | a\\nc | RegExpSequence | -| regexp.cpp:95:43:95:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:95:45:95:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:41:96:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:41:96:44 | a\\nc | RegExpSequence | -| regexp.cpp:96:42:96:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:96:44:96:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:41:97:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:41:97:44 | a\\nc | RegExpSequence | -| regexp.cpp:97:42:97:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:97:44:97:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:25:98:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:25:98:28 | a\\nc | RegExpSequence | -| regexp.cpp:98:26:98:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:98:28:98:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:29:99:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:29:99:32 | a\\nc | RegExpSequence | -| regexp.cpp:99:30:99:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:99:32:99:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:88:20:88:21 | \\0 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:89:20:89:23 | [\\0] | RegExpCharacterClass | +| regexp.cpp:89:21:89:21 | \\ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:89:22:89:22 | 0 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:92:25:92:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:92:25:92:28 | a\\nb | RegExpSequence | +| regexp.cpp:92:26:92:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:92:28:92:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:93:37:93:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:93:37:93:40 | a\\nc | RegExpSequence | +| regexp.cpp:93:38:93:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:93:40:93:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:94:39:94:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:94:39:94:42 | a\\nc | RegExpSequence | +| regexp.cpp:94:40:94:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:94:42:94:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:38:95:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:95:38:95:41 | a\\nc | RegExpSequence | +| regexp.cpp:95:39:95:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:95:41:95:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:38:96:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:38:96:41 | a\\nc | RegExpSequence | +| regexp.cpp:96:39:96:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:96:41:96:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:23:97:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:23:97:26 | a\\nc | RegExpSequence | +| regexp.cpp:97:24:97:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:97:26:97:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:40:98:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:40:98:43 | a\\nc | RegExpSequence | +| regexp.cpp:98:41:98:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:98:43:98:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:42:99:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:42:99:45 | a\\nc | RegExpSequence | +| regexp.cpp:99:43:99:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:99:45:99:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:41:100:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:41:100:44 | a\\nc | RegExpSequence | +| regexp.cpp:100:42:100:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:100:44:100:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:41:101:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:41:101:44 | a\\nc | RegExpSequence | +| regexp.cpp:101:42:101:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:101:44:101:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:25:102:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:25:102:28 | a\\nc | RegExpSequence | +| regexp.cpp:102:26:102:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:102:28:102:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:29:103:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:29:103:32 | a\\nc | RegExpSequence | +| regexp.cpp:103:30:103:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:103:32:103:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -271,39 +275,42 @@ regExpNormalCharValue | regexp.cpp:81:19:81:24 | \\u{987 | \u0987 | | regexp.cpp:84:21:84:23 | \\cA | cA | | regexp.cpp:85:22:85:24 | \\cZ | cZ | -| regexp.cpp:88:25:88:25 | a | a | -| regexp.cpp:88:26:88:27 | \\n | \n | -| regexp.cpp:88:28:88:28 | b | b | -| regexp.cpp:89:37:89:37 | a | a | -| regexp.cpp:89:38:89:39 | \\n | \n | -| regexp.cpp:89:40:89:40 | c | c | -| regexp.cpp:90:39:90:39 | a | a | -| regexp.cpp:90:40:90:41 | \\n | \n | -| regexp.cpp:90:42:90:42 | c | c | -| regexp.cpp:91:38:91:38 | a | a | -| regexp.cpp:91:39:91:40 | \\n | \n | -| regexp.cpp:91:41:91:41 | c | c | -| regexp.cpp:92:38:92:38 | a | a | -| regexp.cpp:92:39:92:40 | \\n | \n | -| regexp.cpp:92:41:92:41 | c | c | -| regexp.cpp:93:23:93:23 | a | a | -| regexp.cpp:93:24:93:25 | \\n | \n | -| regexp.cpp:93:26:93:26 | c | c | -| regexp.cpp:94:40:94:40 | a | a | -| regexp.cpp:94:41:94:42 | \\n | \n | -| regexp.cpp:94:43:94:43 | c | c | -| regexp.cpp:95:42:95:42 | a | a | -| regexp.cpp:95:43:95:44 | \\n | \n | -| regexp.cpp:95:45:95:45 | c | c | -| regexp.cpp:96:41:96:41 | a | a | -| regexp.cpp:96:42:96:43 | \\n | \n | -| regexp.cpp:96:44:96:44 | c | c | -| regexp.cpp:97:41:97:41 | a | a | -| regexp.cpp:97:42:97:43 | \\n | \n | -| regexp.cpp:97:44:97:44 | c | c | -| regexp.cpp:98:25:98:25 | a | a | -| regexp.cpp:98:26:98:27 | \\n | \n | -| regexp.cpp:98:28:98:28 | c | c | -| regexp.cpp:99:29:99:29 | a | a | -| regexp.cpp:99:30:99:31 | \\n | \n | -| regexp.cpp:99:32:99:32 | c | c | +| regexp.cpp:88:20:88:21 | \\0 | \\0 | +| regexp.cpp:89:21:89:21 | \\ | \\ | +| regexp.cpp:89:22:89:22 | 0 | 0 | +| regexp.cpp:92:25:92:25 | a | a | +| regexp.cpp:92:26:92:27 | \\n | \n | +| regexp.cpp:92:28:92:28 | b | b | +| regexp.cpp:93:37:93:37 | a | a | +| regexp.cpp:93:38:93:39 | \\n | \n | +| regexp.cpp:93:40:93:40 | c | c | +| regexp.cpp:94:39:94:39 | a | a | +| regexp.cpp:94:40:94:41 | \\n | \n | +| regexp.cpp:94:42:94:42 | c | c | +| regexp.cpp:95:38:95:38 | a | a | +| regexp.cpp:95:39:95:40 | \\n | \n | +| regexp.cpp:95:41:95:41 | c | c | +| regexp.cpp:96:38:96:38 | a | a | +| regexp.cpp:96:39:96:40 | \\n | \n | +| regexp.cpp:96:41:96:41 | c | c | +| regexp.cpp:97:23:97:23 | a | a | +| regexp.cpp:97:24:97:25 | \\n | \n | +| regexp.cpp:97:26:97:26 | c | c | +| regexp.cpp:98:40:98:40 | a | a | +| regexp.cpp:98:41:98:42 | \\n | \n | +| regexp.cpp:98:43:98:43 | c | c | +| regexp.cpp:99:42:99:42 | a | a | +| regexp.cpp:99:43:99:44 | \\n | \n | +| regexp.cpp:99:45:99:45 | c | c | +| regexp.cpp:100:41:100:41 | a | a | +| regexp.cpp:100:42:100:43 | \\n | \n | +| regexp.cpp:100:44:100:44 | c | c | +| regexp.cpp:101:41:101:41 | a | a | +| regexp.cpp:101:42:101:43 | \\n | \n | +| regexp.cpp:101:44:101:44 | c | c | +| regexp.cpp:102:25:102:25 | a | a | +| regexp.cpp:102:26:102:27 | \\n | \n | +| regexp.cpp:102:28:102:28 | c | c | +| regexp.cpp:103:29:103:29 | a | a | +| regexp.cpp:103:30:103:31 | \\n | \n | +| regexp.cpp:103:32:103:32 | c | c | From 81783e64f816bdefbf4e0de76a4456d2557c85af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:19:10 +0000 Subject: [PATCH 20/37] C++: Implement \0 NUL escape --- .../lib/semmle/code/cpp/regex/internal/ParseRegExp.qll | 9 ++++++--- cpp/ql/test/library-tests/regex/parse.expected | 9 +++------ cpp/ql/test/library-tests/regex/regexp.expected | 10 ++++------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 8e9e4992d595..af76baaa0f68 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -325,6 +325,11 @@ abstract class RegExp extends StringLiteral { // control char \cX this.getChar(start + 1) = "c" and end = start + 3 or + // nul escape \0 (when not followed by a digit) + this.getChar(start + 1) = "0" and + end = start + 2 and + not exists(this.getChar(start + 2).toInt()) + or // escape not handled above; update when adding a new case not this.getChar(start + 1) in ["x", "u", "c"] and not exists(this.getChar(start + 1).toInt()) and @@ -598,9 +603,7 @@ abstract class RegExp extends StringLiteral { } /** Whether the text in the range `start,end` is a back reference */ - predicate backreference(int start, int end) { - this.numberedBackreference(start, end, _) - } + predicate backreference(int start, int end) { this.numberedBackreference(start, end, _) } /** Gets the number of the back reference in start,end */ int getBackRefNumber(int start, int end) { this.numberedBackreference(start, end, result) } diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 418b245c5140..b81d05e1984f 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -408,15 +408,12 @@ regexp.cpp: # 85| [RegExpConstant, RegExpEscape] \cZ -# 88| [RegExpConstant, RegExpNormalChar] \0 +# 88| [RegExpConstant, RegExpEscape] \0 # 89| [RegExpCharacterClass] [\0] -#-----| 0 -> [RegExpConstant, RegExpNormalChar] \ -#-----| 1 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 0 -> [RegExpConstant, RegExpEscape] \0 -# 89| [RegExpConstant, RegExpNormalChar] \ - -# 89| [RegExpConstant, RegExpNormalChar] 0 +# 89| [RegExpConstant, RegExpEscape] \0 # 92| [RegExpConstant, RegExpNormalChar] a diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index e2d6abe48a39..3dc7b20753af 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -153,10 +153,9 @@ term | regexp.cpp:84:21:84:23 | \\cA | RegExpConstant,RegExpEscape | | regexp.cpp:85:21:85:25 | [\\cZ] | RegExpCharacterClass | | regexp.cpp:85:22:85:24 | \\cZ | RegExpConstant,RegExpEscape | -| regexp.cpp:88:20:88:21 | \\0 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:88:20:88:21 | \\0 | RegExpConstant,RegExpEscape | | regexp.cpp:89:20:89:23 | [\\0] | RegExpCharacterClass | -| regexp.cpp:89:21:89:21 | \\ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:89:22:89:22 | 0 | RegExpConstant,RegExpNormalChar | +| regexp.cpp:89:21:89:22 | \\0 | RegExpConstant,RegExpEscape | | regexp.cpp:92:25:92:25 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:92:25:92:28 | a\\nb | RegExpSequence | | regexp.cpp:92:26:92:27 | \\n | RegExpConstant,RegExpEscape | @@ -275,9 +274,8 @@ regExpNormalCharValue | regexp.cpp:81:19:81:24 | \\u{987 | \u0987 | | regexp.cpp:84:21:84:23 | \\cA | cA | | regexp.cpp:85:22:85:24 | \\cZ | cZ | -| regexp.cpp:88:20:88:21 | \\0 | \\0 | -| regexp.cpp:89:21:89:21 | \\ | \\ | -| regexp.cpp:89:22:89:22 | 0 | 0 | +| regexp.cpp:88:20:88:21 | \\0 | 0 | +| regexp.cpp:89:21:89:22 | \\0 | 0 | | regexp.cpp:92:25:92:25 | a | a | | regexp.cpp:92:26:92:27 | \\n | \n | | regexp.cpp:92:28:92:28 | b | b | From 03625be23222d3374563763e9e80a2199928433f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:19:40 +0000 Subject: [PATCH 21/37] C++: Add POSIX collating-symbol tests (pre-fix) --- .../library-tests/regex/locations.expected | 36 +-- .../test/library-tests/regex/parse.expected | 131 ++++++---- cpp/ql/test/library-tests/regex/regexp.cpp | 4 + .../test/library-tests/regex/regexp.expected | 223 ++++++++++-------- 4 files changed, 239 insertions(+), 155 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 40cd68cf9eb0..d19f83db4503 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -32,20 +32,22 @@ | regexp.cpp:69:22:69:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 69 | 22 | 69 | 43 | | regexp.cpp:72:22:72:41 | [[:alpha:][:digit:]] | regexp.cpp | 72 | 22 | 72 | 41 | | regexp.cpp:75:22:75:38 | [A-F[:digit:]a-f] | regexp.cpp | 75 | 22 | 75 | 38 | -| regexp.cpp:78:22:78:30 | [:digit:] | regexp.cpp | 78 | 22 | 78 | 30 | -| regexp.cpp:84:21:84:23 | \\cA | regexp.cpp | 84 | 21 | 84 | 23 | -| regexp.cpp:85:21:85:25 | [\\cZ] | regexp.cpp | 85 | 21 | 85 | 25 | -| regexp.cpp:88:20:88:21 | \\0 | regexp.cpp | 88 | 20 | 88 | 21 | -| regexp.cpp:89:20:89:23 | [\\0] | regexp.cpp | 89 | 20 | 89 | 23 | -| regexp.cpp:92:25:92:28 | a\\nb | regexp.cpp | 92 | 25 | 92 | 28 | -| regexp.cpp:93:37:93:40 | a\\nc | regexp.cpp | 93 | 37 | 93 | 40 | -| regexp.cpp:94:39:94:42 | a\\nc | regexp.cpp | 94 | 39 | 94 | 42 | -| regexp.cpp:95:38:95:41 | a\\nc | regexp.cpp | 95 | 38 | 95 | 41 | -| regexp.cpp:96:38:96:41 | a\\nc | regexp.cpp | 96 | 38 | 96 | 41 | -| regexp.cpp:97:23:97:26 | a\\nc | regexp.cpp | 97 | 23 | 97 | 26 | -| regexp.cpp:98:40:98:43 | a\\nc | regexp.cpp | 98 | 40 | 98 | 43 | -| regexp.cpp:99:42:99:45 | a\\nc | regexp.cpp | 99 | 42 | 99 | 45 | -| regexp.cpp:100:41:100:44 | a\\nc | regexp.cpp | 100 | 41 | 100 | 44 | -| regexp.cpp:101:41:101:44 | a\\nc | regexp.cpp | 101 | 41 | 101 | 44 | -| regexp.cpp:102:25:102:28 | a\\nc | regexp.cpp | 102 | 25 | 102 | 28 | -| regexp.cpp:103:29:103:32 | a\\nc | regexp.cpp | 103 | 29 | 103 | 32 | +| regexp.cpp:78:27:78:33 | [[.a.]] | regexp.cpp | 78 | 27 | 78 | 33 | +| regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | regexp.cpp | 79 | 27 | 79 | 39 | +| regexp.cpp:82:22:82:30 | [:digit:] | regexp.cpp | 82 | 22 | 82 | 30 | +| regexp.cpp:88:21:88:23 | \\cA | regexp.cpp | 88 | 21 | 88 | 23 | +| regexp.cpp:89:21:89:25 | [\\cZ] | regexp.cpp | 89 | 21 | 89 | 25 | +| regexp.cpp:92:20:92:21 | \\0 | regexp.cpp | 92 | 20 | 92 | 21 | +| regexp.cpp:93:20:93:23 | [\\0] | regexp.cpp | 93 | 20 | 93 | 23 | +| regexp.cpp:96:25:96:28 | a\\nb | regexp.cpp | 96 | 25 | 96 | 28 | +| regexp.cpp:97:37:97:40 | a\\nc | regexp.cpp | 97 | 37 | 97 | 40 | +| regexp.cpp:98:39:98:42 | a\\nc | regexp.cpp | 98 | 39 | 98 | 42 | +| regexp.cpp:99:38:99:41 | a\\nc | regexp.cpp | 99 | 38 | 99 | 41 | +| regexp.cpp:100:38:100:41 | a\\nc | regexp.cpp | 100 | 38 | 100 | 41 | +| regexp.cpp:101:23:101:26 | a\\nc | regexp.cpp | 101 | 23 | 101 | 26 | +| regexp.cpp:102:40:102:43 | a\\nc | regexp.cpp | 102 | 40 | 102 | 43 | +| regexp.cpp:103:42:103:45 | a\\nc | regexp.cpp | 103 | 42 | 103 | 45 | +| regexp.cpp:104:41:104:44 | a\\nc | regexp.cpp | 104 | 41 | 104 | 44 | +| regexp.cpp:105:41:105:44 | a\\nc | regexp.cpp | 105 | 41 | 105 | 44 | +| regexp.cpp:106:25:106:28 | a\\nc | regexp.cpp | 106 | 25 | 106 | 28 | +| regexp.cpp:107:29:107:32 | a\\nc | regexp.cpp | 107 | 29 | 107 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index b81d05e1984f..ac066391db12 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -397,78 +397,83 @@ regexp.cpp: # 75| [RegExpConstant, RegExpNormalChar] f -# 78| [RegExpNamedCharacterProperty] [:digit:] +# 78| [RegExpCharacterClass] [[.a.] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] [ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] . +#-----| 2 -> [RegExpConstant, RegExpNormalChar] a +#-----| 3 -> [RegExpConstant, RegExpNormalChar] . -# 81| [RegExpConstant, RegExpEscape] \u{987 +# 78| [RegExpSequence] [[.a.]] +#-----| 0 -> [RegExpCharacterClass] [[.a.] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ] -# 84| [RegExpConstant, RegExpEscape] \cA +# 78| [RegExpConstant, RegExpNormalChar] [ -# 85| [RegExpCharacterClass] [\cZ] -#-----| 0 -> [RegExpConstant, RegExpEscape] \cZ +# 78| [RegExpConstant, RegExpNormalChar] . -# 85| [RegExpConstant, RegExpEscape] \cZ +# 78| [RegExpConstant, RegExpNormalChar] a -# 88| [RegExpConstant, RegExpEscape] \0 +# 78| [RegExpConstant, RegExpNormalChar] . -# 89| [RegExpCharacterClass] [\0] -#-----| 0 -> [RegExpConstant, RegExpEscape] \0 +# 78| [RegExpConstant, RegExpNormalChar] ] -# 89| [RegExpConstant, RegExpEscape] \0 +# 79| [RegExpCharacterClass] [A-F[.b.] +#-----| 0 -> [RegExpCharacterRange] A-F +#-----| 1 -> [RegExpConstant, RegExpNormalChar] [ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] . +#-----| 3 -> [RegExpConstant, RegExpNormalChar] b +#-----| 4 -> [RegExpConstant, RegExpNormalChar] . -# 92| [RegExpConstant, RegExpNormalChar] a +# 79| [RegExpSequence] [A-F[.b.]a-f] +#-----| 0 -> [RegExpCharacterClass] [A-F[.b.] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a-f] -# 92| [RegExpSequence] a\nb -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] b +# 79| [RegExpConstant, RegExpNormalChar] A -# 92| [RegExpConstant, RegExpEscape] \n +# 79| [RegExpCharacterRange] A-F +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 92| [RegExpConstant, RegExpNormalChar] b +# 79| [RegExpConstant, RegExpNormalChar] F -# 93| [RegExpConstant, RegExpNormalChar] a +# 79| [RegExpConstant, RegExpNormalChar] [ -# 93| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +# 79| [RegExpConstant, RegExpNormalChar] . -# 93| [RegExpConstant, RegExpEscape] \n +# 79| [RegExpConstant, RegExpNormalChar] b -# 93| [RegExpConstant, RegExpNormalChar] c +# 79| [RegExpConstant, RegExpNormalChar] . -# 94| [RegExpConstant, RegExpNormalChar] a +# 79| [RegExpConstant, RegExpNormalChar] a-f] -# 94| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +# 82| [RegExpNamedCharacterProperty] [:digit:] -# 94| [RegExpConstant, RegExpEscape] \n +# 85| [RegExpConstant, RegExpEscape] \u{987 -# 94| [RegExpConstant, RegExpNormalChar] c +# 88| [RegExpConstant, RegExpEscape] \cA -# 95| [RegExpConstant, RegExpNormalChar] a +# 89| [RegExpCharacterClass] [\cZ] +#-----| 0 -> [RegExpConstant, RegExpEscape] \cZ -# 95| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +# 89| [RegExpConstant, RegExpEscape] \cZ -# 95| [RegExpConstant, RegExpEscape] \n +# 92| [RegExpConstant, RegExpEscape] \0 -# 95| [RegExpConstant, RegExpNormalChar] c +# 93| [RegExpCharacterClass] [\0] +#-----| 0 -> [RegExpConstant, RegExpEscape] \0 + +# 93| [RegExpConstant, RegExpEscape] \0 # 96| [RegExpConstant, RegExpNormalChar] a -# 96| [RegExpSequence] a\nc +# 96| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +#-----| 2 -> [RegExpConstant, RegExpNormalChar] b # 96| [RegExpConstant, RegExpEscape] \n -# 96| [RegExpConstant, RegExpNormalChar] c +# 96| [RegExpConstant, RegExpNormalChar] b # 97| [RegExpConstant, RegExpNormalChar] a @@ -546,3 +551,47 @@ regexp.cpp: # 103| [RegExpConstant, RegExpEscape] \n # 103| [RegExpConstant, RegExpNormalChar] c + +# 104| [RegExpConstant, RegExpNormalChar] a + +# 104| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 104| [RegExpConstant, RegExpEscape] \n + +# 104| [RegExpConstant, RegExpNormalChar] c + +# 105| [RegExpConstant, RegExpNormalChar] a + +# 105| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 105| [RegExpConstant, RegExpEscape] \n + +# 105| [RegExpConstant, RegExpNormalChar] c + +# 106| [RegExpConstant, RegExpNormalChar] a + +# 106| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 106| [RegExpConstant, RegExpEscape] \n + +# 106| [RegExpConstant, RegExpNormalChar] c + +# 107| [RegExpConstant, RegExpNormalChar] a + +# 107| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 107| [RegExpConstant, RegExpEscape] \n + +# 107| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 57dc43bd4714..496cf60e4e0d 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -74,6 +74,10 @@ std::regex r_posix2("[[:alpha:][:digit:]]"); // A single character class containing two ranges and one POSIX bracket expression std::regex r_posix3("[A-F[:digit:]a-f]"); +// POSIX collating symbols +std::regex r_posix_coll1("[[.a.]]"); +std::regex r_posix_coll2("[A-F[.b.]a-f]"); + // *Not* a POSIX bracket expression; just a regular character class. std::regex r_posix4("[:digit:]"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 3dc7b20753af..325ff3cebaaa 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -148,62 +148,79 @@ term | regexp.cpp:75:35:75:35 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:75:35:75:37 | a-f | RegExpCharacterRange | | regexp.cpp:75:37:75:37 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:22:78:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:81:19:81:24 | \\u{987 | RegExpConstant,RegExpEscape | -| regexp.cpp:84:21:84:23 | \\cA | RegExpConstant,RegExpEscape | -| regexp.cpp:85:21:85:25 | [\\cZ] | RegExpCharacterClass | -| regexp.cpp:85:22:85:24 | \\cZ | RegExpConstant,RegExpEscape | -| regexp.cpp:88:20:88:21 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:89:20:89:23 | [\\0] | RegExpCharacterClass | -| regexp.cpp:89:21:89:22 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:92:25:92:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:92:25:92:28 | a\\nb | RegExpSequence | -| regexp.cpp:92:26:92:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:92:28:92:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:93:37:93:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:93:37:93:40 | a\\nc | RegExpSequence | -| regexp.cpp:93:38:93:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:93:40:93:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:94:39:94:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:94:39:94:42 | a\\nc | RegExpSequence | -| regexp.cpp:94:40:94:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:94:42:94:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:38:95:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:95:38:95:41 | a\\nc | RegExpSequence | -| regexp.cpp:95:39:95:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:95:41:95:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:38:96:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:38:96:41 | a\\nc | RegExpSequence | -| regexp.cpp:96:39:96:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:96:41:96:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:23:97:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:23:97:26 | a\\nc | RegExpSequence | -| regexp.cpp:97:24:97:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:97:26:97:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:40:98:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:40:98:43 | a\\nc | RegExpSequence | -| regexp.cpp:98:41:98:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:98:43:98:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:42:99:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:42:99:45 | a\\nc | RegExpSequence | -| regexp.cpp:99:43:99:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:99:45:99:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:41:100:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:41:100:44 | a\\nc | RegExpSequence | -| regexp.cpp:100:42:100:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:100:44:100:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:41:101:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:41:101:44 | a\\nc | RegExpSequence | -| regexp.cpp:101:42:101:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:101:44:101:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:25:102:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:25:102:28 | a\\nc | RegExpSequence | -| regexp.cpp:102:26:102:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:102:28:102:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:29:103:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:29:103:32 | a\\nc | RegExpSequence | -| regexp.cpp:103:30:103:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:103:32:103:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:78:27:78:32 | [[.a.] | RegExpCharacterClass | +| regexp.cpp:78:27:78:33 | [[.a.]] | RegExpSequence | +| regexp.cpp:78:28:78:28 | [ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:78:29:78:29 | . | RegExpConstant,RegExpNormalChar | +| regexp.cpp:78:30:78:30 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:78:31:78:31 | . | RegExpConstant,RegExpNormalChar | +| regexp.cpp:78:33:78:33 | ] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:27:79:35 | [A-F[.b.] | RegExpCharacterClass | +| regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | RegExpSequence | +| regexp.cpp:79:28:79:28 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:28:79:30 | A-F | RegExpCharacterRange | +| regexp.cpp:79:30:79:30 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:31:79:31 | [ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:32:79:32 | . | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:33:79:33 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:34:79:34 | . | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:36:79:39 | a-f] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:82:22:82:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:85:19:85:24 | \\u{987 | RegExpConstant,RegExpEscape | +| regexp.cpp:88:21:88:23 | \\cA | RegExpConstant,RegExpEscape | +| regexp.cpp:89:21:89:25 | [\\cZ] | RegExpCharacterClass | +| regexp.cpp:89:22:89:24 | \\cZ | RegExpConstant,RegExpEscape | +| regexp.cpp:92:20:92:21 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:93:20:93:23 | [\\0] | RegExpCharacterClass | +| regexp.cpp:93:21:93:22 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:96:25:96:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:96:25:96:28 | a\\nb | RegExpSequence | +| regexp.cpp:96:26:96:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:96:28:96:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:37:97:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:97:37:97:40 | a\\nc | RegExpSequence | +| regexp.cpp:97:38:97:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:97:40:97:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:39:98:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:98:39:98:42 | a\\nc | RegExpSequence | +| regexp.cpp:98:40:98:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:98:42:98:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:38:99:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:99:38:99:41 | a\\nc | RegExpSequence | +| regexp.cpp:99:39:99:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:99:41:99:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:38:100:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:38:100:41 | a\\nc | RegExpSequence | +| regexp.cpp:100:39:100:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:100:41:100:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:23:101:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:23:101:26 | a\\nc | RegExpSequence | +| regexp.cpp:101:24:101:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:101:26:101:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:40:102:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:40:102:43 | a\\nc | RegExpSequence | +| regexp.cpp:102:41:102:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:102:43:102:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:42:103:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:42:103:45 | a\\nc | RegExpSequence | +| regexp.cpp:103:43:103:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:103:45:103:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:41:104:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:41:104:44 | a\\nc | RegExpSequence | +| regexp.cpp:104:42:104:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:104:44:104:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:41:105:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:41:105:44 | a\\nc | RegExpSequence | +| regexp.cpp:105:42:105:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:105:44:105:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:106:25:106:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:106:25:106:28 | a\\nc | RegExpSequence | +| regexp.cpp:106:26:106:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:106:28:106:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:107:29:107:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:107:29:107:32 | a\\nc | RegExpSequence | +| regexp.cpp:107:30:107:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:107:32:107:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -271,44 +288,56 @@ regExpNormalCharValue | regexp.cpp:75:25:75:25 | F | F | | regexp.cpp:75:35:75:35 | a | a | | regexp.cpp:75:37:75:37 | f | f | -| regexp.cpp:81:19:81:24 | \\u{987 | \u0987 | -| regexp.cpp:84:21:84:23 | \\cA | cA | -| regexp.cpp:85:22:85:24 | \\cZ | cZ | -| regexp.cpp:88:20:88:21 | \\0 | 0 | -| regexp.cpp:89:21:89:22 | \\0 | 0 | -| regexp.cpp:92:25:92:25 | a | a | -| regexp.cpp:92:26:92:27 | \\n | \n | -| regexp.cpp:92:28:92:28 | b | b | -| regexp.cpp:93:37:93:37 | a | a | -| regexp.cpp:93:38:93:39 | \\n | \n | -| regexp.cpp:93:40:93:40 | c | c | -| regexp.cpp:94:39:94:39 | a | a | -| regexp.cpp:94:40:94:41 | \\n | \n | -| regexp.cpp:94:42:94:42 | c | c | -| regexp.cpp:95:38:95:38 | a | a | -| regexp.cpp:95:39:95:40 | \\n | \n | -| regexp.cpp:95:41:95:41 | c | c | -| regexp.cpp:96:38:96:38 | a | a | -| regexp.cpp:96:39:96:40 | \\n | \n | -| regexp.cpp:96:41:96:41 | c | c | -| regexp.cpp:97:23:97:23 | a | a | -| regexp.cpp:97:24:97:25 | \\n | \n | -| regexp.cpp:97:26:97:26 | c | c | -| regexp.cpp:98:40:98:40 | a | a | -| regexp.cpp:98:41:98:42 | \\n | \n | -| regexp.cpp:98:43:98:43 | c | c | -| regexp.cpp:99:42:99:42 | a | a | -| regexp.cpp:99:43:99:44 | \\n | \n | -| regexp.cpp:99:45:99:45 | c | c | -| regexp.cpp:100:41:100:41 | a | a | -| regexp.cpp:100:42:100:43 | \\n | \n | -| regexp.cpp:100:44:100:44 | c | c | -| regexp.cpp:101:41:101:41 | a | a | -| regexp.cpp:101:42:101:43 | \\n | \n | -| regexp.cpp:101:44:101:44 | c | c | -| regexp.cpp:102:25:102:25 | a | a | -| regexp.cpp:102:26:102:27 | \\n | \n | -| regexp.cpp:102:28:102:28 | c | c | -| regexp.cpp:103:29:103:29 | a | a | -| regexp.cpp:103:30:103:31 | \\n | \n | -| regexp.cpp:103:32:103:32 | c | c | +| regexp.cpp:78:28:78:28 | [ | [ | +| regexp.cpp:78:29:78:29 | . | . | +| regexp.cpp:78:30:78:30 | a | a | +| regexp.cpp:78:31:78:31 | . | . | +| regexp.cpp:78:33:78:33 | ] | ] | +| regexp.cpp:79:28:79:28 | A | A | +| regexp.cpp:79:30:79:30 | F | F | +| regexp.cpp:79:31:79:31 | [ | [ | +| regexp.cpp:79:32:79:32 | . | . | +| regexp.cpp:79:33:79:33 | b | b | +| regexp.cpp:79:34:79:34 | . | . | +| regexp.cpp:79:36:79:39 | a-f] | a-f] | +| regexp.cpp:85:19:85:24 | \\u{987 | \u0987 | +| regexp.cpp:88:21:88:23 | \\cA | cA | +| regexp.cpp:89:22:89:24 | \\cZ | cZ | +| regexp.cpp:92:20:92:21 | \\0 | 0 | +| regexp.cpp:93:21:93:22 | \\0 | 0 | +| regexp.cpp:96:25:96:25 | a | a | +| regexp.cpp:96:26:96:27 | \\n | \n | +| regexp.cpp:96:28:96:28 | b | b | +| regexp.cpp:97:37:97:37 | a | a | +| regexp.cpp:97:38:97:39 | \\n | \n | +| regexp.cpp:97:40:97:40 | c | c | +| regexp.cpp:98:39:98:39 | a | a | +| regexp.cpp:98:40:98:41 | \\n | \n | +| regexp.cpp:98:42:98:42 | c | c | +| regexp.cpp:99:38:99:38 | a | a | +| regexp.cpp:99:39:99:40 | \\n | \n | +| regexp.cpp:99:41:99:41 | c | c | +| regexp.cpp:100:38:100:38 | a | a | +| regexp.cpp:100:39:100:40 | \\n | \n | +| regexp.cpp:100:41:100:41 | c | c | +| regexp.cpp:101:23:101:23 | a | a | +| regexp.cpp:101:24:101:25 | \\n | \n | +| regexp.cpp:101:26:101:26 | c | c | +| regexp.cpp:102:40:102:40 | a | a | +| regexp.cpp:102:41:102:42 | \\n | \n | +| regexp.cpp:102:43:102:43 | c | c | +| regexp.cpp:103:42:103:42 | a | a | +| regexp.cpp:103:43:103:44 | \\n | \n | +| regexp.cpp:103:45:103:45 | c | c | +| regexp.cpp:104:41:104:41 | a | a | +| regexp.cpp:104:42:104:43 | \\n | \n | +| regexp.cpp:104:44:104:44 | c | c | +| regexp.cpp:105:41:105:41 | a | a | +| regexp.cpp:105:42:105:43 | \\n | \n | +| regexp.cpp:105:44:105:44 | c | c | +| regexp.cpp:106:25:106:25 | a | a | +| regexp.cpp:106:26:106:27 | \\n | \n | +| regexp.cpp:106:28:106:28 | c | c | +| regexp.cpp:107:29:107:29 | a | a | +| regexp.cpp:107:30:107:31 | \\n | \n | +| regexp.cpp:107:32:107:32 | c | c | From 32e3a11d23a243f6a7fd9fc242955f2be79678c4 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 16:28:51 +0200 Subject: [PATCH 22/37] C++: Fix typo --- cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index af76baaa0f68..cfcb9f0085e6 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -98,7 +98,7 @@ abstract class RegExp extends StringLiteral { pos = rank[index](int p | (this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and - // Brackets that art part of POSIX expressions should not count as + // Brackets that are part of POSIX expressions should not count as // char-set delimiters. not exists(int x, int y | this.posixStyleNamedCharacterProperty(x, y, _) and pos >= x and pos < y From 57f5d03347a588a5f65aa8a37a0697d36ee9708b Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 17:09:43 +0200 Subject: [PATCH 23/37] C++: Implement POSIX collating symbols --- .../semmle/code/cpp/regex/RegexTreeView.qll | 12 +++-- .../code/cpp/regex/internal/ParseRegExp.qll | 40 ++++++++--------- .../test/library-tests/regex/parse.expected | 45 +++++-------------- .../test/library-tests/regex/regexp.expected | 33 ++++---------- 4 files changed, 50 insertions(+), 80 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index e9b27d27a79d..0e062f358af5 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -50,7 +50,7 @@ private newtype TRegExpParent = TRegExpBackRef(RegExp re, int start, int end) { re.backreference(start, end) } or /** A named character property */ TRegExpNamedCharacterProperty(RegExp re, int start, int end) { - re.namedCharacterProperty(start, end, _) + re.namedCharacterProperty(start, end, [":", "."], _) } /** An implementation that satisfies the RegexTreeView signature. */ @@ -1098,7 +1098,7 @@ private module Impl implements RegexTreeViewSig { /** * A named character property. For example, the POSIX bracket expression - * `[[:digit:]]`. + * `[:digit:]`. */ additional class RegExpNamedCharacterProperty extends RegExpTerm, TRegExpNamedCharacterProperty { RegExpNamedCharacterProperty() { this = TRegExpNamedCharacterProperty(re, start, end) } @@ -1108,10 +1108,16 @@ private module Impl implements RegexTreeViewSig { override string getAPrimaryQlClass() { result = "RegExpNamedCharacterProperty" } /** - * Gets the property name. For example, in `[[:digit:]]`, the result is + * Gets the property name. For example, in `[:digit:]`, the result is * `"digit"`. */ string getName() { result = re.getCharacterPropertyName(start, end) } + + /** + * Gets the property symbol. For example, in `[:digit:]`, the result is + * `":"`. + */ + string getSymbol() { result = re.getCharacterPropertySymbol(start, end) } } class Top = RegExpParent; diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index cfcb9f0085e6..2184c5f1b45a 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -100,9 +100,7 @@ abstract class RegExp extends StringLiteral { (this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and // Brackets that are part of POSIX expressions should not count as // char-set delimiters. - not exists(int x, int y | - this.posixStyleNamedCharacterProperty(x, y, _) and pos >= x and pos < y - ) + not exists(int x, int y | this.posixStyleProperty(x, y, _, _) and pos >= x and pos < y) ) and ( this.nonEscapedCharAt(pos) = "[" and result = true @@ -132,9 +130,7 @@ abstract class RegExp extends StringLiteral { min(int e | e > innerStart and this.nonEscapedCharAt(e) = "]" and - not exists(int x, int y | - this.posixStyleNamedCharacterProperty(x, y, _) and e >= x and e < y - ) + not exists(int x, int y | this.posixStyleProperty(x, y, _, _) and e >= x and e < y) | e ) @@ -160,7 +156,7 @@ abstract class RegExp extends StringLiteral { ( this.escapedCharacter(start, end) or - this.namedCharacterProperty(start, end, _) + this.namedCharacterProperty(start, end, _, _) or exists(this.nonEscapedCharAt(start)) and end = start + 1 ) @@ -169,7 +165,7 @@ abstract class RegExp extends StringLiteral { ( this.escapedCharacter(start, end) or - this.namedCharacterProperty(start, end, _) + this.namedCharacterProperty(start, end, _, _) or exists(this.nonEscapedCharAt(start)) and end = start + 1 and @@ -282,28 +278,34 @@ abstract class RegExp extends StringLiteral { ) } - /** Matches named character properties such as `[[:digit:]]`. */ - predicate namedCharacterProperty(int start, int end, string name) { - this.posixStyleNamedCharacterProperty(start, end, name) + /** Matches named character properties such as `[:digit:]`. */ + predicate namedCharacterProperty(int start, int end, string symbol, string name) { + this.posixStyleProperty(start, end, symbol, name) } /** Gets the name of the character property in start,end */ string getCharacterPropertyName(int start, int end) { - this.namedCharacterProperty(start, end, result) + this.namedCharacterProperty(start, end, _, result) + } + + /** Gets the symbol of the character property in start,end */ + string getCharacterPropertySymbol(int start, int end) { + this.namedCharacterProperty(start, end, result, _) } /** Matches a POSIX bracket expression such as `[:alnum:]` within a character class. */ - private predicate posixStyleNamedCharacterProperty(int start, int end, string name) { + private predicate posixStyleProperty(int start, int end, string symbol, string name) { this.getChar(start) = "[" and - this.getChar(start + 1) = ":" and + this.getChar(start + 1) = symbol and end = min(int e | e > start and - this.getChar(e - 2) = ":" and + this.getChar(e - 2) = symbol and this.getChar(e - 1) = "]" | e ) and + symbol in [":", ".", "="] and name = this.getText().substring(start + 2, end - 2) } @@ -348,9 +350,7 @@ abstract class RegExp extends StringLiteral { * Holds if the character at `index` is inside a posix bracket. */ predicate inPosixBracket(int index) { - exists(int x, int y | - this.posixStyleNamedCharacterProperty(x, y, _) and index in [x + 1 .. y - 2] - ) + exists(int x, int y | this.posixStyleProperty(x, y, _, _) and index in [x + 1 .. y - 2]) } /** @@ -361,7 +361,7 @@ abstract class RegExp extends StringLiteral { not this.charSet(start, _) and not this.charSet(_, start + 1) and not exists(int x, int y | - this.posixStyleNamedCharacterProperty(x, y, _) and + this.posixStyleProperty(x, y, _, _) and start >= x and end <= y ) and @@ -731,7 +731,7 @@ abstract class RegExp extends StringLiteral { this.isGroupStart(start) or this.charSet(start, _) or this.backreference(start, _) or - this.namedCharacterProperty(start, _, _) + this.namedCharacterProperty(start, _, _, _) } private predicate itemEnd(int end) { diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index ac066391db12..059d2c0b53c7 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -397,36 +397,15 @@ regexp.cpp: # 75| [RegExpConstant, RegExpNormalChar] f -# 78| [RegExpCharacterClass] [[.a.] -#-----| 0 -> [RegExpConstant, RegExpNormalChar] [ -#-----| 1 -> [RegExpConstant, RegExpNormalChar] . -#-----| 2 -> [RegExpConstant, RegExpNormalChar] a -#-----| 3 -> [RegExpConstant, RegExpNormalChar] . - -# 78| [RegExpSequence] [[.a.]] -#-----| 0 -> [RegExpCharacterClass] [[.a.] -#-----| 1 -> [RegExpConstant, RegExpNormalChar] ] - -# 78| [RegExpConstant, RegExpNormalChar] [ - -# 78| [RegExpConstant, RegExpNormalChar] . - -# 78| [RegExpConstant, RegExpNormalChar] a - -# 78| [RegExpConstant, RegExpNormalChar] . +# 78| [RegExpCharacterClass] [[.a.]] +#-----| 0 -> [RegExpNamedCharacterProperty] [.a.] -# 78| [RegExpConstant, RegExpNormalChar] ] +# 78| [RegExpNamedCharacterProperty] [.a.] -# 79| [RegExpCharacterClass] [A-F[.b.] +# 79| [RegExpCharacterClass] [A-F[.b.]a-f] #-----| 0 -> [RegExpCharacterRange] A-F -#-----| 1 -> [RegExpConstant, RegExpNormalChar] [ -#-----| 2 -> [RegExpConstant, RegExpNormalChar] . -#-----| 3 -> [RegExpConstant, RegExpNormalChar] b -#-----| 4 -> [RegExpConstant, RegExpNormalChar] . - -# 79| [RegExpSequence] [A-F[.b.]a-f] -#-----| 0 -> [RegExpCharacterClass] [A-F[.b.] -#-----| 1 -> [RegExpConstant, RegExpNormalChar] a-f] +#-----| 1 -> [RegExpNamedCharacterProperty] [.b.] +#-----| 2 -> [RegExpCharacterRange] a-f # 79| [RegExpConstant, RegExpNormalChar] A @@ -436,15 +415,15 @@ regexp.cpp: # 79| [RegExpConstant, RegExpNormalChar] F -# 79| [RegExpConstant, RegExpNormalChar] [ - -# 79| [RegExpConstant, RegExpNormalChar] . +# 79| [RegExpNamedCharacterProperty] [.b.] -# 79| [RegExpConstant, RegExpNormalChar] b +# 79| [RegExpConstant, RegExpNormalChar] a -# 79| [RegExpConstant, RegExpNormalChar] . +# 79| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 79| [RegExpConstant, RegExpNormalChar] a-f] +# 79| [RegExpConstant, RegExpNormalChar] f # 82| [RegExpNamedCharacterProperty] [:digit:] diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 325ff3cebaaa..e6fb53d9d0ee 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -148,23 +148,16 @@ term | regexp.cpp:75:35:75:35 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:75:35:75:37 | a-f | RegExpCharacterRange | | regexp.cpp:75:37:75:37 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:27:78:32 | [[.a.] | RegExpCharacterClass | -| regexp.cpp:78:27:78:33 | [[.a.]] | RegExpSequence | -| regexp.cpp:78:28:78:28 | [ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:29:78:29 | . | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:30:78:30 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:31:78:31 | . | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:33:78:33 | ] | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:27:79:35 | [A-F[.b.] | RegExpCharacterClass | -| regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | RegExpSequence | +| regexp.cpp:78:27:78:33 | [[.a.]] | RegExpCharacterClass | +| regexp.cpp:78:28:78:32 | [.a.] | RegExpNamedCharacterProperty | +| regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | RegExpCharacterClass | | regexp.cpp:79:28:79:28 | A | RegExpConstant,RegExpNormalChar | | regexp.cpp:79:28:79:30 | A-F | RegExpCharacterRange | | regexp.cpp:79:30:79:30 | F | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:31:79:31 | [ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:32:79:32 | . | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:33:79:33 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:34:79:34 | . | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:36:79:39 | a-f] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:31:79:35 | [.b.] | RegExpNamedCharacterProperty | +| regexp.cpp:79:36:79:36 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:36:79:38 | a-f | RegExpCharacterRange | +| regexp.cpp:79:38:79:38 | f | RegExpConstant,RegExpNormalChar | | regexp.cpp:82:22:82:30 | [:digit:] | RegExpNamedCharacterProperty | | regexp.cpp:85:19:85:24 | \\u{987 | RegExpConstant,RegExpEscape | | regexp.cpp:88:21:88:23 | \\cA | RegExpConstant,RegExpEscape | @@ -288,18 +281,10 @@ regExpNormalCharValue | regexp.cpp:75:25:75:25 | F | F | | regexp.cpp:75:35:75:35 | a | a | | regexp.cpp:75:37:75:37 | f | f | -| regexp.cpp:78:28:78:28 | [ | [ | -| regexp.cpp:78:29:78:29 | . | . | -| regexp.cpp:78:30:78:30 | a | a | -| regexp.cpp:78:31:78:31 | . | . | -| regexp.cpp:78:33:78:33 | ] | ] | | regexp.cpp:79:28:79:28 | A | A | | regexp.cpp:79:30:79:30 | F | F | -| regexp.cpp:79:31:79:31 | [ | [ | -| regexp.cpp:79:32:79:32 | . | . | -| regexp.cpp:79:33:79:33 | b | b | -| regexp.cpp:79:34:79:34 | . | . | -| regexp.cpp:79:36:79:39 | a-f] | a-f] | +| regexp.cpp:79:36:79:36 | a | a | +| regexp.cpp:79:38:79:38 | f | f | | regexp.cpp:85:19:85:24 | \\u{987 | \u0987 | | regexp.cpp:88:21:88:23 | \\cA | cA | | regexp.cpp:89:22:89:24 | \\cZ | cZ | From 96c9214dbbe8558ff082426f5a1fe705ff633422 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:22:51 +0000 Subject: [PATCH 24/37] C++: Add POSIX equivalence-class tests (pre-fix) --- .../library-tests/regex/locations.expected | 36 +-- .../test/library-tests/regex/parse.expected | 117 ++++++---- cpp/ql/test/library-tests/regex/regexp.cpp | 4 + .../test/library-tests/regex/regexp.expected | 206 +++++++++--------- 4 files changed, 201 insertions(+), 162 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index d19f83db4503..cc8164c700c7 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -34,20 +34,22 @@ | regexp.cpp:75:22:75:38 | [A-F[:digit:]a-f] | regexp.cpp | 75 | 22 | 75 | 38 | | regexp.cpp:78:27:78:33 | [[.a.]] | regexp.cpp | 78 | 27 | 78 | 33 | | regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | regexp.cpp | 79 | 27 | 79 | 39 | -| regexp.cpp:82:22:82:30 | [:digit:] | regexp.cpp | 82 | 22 | 82 | 30 | -| regexp.cpp:88:21:88:23 | \\cA | regexp.cpp | 88 | 21 | 88 | 23 | -| regexp.cpp:89:21:89:25 | [\\cZ] | regexp.cpp | 89 | 21 | 89 | 25 | -| regexp.cpp:92:20:92:21 | \\0 | regexp.cpp | 92 | 20 | 92 | 21 | -| regexp.cpp:93:20:93:23 | [\\0] | regexp.cpp | 93 | 20 | 93 | 23 | -| regexp.cpp:96:25:96:28 | a\\nb | regexp.cpp | 96 | 25 | 96 | 28 | -| regexp.cpp:97:37:97:40 | a\\nc | regexp.cpp | 97 | 37 | 97 | 40 | -| regexp.cpp:98:39:98:42 | a\\nc | regexp.cpp | 98 | 39 | 98 | 42 | -| regexp.cpp:99:38:99:41 | a\\nc | regexp.cpp | 99 | 38 | 99 | 41 | -| regexp.cpp:100:38:100:41 | a\\nc | regexp.cpp | 100 | 38 | 100 | 41 | -| regexp.cpp:101:23:101:26 | a\\nc | regexp.cpp | 101 | 23 | 101 | 26 | -| regexp.cpp:102:40:102:43 | a\\nc | regexp.cpp | 102 | 40 | 102 | 43 | -| regexp.cpp:103:42:103:45 | a\\nc | regexp.cpp | 103 | 42 | 103 | 45 | -| regexp.cpp:104:41:104:44 | a\\nc | regexp.cpp | 104 | 41 | 104 | 44 | -| regexp.cpp:105:41:105:44 | a\\nc | regexp.cpp | 105 | 41 | 105 | 44 | -| regexp.cpp:106:25:106:28 | a\\nc | regexp.cpp | 106 | 25 | 106 | 28 | -| regexp.cpp:107:29:107:32 | a\\nc | regexp.cpp | 107 | 29 | 107 | 32 | +| regexp.cpp:82:28:82:34 | [[=a=]] | regexp.cpp | 82 | 28 | 82 | 34 | +| regexp.cpp:83:28:83:40 | [A-F[=b=]a-f] | regexp.cpp | 83 | 28 | 83 | 40 | +| regexp.cpp:86:22:86:30 | [:digit:] | regexp.cpp | 86 | 22 | 86 | 30 | +| regexp.cpp:92:21:92:23 | \\cA | regexp.cpp | 92 | 21 | 92 | 23 | +| regexp.cpp:93:21:93:25 | [\\cZ] | regexp.cpp | 93 | 21 | 93 | 25 | +| regexp.cpp:96:20:96:21 | \\0 | regexp.cpp | 96 | 20 | 96 | 21 | +| regexp.cpp:97:20:97:23 | [\\0] | regexp.cpp | 97 | 20 | 97 | 23 | +| regexp.cpp:100:25:100:28 | a\\nb | regexp.cpp | 100 | 25 | 100 | 28 | +| regexp.cpp:101:37:101:40 | a\\nc | regexp.cpp | 101 | 37 | 101 | 40 | +| regexp.cpp:102:39:102:42 | a\\nc | regexp.cpp | 102 | 39 | 102 | 42 | +| regexp.cpp:103:38:103:41 | a\\nc | regexp.cpp | 103 | 38 | 103 | 41 | +| regexp.cpp:104:38:104:41 | a\\nc | regexp.cpp | 104 | 38 | 104 | 41 | +| regexp.cpp:105:23:105:26 | a\\nc | regexp.cpp | 105 | 23 | 105 | 26 | +| regexp.cpp:106:40:106:43 | a\\nc | regexp.cpp | 106 | 40 | 106 | 43 | +| regexp.cpp:107:42:107:45 | a\\nc | regexp.cpp | 107 | 42 | 107 | 45 | +| regexp.cpp:108:41:108:44 | a\\nc | regexp.cpp | 108 | 41 | 108 | 44 | +| regexp.cpp:109:41:109:44 | a\\nc | regexp.cpp | 109 | 41 | 109 | 44 | +| regexp.cpp:110:25:110:28 | a\\nc | regexp.cpp | 110 | 25 | 110 | 28 | +| regexp.cpp:111:29:111:32 | a\\nc | regexp.cpp | 111 | 29 | 111 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 059d2c0b53c7..f34b91fd15f2 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -425,78 +425,55 @@ regexp.cpp: # 79| [RegExpConstant, RegExpNormalChar] f -# 82| [RegExpNamedCharacterProperty] [:digit:] +# 82| [RegExpCharacterClass] [[=a=]] -# 85| [RegExpConstant, RegExpEscape] \u{987 - -# 88| [RegExpConstant, RegExpEscape] \cA - -# 89| [RegExpCharacterClass] [\cZ] -#-----| 0 -> [RegExpConstant, RegExpEscape] \cZ - -# 89| [RegExpConstant, RegExpEscape] \cZ - -# 92| [RegExpConstant, RegExpEscape] \0 - -# 93| [RegExpCharacterClass] [\0] -#-----| 0 -> [RegExpConstant, RegExpEscape] \0 - -# 93| [RegExpConstant, RegExpEscape] \0 +# 83| [RegExpCharacterClass] [A-F[=b=]a-f] +#-----| 0 -> [RegExpCharacterRange] A-F -# 96| [RegExpConstant, RegExpNormalChar] a +# 83| [RegExpConstant, RegExpNormalChar] A -# 96| [RegExpSequence] a\nb -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] b - -# 96| [RegExpConstant, RegExpEscape] \n +# 83| [RegExpCharacterRange] A-F +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 96| [RegExpConstant, RegExpNormalChar] b +# 83| [RegExpConstant, RegExpNormalChar] F -# 97| [RegExpConstant, RegExpNormalChar] a +# 83| [RegExpConstant, RegExpNormalChar] a -# 97| [RegExpSequence] a\nc +# 83| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c - -# 97| [RegExpConstant, RegExpEscape] \n +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 97| [RegExpConstant, RegExpNormalChar] c +# 83| [RegExpConstant, RegExpNormalChar] f -# 98| [RegExpConstant, RegExpNormalChar] a +# 86| [RegExpNamedCharacterProperty] [:digit:] -# 98| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +# 89| [RegExpConstant, RegExpEscape] \u{987 -# 98| [RegExpConstant, RegExpEscape] \n +# 92| [RegExpConstant, RegExpEscape] \cA -# 98| [RegExpConstant, RegExpNormalChar] c +# 93| [RegExpCharacterClass] [\cZ] +#-----| 0 -> [RegExpConstant, RegExpEscape] \cZ -# 99| [RegExpConstant, RegExpNormalChar] a +# 93| [RegExpConstant, RegExpEscape] \cZ -# 99| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +# 96| [RegExpConstant, RegExpEscape] \0 -# 99| [RegExpConstant, RegExpEscape] \n +# 97| [RegExpCharacterClass] [\0] +#-----| 0 -> [RegExpConstant, RegExpEscape] \0 -# 99| [RegExpConstant, RegExpNormalChar] c +# 97| [RegExpConstant, RegExpEscape] \0 # 100| [RegExpConstant, RegExpNormalChar] a -# 100| [RegExpSequence] a\nc +# 100| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +#-----| 2 -> [RegExpConstant, RegExpNormalChar] b # 100| [RegExpConstant, RegExpEscape] \n -# 100| [RegExpConstant, RegExpNormalChar] c +# 100| [RegExpConstant, RegExpNormalChar] b # 101| [RegExpConstant, RegExpNormalChar] a @@ -574,3 +551,47 @@ regexp.cpp: # 107| [RegExpConstant, RegExpEscape] \n # 107| [RegExpConstant, RegExpNormalChar] c + +# 108| [RegExpConstant, RegExpNormalChar] a + +# 108| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 108| [RegExpConstant, RegExpEscape] \n + +# 108| [RegExpConstant, RegExpNormalChar] c + +# 109| [RegExpConstant, RegExpNormalChar] a + +# 109| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 109| [RegExpConstant, RegExpEscape] \n + +# 109| [RegExpConstant, RegExpNormalChar] c + +# 110| [RegExpConstant, RegExpNormalChar] a + +# 110| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 110| [RegExpConstant, RegExpEscape] \n + +# 110| [RegExpConstant, RegExpNormalChar] c + +# 111| [RegExpConstant, RegExpNormalChar] a + +# 111| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 111| [RegExpConstant, RegExpEscape] \n + +# 111| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 496cf60e4e0d..43115442c1d4 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -78,6 +78,10 @@ std::regex r_posix3("[A-F[:digit:]a-f]"); std::regex r_posix_coll1("[[.a.]]"); std::regex r_posix_coll2("[A-F[.b.]a-f]"); +// POSIX equivalence classes +std::regex r_posix_equiv1("[[=a=]]"); +std::regex r_posix_equiv2("[A-F[=b=]a-f]"); + // *Not* a POSIX bracket expression; just a regular character class. std::regex r_posix4("[:digit:]"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index e6fb53d9d0ee..bec5af96a6cd 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -158,62 +158,70 @@ term | regexp.cpp:79:36:79:36 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:79:36:79:38 | a-f | RegExpCharacterRange | | regexp.cpp:79:38:79:38 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:82:22:82:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:85:19:85:24 | \\u{987 | RegExpConstant,RegExpEscape | -| regexp.cpp:88:21:88:23 | \\cA | RegExpConstant,RegExpEscape | -| regexp.cpp:89:21:89:25 | [\\cZ] | RegExpCharacterClass | -| regexp.cpp:89:22:89:24 | \\cZ | RegExpConstant,RegExpEscape | -| regexp.cpp:92:20:92:21 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:93:20:93:23 | [\\0] | RegExpCharacterClass | -| regexp.cpp:93:21:93:22 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:96:25:96:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:96:25:96:28 | a\\nb | RegExpSequence | -| regexp.cpp:96:26:96:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:96:28:96:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:37:97:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:97:37:97:40 | a\\nc | RegExpSequence | -| regexp.cpp:97:38:97:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:97:40:97:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:39:98:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:98:39:98:42 | a\\nc | RegExpSequence | -| regexp.cpp:98:40:98:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:98:42:98:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:38:99:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:99:38:99:41 | a\\nc | RegExpSequence | -| regexp.cpp:99:39:99:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:99:41:99:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:38:100:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:38:100:41 | a\\nc | RegExpSequence | -| regexp.cpp:100:39:100:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:100:41:100:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:23:101:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:23:101:26 | a\\nc | RegExpSequence | -| regexp.cpp:101:24:101:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:101:26:101:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:40:102:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:40:102:43 | a\\nc | RegExpSequence | -| regexp.cpp:102:41:102:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:102:43:102:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:42:103:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:42:103:45 | a\\nc | RegExpSequence | -| regexp.cpp:103:43:103:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:103:45:103:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:41:104:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:41:104:44 | a\\nc | RegExpSequence | -| regexp.cpp:104:42:104:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:104:44:104:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:41:105:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:41:105:44 | a\\nc | RegExpSequence | -| regexp.cpp:105:42:105:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:105:44:105:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:106:25:106:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:106:25:106:28 | a\\nc | RegExpSequence | -| regexp.cpp:106:26:106:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:106:28:106:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:107:29:107:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:107:29:107:32 | a\\nc | RegExpSequence | -| regexp.cpp:107:30:107:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:107:32:107:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:82:28:82:34 | [[=a=]] | RegExpCharacterClass | +| regexp.cpp:83:28:83:40 | [A-F[=b=]a-f] | RegExpCharacterClass | +| regexp.cpp:83:29:83:29 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:83:29:83:31 | A-F | RegExpCharacterRange | +| regexp.cpp:83:31:83:31 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:83:37:83:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:83:37:83:39 | a-f | RegExpCharacterRange | +| regexp.cpp:83:39:83:39 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:86:22:86:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:89:19:89:24 | \\u{987 | RegExpConstant,RegExpEscape | +| regexp.cpp:92:21:92:23 | \\cA | RegExpConstant,RegExpEscape | +| regexp.cpp:93:21:93:25 | [\\cZ] | RegExpCharacterClass | +| regexp.cpp:93:22:93:24 | \\cZ | RegExpConstant,RegExpEscape | +| regexp.cpp:96:20:96:21 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:97:20:97:23 | [\\0] | RegExpCharacterClass | +| regexp.cpp:97:21:97:22 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:100:25:100:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:25:100:28 | a\\nb | RegExpSequence | +| regexp.cpp:100:26:100:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:100:28:100:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:37:101:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:37:101:40 | a\\nc | RegExpSequence | +| regexp.cpp:101:38:101:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:101:40:101:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:39:102:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:39:102:42 | a\\nc | RegExpSequence | +| regexp.cpp:102:40:102:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:102:42:102:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:38:103:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:38:103:41 | a\\nc | RegExpSequence | +| regexp.cpp:103:39:103:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:103:41:103:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:38:104:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:38:104:41 | a\\nc | RegExpSequence | +| regexp.cpp:104:39:104:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:104:41:104:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:23:105:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:23:105:26 | a\\nc | RegExpSequence | +| regexp.cpp:105:24:105:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:105:26:105:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:106:40:106:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:106:40:106:43 | a\\nc | RegExpSequence | +| regexp.cpp:106:41:106:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:106:43:106:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:107:42:107:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:107:42:107:45 | a\\nc | RegExpSequence | +| regexp.cpp:107:43:107:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:107:45:107:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:108:41:108:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:108:41:108:44 | a\\nc | RegExpSequence | +| regexp.cpp:108:42:108:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:108:44:108:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:109:41:109:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:109:41:109:44 | a\\nc | RegExpSequence | +| regexp.cpp:109:42:109:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:109:44:109:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:110:25:110:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:110:25:110:28 | a\\nc | RegExpSequence | +| regexp.cpp:110:26:110:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:110:28:110:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:111:29:111:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:111:29:111:32 | a\\nc | RegExpSequence | +| regexp.cpp:111:30:111:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:111:32:111:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -285,44 +293,48 @@ regExpNormalCharValue | regexp.cpp:79:30:79:30 | F | F | | regexp.cpp:79:36:79:36 | a | a | | regexp.cpp:79:38:79:38 | f | f | -| regexp.cpp:85:19:85:24 | \\u{987 | \u0987 | -| regexp.cpp:88:21:88:23 | \\cA | cA | -| regexp.cpp:89:22:89:24 | \\cZ | cZ | -| regexp.cpp:92:20:92:21 | \\0 | 0 | -| regexp.cpp:93:21:93:22 | \\0 | 0 | -| regexp.cpp:96:25:96:25 | a | a | -| regexp.cpp:96:26:96:27 | \\n | \n | -| regexp.cpp:96:28:96:28 | b | b | -| regexp.cpp:97:37:97:37 | a | a | -| regexp.cpp:97:38:97:39 | \\n | \n | -| regexp.cpp:97:40:97:40 | c | c | -| regexp.cpp:98:39:98:39 | a | a | -| regexp.cpp:98:40:98:41 | \\n | \n | -| regexp.cpp:98:42:98:42 | c | c | -| regexp.cpp:99:38:99:38 | a | a | -| regexp.cpp:99:39:99:40 | \\n | \n | -| regexp.cpp:99:41:99:41 | c | c | -| regexp.cpp:100:38:100:38 | a | a | -| regexp.cpp:100:39:100:40 | \\n | \n | -| regexp.cpp:100:41:100:41 | c | c | -| regexp.cpp:101:23:101:23 | a | a | -| regexp.cpp:101:24:101:25 | \\n | \n | -| regexp.cpp:101:26:101:26 | c | c | -| regexp.cpp:102:40:102:40 | a | a | -| regexp.cpp:102:41:102:42 | \\n | \n | -| regexp.cpp:102:43:102:43 | c | c | -| regexp.cpp:103:42:103:42 | a | a | -| regexp.cpp:103:43:103:44 | \\n | \n | -| regexp.cpp:103:45:103:45 | c | c | -| regexp.cpp:104:41:104:41 | a | a | -| regexp.cpp:104:42:104:43 | \\n | \n | -| regexp.cpp:104:44:104:44 | c | c | -| regexp.cpp:105:41:105:41 | a | a | -| regexp.cpp:105:42:105:43 | \\n | \n | -| regexp.cpp:105:44:105:44 | c | c | -| regexp.cpp:106:25:106:25 | a | a | -| regexp.cpp:106:26:106:27 | \\n | \n | -| regexp.cpp:106:28:106:28 | c | c | -| regexp.cpp:107:29:107:29 | a | a | -| regexp.cpp:107:30:107:31 | \\n | \n | -| regexp.cpp:107:32:107:32 | c | c | +| regexp.cpp:83:29:83:29 | A | A | +| regexp.cpp:83:31:83:31 | F | F | +| regexp.cpp:83:37:83:37 | a | a | +| regexp.cpp:83:39:83:39 | f | f | +| regexp.cpp:89:19:89:24 | \\u{987 | \u0987 | +| regexp.cpp:92:21:92:23 | \\cA | cA | +| regexp.cpp:93:22:93:24 | \\cZ | cZ | +| regexp.cpp:96:20:96:21 | \\0 | 0 | +| regexp.cpp:97:21:97:22 | \\0 | 0 | +| regexp.cpp:100:25:100:25 | a | a | +| regexp.cpp:100:26:100:27 | \\n | \n | +| regexp.cpp:100:28:100:28 | b | b | +| regexp.cpp:101:37:101:37 | a | a | +| regexp.cpp:101:38:101:39 | \\n | \n | +| regexp.cpp:101:40:101:40 | c | c | +| regexp.cpp:102:39:102:39 | a | a | +| regexp.cpp:102:40:102:41 | \\n | \n | +| regexp.cpp:102:42:102:42 | c | c | +| regexp.cpp:103:38:103:38 | a | a | +| regexp.cpp:103:39:103:40 | \\n | \n | +| regexp.cpp:103:41:103:41 | c | c | +| regexp.cpp:104:38:104:38 | a | a | +| regexp.cpp:104:39:104:40 | \\n | \n | +| regexp.cpp:104:41:104:41 | c | c | +| regexp.cpp:105:23:105:23 | a | a | +| regexp.cpp:105:24:105:25 | \\n | \n | +| regexp.cpp:105:26:105:26 | c | c | +| regexp.cpp:106:40:106:40 | a | a | +| regexp.cpp:106:41:106:42 | \\n | \n | +| regexp.cpp:106:43:106:43 | c | c | +| regexp.cpp:107:42:107:42 | a | a | +| regexp.cpp:107:43:107:44 | \\n | \n | +| regexp.cpp:107:45:107:45 | c | c | +| regexp.cpp:108:41:108:41 | a | a | +| regexp.cpp:108:42:108:43 | \\n | \n | +| regexp.cpp:108:44:108:44 | c | c | +| regexp.cpp:109:41:109:41 | a | a | +| regexp.cpp:109:42:109:43 | \\n | \n | +| regexp.cpp:109:44:109:44 | c | c | +| regexp.cpp:110:25:110:25 | a | a | +| regexp.cpp:110:26:110:27 | \\n | \n | +| regexp.cpp:110:28:110:28 | c | c | +| regexp.cpp:111:29:111:29 | a | a | +| regexp.cpp:111:30:111:31 | \\n | \n | +| regexp.cpp:111:32:111:32 | c | c | From 5cce52ec8d9cd9bc6a004e947684e4ec9ebabbce Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 17:12:41 +0200 Subject: [PATCH 25/37] C++: Implement POSIX equivalence classes --- cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll | 2 +- cpp/ql/test/library-tests/regex/parse.expected | 7 +++++++ cpp/ql/test/library-tests/regex/regexp.expected | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 0e062f358af5..013348c55363 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -50,7 +50,7 @@ private newtype TRegExpParent = TRegExpBackRef(RegExp re, int start, int end) { re.backreference(start, end) } or /** A named character property */ TRegExpNamedCharacterProperty(RegExp re, int start, int end) { - re.namedCharacterProperty(start, end, [":", "."], _) + re.namedCharacterProperty(start, end, _, _) } /** An implementation that satisfies the RegexTreeView signature. */ diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index f34b91fd15f2..aa2d64c38c89 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -426,9 +426,14 @@ regexp.cpp: # 79| [RegExpConstant, RegExpNormalChar] f # 82| [RegExpCharacterClass] [[=a=]] +#-----| 0 -> [RegExpNamedCharacterProperty] [=a=] + +# 82| [RegExpNamedCharacterProperty] [=a=] # 83| [RegExpCharacterClass] [A-F[=b=]a-f] #-----| 0 -> [RegExpCharacterRange] A-F +#-----| 1 -> [RegExpNamedCharacterProperty] [=b=] +#-----| 2 -> [RegExpCharacterRange] a-f # 83| [RegExpConstant, RegExpNormalChar] A @@ -438,6 +443,8 @@ regexp.cpp: # 83| [RegExpConstant, RegExpNormalChar] F +# 83| [RegExpNamedCharacterProperty] [=b=] + # 83| [RegExpConstant, RegExpNormalChar] a # 83| [RegExpCharacterRange] a-f diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index bec5af96a6cd..44bfb4d6fefa 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -159,10 +159,12 @@ term | regexp.cpp:79:36:79:38 | a-f | RegExpCharacterRange | | regexp.cpp:79:38:79:38 | f | RegExpConstant,RegExpNormalChar | | regexp.cpp:82:28:82:34 | [[=a=]] | RegExpCharacterClass | +| regexp.cpp:82:29:82:33 | [=a=] | RegExpNamedCharacterProperty | | regexp.cpp:83:28:83:40 | [A-F[=b=]a-f] | RegExpCharacterClass | | regexp.cpp:83:29:83:29 | A | RegExpConstant,RegExpNormalChar | | regexp.cpp:83:29:83:31 | A-F | RegExpCharacterRange | | regexp.cpp:83:31:83:31 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:83:32:83:36 | [=b=] | RegExpNamedCharacterProperty | | regexp.cpp:83:37:83:37 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:83:37:83:39 | a-f | RegExpCharacterRange | | regexp.cpp:83:39:83:39 | f | RegExpConstant,RegExpNormalChar | From b1e7fb78b40425d72a382b409b9e036556e676d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:24:37 +0000 Subject: [PATCH 26/37] C++: Replace non-standard \u{...} test with \uhhhh --- cpp/ql/test/library-tests/regex/locations.expected | 1 + cpp/ql/test/library-tests/regex/parse.expected | 2 +- cpp/ql/test/library-tests/regex/regexp.cpp | 2 +- cpp/ql/test/library-tests/regex/regexp.expected | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index cc8164c700c7..43da53ad3763 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -37,6 +37,7 @@ | regexp.cpp:82:28:82:34 | [[=a=]] | regexp.cpp | 82 | 28 | 82 | 34 | | regexp.cpp:83:28:83:40 | [A-F[=b=]a-f] | regexp.cpp | 83 | 28 | 83 | 40 | | regexp.cpp:86:22:86:30 | [:digit:] | regexp.cpp | 86 | 22 | 86 | 30 | +| regexp.cpp:89:19:89:24 | \\u0061 | regexp.cpp | 89 | 19 | 89 | 24 | | regexp.cpp:92:21:92:23 | \\cA | regexp.cpp | 92 | 21 | 92 | 23 | | regexp.cpp:93:21:93:25 | [\\cZ] | regexp.cpp | 93 | 21 | 93 | 25 | | regexp.cpp:96:20:96:21 | \\0 | regexp.cpp | 96 | 20 | 96 | 21 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index aa2d64c38c89..83eadac1b6d9 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -455,7 +455,7 @@ regexp.cpp: # 86| [RegExpNamedCharacterProperty] [:digit:] -# 89| [RegExpConstant, RegExpEscape] \u{987 +# 89| [RegExpConstant, RegExpEscape] \u0061 # 92| [RegExpConstant, RegExpEscape] \cA diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index 43115442c1d4..ea56d12bed35 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -86,7 +86,7 @@ std::regex r_posix_equiv2("[A-F[=b=]a-f]"); std::regex r_posix4("[:digit:]"); // unicode -std::regex r_uni("\\u{9879}"); +std::regex r_uni("\\u0061"); // control escapes std::regex r_ctrl1("\\cA"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 44bfb4d6fefa..5f70909c5f34 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -169,7 +169,7 @@ term | regexp.cpp:83:37:83:39 | a-f | RegExpCharacterRange | | regexp.cpp:83:39:83:39 | f | RegExpConstant,RegExpNormalChar | | regexp.cpp:86:22:86:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:89:19:89:24 | \\u{987 | RegExpConstant,RegExpEscape | +| regexp.cpp:89:19:89:24 | \\u0061 | RegExpConstant,RegExpEscape | | regexp.cpp:92:21:92:23 | \\cA | RegExpConstant,RegExpEscape | | regexp.cpp:93:21:93:25 | [\\cZ] | RegExpCharacterClass | | regexp.cpp:93:22:93:24 | \\cZ | RegExpConstant,RegExpEscape | @@ -299,7 +299,7 @@ regExpNormalCharValue | regexp.cpp:83:31:83:31 | F | F | | regexp.cpp:83:37:83:37 | a | a | | regexp.cpp:83:39:83:39 | f | f | -| regexp.cpp:89:19:89:24 | \\u{987 | \u0987 | +| regexp.cpp:89:19:89:24 | \\u0061 | a | | regexp.cpp:92:21:92:23 | \\cA | cA | | regexp.cpp:93:22:93:24 | \\cZ | cZ | | regexp.cpp:96:20:96:21 | \\0 | 0 | From f7797ed89322db654889fbdd5e3997254e41e8bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:25:11 +0000 Subject: [PATCH 27/37] C++: Add coverage for lazy quantifiers and \f/\v escapes --- .../library-tests/regex/locations.expected | 29 +-- .../test/library-tests/regex/parse.expected | 122 +++++++----- cpp/ql/test/library-tests/regex/regexp.cpp | 7 + .../test/library-tests/regex/regexp.expected | 185 ++++++++++-------- 4 files changed, 200 insertions(+), 143 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 43da53ad3763..2c85989fc5e9 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -42,15 +42,20 @@ | regexp.cpp:93:21:93:25 | [\\cZ] | regexp.cpp | 93 | 21 | 93 | 25 | | regexp.cpp:96:20:96:21 | \\0 | regexp.cpp | 96 | 20 | 96 | 21 | | regexp.cpp:97:20:97:23 | [\\0] | regexp.cpp | 97 | 20 | 97 | 23 | -| regexp.cpp:100:25:100:28 | a\\nb | regexp.cpp | 100 | 25 | 100 | 28 | -| regexp.cpp:101:37:101:40 | a\\nc | regexp.cpp | 101 | 37 | 101 | 40 | -| regexp.cpp:102:39:102:42 | a\\nc | regexp.cpp | 102 | 39 | 102 | 42 | -| regexp.cpp:103:38:103:41 | a\\nc | regexp.cpp | 103 | 38 | 103 | 41 | -| regexp.cpp:104:38:104:41 | a\\nc | regexp.cpp | 104 | 38 | 104 | 41 | -| regexp.cpp:105:23:105:26 | a\\nc | regexp.cpp | 105 | 23 | 105 | 26 | -| regexp.cpp:106:40:106:43 | a\\nc | regexp.cpp | 106 | 40 | 106 | 43 | -| regexp.cpp:107:42:107:45 | a\\nc | regexp.cpp | 107 | 42 | 107 | 45 | -| regexp.cpp:108:41:108:44 | a\\nc | regexp.cpp | 108 | 41 | 108 | 44 | -| regexp.cpp:109:41:109:44 | a\\nc | regexp.cpp | 109 | 41 | 109 | 44 | -| regexp.cpp:110:25:110:28 | a\\nc | regexp.cpp | 110 | 25 | 110 | 28 | -| regexp.cpp:111:29:111:32 | a\\nc | regexp.cpp | 111 | 29 | 111 | 32 | +| regexp.cpp:100:21:100:23 | a*? | regexp.cpp | 100 | 21 | 100 | 23 | +| regexp.cpp:101:21:101:23 | b+? | regexp.cpp | 101 | 21 | 101 | 23 | +| regexp.cpp:102:21:102:23 | c?? | regexp.cpp | 102 | 21 | 102 | 23 | +| regexp.cpp:103:21:103:27 | d{2,3}? | regexp.cpp | 103 | 21 | 103 | 27 | +| regexp.cpp:104:18:104:21 | \\f\\v | regexp.cpp | 104 | 18 | 104 | 21 | +| regexp.cpp:107:25:107:28 | a\\nb | regexp.cpp | 107 | 25 | 107 | 28 | +| regexp.cpp:108:37:108:40 | a\\nc | regexp.cpp | 108 | 37 | 108 | 40 | +| regexp.cpp:109:39:109:42 | a\\nc | regexp.cpp | 109 | 39 | 109 | 42 | +| regexp.cpp:110:38:110:41 | a\\nc | regexp.cpp | 110 | 38 | 110 | 41 | +| regexp.cpp:111:38:111:41 | a\\nc | regexp.cpp | 111 | 38 | 111 | 41 | +| regexp.cpp:112:23:112:26 | a\\nc | regexp.cpp | 112 | 23 | 112 | 26 | +| regexp.cpp:113:40:113:43 | a\\nc | regexp.cpp | 113 | 40 | 113 | 43 | +| regexp.cpp:114:42:114:45 | a\\nc | regexp.cpp | 114 | 42 | 114 | 45 | +| regexp.cpp:115:41:115:44 | a\\nc | regexp.cpp | 115 | 41 | 115 | 44 | +| regexp.cpp:116:41:116:44 | a\\nc | regexp.cpp | 116 | 41 | 116 | 44 | +| regexp.cpp:117:25:117:28 | a\\nc | regexp.cpp | 117 | 25 | 117 | 28 | +| regexp.cpp:118:29:118:32 | a\\nc | regexp.cpp | 118 | 29 | 118 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 83eadac1b6d9..e4bd6e08b2d9 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -473,132 +473,160 @@ regexp.cpp: # 100| [RegExpConstant, RegExpNormalChar] a -# 100| [RegExpSequence] a\nb +# 100| [RegExpStar] a*? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 101| [RegExpConstant, RegExpNormalChar] b + +# 101| [RegExpPlus] b+? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 102| [RegExpConstant, RegExpNormalChar] c + +# 102| [RegExpOpt] c?? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c + +# 103| [RegExpConstant, RegExpNormalChar] d + +# 103| [RegExpQuantifier] d{2,3}? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] d + +# 104| [RegExpConstant, RegExpEscape] \f + +# 104| [RegExpSequence] \f\v +#-----| 0 -> [RegExpConstant, RegExpEscape] \f +#-----| 1 -> [RegExpConstant, RegExpEscape] \v + +# 104| [RegExpConstant, RegExpEscape] \v + +# 107| [RegExpConstant, RegExpNormalChar] a + +# 107| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] b -# 100| [RegExpConstant, RegExpEscape] \n +# 107| [RegExpConstant, RegExpEscape] \n -# 100| [RegExpConstant, RegExpNormalChar] b +# 107| [RegExpConstant, RegExpNormalChar] b -# 101| [RegExpConstant, RegExpNormalChar] a +# 108| [RegExpConstant, RegExpNormalChar] a -# 101| [RegExpSequence] a\nc +# 108| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 101| [RegExpConstant, RegExpEscape] \n +# 108| [RegExpConstant, RegExpEscape] \n -# 101| [RegExpConstant, RegExpNormalChar] c +# 108| [RegExpConstant, RegExpNormalChar] c -# 102| [RegExpConstant, RegExpNormalChar] a +# 109| [RegExpConstant, RegExpNormalChar] a -# 102| [RegExpSequence] a\nc +# 109| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 102| [RegExpConstant, RegExpEscape] \n +# 109| [RegExpConstant, RegExpEscape] \n -# 102| [RegExpConstant, RegExpNormalChar] c +# 109| [RegExpConstant, RegExpNormalChar] c -# 103| [RegExpConstant, RegExpNormalChar] a +# 110| [RegExpConstant, RegExpNormalChar] a -# 103| [RegExpSequence] a\nc +# 110| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 103| [RegExpConstant, RegExpEscape] \n +# 110| [RegExpConstant, RegExpEscape] \n -# 103| [RegExpConstant, RegExpNormalChar] c +# 110| [RegExpConstant, RegExpNormalChar] c -# 104| [RegExpConstant, RegExpNormalChar] a +# 111| [RegExpConstant, RegExpNormalChar] a -# 104| [RegExpSequence] a\nc +# 111| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 104| [RegExpConstant, RegExpEscape] \n +# 111| [RegExpConstant, RegExpEscape] \n -# 104| [RegExpConstant, RegExpNormalChar] c +# 111| [RegExpConstant, RegExpNormalChar] c -# 105| [RegExpConstant, RegExpNormalChar] a +# 112| [RegExpConstant, RegExpNormalChar] a -# 105| [RegExpSequence] a\nc +# 112| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 105| [RegExpConstant, RegExpEscape] \n +# 112| [RegExpConstant, RegExpEscape] \n -# 105| [RegExpConstant, RegExpNormalChar] c +# 112| [RegExpConstant, RegExpNormalChar] c -# 106| [RegExpConstant, RegExpNormalChar] a +# 113| [RegExpConstant, RegExpNormalChar] a -# 106| [RegExpSequence] a\nc +# 113| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 106| [RegExpConstant, RegExpEscape] \n +# 113| [RegExpConstant, RegExpEscape] \n -# 106| [RegExpConstant, RegExpNormalChar] c +# 113| [RegExpConstant, RegExpNormalChar] c -# 107| [RegExpConstant, RegExpNormalChar] a +# 114| [RegExpConstant, RegExpNormalChar] a -# 107| [RegExpSequence] a\nc +# 114| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 107| [RegExpConstant, RegExpEscape] \n +# 114| [RegExpConstant, RegExpEscape] \n -# 107| [RegExpConstant, RegExpNormalChar] c +# 114| [RegExpConstant, RegExpNormalChar] c -# 108| [RegExpConstant, RegExpNormalChar] a +# 115| [RegExpConstant, RegExpNormalChar] a -# 108| [RegExpSequence] a\nc +# 115| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 108| [RegExpConstant, RegExpEscape] \n +# 115| [RegExpConstant, RegExpEscape] \n -# 108| [RegExpConstant, RegExpNormalChar] c +# 115| [RegExpConstant, RegExpNormalChar] c -# 109| [RegExpConstant, RegExpNormalChar] a +# 116| [RegExpConstant, RegExpNormalChar] a -# 109| [RegExpSequence] a\nc +# 116| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 109| [RegExpConstant, RegExpEscape] \n +# 116| [RegExpConstant, RegExpEscape] \n -# 109| [RegExpConstant, RegExpNormalChar] c +# 116| [RegExpConstant, RegExpNormalChar] c -# 110| [RegExpConstant, RegExpNormalChar] a +# 117| [RegExpConstant, RegExpNormalChar] a -# 110| [RegExpSequence] a\nc +# 117| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 110| [RegExpConstant, RegExpEscape] \n +# 117| [RegExpConstant, RegExpEscape] \n -# 110| [RegExpConstant, RegExpNormalChar] c +# 117| [RegExpConstant, RegExpNormalChar] c -# 111| [RegExpConstant, RegExpNormalChar] a +# 118| [RegExpConstant, RegExpNormalChar] a -# 111| [RegExpSequence] a\nc +# 118| [RegExpSequence] a\nc #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n #-----| 2 -> [RegExpConstant, RegExpNormalChar] c -# 111| [RegExpConstant, RegExpEscape] \n +# 118| [RegExpConstant, RegExpEscape] \n -# 111| [RegExpConstant, RegExpNormalChar] c +# 118| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index ea56d12bed35..a5e65c66f888 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -96,6 +96,13 @@ std::regex r_ctrl2("[\\cZ]"); std::regex r_nul1("\\0"); std::regex r_nul2("[\\0]"); +// Lazy quantifiers and additional supported escapes +std::regex r_lazy1("a*?"); +std::regex r_lazy2("b+?"); +std::regex r_lazy3("c??"); +std::regex r_lazy4("d{2,3}?"); +std::regex r_fv("\\f\\v"); + // String literal spellings for location tests std::regex r_loc_plain("a\\nb"); std::basic_regex r_loc_L(L"a\\nc"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 5f70909c5f34..9ab709db8920 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -176,54 +176,65 @@ term | regexp.cpp:96:20:96:21 | \\0 | RegExpConstant,RegExpEscape | | regexp.cpp:97:20:97:23 | [\\0] | RegExpCharacterClass | | regexp.cpp:97:21:97:22 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:100:25:100:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:25:100:28 | a\\nb | RegExpSequence | -| regexp.cpp:100:26:100:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:100:28:100:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:37:101:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:37:101:40 | a\\nc | RegExpSequence | -| regexp.cpp:101:38:101:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:101:40:101:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:39:102:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:39:102:42 | a\\nc | RegExpSequence | -| regexp.cpp:102:40:102:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:102:42:102:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:38:103:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:38:103:41 | a\\nc | RegExpSequence | -| regexp.cpp:103:39:103:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:103:41:103:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:38:104:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:38:104:41 | a\\nc | RegExpSequence | -| regexp.cpp:104:39:104:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:104:41:104:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:23:105:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:105:23:105:26 | a\\nc | RegExpSequence | -| regexp.cpp:105:24:105:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:105:26:105:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:106:40:106:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:106:40:106:43 | a\\nc | RegExpSequence | -| regexp.cpp:106:41:106:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:106:43:106:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:107:42:107:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:107:42:107:45 | a\\nc | RegExpSequence | -| regexp.cpp:107:43:107:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:107:45:107:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:108:41:108:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:108:41:108:44 | a\\nc | RegExpSequence | -| regexp.cpp:108:42:108:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:108:44:108:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:109:41:109:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:109:41:109:44 | a\\nc | RegExpSequence | -| regexp.cpp:109:42:109:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:109:44:109:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:110:25:110:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:110:25:110:28 | a\\nc | RegExpSequence | -| regexp.cpp:110:26:110:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:110:28:110:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:111:29:111:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:111:29:111:32 | a\\nc | RegExpSequence | -| regexp.cpp:111:30:111:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:111:32:111:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:21:100:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:100:21:100:23 | a*? | RegExpStar | +| regexp.cpp:101:21:101:21 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:21:101:23 | b+? | RegExpPlus | +| regexp.cpp:102:21:102:21 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:21:102:23 | c?? | RegExpOpt | +| regexp.cpp:103:21:103:21 | d | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:21:103:27 | d{2,3}? | RegExpQuantifier | +| regexp.cpp:104:18:104:19 | \\f | RegExpConstant,RegExpEscape | +| regexp.cpp:104:18:104:21 | \\f\\v | RegExpSequence | +| regexp.cpp:104:20:104:21 | \\v | RegExpConstant,RegExpEscape | +| regexp.cpp:107:25:107:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:107:25:107:28 | a\\nb | RegExpSequence | +| regexp.cpp:107:26:107:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:107:28:107:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:108:37:108:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:108:37:108:40 | a\\nc | RegExpSequence | +| regexp.cpp:108:38:108:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:108:40:108:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:109:39:109:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:109:39:109:42 | a\\nc | RegExpSequence | +| regexp.cpp:109:40:109:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:109:42:109:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:110:38:110:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:110:38:110:41 | a\\nc | RegExpSequence | +| regexp.cpp:110:39:110:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:110:41:110:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:111:38:111:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:111:38:111:41 | a\\nc | RegExpSequence | +| regexp.cpp:111:39:111:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:111:41:111:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:112:23:112:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:112:23:112:26 | a\\nc | RegExpSequence | +| regexp.cpp:112:24:112:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:112:26:112:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:113:40:113:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:113:40:113:43 | a\\nc | RegExpSequence | +| regexp.cpp:113:41:113:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:113:43:113:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:114:42:114:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:114:42:114:45 | a\\nc | RegExpSequence | +| regexp.cpp:114:43:114:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:114:45:114:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:115:41:115:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:115:41:115:44 | a\\nc | RegExpSequence | +| regexp.cpp:115:42:115:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:115:44:115:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:116:41:116:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:116:41:116:44 | a\\nc | RegExpSequence | +| regexp.cpp:116:42:116:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:116:44:116:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:117:25:117:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:117:25:117:28 | a\\nc | RegExpSequence | +| regexp.cpp:117:26:117:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:117:28:117:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:118:29:118:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:118:29:118:32 | a\\nc | RegExpSequence | +| regexp.cpp:118:30:118:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:118:32:118:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -304,39 +315,45 @@ regExpNormalCharValue | regexp.cpp:93:22:93:24 | \\cZ | cZ | | regexp.cpp:96:20:96:21 | \\0 | 0 | | regexp.cpp:97:21:97:22 | \\0 | 0 | -| regexp.cpp:100:25:100:25 | a | a | -| regexp.cpp:100:26:100:27 | \\n | \n | -| regexp.cpp:100:28:100:28 | b | b | -| regexp.cpp:101:37:101:37 | a | a | -| regexp.cpp:101:38:101:39 | \\n | \n | -| regexp.cpp:101:40:101:40 | c | c | -| regexp.cpp:102:39:102:39 | a | a | -| regexp.cpp:102:40:102:41 | \\n | \n | -| regexp.cpp:102:42:102:42 | c | c | -| regexp.cpp:103:38:103:38 | a | a | -| regexp.cpp:103:39:103:40 | \\n | \n | -| regexp.cpp:103:41:103:41 | c | c | -| regexp.cpp:104:38:104:38 | a | a | -| regexp.cpp:104:39:104:40 | \\n | \n | -| regexp.cpp:104:41:104:41 | c | c | -| regexp.cpp:105:23:105:23 | a | a | -| regexp.cpp:105:24:105:25 | \\n | \n | -| regexp.cpp:105:26:105:26 | c | c | -| regexp.cpp:106:40:106:40 | a | a | -| regexp.cpp:106:41:106:42 | \\n | \n | -| regexp.cpp:106:43:106:43 | c | c | -| regexp.cpp:107:42:107:42 | a | a | -| regexp.cpp:107:43:107:44 | \\n | \n | -| regexp.cpp:107:45:107:45 | c | c | -| regexp.cpp:108:41:108:41 | a | a | -| regexp.cpp:108:42:108:43 | \\n | \n | -| regexp.cpp:108:44:108:44 | c | c | -| regexp.cpp:109:41:109:41 | a | a | -| regexp.cpp:109:42:109:43 | \\n | \n | -| regexp.cpp:109:44:109:44 | c | c | -| regexp.cpp:110:25:110:25 | a | a | -| regexp.cpp:110:26:110:27 | \\n | \n | -| regexp.cpp:110:28:110:28 | c | c | -| regexp.cpp:111:29:111:29 | a | a | -| regexp.cpp:111:30:111:31 | \\n | \n | -| regexp.cpp:111:32:111:32 | c | c | +| regexp.cpp:100:21:100:21 | a | a | +| regexp.cpp:101:21:101:21 | b | b | +| regexp.cpp:102:21:102:21 | c | c | +| regexp.cpp:103:21:103:21 | d | d | +| regexp.cpp:104:18:104:19 | \\f | \u000c | +| regexp.cpp:104:20:104:21 | \\v | \u000b | +| regexp.cpp:107:25:107:25 | a | a | +| regexp.cpp:107:26:107:27 | \\n | \n | +| regexp.cpp:107:28:107:28 | b | b | +| regexp.cpp:108:37:108:37 | a | a | +| regexp.cpp:108:38:108:39 | \\n | \n | +| regexp.cpp:108:40:108:40 | c | c | +| regexp.cpp:109:39:109:39 | a | a | +| regexp.cpp:109:40:109:41 | \\n | \n | +| regexp.cpp:109:42:109:42 | c | c | +| regexp.cpp:110:38:110:38 | a | a | +| regexp.cpp:110:39:110:40 | \\n | \n | +| regexp.cpp:110:41:110:41 | c | c | +| regexp.cpp:111:38:111:38 | a | a | +| regexp.cpp:111:39:111:40 | \\n | \n | +| regexp.cpp:111:41:111:41 | c | c | +| regexp.cpp:112:23:112:23 | a | a | +| regexp.cpp:112:24:112:25 | \\n | \n | +| regexp.cpp:112:26:112:26 | c | c | +| regexp.cpp:113:40:113:40 | a | a | +| regexp.cpp:113:41:113:42 | \\n | \n | +| regexp.cpp:113:43:113:43 | c | c | +| regexp.cpp:114:42:114:42 | a | a | +| regexp.cpp:114:43:114:44 | \\n | \n | +| regexp.cpp:114:45:114:45 | c | c | +| regexp.cpp:115:41:115:41 | a | a | +| regexp.cpp:115:42:115:43 | \\n | \n | +| regexp.cpp:115:44:115:44 | c | c | +| regexp.cpp:116:41:116:41 | a | a | +| regexp.cpp:116:42:116:43 | \\n | \n | +| regexp.cpp:116:44:116:44 | c | c | +| regexp.cpp:117:25:117:25 | a | a | +| regexp.cpp:117:26:117:27 | \\n | \n | +| regexp.cpp:117:28:117:28 | c | c | +| regexp.cpp:118:29:118:29 | a | a | +| regexp.cpp:118:30:118:31 | \\n | \n | +| regexp.cpp:118:32:118:32 | c | c | From 009ae4332fe2b2e1d949361dbb82a2f3d1ca5cfb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:25:48 +0000 Subject: [PATCH 28/37] C++: Verify [\b] is backspace inside character classes --- .../library-tests/regex/locations.expected | 89 +-- .../test/library-tests/regex/parse.expected | 289 +++++----- cpp/ql/test/library-tests/regex/regexp.cpp | 1 + .../test/library-tests/regex/regexp.expected | 513 +++++++++--------- 4 files changed, 451 insertions(+), 441 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 2c85989fc5e9..5d57a25de42d 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -13,49 +13,50 @@ | regexp.cpp:36:19:36:22 | [^]] | regexp.cpp | 36 | 19 | 36 | 22 | | regexp.cpp:37:19:37:22 | [^-] | regexp.cpp | 37 | 19 | 37 | 22 | | regexp.cpp:38:19:38:21 | [\|] | regexp.cpp | 38 | 19 | 38 | 21 | -| regexp.cpp:41:22:41:31 | [[a-f]A-F] | regexp.cpp | 41 | 22 | 41 | 31 | -| regexp.cpp:44:21:44:22 | .* | regexp.cpp | 44 | 21 | 44 | 22 | -| regexp.cpp:45:21:45:25 | \\w+\\W | regexp.cpp | 45 | 21 | 45 | 25 | -| regexp.cpp:46:21:46:24 | \\s\\S | regexp.cpp | 46 | 21 | 46 | 24 | -| regexp.cpp:47:21:47:24 | \\d\\D | regexp.cpp | 47 | 21 | 47 | 24 | -| regexp.cpp:48:21:48:26 | \\n\\r\\t | regexp.cpp | 48 | 21 | 48 | 26 | -| regexp.cpp:51:20:51:25 | \\b!a\\B | regexp.cpp | 51 | 20 | 51 | 25 | -| regexp.cpp:54:20:54:28 | (foo)*bar | regexp.cpp | 54 | 20 | 54 | 28 | -| regexp.cpp:55:20:55:28 | fo(o\|b)ar | regexp.cpp | 55 | 20 | 55 | 28 | -| regexp.cpp:56:20:56:28 | (a\|b\|cd)e | regexp.cpp | 56 | 20 | 56 | 28 | -| regexp.cpp:57:20:57:27 | (?::+)\\w | regexp.cpp | 57 | 20 | 57 | 27 | -| regexp.cpp:60:21:60:28 | (a+)b+\\1 | regexp.cpp | 60 | 21 | 60 | 28 | -| regexp.cpp:63:21:63:32 | [[:alnum:]]* | regexp.cpp | 63 | 21 | 63 | 32 | -| regexp.cpp:64:21:64:32 | [[:digit:]]+ | regexp.cpp | 64 | 21 | 64 | 32 | -| regexp.cpp:65:21:65:36 | [[:alnum:]]{2,3} | regexp.cpp | 65 | 21 | 65 | 36 | -| regexp.cpp:66:21:66:35 | [a-f[:digit:]]+ | regexp.cpp | 66 | 21 | 66 | 35 | -| regexp.cpp:69:22:69:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 69 | 22 | 69 | 43 | -| regexp.cpp:72:22:72:41 | [[:alpha:][:digit:]] | regexp.cpp | 72 | 22 | 72 | 41 | -| regexp.cpp:75:22:75:38 | [A-F[:digit:]a-f] | regexp.cpp | 75 | 22 | 75 | 38 | -| regexp.cpp:78:27:78:33 | [[.a.]] | regexp.cpp | 78 | 27 | 78 | 33 | -| regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | regexp.cpp | 79 | 27 | 79 | 39 | -| regexp.cpp:82:28:82:34 | [[=a=]] | regexp.cpp | 82 | 28 | 82 | 34 | -| regexp.cpp:83:28:83:40 | [A-F[=b=]a-f] | regexp.cpp | 83 | 28 | 83 | 40 | -| regexp.cpp:86:22:86:30 | [:digit:] | regexp.cpp | 86 | 22 | 86 | 30 | -| regexp.cpp:89:19:89:24 | \\u0061 | regexp.cpp | 89 | 19 | 89 | 24 | -| regexp.cpp:92:21:92:23 | \\cA | regexp.cpp | 92 | 21 | 92 | 23 | -| regexp.cpp:93:21:93:25 | [\\cZ] | regexp.cpp | 93 | 21 | 93 | 25 | -| regexp.cpp:96:20:96:21 | \\0 | regexp.cpp | 96 | 20 | 96 | 21 | -| regexp.cpp:97:20:97:23 | [\\0] | regexp.cpp | 97 | 20 | 97 | 23 | -| regexp.cpp:100:21:100:23 | a*? | regexp.cpp | 100 | 21 | 100 | 23 | -| regexp.cpp:101:21:101:23 | b+? | regexp.cpp | 101 | 21 | 101 | 23 | -| regexp.cpp:102:21:102:23 | c?? | regexp.cpp | 102 | 21 | 102 | 23 | -| regexp.cpp:103:21:103:27 | d{2,3}? | regexp.cpp | 103 | 21 | 103 | 27 | -| regexp.cpp:104:18:104:21 | \\f\\v | regexp.cpp | 104 | 18 | 104 | 21 | -| regexp.cpp:107:25:107:28 | a\\nb | regexp.cpp | 107 | 25 | 107 | 28 | -| regexp.cpp:108:37:108:40 | a\\nc | regexp.cpp | 108 | 37 | 108 | 40 | -| regexp.cpp:109:39:109:42 | a\\nc | regexp.cpp | 109 | 39 | 109 | 42 | -| regexp.cpp:110:38:110:41 | a\\nc | regexp.cpp | 110 | 38 | 110 | 41 | +| regexp.cpp:39:20:39:23 | [\\b] | regexp.cpp | 39 | 20 | 39 | 23 | +| regexp.cpp:42:22:42:31 | [[a-f]A-F] | regexp.cpp | 42 | 22 | 42 | 31 | +| regexp.cpp:45:21:45:22 | .* | regexp.cpp | 45 | 21 | 45 | 22 | +| regexp.cpp:46:21:46:25 | \\w+\\W | regexp.cpp | 46 | 21 | 46 | 25 | +| regexp.cpp:47:21:47:24 | \\s\\S | regexp.cpp | 47 | 21 | 47 | 24 | +| regexp.cpp:48:21:48:24 | \\d\\D | regexp.cpp | 48 | 21 | 48 | 24 | +| regexp.cpp:49:21:49:26 | \\n\\r\\t | regexp.cpp | 49 | 21 | 49 | 26 | +| regexp.cpp:52:20:52:25 | \\b!a\\B | regexp.cpp | 52 | 20 | 52 | 25 | +| regexp.cpp:55:20:55:28 | (foo)*bar | regexp.cpp | 55 | 20 | 55 | 28 | +| regexp.cpp:56:20:56:28 | fo(o\|b)ar | regexp.cpp | 56 | 20 | 56 | 28 | +| regexp.cpp:57:20:57:28 | (a\|b\|cd)e | regexp.cpp | 57 | 20 | 57 | 28 | +| regexp.cpp:58:20:58:27 | (?::+)\\w | regexp.cpp | 58 | 20 | 58 | 27 | +| regexp.cpp:61:21:61:28 | (a+)b+\\1 | regexp.cpp | 61 | 21 | 61 | 28 | +| regexp.cpp:64:21:64:32 | [[:alnum:]]* | regexp.cpp | 64 | 21 | 64 | 32 | +| regexp.cpp:65:21:65:32 | [[:digit:]]+ | regexp.cpp | 65 | 21 | 65 | 32 | +| regexp.cpp:66:21:66:36 | [[:alnum:]]{2,3} | regexp.cpp | 66 | 21 | 66 | 36 | +| regexp.cpp:67:21:67:35 | [a-f[:digit:]]+ | regexp.cpp | 67 | 21 | 67 | 35 | +| regexp.cpp:70:22:70:43 | [[:alpha:]][[:digit:]] | regexp.cpp | 70 | 22 | 70 | 43 | +| regexp.cpp:73:22:73:41 | [[:alpha:][:digit:]] | regexp.cpp | 73 | 22 | 73 | 41 | +| regexp.cpp:76:22:76:38 | [A-F[:digit:]a-f] | regexp.cpp | 76 | 22 | 76 | 38 | +| regexp.cpp:79:27:79:33 | [[.a.]] | regexp.cpp | 79 | 27 | 79 | 33 | +| regexp.cpp:80:27:80:39 | [A-F[.b.]a-f] | regexp.cpp | 80 | 27 | 80 | 39 | +| regexp.cpp:83:28:83:34 | [[=a=]] | regexp.cpp | 83 | 28 | 83 | 34 | +| regexp.cpp:84:28:84:40 | [A-F[=b=]a-f] | regexp.cpp | 84 | 28 | 84 | 40 | +| regexp.cpp:87:22:87:30 | [:digit:] | regexp.cpp | 87 | 22 | 87 | 30 | +| regexp.cpp:90:19:90:24 | \\u0061 | regexp.cpp | 90 | 19 | 90 | 24 | +| regexp.cpp:93:21:93:23 | \\cA | regexp.cpp | 93 | 21 | 93 | 23 | +| regexp.cpp:94:21:94:25 | [\\cZ] | regexp.cpp | 94 | 21 | 94 | 25 | +| regexp.cpp:97:20:97:21 | \\0 | regexp.cpp | 97 | 20 | 97 | 21 | +| regexp.cpp:98:20:98:23 | [\\0] | regexp.cpp | 98 | 20 | 98 | 23 | +| regexp.cpp:101:21:101:23 | a*? | regexp.cpp | 101 | 21 | 101 | 23 | +| regexp.cpp:102:21:102:23 | b+? | regexp.cpp | 102 | 21 | 102 | 23 | +| regexp.cpp:103:21:103:23 | c?? | regexp.cpp | 103 | 21 | 103 | 23 | +| regexp.cpp:104:21:104:27 | d{2,3}? | regexp.cpp | 104 | 21 | 104 | 27 | +| regexp.cpp:105:18:105:21 | \\f\\v | regexp.cpp | 105 | 18 | 105 | 21 | +| regexp.cpp:108:25:108:28 | a\\nb | regexp.cpp | 108 | 25 | 108 | 28 | +| regexp.cpp:109:37:109:40 | a\\nc | regexp.cpp | 109 | 37 | 109 | 40 | +| regexp.cpp:110:39:110:42 | a\\nc | regexp.cpp | 110 | 39 | 110 | 42 | | regexp.cpp:111:38:111:41 | a\\nc | regexp.cpp | 111 | 38 | 111 | 41 | -| regexp.cpp:112:23:112:26 | a\\nc | regexp.cpp | 112 | 23 | 112 | 26 | -| regexp.cpp:113:40:113:43 | a\\nc | regexp.cpp | 113 | 40 | 113 | 43 | -| regexp.cpp:114:42:114:45 | a\\nc | regexp.cpp | 114 | 42 | 114 | 45 | -| regexp.cpp:115:41:115:44 | a\\nc | regexp.cpp | 115 | 41 | 115 | 44 | +| regexp.cpp:112:38:112:41 | a\\nc | regexp.cpp | 112 | 38 | 112 | 41 | +| regexp.cpp:113:23:113:26 | a\\nc | regexp.cpp | 113 | 23 | 113 | 26 | +| regexp.cpp:114:40:114:43 | a\\nc | regexp.cpp | 114 | 40 | 114 | 43 | +| regexp.cpp:115:42:115:45 | a\\nc | regexp.cpp | 115 | 42 | 115 | 45 | | regexp.cpp:116:41:116:44 | a\\nc | regexp.cpp | 116 | 41 | 116 | 44 | -| regexp.cpp:117:25:117:28 | a\\nc | regexp.cpp | 117 | 25 | 117 | 28 | -| regexp.cpp:118:29:118:32 | a\\nc | regexp.cpp | 118 | 29 | 118 | 32 | +| regexp.cpp:117:41:117:44 | a\\nc | regexp.cpp | 117 | 41 | 117 | 44 | +| regexp.cpp:118:25:118:28 | a\\nc | regexp.cpp | 118 | 25 | 118 | 28 | +| regexp.cpp:119:29:119:32 | a\\nc | regexp.cpp | 119 | 29 | 119 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index e4bd6e08b2d9..8993b650e1ea 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -149,377 +149,371 @@ regexp.cpp: # 38| [RegExpConstant, RegExpNormalChar] | -# 41| [RegExpCharacterClass] [[a-f] +# 39| [RegExpCharacterClass] [\b] +#-----| 0 -> [RegExpConstant, RegExpEscape] \b + +# 39| [RegExpConstant, RegExpEscape] \b + +# 42| [RegExpCharacterClass] [[a-f] #-----| 0 -> [RegExpConstant, RegExpNormalChar] [ #-----| 1 -> [RegExpCharacterRange] a-f -# 41| [RegExpSequence] [[a-f]A-F] +# 42| [RegExpSequence] [[a-f]A-F] #-----| 0 -> [RegExpCharacterClass] [[a-f] #-----| 1 -> [RegExpConstant, RegExpNormalChar] A-F] -# 41| [RegExpConstant, RegExpNormalChar] [ +# 42| [RegExpConstant, RegExpNormalChar] [ -# 41| [RegExpConstant, RegExpNormalChar] a +# 42| [RegExpConstant, RegExpNormalChar] a -# 41| [RegExpCharacterRange] a-f +# 42| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 41| [RegExpConstant, RegExpNormalChar] f +# 42| [RegExpConstant, RegExpNormalChar] f -# 41| [RegExpConstant, RegExpNormalChar] A-F] +# 42| [RegExpConstant, RegExpNormalChar] A-F] -# 44| [RegExpDot] . +# 45| [RegExpDot] . -# 44| [RegExpStar] .* +# 45| [RegExpStar] .* #-----| 0 -> [RegExpDot] . -# 45| [RegExpCharacterClassEscape] \w +# 46| [RegExpCharacterClassEscape] \w -# 45| [RegExpPlus] \w+ +# 46| [RegExpPlus] \w+ #-----| 0 -> [RegExpCharacterClassEscape] \w -# 45| [RegExpSequence] \w+\W +# 46| [RegExpSequence] \w+\W #-----| 0 -> [RegExpPlus] \w+ #-----| 1 -> [RegExpCharacterClassEscape] \W -# 45| [RegExpCharacterClassEscape] \W +# 46| [RegExpCharacterClassEscape] \W -# 46| [RegExpCharacterClassEscape] \s +# 47| [RegExpCharacterClassEscape] \s -# 46| [RegExpSequence] \s\S +# 47| [RegExpSequence] \s\S #-----| 0 -> [RegExpCharacterClassEscape] \s #-----| 1 -> [RegExpCharacterClassEscape] \S -# 46| [RegExpCharacterClassEscape] \S +# 47| [RegExpCharacterClassEscape] \S -# 47| [RegExpCharacterClassEscape] \d +# 48| [RegExpCharacterClassEscape] \d -# 47| [RegExpSequence] \d\D +# 48| [RegExpSequence] \d\D #-----| 0 -> [RegExpCharacterClassEscape] \d #-----| 1 -> [RegExpCharacterClassEscape] \D -# 47| [RegExpCharacterClassEscape] \D +# 48| [RegExpCharacterClassEscape] \D -# 48| [RegExpConstant, RegExpEscape] \n +# 49| [RegExpConstant, RegExpEscape] \n -# 48| [RegExpSequence] \n\r\t +# 49| [RegExpSequence] \n\r\t #-----| 0 -> [RegExpConstant, RegExpEscape] \n #-----| 1 -> [RegExpConstant, RegExpEscape] \r #-----| 2 -> [RegExpConstant, RegExpEscape] \t -# 48| [RegExpConstant, RegExpEscape] \r +# 49| [RegExpConstant, RegExpEscape] \r -# 48| [RegExpConstant, RegExpEscape] \t +# 49| [RegExpConstant, RegExpEscape] \t -# 51| [RegExpSpecialChar] \b +# 52| [RegExpSpecialChar] \b -# 51| [RegExpSequence] \b!a\B +# 52| [RegExpSequence] \b!a\B #-----| 0 -> [RegExpSpecialChar] \b #-----| 1 -> [RegExpConstant, RegExpNormalChar] !a #-----| 2 -> [RegExpNonWordBoundary] \B -# 51| [RegExpConstant, RegExpNormalChar] !a +# 52| [RegExpConstant, RegExpNormalChar] !a -# 51| [RegExpNonWordBoundary] \B +# 52| [RegExpNonWordBoundary] \B -# 54| [RegExpGroup] (foo) +# 55| [RegExpGroup] (foo) #-----| 0 -> [RegExpConstant, RegExpNormalChar] foo -# 54| [RegExpStar] (foo)* +# 55| [RegExpStar] (foo)* #-----| 0 -> [RegExpGroup] (foo) -# 54| [RegExpSequence] (foo)*bar +# 55| [RegExpSequence] (foo)*bar #-----| 0 -> [RegExpStar] (foo)* #-----| 1 -> [RegExpConstant, RegExpNormalChar] bar -# 54| [RegExpConstant, RegExpNormalChar] foo +# 55| [RegExpConstant, RegExpNormalChar] foo -# 54| [RegExpConstant, RegExpNormalChar] bar +# 55| [RegExpConstant, RegExpNormalChar] bar -# 55| [RegExpConstant, RegExpNormalChar] fo +# 56| [RegExpConstant, RegExpNormalChar] fo -# 55| [RegExpSequence] fo(o|b)ar +# 56| [RegExpSequence] fo(o|b)ar #-----| 0 -> [RegExpConstant, RegExpNormalChar] fo #-----| 1 -> [RegExpGroup] (o|b) #-----| 2 -> [RegExpConstant, RegExpNormalChar] ar -# 55| [RegExpGroup] (o|b) +# 56| [RegExpGroup] (o|b) #-----| 0 -> [RegExpAlt] o|b -# 55| [RegExpConstant, RegExpNormalChar] o +# 56| [RegExpConstant, RegExpNormalChar] o -# 55| [RegExpAlt] o|b +# 56| [RegExpAlt] o|b #-----| 0 -> [RegExpConstant, RegExpNormalChar] o #-----| 1 -> [RegExpConstant, RegExpNormalChar] b -# 55| [RegExpConstant, RegExpNormalChar] b +# 56| [RegExpConstant, RegExpNormalChar] b -# 55| [RegExpConstant, RegExpNormalChar] ar +# 56| [RegExpConstant, RegExpNormalChar] ar -# 56| [RegExpGroup] (a|b|cd) +# 57| [RegExpGroup] (a|b|cd) #-----| 0 -> [RegExpAlt] a|b|cd -# 56| [RegExpSequence] (a|b|cd)e +# 57| [RegExpSequence] (a|b|cd)e #-----| 0 -> [RegExpGroup] (a|b|cd) #-----| 1 -> [RegExpConstant, RegExpNormalChar] e -# 56| [RegExpConstant, RegExpNormalChar] a +# 57| [RegExpConstant, RegExpNormalChar] a -# 56| [RegExpAlt] a|b|cd +# 57| [RegExpAlt] a|b|cd #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] b #-----| 2 -> [RegExpConstant, RegExpNormalChar] cd -# 56| [RegExpConstant, RegExpNormalChar] b +# 57| [RegExpConstant, RegExpNormalChar] b -# 56| [RegExpConstant, RegExpNormalChar] cd +# 57| [RegExpConstant, RegExpNormalChar] cd -# 56| [RegExpConstant, RegExpNormalChar] e +# 57| [RegExpConstant, RegExpNormalChar] e -# 57| [RegExpGroup] (?::+) +# 58| [RegExpGroup] (?::+) #-----| 0 -> [RegExpPlus] :+ -# 57| [RegExpSequence] (?::+)\w +# 58| [RegExpSequence] (?::+)\w #-----| 0 -> [RegExpGroup] (?::+) #-----| 1 -> [RegExpCharacterClassEscape] \w -# 57| [RegExpConstant, RegExpNormalChar] : +# 58| [RegExpConstant, RegExpNormalChar] : -# 57| [RegExpPlus] :+ +# 58| [RegExpPlus] :+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] : -# 57| [RegExpCharacterClassEscape] \w +# 58| [RegExpCharacterClassEscape] \w -# 60| [RegExpGroup] (a+) +# 61| [RegExpGroup] (a+) #-----| 0 -> [RegExpPlus] a+ -# 60| [RegExpSequence] (a+)b+\1 +# 61| [RegExpSequence] (a+)b+\1 #-----| 0 -> [RegExpGroup] (a+) #-----| 1 -> [RegExpPlus] b+ #-----| 2 -> [RegExpBackRef] \1 -# 60| [RegExpConstant, RegExpNormalChar] a +# 61| [RegExpConstant, RegExpNormalChar] a -# 60| [RegExpPlus] a+ +# 61| [RegExpPlus] a+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 60| [RegExpConstant, RegExpNormalChar] b +# 61| [RegExpConstant, RegExpNormalChar] b -# 60| [RegExpPlus] b+ +# 61| [RegExpPlus] b+ #-----| 0 -> [RegExpConstant, RegExpNormalChar] b -# 60| [RegExpBackRef] \1 +# 61| [RegExpBackRef] \1 -# 63| [RegExpCharacterClass] [[:alnum:]] +# 64| [RegExpCharacterClass] [[:alnum:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] -# 63| [RegExpStar] [[:alnum:]]* +# 64| [RegExpStar] [[:alnum:]]* #-----| 0 -> [RegExpCharacterClass] [[:alnum:]] -# 63| [RegExpNamedCharacterProperty] [:alnum:] +# 64| [RegExpNamedCharacterProperty] [:alnum:] -# 64| [RegExpCharacterClass] [[:digit:]] +# 65| [RegExpCharacterClass] [[:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] -# 64| [RegExpPlus] [[:digit:]]+ +# 65| [RegExpPlus] [[:digit:]]+ #-----| 0 -> [RegExpCharacterClass] [[:digit:]] -# 64| [RegExpNamedCharacterProperty] [:digit:] +# 65| [RegExpNamedCharacterProperty] [:digit:] -# 65| [RegExpCharacterClass] [[:alnum:]] +# 66| [RegExpCharacterClass] [[:alnum:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alnum:] -# 65| [RegExpRange] [[:alnum:]]{2,3} +# 66| [RegExpRange] [[:alnum:]]{2,3} #-----| 0 -> [RegExpCharacterClass] [[:alnum:]] -# 65| [RegExpNamedCharacterProperty] [:alnum:] +# 66| [RegExpNamedCharacterProperty] [:alnum:] -# 66| [RegExpCharacterClass] [a-f[:digit:]] +# 67| [RegExpCharacterClass] [a-f[:digit:]] #-----| 0 -> [RegExpCharacterRange] a-f #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] -# 66| [RegExpPlus] [a-f[:digit:]]+ +# 67| [RegExpPlus] [a-f[:digit:]]+ #-----| 0 -> [RegExpCharacterClass] [a-f[:digit:]] -# 66| [RegExpConstant, RegExpNormalChar] a +# 67| [RegExpConstant, RegExpNormalChar] a -# 66| [RegExpCharacterRange] a-f +# 67| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 66| [RegExpConstant, RegExpNormalChar] f +# 67| [RegExpConstant, RegExpNormalChar] f -# 66| [RegExpNamedCharacterProperty] [:digit:] +# 67| [RegExpNamedCharacterProperty] [:digit:] -# 69| [RegExpCharacterClass] [[:alpha:]] +# 70| [RegExpCharacterClass] [[:alpha:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] -# 69| [RegExpSequence] [[:alpha:]][[:digit:]] +# 70| [RegExpSequence] [[:alpha:]][[:digit:]] #-----| 0 -> [RegExpCharacterClass] [[:alpha:]] #-----| 1 -> [RegExpCharacterClass] [[:digit:]] -# 69| [RegExpNamedCharacterProperty] [:alpha:] +# 70| [RegExpNamedCharacterProperty] [:alpha:] -# 69| [RegExpCharacterClass] [[:digit:]] +# 70| [RegExpCharacterClass] [[:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] -# 69| [RegExpNamedCharacterProperty] [:digit:] +# 70| [RegExpNamedCharacterProperty] [:digit:] -# 72| [RegExpCharacterClass] [[:alpha:][:digit:]] +# 73| [RegExpCharacterClass] [[:alpha:][:digit:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] -# 72| [RegExpNamedCharacterProperty] [:alpha:] +# 73| [RegExpNamedCharacterProperty] [:alpha:] -# 72| [RegExpNamedCharacterProperty] [:digit:] +# 73| [RegExpNamedCharacterProperty] [:digit:] -# 75| [RegExpCharacterClass] [A-F[:digit:]a-f] +# 76| [RegExpCharacterClass] [A-F[:digit:]a-f] #-----| 0 -> [RegExpCharacterRange] A-F #-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] #-----| 2 -> [RegExpCharacterRange] a-f -# 75| [RegExpConstant, RegExpNormalChar] A +# 76| [RegExpConstant, RegExpNormalChar] A -# 75| [RegExpCharacterRange] A-F +# 76| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 75| [RegExpConstant, RegExpNormalChar] F +# 76| [RegExpConstant, RegExpNormalChar] F -# 75| [RegExpNamedCharacterProperty] [:digit:] +# 76| [RegExpNamedCharacterProperty] [:digit:] -# 75| [RegExpConstant, RegExpNormalChar] a +# 76| [RegExpConstant, RegExpNormalChar] a -# 75| [RegExpCharacterRange] a-f +# 76| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 75| [RegExpConstant, RegExpNormalChar] f +# 76| [RegExpConstant, RegExpNormalChar] f -# 78| [RegExpCharacterClass] [[.a.]] +# 79| [RegExpCharacterClass] [[.a.]] #-----| 0 -> [RegExpNamedCharacterProperty] [.a.] -# 78| [RegExpNamedCharacterProperty] [.a.] +# 79| [RegExpNamedCharacterProperty] [.a.] -# 79| [RegExpCharacterClass] [A-F[.b.]a-f] +# 80| [RegExpCharacterClass] [A-F[.b.]a-f] #-----| 0 -> [RegExpCharacterRange] A-F #-----| 1 -> [RegExpNamedCharacterProperty] [.b.] #-----| 2 -> [RegExpCharacterRange] a-f -# 79| [RegExpConstant, RegExpNormalChar] A +# 80| [RegExpConstant, RegExpNormalChar] A -# 79| [RegExpCharacterRange] A-F +# 80| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 79| [RegExpConstant, RegExpNormalChar] F +# 80| [RegExpConstant, RegExpNormalChar] F -# 79| [RegExpNamedCharacterProperty] [.b.] +# 80| [RegExpNamedCharacterProperty] [.b.] -# 79| [RegExpConstant, RegExpNormalChar] a +# 80| [RegExpConstant, RegExpNormalChar] a -# 79| [RegExpCharacterRange] a-f +# 80| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 79| [RegExpConstant, RegExpNormalChar] f +# 80| [RegExpConstant, RegExpNormalChar] f -# 82| [RegExpCharacterClass] [[=a=]] +# 83| [RegExpCharacterClass] [[=a=]] #-----| 0 -> [RegExpNamedCharacterProperty] [=a=] -# 82| [RegExpNamedCharacterProperty] [=a=] +# 83| [RegExpNamedCharacterProperty] [=a=] -# 83| [RegExpCharacterClass] [A-F[=b=]a-f] +# 84| [RegExpCharacterClass] [A-F[=b=]a-f] #-----| 0 -> [RegExpCharacterRange] A-F #-----| 1 -> [RegExpNamedCharacterProperty] [=b=] #-----| 2 -> [RegExpCharacterRange] a-f -# 83| [RegExpConstant, RegExpNormalChar] A +# 84| [RegExpConstant, RegExpNormalChar] A -# 83| [RegExpCharacterRange] A-F +# 84| [RegExpCharacterRange] A-F #-----| 0 -> [RegExpConstant, RegExpNormalChar] A #-----| 1 -> [RegExpConstant, RegExpNormalChar] F -# 83| [RegExpConstant, RegExpNormalChar] F +# 84| [RegExpConstant, RegExpNormalChar] F -# 83| [RegExpNamedCharacterProperty] [=b=] +# 84| [RegExpNamedCharacterProperty] [=b=] -# 83| [RegExpConstant, RegExpNormalChar] a +# 84| [RegExpConstant, RegExpNormalChar] a -# 83| [RegExpCharacterRange] a-f +# 84| [RegExpCharacterRange] a-f #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpNormalChar] f -# 83| [RegExpConstant, RegExpNormalChar] f +# 84| [RegExpConstant, RegExpNormalChar] f -# 86| [RegExpNamedCharacterProperty] [:digit:] +# 87| [RegExpNamedCharacterProperty] [:digit:] -# 89| [RegExpConstant, RegExpEscape] \u0061 +# 90| [RegExpConstant, RegExpEscape] \u0061 -# 92| [RegExpConstant, RegExpEscape] \cA +# 93| [RegExpConstant, RegExpEscape] \cA -# 93| [RegExpCharacterClass] [\cZ] +# 94| [RegExpCharacterClass] [\cZ] #-----| 0 -> [RegExpConstant, RegExpEscape] \cZ -# 93| [RegExpConstant, RegExpEscape] \cZ +# 94| [RegExpConstant, RegExpEscape] \cZ -# 96| [RegExpConstant, RegExpEscape] \0 +# 97| [RegExpConstant, RegExpEscape] \0 -# 97| [RegExpCharacterClass] [\0] +# 98| [RegExpCharacterClass] [\0] #-----| 0 -> [RegExpConstant, RegExpEscape] \0 -# 97| [RegExpConstant, RegExpEscape] \0 +# 98| [RegExpConstant, RegExpEscape] \0 -# 100| [RegExpConstant, RegExpNormalChar] a +# 101| [RegExpConstant, RegExpNormalChar] a -# 100| [RegExpStar] a*? +# 101| [RegExpStar] a*? #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 101| [RegExpConstant, RegExpNormalChar] b +# 102| [RegExpConstant, RegExpNormalChar] b -# 101| [RegExpPlus] b+? +# 102| [RegExpPlus] b+? #-----| 0 -> [RegExpConstant, RegExpNormalChar] b -# 102| [RegExpConstant, RegExpNormalChar] c +# 103| [RegExpConstant, RegExpNormalChar] c -# 102| [RegExpOpt] c?? +# 103| [RegExpOpt] c?? #-----| 0 -> [RegExpConstant, RegExpNormalChar] c -# 103| [RegExpConstant, RegExpNormalChar] d +# 104| [RegExpConstant, RegExpNormalChar] d -# 103| [RegExpQuantifier] d{2,3}? +# 104| [RegExpQuantifier] d{2,3}? #-----| 0 -> [RegExpConstant, RegExpNormalChar] d -# 104| [RegExpConstant, RegExpEscape] \f +# 105| [RegExpConstant, RegExpEscape] \f -# 104| [RegExpSequence] \f\v +# 105| [RegExpSequence] \f\v #-----| 0 -> [RegExpConstant, RegExpEscape] \f #-----| 1 -> [RegExpConstant, RegExpEscape] \v -# 104| [RegExpConstant, RegExpEscape] \v - -# 107| [RegExpConstant, RegExpNormalChar] a - -# 107| [RegExpSequence] a\nb -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] b - -# 107| [RegExpConstant, RegExpEscape] \n - -# 107| [RegExpConstant, RegExpNormalChar] b +# 105| [RegExpConstant, RegExpEscape] \v # 108| [RegExpConstant, RegExpNormalChar] a -# 108| [RegExpSequence] a\nc +# 108| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +#-----| 2 -> [RegExpConstant, RegExpNormalChar] b # 108| [RegExpConstant, RegExpEscape] \n -# 108| [RegExpConstant, RegExpNormalChar] c +# 108| [RegExpConstant, RegExpNormalChar] b # 109| [RegExpConstant, RegExpNormalChar] a @@ -630,3 +624,14 @@ regexp.cpp: # 118| [RegExpConstant, RegExpEscape] \n # 118| [RegExpConstant, RegExpNormalChar] c + +# 119| [RegExpConstant, RegExpNormalChar] a + +# 119| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 119| [RegExpConstant, RegExpEscape] \n + +# 119| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index a5e65c66f888..b1cfd64aa5c5 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -36,6 +36,7 @@ std::regex r_cc6("[]]"); // MRI gives a warning, but accepts this as matching std::regex r_cc7("[^]]"); // MRI gives a warning, but accepts this as matching anything except ']' std::regex r_cc8("[^-]"); std::regex r_cc9("[|]"); +std::regex r_cc10("[\\b]"); // Nested character classes (BAD - not parsed correctly) std::regex r_nested("[[a-f]A-F]"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 9ab709db8920..d82f3aa23547 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -1,8 +1,8 @@ groupNumber -| regexp.cpp:54:20:54:24 | (foo) | 1 | -| regexp.cpp:55:22:55:26 | (o\|b) | 1 | -| regexp.cpp:56:20:56:27 | (a\|b\|cd) | 1 | -| regexp.cpp:60:21:60:24 | (a+) | 1 | +| regexp.cpp:55:20:55:24 | (foo) | 1 | +| regexp.cpp:56:22:56:26 | (o\|b) | 1 | +| regexp.cpp:57:20:57:27 | (a\|b\|cd) | 1 | +| regexp.cpp:61:21:61:24 | (a+) | 1 | term | regexp.cpp:18:19:18:21 | abc | RegExpConstant,RegExpNormalChar | | regexp.cpp:21:20:21:20 | a | RegExpConstant,RegExpNormalChar | @@ -59,182 +59,184 @@ term | regexp.cpp:37:21:37:21 | - | RegExpConstant,RegExpNormalChar | | regexp.cpp:38:19:38:21 | [\|] | RegExpCharacterClass | | regexp.cpp:38:20:38:20 | \| | RegExpConstant,RegExpNormalChar | -| regexp.cpp:41:22:41:27 | [[a-f] | RegExpCharacterClass | -| regexp.cpp:41:22:41:31 | [[a-f]A-F] | RegExpSequence | -| regexp.cpp:41:23:41:23 | [ | RegExpConstant,RegExpNormalChar | -| regexp.cpp:41:24:41:24 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:41:24:41:26 | a-f | RegExpCharacterRange | -| regexp.cpp:41:26:41:26 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:41:28:41:31 | A-F] | RegExpConstant,RegExpNormalChar | -| regexp.cpp:44:21:44:21 | . | RegExpDot | -| regexp.cpp:44:21:44:22 | .* | RegExpStar | -| regexp.cpp:45:21:45:22 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:45:21:45:23 | \\w+ | RegExpPlus | -| regexp.cpp:45:21:45:25 | \\w+\\W | RegExpSequence | -| regexp.cpp:45:24:45:25 | \\W | RegExpCharacterClassEscape | -| regexp.cpp:46:21:46:22 | \\s | RegExpCharacterClassEscape | -| regexp.cpp:46:21:46:24 | \\s\\S | RegExpSequence | -| regexp.cpp:46:23:46:24 | \\S | RegExpCharacterClassEscape | -| regexp.cpp:47:21:47:22 | \\d | RegExpCharacterClassEscape | -| regexp.cpp:47:21:47:24 | \\d\\D | RegExpSequence | -| regexp.cpp:47:23:47:24 | \\D | RegExpCharacterClassEscape | -| regexp.cpp:48:21:48:22 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:48:21:48:26 | \\n\\r\\t | RegExpSequence | -| regexp.cpp:48:23:48:24 | \\r | RegExpConstant,RegExpEscape | -| regexp.cpp:48:25:48:26 | \\t | RegExpConstant,RegExpEscape | -| regexp.cpp:51:20:51:21 | \\b | RegExpSpecialChar | -| regexp.cpp:51:20:51:25 | \\b!a\\B | RegExpSequence | -| regexp.cpp:51:22:51:23 | !a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:51:24:51:25 | \\B | RegExpNonWordBoundary | -| regexp.cpp:54:20:54:24 | (foo) | RegExpGroup | -| regexp.cpp:54:20:54:25 | (foo)* | RegExpStar | -| regexp.cpp:54:20:54:28 | (foo)*bar | RegExpSequence | -| regexp.cpp:54:21:54:23 | foo | RegExpConstant,RegExpNormalChar | -| regexp.cpp:54:26:54:28 | bar | RegExpConstant,RegExpNormalChar | -| regexp.cpp:55:20:55:21 | fo | RegExpConstant,RegExpNormalChar | -| regexp.cpp:55:20:55:28 | fo(o\|b)ar | RegExpSequence | -| regexp.cpp:55:22:55:26 | (o\|b) | RegExpGroup | -| regexp.cpp:55:23:55:23 | o | RegExpConstant,RegExpNormalChar | -| regexp.cpp:55:23:55:25 | o\|b | RegExpAlt | -| regexp.cpp:55:25:55:25 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:55:27:55:28 | ar | RegExpConstant,RegExpNormalChar | -| regexp.cpp:56:20:56:27 | (a\|b\|cd) | RegExpGroup | -| regexp.cpp:56:20:56:28 | (a\|b\|cd)e | RegExpSequence | -| regexp.cpp:56:21:56:21 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:56:21:56:26 | a\|b\|cd | RegExpAlt | -| regexp.cpp:56:23:56:23 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:56:25:56:26 | cd | RegExpConstant,RegExpNormalChar | -| regexp.cpp:56:28:56:28 | e | RegExpConstant,RegExpNormalChar | -| regexp.cpp:57:20:57:25 | (?::+) | RegExpGroup | -| regexp.cpp:57:20:57:27 | (?::+)\\w | RegExpSequence | -| regexp.cpp:57:23:57:23 | : | RegExpConstant,RegExpNormalChar | -| regexp.cpp:57:23:57:24 | :+ | RegExpPlus | -| regexp.cpp:57:26:57:27 | \\w | RegExpCharacterClassEscape | -| regexp.cpp:60:21:60:24 | (a+) | RegExpGroup | -| regexp.cpp:60:21:60:28 | (a+)b+\\1 | RegExpSequence | -| regexp.cpp:60:22:60:22 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:60:22:60:23 | a+ | RegExpPlus | -| regexp.cpp:60:25:60:25 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:60:25:60:26 | b+ | RegExpPlus | -| regexp.cpp:60:27:60:28 | \\1 | RegExpBackRef | -| regexp.cpp:63:21:63:31 | [[:alnum:]] | RegExpCharacterClass | -| regexp.cpp:63:21:63:32 | [[:alnum:]]* | RegExpStar | -| regexp.cpp:63:22:63:30 | [:alnum:] | RegExpNamedCharacterProperty | -| regexp.cpp:64:21:64:31 | [[:digit:]] | RegExpCharacterClass | -| regexp.cpp:64:21:64:32 | [[:digit:]]+ | RegExpPlus | -| regexp.cpp:64:22:64:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:65:21:65:31 | [[:alnum:]] | RegExpCharacterClass | -| regexp.cpp:65:21:65:36 | [[:alnum:]]{2,3} | RegExpRange | -| regexp.cpp:65:22:65:30 | [:alnum:] | RegExpNamedCharacterProperty | -| regexp.cpp:66:21:66:34 | [a-f[:digit:]] | RegExpCharacterClass | -| regexp.cpp:66:21:66:35 | [a-f[:digit:]]+ | RegExpPlus | -| regexp.cpp:66:22:66:22 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:66:22:66:24 | a-f | RegExpCharacterRange | -| regexp.cpp:66:24:66:24 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:66:25:66:33 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:69:22:69:32 | [[:alpha:]] | RegExpCharacterClass | -| regexp.cpp:69:22:69:43 | [[:alpha:]][[:digit:]] | RegExpSequence | -| regexp.cpp:69:23:69:31 | [:alpha:] | RegExpNamedCharacterProperty | -| regexp.cpp:69:33:69:43 | [[:digit:]] | RegExpCharacterClass | -| regexp.cpp:69:34:69:42 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:72:22:72:41 | [[:alpha:][:digit:]] | RegExpCharacterClass | -| regexp.cpp:72:23:72:31 | [:alpha:] | RegExpNamedCharacterProperty | -| regexp.cpp:72:32:72:40 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:75:22:75:38 | [A-F[:digit:]a-f] | RegExpCharacterClass | -| regexp.cpp:75:23:75:23 | A | RegExpConstant,RegExpNormalChar | -| regexp.cpp:75:23:75:25 | A-F | RegExpCharacterRange | -| regexp.cpp:75:25:75:25 | F | RegExpConstant,RegExpNormalChar | -| regexp.cpp:75:26:75:34 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:75:35:75:35 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:75:35:75:37 | a-f | RegExpCharacterRange | -| regexp.cpp:75:37:75:37 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:78:27:78:33 | [[.a.]] | RegExpCharacterClass | -| regexp.cpp:78:28:78:32 | [.a.] | RegExpNamedCharacterProperty | -| regexp.cpp:79:27:79:39 | [A-F[.b.]a-f] | RegExpCharacterClass | -| regexp.cpp:79:28:79:28 | A | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:28:79:30 | A-F | RegExpCharacterRange | -| regexp.cpp:79:30:79:30 | F | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:31:79:35 | [.b.] | RegExpNamedCharacterProperty | -| regexp.cpp:79:36:79:36 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:79:36:79:38 | a-f | RegExpCharacterRange | -| regexp.cpp:79:38:79:38 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:82:28:82:34 | [[=a=]] | RegExpCharacterClass | -| regexp.cpp:82:29:82:33 | [=a=] | RegExpNamedCharacterProperty | -| regexp.cpp:83:28:83:40 | [A-F[=b=]a-f] | RegExpCharacterClass | -| regexp.cpp:83:29:83:29 | A | RegExpConstant,RegExpNormalChar | -| regexp.cpp:83:29:83:31 | A-F | RegExpCharacterRange | -| regexp.cpp:83:31:83:31 | F | RegExpConstant,RegExpNormalChar | -| regexp.cpp:83:32:83:36 | [=b=] | RegExpNamedCharacterProperty | -| regexp.cpp:83:37:83:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:83:37:83:39 | a-f | RegExpCharacterRange | -| regexp.cpp:83:39:83:39 | f | RegExpConstant,RegExpNormalChar | -| regexp.cpp:86:22:86:30 | [:digit:] | RegExpNamedCharacterProperty | -| regexp.cpp:89:19:89:24 | \\u0061 | RegExpConstant,RegExpEscape | -| regexp.cpp:92:21:92:23 | \\cA | RegExpConstant,RegExpEscape | -| regexp.cpp:93:21:93:25 | [\\cZ] | RegExpCharacterClass | -| regexp.cpp:93:22:93:24 | \\cZ | RegExpConstant,RegExpEscape | -| regexp.cpp:96:20:96:21 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:97:20:97:23 | [\\0] | RegExpCharacterClass | -| regexp.cpp:97:21:97:22 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:100:21:100:21 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:100:21:100:23 | a*? | RegExpStar | -| regexp.cpp:101:21:101:21 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:21:101:23 | b+? | RegExpPlus | -| regexp.cpp:102:21:102:21 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:21:102:23 | c?? | RegExpOpt | -| regexp.cpp:103:21:103:21 | d | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:21:103:27 | d{2,3}? | RegExpQuantifier | -| regexp.cpp:104:18:104:19 | \\f | RegExpConstant,RegExpEscape | -| regexp.cpp:104:18:104:21 | \\f\\v | RegExpSequence | -| regexp.cpp:104:20:104:21 | \\v | RegExpConstant,RegExpEscape | -| regexp.cpp:107:25:107:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:107:25:107:28 | a\\nb | RegExpSequence | -| regexp.cpp:107:26:107:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:107:28:107:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:108:37:108:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:108:37:108:40 | a\\nc | RegExpSequence | -| regexp.cpp:108:38:108:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:108:40:108:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:109:39:109:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:109:39:109:42 | a\\nc | RegExpSequence | -| regexp.cpp:109:40:109:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:109:42:109:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:110:38:110:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:110:38:110:41 | a\\nc | RegExpSequence | -| regexp.cpp:110:39:110:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:110:41:110:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:39:20:39:23 | [\\b] | RegExpCharacterClass | +| regexp.cpp:39:21:39:22 | \\b | RegExpConstant,RegExpEscape | +| regexp.cpp:42:22:42:27 | [[a-f] | RegExpCharacterClass | +| regexp.cpp:42:22:42:31 | [[a-f]A-F] | RegExpSequence | +| regexp.cpp:42:23:42:23 | [ | RegExpConstant,RegExpNormalChar | +| regexp.cpp:42:24:42:24 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:42:24:42:26 | a-f | RegExpCharacterRange | +| regexp.cpp:42:26:42:26 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:42:28:42:31 | A-F] | RegExpConstant,RegExpNormalChar | +| regexp.cpp:45:21:45:21 | . | RegExpDot | +| regexp.cpp:45:21:45:22 | .* | RegExpStar | +| regexp.cpp:46:21:46:22 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:46:21:46:23 | \\w+ | RegExpPlus | +| regexp.cpp:46:21:46:25 | \\w+\\W | RegExpSequence | +| regexp.cpp:46:24:46:25 | \\W | RegExpCharacterClassEscape | +| regexp.cpp:47:21:47:22 | \\s | RegExpCharacterClassEscape | +| regexp.cpp:47:21:47:24 | \\s\\S | RegExpSequence | +| regexp.cpp:47:23:47:24 | \\S | RegExpCharacterClassEscape | +| regexp.cpp:48:21:48:22 | \\d | RegExpCharacterClassEscape | +| regexp.cpp:48:21:48:24 | \\d\\D | RegExpSequence | +| regexp.cpp:48:23:48:24 | \\D | RegExpCharacterClassEscape | +| regexp.cpp:49:21:49:22 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:49:21:49:26 | \\n\\r\\t | RegExpSequence | +| regexp.cpp:49:23:49:24 | \\r | RegExpConstant,RegExpEscape | +| regexp.cpp:49:25:49:26 | \\t | RegExpConstant,RegExpEscape | +| regexp.cpp:52:20:52:21 | \\b | RegExpSpecialChar | +| regexp.cpp:52:20:52:25 | \\b!a\\B | RegExpSequence | +| regexp.cpp:52:22:52:23 | !a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:52:24:52:25 | \\B | RegExpNonWordBoundary | +| regexp.cpp:55:20:55:24 | (foo) | RegExpGroup | +| regexp.cpp:55:20:55:25 | (foo)* | RegExpStar | +| regexp.cpp:55:20:55:28 | (foo)*bar | RegExpSequence | +| regexp.cpp:55:21:55:23 | foo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:55:26:55:28 | bar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:20:56:21 | fo | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:20:56:28 | fo(o\|b)ar | RegExpSequence | +| regexp.cpp:56:22:56:26 | (o\|b) | RegExpGroup | +| regexp.cpp:56:23:56:23 | o | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:23:56:25 | o\|b | RegExpAlt | +| regexp.cpp:56:25:56:25 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:56:27:56:28 | ar | RegExpConstant,RegExpNormalChar | +| regexp.cpp:57:20:57:27 | (a\|b\|cd) | RegExpGroup | +| regexp.cpp:57:20:57:28 | (a\|b\|cd)e | RegExpSequence | +| regexp.cpp:57:21:57:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:57:21:57:26 | a\|b\|cd | RegExpAlt | +| regexp.cpp:57:23:57:23 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:57:25:57:26 | cd | RegExpConstant,RegExpNormalChar | +| regexp.cpp:57:28:57:28 | e | RegExpConstant,RegExpNormalChar | +| regexp.cpp:58:20:58:25 | (?::+) | RegExpGroup | +| regexp.cpp:58:20:58:27 | (?::+)\\w | RegExpSequence | +| regexp.cpp:58:23:58:23 | : | RegExpConstant,RegExpNormalChar | +| regexp.cpp:58:23:58:24 | :+ | RegExpPlus | +| regexp.cpp:58:26:58:27 | \\w | RegExpCharacterClassEscape | +| regexp.cpp:61:21:61:24 | (a+) | RegExpGroup | +| regexp.cpp:61:21:61:28 | (a+)b+\\1 | RegExpSequence | +| regexp.cpp:61:22:61:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:61:22:61:23 | a+ | RegExpPlus | +| regexp.cpp:61:25:61:25 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:61:25:61:26 | b+ | RegExpPlus | +| regexp.cpp:61:27:61:28 | \\1 | RegExpBackRef | +| regexp.cpp:64:21:64:31 | [[:alnum:]] | RegExpCharacterClass | +| regexp.cpp:64:21:64:32 | [[:alnum:]]* | RegExpStar | +| regexp.cpp:64:22:64:30 | [:alnum:] | RegExpNamedCharacterProperty | +| regexp.cpp:65:21:65:31 | [[:digit:]] | RegExpCharacterClass | +| regexp.cpp:65:21:65:32 | [[:digit:]]+ | RegExpPlus | +| regexp.cpp:65:22:65:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:66:21:66:31 | [[:alnum:]] | RegExpCharacterClass | +| regexp.cpp:66:21:66:36 | [[:alnum:]]{2,3} | RegExpRange | +| regexp.cpp:66:22:66:30 | [:alnum:] | RegExpNamedCharacterProperty | +| regexp.cpp:67:21:67:34 | [a-f[:digit:]] | RegExpCharacterClass | +| regexp.cpp:67:21:67:35 | [a-f[:digit:]]+ | RegExpPlus | +| regexp.cpp:67:22:67:22 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:67:22:67:24 | a-f | RegExpCharacterRange | +| regexp.cpp:67:24:67:24 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:67:25:67:33 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:70:22:70:32 | [[:alpha:]] | RegExpCharacterClass | +| regexp.cpp:70:22:70:43 | [[:alpha:]][[:digit:]] | RegExpSequence | +| regexp.cpp:70:23:70:31 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.cpp:70:33:70:43 | [[:digit:]] | RegExpCharacterClass | +| regexp.cpp:70:34:70:42 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:73:22:73:41 | [[:alpha:][:digit:]] | RegExpCharacterClass | +| regexp.cpp:73:23:73:31 | [:alpha:] | RegExpNamedCharacterProperty | +| regexp.cpp:73:32:73:40 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:76:22:76:38 | [A-F[:digit:]a-f] | RegExpCharacterClass | +| regexp.cpp:76:23:76:23 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:76:23:76:25 | A-F | RegExpCharacterRange | +| regexp.cpp:76:25:76:25 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:76:26:76:34 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:76:35:76:35 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:76:35:76:37 | a-f | RegExpCharacterRange | +| regexp.cpp:76:37:76:37 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:79:27:79:33 | [[.a.]] | RegExpCharacterClass | +| regexp.cpp:79:28:79:32 | [.a.] | RegExpNamedCharacterProperty | +| regexp.cpp:80:27:80:39 | [A-F[.b.]a-f] | RegExpCharacterClass | +| regexp.cpp:80:28:80:28 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:80:28:80:30 | A-F | RegExpCharacterRange | +| regexp.cpp:80:30:80:30 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:80:31:80:35 | [.b.] | RegExpNamedCharacterProperty | +| regexp.cpp:80:36:80:36 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:80:36:80:38 | a-f | RegExpCharacterRange | +| regexp.cpp:80:38:80:38 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:83:28:83:34 | [[=a=]] | RegExpCharacterClass | +| regexp.cpp:83:29:83:33 | [=a=] | RegExpNamedCharacterProperty | +| regexp.cpp:84:28:84:40 | [A-F[=b=]a-f] | RegExpCharacterClass | +| regexp.cpp:84:29:84:29 | A | RegExpConstant,RegExpNormalChar | +| regexp.cpp:84:29:84:31 | A-F | RegExpCharacterRange | +| regexp.cpp:84:31:84:31 | F | RegExpConstant,RegExpNormalChar | +| regexp.cpp:84:32:84:36 | [=b=] | RegExpNamedCharacterProperty | +| regexp.cpp:84:37:84:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:84:37:84:39 | a-f | RegExpCharacterRange | +| regexp.cpp:84:39:84:39 | f | RegExpConstant,RegExpNormalChar | +| regexp.cpp:87:22:87:30 | [:digit:] | RegExpNamedCharacterProperty | +| regexp.cpp:90:19:90:24 | \\u0061 | RegExpConstant,RegExpEscape | +| regexp.cpp:93:21:93:23 | \\cA | RegExpConstant,RegExpEscape | +| regexp.cpp:94:21:94:25 | [\\cZ] | RegExpCharacterClass | +| regexp.cpp:94:22:94:24 | \\cZ | RegExpConstant,RegExpEscape | +| regexp.cpp:97:20:97:21 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:98:20:98:23 | [\\0] | RegExpCharacterClass | +| regexp.cpp:98:21:98:22 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:101:21:101:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:101:21:101:23 | a*? | RegExpStar | +| regexp.cpp:102:21:102:21 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:102:21:102:23 | b+? | RegExpPlus | +| regexp.cpp:103:21:103:21 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:103:21:103:23 | c?? | RegExpOpt | +| regexp.cpp:104:21:104:21 | d | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:21:104:27 | d{2,3}? | RegExpQuantifier | +| regexp.cpp:105:18:105:19 | \\f | RegExpConstant,RegExpEscape | +| regexp.cpp:105:18:105:21 | \\f\\v | RegExpSequence | +| regexp.cpp:105:20:105:21 | \\v | RegExpConstant,RegExpEscape | +| regexp.cpp:108:25:108:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:108:25:108:28 | a\\nb | RegExpSequence | +| regexp.cpp:108:26:108:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:108:28:108:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:109:37:109:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:109:37:109:40 | a\\nc | RegExpSequence | +| regexp.cpp:109:38:109:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:109:40:109:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:110:39:110:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:110:39:110:42 | a\\nc | RegExpSequence | +| regexp.cpp:110:40:110:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:110:42:110:42 | c | RegExpConstant,RegExpNormalChar | | regexp.cpp:111:38:111:38 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:111:38:111:41 | a\\nc | RegExpSequence | | regexp.cpp:111:39:111:40 | \\n | RegExpConstant,RegExpEscape | | regexp.cpp:111:41:111:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:112:23:112:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:112:23:112:26 | a\\nc | RegExpSequence | -| regexp.cpp:112:24:112:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:112:26:112:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:113:40:113:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:113:40:113:43 | a\\nc | RegExpSequence | -| regexp.cpp:113:41:113:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:113:43:113:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:114:42:114:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:114:42:114:45 | a\\nc | RegExpSequence | -| regexp.cpp:114:43:114:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:114:45:114:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:115:41:115:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:115:41:115:44 | a\\nc | RegExpSequence | -| regexp.cpp:115:42:115:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:115:44:115:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:112:38:112:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:112:38:112:41 | a\\nc | RegExpSequence | +| regexp.cpp:112:39:112:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:112:41:112:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:113:23:113:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:113:23:113:26 | a\\nc | RegExpSequence | +| regexp.cpp:113:24:113:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:113:26:113:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:114:40:114:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:114:40:114:43 | a\\nc | RegExpSequence | +| regexp.cpp:114:41:114:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:114:43:114:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:115:42:115:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:115:42:115:45 | a\\nc | RegExpSequence | +| regexp.cpp:115:43:115:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:115:45:115:45 | c | RegExpConstant,RegExpNormalChar | | regexp.cpp:116:41:116:41 | a | RegExpConstant,RegExpNormalChar | | regexp.cpp:116:41:116:44 | a\\nc | RegExpSequence | | regexp.cpp:116:42:116:43 | \\n | RegExpConstant,RegExpEscape | | regexp.cpp:116:44:116:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:117:25:117:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:117:25:117:28 | a\\nc | RegExpSequence | -| regexp.cpp:117:26:117:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:117:28:117:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:118:29:118:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:118:29:118:32 | a\\nc | RegExpSequence | -| regexp.cpp:118:30:118:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:118:32:118:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:117:41:117:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:117:41:117:44 | a\\nc | RegExpSequence | +| regexp.cpp:117:42:117:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:117:44:117:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:118:25:118:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:118:25:118:28 | a\\nc | RegExpSequence | +| regexp.cpp:118:26:118:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:118:28:118:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:119:29:119:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:119:29:119:32 | a\\nc | RegExpSequence | +| regexp.cpp:119:30:119:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:119:32:119:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -268,92 +270,93 @@ regExpNormalCharValue | regexp.cpp:36:21:36:21 | ] | ] | | regexp.cpp:37:21:37:21 | - | - | | regexp.cpp:38:20:38:20 | \| | \| | -| regexp.cpp:41:23:41:23 | [ | [ | -| regexp.cpp:41:24:41:24 | a | a | -| regexp.cpp:41:26:41:26 | f | f | -| regexp.cpp:41:28:41:31 | A-F] | A-F] | -| regexp.cpp:45:21:45:22 | \\w | w | -| regexp.cpp:45:24:45:25 | \\W | W | -| regexp.cpp:46:21:46:22 | \\s | s | -| regexp.cpp:46:23:46:24 | \\S | S | -| regexp.cpp:47:21:47:22 | \\d | d | -| regexp.cpp:47:23:47:24 | \\D | D | -| regexp.cpp:48:21:48:22 | \\n | \n | -| regexp.cpp:48:23:48:24 | \\r | \r | -| regexp.cpp:48:25:48:26 | \\t | \t | -| regexp.cpp:51:22:51:23 | !a | !a | -| regexp.cpp:54:21:54:23 | foo | foo | -| regexp.cpp:54:26:54:28 | bar | bar | -| regexp.cpp:55:20:55:21 | fo | fo | -| regexp.cpp:55:23:55:23 | o | o | -| regexp.cpp:55:25:55:25 | b | b | -| regexp.cpp:55:27:55:28 | ar | ar | -| regexp.cpp:56:21:56:21 | a | a | -| regexp.cpp:56:23:56:23 | b | b | -| regexp.cpp:56:25:56:26 | cd | cd | -| regexp.cpp:56:28:56:28 | e | e | -| regexp.cpp:57:23:57:23 | : | : | -| regexp.cpp:57:26:57:27 | \\w | w | -| regexp.cpp:60:22:60:22 | a | a | -| regexp.cpp:60:25:60:25 | b | b | -| regexp.cpp:66:22:66:22 | a | a | -| regexp.cpp:66:24:66:24 | f | f | -| regexp.cpp:75:23:75:23 | A | A | -| regexp.cpp:75:25:75:25 | F | F | -| regexp.cpp:75:35:75:35 | a | a | -| regexp.cpp:75:37:75:37 | f | f | -| regexp.cpp:79:28:79:28 | A | A | -| regexp.cpp:79:30:79:30 | F | F | -| regexp.cpp:79:36:79:36 | a | a | -| regexp.cpp:79:38:79:38 | f | f | -| regexp.cpp:83:29:83:29 | A | A | -| regexp.cpp:83:31:83:31 | F | F | -| regexp.cpp:83:37:83:37 | a | a | -| regexp.cpp:83:39:83:39 | f | f | -| regexp.cpp:89:19:89:24 | \\u0061 | a | -| regexp.cpp:92:21:92:23 | \\cA | cA | -| regexp.cpp:93:22:93:24 | \\cZ | cZ | -| regexp.cpp:96:20:96:21 | \\0 | 0 | -| regexp.cpp:97:21:97:22 | \\0 | 0 | -| regexp.cpp:100:21:100:21 | a | a | -| regexp.cpp:101:21:101:21 | b | b | -| regexp.cpp:102:21:102:21 | c | c | -| regexp.cpp:103:21:103:21 | d | d | -| regexp.cpp:104:18:104:19 | \\f | \u000c | -| regexp.cpp:104:20:104:21 | \\v | \u000b | -| regexp.cpp:107:25:107:25 | a | a | -| regexp.cpp:107:26:107:27 | \\n | \n | -| regexp.cpp:107:28:107:28 | b | b | -| regexp.cpp:108:37:108:37 | a | a | -| regexp.cpp:108:38:108:39 | \\n | \n | -| regexp.cpp:108:40:108:40 | c | c | -| regexp.cpp:109:39:109:39 | a | a | -| regexp.cpp:109:40:109:41 | \\n | \n | -| regexp.cpp:109:42:109:42 | c | c | -| regexp.cpp:110:38:110:38 | a | a | -| regexp.cpp:110:39:110:40 | \\n | \n | -| regexp.cpp:110:41:110:41 | c | c | +| regexp.cpp:39:21:39:22 | \\b | b | +| regexp.cpp:42:23:42:23 | [ | [ | +| regexp.cpp:42:24:42:24 | a | a | +| regexp.cpp:42:26:42:26 | f | f | +| regexp.cpp:42:28:42:31 | A-F] | A-F] | +| regexp.cpp:46:21:46:22 | \\w | w | +| regexp.cpp:46:24:46:25 | \\W | W | +| regexp.cpp:47:21:47:22 | \\s | s | +| regexp.cpp:47:23:47:24 | \\S | S | +| regexp.cpp:48:21:48:22 | \\d | d | +| regexp.cpp:48:23:48:24 | \\D | D | +| regexp.cpp:49:21:49:22 | \\n | \n | +| regexp.cpp:49:23:49:24 | \\r | \r | +| regexp.cpp:49:25:49:26 | \\t | \t | +| regexp.cpp:52:22:52:23 | !a | !a | +| regexp.cpp:55:21:55:23 | foo | foo | +| regexp.cpp:55:26:55:28 | bar | bar | +| regexp.cpp:56:20:56:21 | fo | fo | +| regexp.cpp:56:23:56:23 | o | o | +| regexp.cpp:56:25:56:25 | b | b | +| regexp.cpp:56:27:56:28 | ar | ar | +| regexp.cpp:57:21:57:21 | a | a | +| regexp.cpp:57:23:57:23 | b | b | +| regexp.cpp:57:25:57:26 | cd | cd | +| regexp.cpp:57:28:57:28 | e | e | +| regexp.cpp:58:23:58:23 | : | : | +| regexp.cpp:58:26:58:27 | \\w | w | +| regexp.cpp:61:22:61:22 | a | a | +| regexp.cpp:61:25:61:25 | b | b | +| regexp.cpp:67:22:67:22 | a | a | +| regexp.cpp:67:24:67:24 | f | f | +| regexp.cpp:76:23:76:23 | A | A | +| regexp.cpp:76:25:76:25 | F | F | +| regexp.cpp:76:35:76:35 | a | a | +| regexp.cpp:76:37:76:37 | f | f | +| regexp.cpp:80:28:80:28 | A | A | +| regexp.cpp:80:30:80:30 | F | F | +| regexp.cpp:80:36:80:36 | a | a | +| regexp.cpp:80:38:80:38 | f | f | +| regexp.cpp:84:29:84:29 | A | A | +| regexp.cpp:84:31:84:31 | F | F | +| regexp.cpp:84:37:84:37 | a | a | +| regexp.cpp:84:39:84:39 | f | f | +| regexp.cpp:90:19:90:24 | \\u0061 | a | +| regexp.cpp:93:21:93:23 | \\cA | cA | +| regexp.cpp:94:22:94:24 | \\cZ | cZ | +| regexp.cpp:97:20:97:21 | \\0 | 0 | +| regexp.cpp:98:21:98:22 | \\0 | 0 | +| regexp.cpp:101:21:101:21 | a | a | +| regexp.cpp:102:21:102:21 | b | b | +| regexp.cpp:103:21:103:21 | c | c | +| regexp.cpp:104:21:104:21 | d | d | +| regexp.cpp:105:18:105:19 | \\f | \u000c | +| regexp.cpp:105:20:105:21 | \\v | \u000b | +| regexp.cpp:108:25:108:25 | a | a | +| regexp.cpp:108:26:108:27 | \\n | \n | +| regexp.cpp:108:28:108:28 | b | b | +| regexp.cpp:109:37:109:37 | a | a | +| regexp.cpp:109:38:109:39 | \\n | \n | +| regexp.cpp:109:40:109:40 | c | c | +| regexp.cpp:110:39:110:39 | a | a | +| regexp.cpp:110:40:110:41 | \\n | \n | +| regexp.cpp:110:42:110:42 | c | c | | regexp.cpp:111:38:111:38 | a | a | | regexp.cpp:111:39:111:40 | \\n | \n | | regexp.cpp:111:41:111:41 | c | c | -| regexp.cpp:112:23:112:23 | a | a | -| regexp.cpp:112:24:112:25 | \\n | \n | -| regexp.cpp:112:26:112:26 | c | c | -| regexp.cpp:113:40:113:40 | a | a | -| regexp.cpp:113:41:113:42 | \\n | \n | -| regexp.cpp:113:43:113:43 | c | c | -| regexp.cpp:114:42:114:42 | a | a | -| regexp.cpp:114:43:114:44 | \\n | \n | -| regexp.cpp:114:45:114:45 | c | c | -| regexp.cpp:115:41:115:41 | a | a | -| regexp.cpp:115:42:115:43 | \\n | \n | -| regexp.cpp:115:44:115:44 | c | c | +| regexp.cpp:112:38:112:38 | a | a | +| regexp.cpp:112:39:112:40 | \\n | \n | +| regexp.cpp:112:41:112:41 | c | c | +| regexp.cpp:113:23:113:23 | a | a | +| regexp.cpp:113:24:113:25 | \\n | \n | +| regexp.cpp:113:26:113:26 | c | c | +| regexp.cpp:114:40:114:40 | a | a | +| regexp.cpp:114:41:114:42 | \\n | \n | +| regexp.cpp:114:43:114:43 | c | c | +| regexp.cpp:115:42:115:42 | a | a | +| regexp.cpp:115:43:115:44 | \\n | \n | +| regexp.cpp:115:45:115:45 | c | c | | regexp.cpp:116:41:116:41 | a | a | | regexp.cpp:116:42:116:43 | \\n | \n | | regexp.cpp:116:44:116:44 | c | c | -| regexp.cpp:117:25:117:25 | a | a | -| regexp.cpp:117:26:117:27 | \\n | \n | -| regexp.cpp:117:28:117:28 | c | c | -| regexp.cpp:118:29:118:29 | a | a | -| regexp.cpp:118:30:118:31 | \\n | \n | -| regexp.cpp:118:32:118:32 | c | c | +| regexp.cpp:117:41:117:41 | a | a | +| regexp.cpp:117:42:117:43 | \\n | \n | +| regexp.cpp:117:44:117:44 | c | c | +| regexp.cpp:118:25:118:25 | a | a | +| regexp.cpp:118:26:118:27 | \\n | \n | +| regexp.cpp:118:28:118:28 | c | c | +| regexp.cpp:119:29:119:29 | a | a | +| regexp.cpp:119:30:119:31 | \\n | \n | +| regexp.cpp:119:32:119:32 | c | c | From 66c191a1c50df78983c46a0117c60a8dba851fb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 10:27:00 +0000 Subject: [PATCH 29/37] C++: Add change note for std::regex ECMAScript parser alignment --- .../change-notes/2026-07-23-std-regex-ecmascript-parser.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md diff --git a/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md b/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md new file mode 100644 index 000000000000..f12261d73365 --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added a C++ regular-expression parser for the ECMAScript grammar used by `std::regex`. From b2fc6a10d60a93d7a0d516b2003aa5866e3b8de6 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 17:39:27 +0200 Subject: [PATCH 30/37] C++: Drop unneeded `\U` from `isUnicode` --- cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 013348c55363..a4ca0ada6633 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -609,7 +609,7 @@ private module Impl implements RegexTreeViewSig { /** * Holds if this is a unicode escape. */ - private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] } + private predicate isUnicode() { this.getText().prefix(2) = "\\u" } /** * Gets the unicode char for this escape. From aa141626ece673a4a9741c6205a888b8f98888e9 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 17:45:36 +0200 Subject: [PATCH 31/37] C++: add hex escape test --- .../library-tests/regex/locations.expected | 43 ++-- .../test/library-tests/regex/parse.expected | 108 ++++----- cpp/ql/test/library-tests/regex/regexp.cpp | 3 + .../test/library-tests/regex/regexp.expected | 224 +++++++++--------- 4 files changed, 193 insertions(+), 185 deletions(-) diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 5d57a25de42d..1e13b83cd2e7 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -39,24 +39,25 @@ | regexp.cpp:84:28:84:40 | [A-F[=b=]a-f] | regexp.cpp | 84 | 28 | 84 | 40 | | regexp.cpp:87:22:87:30 | [:digit:] | regexp.cpp | 87 | 22 | 87 | 30 | | regexp.cpp:90:19:90:24 | \\u0061 | regexp.cpp | 90 | 19 | 90 | 24 | -| regexp.cpp:93:21:93:23 | \\cA | regexp.cpp | 93 | 21 | 93 | 23 | -| regexp.cpp:94:21:94:25 | [\\cZ] | regexp.cpp | 94 | 21 | 94 | 25 | -| regexp.cpp:97:20:97:21 | \\0 | regexp.cpp | 97 | 20 | 97 | 21 | -| regexp.cpp:98:20:98:23 | [\\0] | regexp.cpp | 98 | 20 | 98 | 23 | -| regexp.cpp:101:21:101:23 | a*? | regexp.cpp | 101 | 21 | 101 | 23 | -| regexp.cpp:102:21:102:23 | b+? | regexp.cpp | 102 | 21 | 102 | 23 | -| regexp.cpp:103:21:103:23 | c?? | regexp.cpp | 103 | 21 | 103 | 23 | -| regexp.cpp:104:21:104:27 | d{2,3}? | regexp.cpp | 104 | 21 | 104 | 27 | -| regexp.cpp:105:18:105:21 | \\f\\v | regexp.cpp | 105 | 18 | 105 | 21 | -| regexp.cpp:108:25:108:28 | a\\nb | regexp.cpp | 108 | 25 | 108 | 28 | -| regexp.cpp:109:37:109:40 | a\\nc | regexp.cpp | 109 | 37 | 109 | 40 | -| regexp.cpp:110:39:110:42 | a\\nc | regexp.cpp | 110 | 39 | 110 | 42 | -| regexp.cpp:111:38:111:41 | a\\nc | regexp.cpp | 111 | 38 | 111 | 41 | -| regexp.cpp:112:38:112:41 | a\\nc | regexp.cpp | 112 | 38 | 112 | 41 | -| regexp.cpp:113:23:113:26 | a\\nc | regexp.cpp | 113 | 23 | 113 | 26 | -| regexp.cpp:114:40:114:43 | a\\nc | regexp.cpp | 114 | 40 | 114 | 43 | -| regexp.cpp:115:42:115:45 | a\\nc | regexp.cpp | 115 | 42 | 115 | 45 | -| regexp.cpp:116:41:116:44 | a\\nc | regexp.cpp | 116 | 41 | 116 | 44 | -| regexp.cpp:117:41:117:44 | a\\nc | regexp.cpp | 117 | 41 | 117 | 44 | -| regexp.cpp:118:25:118:28 | a\\nc | regexp.cpp | 118 | 25 | 118 | 28 | -| regexp.cpp:119:29:119:32 | a\\nc | regexp.cpp | 119 | 29 | 119 | 32 | +| regexp.cpp:93:19:93:22 | \\x61 | regexp.cpp | 93 | 19 | 93 | 22 | +| regexp.cpp:96:21:96:23 | \\cA | regexp.cpp | 96 | 21 | 96 | 23 | +| regexp.cpp:97:21:97:25 | [\\cZ] | regexp.cpp | 97 | 21 | 97 | 25 | +| regexp.cpp:100:20:100:21 | \\0 | regexp.cpp | 100 | 20 | 100 | 21 | +| regexp.cpp:101:20:101:23 | [\\0] | regexp.cpp | 101 | 20 | 101 | 23 | +| regexp.cpp:104:21:104:23 | a*? | regexp.cpp | 104 | 21 | 104 | 23 | +| regexp.cpp:105:21:105:23 | b+? | regexp.cpp | 105 | 21 | 105 | 23 | +| regexp.cpp:106:21:106:23 | c?? | regexp.cpp | 106 | 21 | 106 | 23 | +| regexp.cpp:107:21:107:27 | d{2,3}? | regexp.cpp | 107 | 21 | 107 | 27 | +| regexp.cpp:108:18:108:21 | \\f\\v | regexp.cpp | 108 | 18 | 108 | 21 | +| regexp.cpp:111:25:111:28 | a\\nb | regexp.cpp | 111 | 25 | 111 | 28 | +| regexp.cpp:112:37:112:40 | a\\nc | regexp.cpp | 112 | 37 | 112 | 40 | +| regexp.cpp:113:39:113:42 | a\\nc | regexp.cpp | 113 | 39 | 113 | 42 | +| regexp.cpp:114:38:114:41 | a\\nc | regexp.cpp | 114 | 38 | 114 | 41 | +| regexp.cpp:115:38:115:41 | a\\nc | regexp.cpp | 115 | 38 | 115 | 41 | +| regexp.cpp:116:23:116:26 | a\\nc | regexp.cpp | 116 | 23 | 116 | 26 | +| regexp.cpp:117:40:117:43 | a\\nc | regexp.cpp | 117 | 40 | 117 | 43 | +| regexp.cpp:118:42:118:45 | a\\nc | regexp.cpp | 118 | 42 | 118 | 45 | +| regexp.cpp:119:41:119:44 | a\\nc | regexp.cpp | 119 | 41 | 119 | 44 | +| regexp.cpp:120:41:120:44 | a\\nc | regexp.cpp | 120 | 41 | 120 | 44 | +| regexp.cpp:121:25:121:28 | a\\nc | regexp.cpp | 121 | 25 | 121 | 28 | +| regexp.cpp:122:29:122:32 | a\\nc | regexp.cpp | 122 | 29 | 122 | 32 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 8993b650e1ea..b6c809ad91de 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -462,91 +462,60 @@ regexp.cpp: # 90| [RegExpConstant, RegExpEscape] \u0061 -# 93| [RegExpConstant, RegExpEscape] \cA +# 93| [RegExpConstant, RegExpEscape] \x61 -# 94| [RegExpCharacterClass] [\cZ] +# 96| [RegExpConstant, RegExpEscape] \cA + +# 97| [RegExpCharacterClass] [\cZ] #-----| 0 -> [RegExpConstant, RegExpEscape] \cZ -# 94| [RegExpConstant, RegExpEscape] \cZ +# 97| [RegExpConstant, RegExpEscape] \cZ -# 97| [RegExpConstant, RegExpEscape] \0 +# 100| [RegExpConstant, RegExpEscape] \0 -# 98| [RegExpCharacterClass] [\0] +# 101| [RegExpCharacterClass] [\0] #-----| 0 -> [RegExpConstant, RegExpEscape] \0 -# 98| [RegExpConstant, RegExpEscape] \0 +# 101| [RegExpConstant, RegExpEscape] \0 -# 101| [RegExpConstant, RegExpNormalChar] a +# 104| [RegExpConstant, RegExpNormalChar] a -# 101| [RegExpStar] a*? +# 104| [RegExpStar] a*? #-----| 0 -> [RegExpConstant, RegExpNormalChar] a -# 102| [RegExpConstant, RegExpNormalChar] b +# 105| [RegExpConstant, RegExpNormalChar] b -# 102| [RegExpPlus] b+? +# 105| [RegExpPlus] b+? #-----| 0 -> [RegExpConstant, RegExpNormalChar] b -# 103| [RegExpConstant, RegExpNormalChar] c +# 106| [RegExpConstant, RegExpNormalChar] c -# 103| [RegExpOpt] c?? +# 106| [RegExpOpt] c?? #-----| 0 -> [RegExpConstant, RegExpNormalChar] c -# 104| [RegExpConstant, RegExpNormalChar] d +# 107| [RegExpConstant, RegExpNormalChar] d -# 104| [RegExpQuantifier] d{2,3}? +# 107| [RegExpQuantifier] d{2,3}? #-----| 0 -> [RegExpConstant, RegExpNormalChar] d -# 105| [RegExpConstant, RegExpEscape] \f +# 108| [RegExpConstant, RegExpEscape] \f -# 105| [RegExpSequence] \f\v +# 108| [RegExpSequence] \f\v #-----| 0 -> [RegExpConstant, RegExpEscape] \f #-----| 1 -> [RegExpConstant, RegExpEscape] \v -# 105| [RegExpConstant, RegExpEscape] \v - -# 108| [RegExpConstant, RegExpNormalChar] a - -# 108| [RegExpSequence] a\nb -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] b - -# 108| [RegExpConstant, RegExpEscape] \n - -# 108| [RegExpConstant, RegExpNormalChar] b - -# 109| [RegExpConstant, RegExpNormalChar] a - -# 109| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c - -# 109| [RegExpConstant, RegExpEscape] \n - -# 109| [RegExpConstant, RegExpNormalChar] c - -# 110| [RegExpConstant, RegExpNormalChar] a - -# 110| [RegExpSequence] a\nc -#-----| 0 -> [RegExpConstant, RegExpNormalChar] a -#-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c - -# 110| [RegExpConstant, RegExpEscape] \n - -# 110| [RegExpConstant, RegExpNormalChar] c +# 108| [RegExpConstant, RegExpEscape] \v # 111| [RegExpConstant, RegExpNormalChar] a -# 111| [RegExpSequence] a\nc +# 111| [RegExpSequence] a\nb #-----| 0 -> [RegExpConstant, RegExpNormalChar] a #-----| 1 -> [RegExpConstant, RegExpEscape] \n -#-----| 2 -> [RegExpConstant, RegExpNormalChar] c +#-----| 2 -> [RegExpConstant, RegExpNormalChar] b # 111| [RegExpConstant, RegExpEscape] \n -# 111| [RegExpConstant, RegExpNormalChar] c +# 111| [RegExpConstant, RegExpNormalChar] b # 112| [RegExpConstant, RegExpNormalChar] a @@ -635,3 +604,36 @@ regexp.cpp: # 119| [RegExpConstant, RegExpEscape] \n # 119| [RegExpConstant, RegExpNormalChar] c + +# 120| [RegExpConstant, RegExpNormalChar] a + +# 120| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 120| [RegExpConstant, RegExpEscape] \n + +# 120| [RegExpConstant, RegExpNormalChar] c + +# 121| [RegExpConstant, RegExpNormalChar] a + +# 121| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 121| [RegExpConstant, RegExpEscape] \n + +# 121| [RegExpConstant, RegExpNormalChar] c + +# 122| [RegExpConstant, RegExpNormalChar] a + +# 122| [RegExpSequence] a\nc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 122| [RegExpConstant, RegExpEscape] \n + +# 122| [RegExpConstant, RegExpNormalChar] c diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index b1cfd64aa5c5..c26794296179 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -89,6 +89,9 @@ std::regex r_posix4("[:digit:]"); // unicode std::regex r_uni("\\u0061"); +// hex +std::regex r_hex("\\x61"); + // control escapes std::regex r_ctrl1("\\cA"); std::regex r_ctrl2("[\\cZ]"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index d82f3aa23547..95159d356332 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -172,71 +172,72 @@ term | regexp.cpp:84:39:84:39 | f | RegExpConstant,RegExpNormalChar | | regexp.cpp:87:22:87:30 | [:digit:] | RegExpNamedCharacterProperty | | regexp.cpp:90:19:90:24 | \\u0061 | RegExpConstant,RegExpEscape | -| regexp.cpp:93:21:93:23 | \\cA | RegExpConstant,RegExpEscape | -| regexp.cpp:94:21:94:25 | [\\cZ] | RegExpCharacterClass | -| regexp.cpp:94:22:94:24 | \\cZ | RegExpConstant,RegExpEscape | -| regexp.cpp:97:20:97:21 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:98:20:98:23 | [\\0] | RegExpCharacterClass | -| regexp.cpp:98:21:98:22 | \\0 | RegExpConstant,RegExpEscape | -| regexp.cpp:101:21:101:21 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:101:21:101:23 | a*? | RegExpStar | -| regexp.cpp:102:21:102:21 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:102:21:102:23 | b+? | RegExpPlus | -| regexp.cpp:103:21:103:21 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:103:21:103:23 | c?? | RegExpOpt | -| regexp.cpp:104:21:104:21 | d | RegExpConstant,RegExpNormalChar | -| regexp.cpp:104:21:104:27 | d{2,3}? | RegExpQuantifier | -| regexp.cpp:105:18:105:19 | \\f | RegExpConstant,RegExpEscape | -| regexp.cpp:105:18:105:21 | \\f\\v | RegExpSequence | -| regexp.cpp:105:20:105:21 | \\v | RegExpConstant,RegExpEscape | -| regexp.cpp:108:25:108:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:108:25:108:28 | a\\nb | RegExpSequence | -| regexp.cpp:108:26:108:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:108:28:108:28 | b | RegExpConstant,RegExpNormalChar | -| regexp.cpp:109:37:109:37 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:109:37:109:40 | a\\nc | RegExpSequence | -| regexp.cpp:109:38:109:39 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:109:40:109:40 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:110:39:110:39 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:110:39:110:42 | a\\nc | RegExpSequence | -| regexp.cpp:110:40:110:41 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:110:42:110:42 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:111:38:111:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:111:38:111:41 | a\\nc | RegExpSequence | -| regexp.cpp:111:39:111:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:111:41:111:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:112:38:112:38 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:112:38:112:41 | a\\nc | RegExpSequence | -| regexp.cpp:112:39:112:40 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:112:41:112:41 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:113:23:113:23 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:113:23:113:26 | a\\nc | RegExpSequence | -| regexp.cpp:113:24:113:25 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:113:26:113:26 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:114:40:114:40 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:114:40:114:43 | a\\nc | RegExpSequence | -| regexp.cpp:114:41:114:42 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:114:43:114:43 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:115:42:115:42 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:115:42:115:45 | a\\nc | RegExpSequence | -| regexp.cpp:115:43:115:44 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:115:45:115:45 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:116:41:116:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:116:41:116:44 | a\\nc | RegExpSequence | -| regexp.cpp:116:42:116:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:116:44:116:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:117:41:117:41 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:117:41:117:44 | a\\nc | RegExpSequence | -| regexp.cpp:117:42:117:43 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:117:44:117:44 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:118:25:118:25 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:118:25:118:28 | a\\nc | RegExpSequence | -| regexp.cpp:118:26:118:27 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:118:28:118:28 | c | RegExpConstant,RegExpNormalChar | -| regexp.cpp:119:29:119:29 | a | RegExpConstant,RegExpNormalChar | -| regexp.cpp:119:29:119:32 | a\\nc | RegExpSequence | -| regexp.cpp:119:30:119:31 | \\n | RegExpConstant,RegExpEscape | -| regexp.cpp:119:32:119:32 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:93:19:93:22 | \\x61 | RegExpConstant,RegExpEscape | +| regexp.cpp:96:21:96:23 | \\cA | RegExpConstant,RegExpEscape | +| regexp.cpp:97:21:97:25 | [\\cZ] | RegExpCharacterClass | +| regexp.cpp:97:22:97:24 | \\cZ | RegExpConstant,RegExpEscape | +| regexp.cpp:100:20:100:21 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:101:20:101:23 | [\\0] | RegExpCharacterClass | +| regexp.cpp:101:21:101:22 | \\0 | RegExpConstant,RegExpEscape | +| regexp.cpp:104:21:104:21 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:104:21:104:23 | a*? | RegExpStar | +| regexp.cpp:105:21:105:21 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:105:21:105:23 | b+? | RegExpPlus | +| regexp.cpp:106:21:106:21 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:106:21:106:23 | c?? | RegExpOpt | +| regexp.cpp:107:21:107:21 | d | RegExpConstant,RegExpNormalChar | +| regexp.cpp:107:21:107:27 | d{2,3}? | RegExpQuantifier | +| regexp.cpp:108:18:108:19 | \\f | RegExpConstant,RegExpEscape | +| regexp.cpp:108:18:108:21 | \\f\\v | RegExpSequence | +| regexp.cpp:108:20:108:21 | \\v | RegExpConstant,RegExpEscape | +| regexp.cpp:111:25:111:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:111:25:111:28 | a\\nb | RegExpSequence | +| regexp.cpp:111:26:111:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:111:28:111:28 | b | RegExpConstant,RegExpNormalChar | +| regexp.cpp:112:37:112:37 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:112:37:112:40 | a\\nc | RegExpSequence | +| regexp.cpp:112:38:112:39 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:112:40:112:40 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:113:39:113:39 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:113:39:113:42 | a\\nc | RegExpSequence | +| regexp.cpp:113:40:113:41 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:113:42:113:42 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:114:38:114:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:114:38:114:41 | a\\nc | RegExpSequence | +| regexp.cpp:114:39:114:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:114:41:114:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:115:38:115:38 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:115:38:115:41 | a\\nc | RegExpSequence | +| regexp.cpp:115:39:115:40 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:115:41:115:41 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:116:23:116:23 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:116:23:116:26 | a\\nc | RegExpSequence | +| regexp.cpp:116:24:116:25 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:116:26:116:26 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:117:40:117:40 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:117:40:117:43 | a\\nc | RegExpSequence | +| regexp.cpp:117:41:117:42 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:117:43:117:43 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:118:42:118:42 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:118:42:118:45 | a\\nc | RegExpSequence | +| regexp.cpp:118:43:118:44 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:118:45:118:45 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:119:41:119:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:119:41:119:44 | a\\nc | RegExpSequence | +| regexp.cpp:119:42:119:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:119:44:119:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:120:41:120:41 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:120:41:120:44 | a\\nc | RegExpSequence | +| regexp.cpp:120:42:120:43 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:120:44:120:44 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:121:25:121:25 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:121:25:121:28 | a\\nc | RegExpSequence | +| regexp.cpp:121:26:121:27 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:121:28:121:28 | c | RegExpConstant,RegExpNormalChar | +| regexp.cpp:122:29:122:29 | a | RegExpConstant,RegExpNormalChar | +| regexp.cpp:122:29:122:32 | a\\nc | RegExpSequence | +| regexp.cpp:122:30:122:31 | \\n | RegExpConstant,RegExpEscape | +| regexp.cpp:122:32:122:32 | c | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | regexp.cpp:18:19:18:21 | abc | abc | | regexp.cpp:21:20:21:20 | a | a | @@ -314,49 +315,50 @@ regExpNormalCharValue | regexp.cpp:84:37:84:37 | a | a | | regexp.cpp:84:39:84:39 | f | f | | regexp.cpp:90:19:90:24 | \\u0061 | a | -| regexp.cpp:93:21:93:23 | \\cA | cA | -| regexp.cpp:94:22:94:24 | \\cZ | cZ | -| regexp.cpp:97:20:97:21 | \\0 | 0 | -| regexp.cpp:98:21:98:22 | \\0 | 0 | -| regexp.cpp:101:21:101:21 | a | a | -| regexp.cpp:102:21:102:21 | b | b | -| regexp.cpp:103:21:103:21 | c | c | -| regexp.cpp:104:21:104:21 | d | d | -| regexp.cpp:105:18:105:19 | \\f | \u000c | -| regexp.cpp:105:20:105:21 | \\v | \u000b | -| regexp.cpp:108:25:108:25 | a | a | -| regexp.cpp:108:26:108:27 | \\n | \n | -| regexp.cpp:108:28:108:28 | b | b | -| regexp.cpp:109:37:109:37 | a | a | -| regexp.cpp:109:38:109:39 | \\n | \n | -| regexp.cpp:109:40:109:40 | c | c | -| regexp.cpp:110:39:110:39 | a | a | -| regexp.cpp:110:40:110:41 | \\n | \n | -| regexp.cpp:110:42:110:42 | c | c | -| regexp.cpp:111:38:111:38 | a | a | -| regexp.cpp:111:39:111:40 | \\n | \n | -| regexp.cpp:111:41:111:41 | c | c | -| regexp.cpp:112:38:112:38 | a | a | -| regexp.cpp:112:39:112:40 | \\n | \n | -| regexp.cpp:112:41:112:41 | c | c | -| regexp.cpp:113:23:113:23 | a | a | -| regexp.cpp:113:24:113:25 | \\n | \n | -| regexp.cpp:113:26:113:26 | c | c | -| regexp.cpp:114:40:114:40 | a | a | -| regexp.cpp:114:41:114:42 | \\n | \n | -| regexp.cpp:114:43:114:43 | c | c | -| regexp.cpp:115:42:115:42 | a | a | -| regexp.cpp:115:43:115:44 | \\n | \n | -| regexp.cpp:115:45:115:45 | c | c | -| regexp.cpp:116:41:116:41 | a | a | -| regexp.cpp:116:42:116:43 | \\n | \n | -| regexp.cpp:116:44:116:44 | c | c | -| regexp.cpp:117:41:117:41 | a | a | -| regexp.cpp:117:42:117:43 | \\n | \n | -| regexp.cpp:117:44:117:44 | c | c | -| regexp.cpp:118:25:118:25 | a | a | -| regexp.cpp:118:26:118:27 | \\n | \n | -| regexp.cpp:118:28:118:28 | c | c | -| regexp.cpp:119:29:119:29 | a | a | -| regexp.cpp:119:30:119:31 | \\n | \n | -| regexp.cpp:119:32:119:32 | c | c | +| regexp.cpp:93:19:93:22 | \\x61 | x61 | +| regexp.cpp:96:21:96:23 | \\cA | cA | +| regexp.cpp:97:22:97:24 | \\cZ | cZ | +| regexp.cpp:100:20:100:21 | \\0 | 0 | +| regexp.cpp:101:21:101:22 | \\0 | 0 | +| regexp.cpp:104:21:104:21 | a | a | +| regexp.cpp:105:21:105:21 | b | b | +| regexp.cpp:106:21:106:21 | c | c | +| regexp.cpp:107:21:107:21 | d | d | +| regexp.cpp:108:18:108:19 | \\f | \u000c | +| regexp.cpp:108:20:108:21 | \\v | \u000b | +| regexp.cpp:111:25:111:25 | a | a | +| regexp.cpp:111:26:111:27 | \\n | \n | +| regexp.cpp:111:28:111:28 | b | b | +| regexp.cpp:112:37:112:37 | a | a | +| regexp.cpp:112:38:112:39 | \\n | \n | +| regexp.cpp:112:40:112:40 | c | c | +| regexp.cpp:113:39:113:39 | a | a | +| regexp.cpp:113:40:113:41 | \\n | \n | +| regexp.cpp:113:42:113:42 | c | c | +| regexp.cpp:114:38:114:38 | a | a | +| regexp.cpp:114:39:114:40 | \\n | \n | +| regexp.cpp:114:41:114:41 | c | c | +| regexp.cpp:115:38:115:38 | a | a | +| regexp.cpp:115:39:115:40 | \\n | \n | +| regexp.cpp:115:41:115:41 | c | c | +| regexp.cpp:116:23:116:23 | a | a | +| regexp.cpp:116:24:116:25 | \\n | \n | +| regexp.cpp:116:26:116:26 | c | c | +| regexp.cpp:117:40:117:40 | a | a | +| regexp.cpp:117:41:117:42 | \\n | \n | +| regexp.cpp:117:43:117:43 | c | c | +| regexp.cpp:118:42:118:42 | a | a | +| regexp.cpp:118:43:118:44 | \\n | \n | +| regexp.cpp:118:45:118:45 | c | c | +| regexp.cpp:119:41:119:41 | a | a | +| regexp.cpp:119:42:119:43 | \\n | \n | +| regexp.cpp:119:44:119:44 | c | c | +| regexp.cpp:120:41:120:41 | a | a | +| regexp.cpp:120:42:120:43 | \\n | \n | +| regexp.cpp:120:44:120:44 | c | c | +| regexp.cpp:121:25:121:25 | a | a | +| regexp.cpp:121:26:121:27 | \\n | \n | +| regexp.cpp:121:28:121:28 | c | c | +| regexp.cpp:122:29:122:29 | a | a | +| regexp.cpp:122:30:122:31 | \\n | \n | +| regexp.cpp:122:32:122:32 | c | c | From ff038a2cb8b8763ec68cb65656edd42957fd012f Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 17:49:05 +0200 Subject: [PATCH 32/37] C++: Handle `\0` in `RegExpEscape` --- cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll | 4 +++- cpp/ql/test/library-tests/regex/regexp.expected | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index a4ca0ada6633..0e9086577c19 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -587,13 +587,15 @@ private module Impl implements RegexTreeViewSig { or this.getUnescaped() = "v" and result = 11.toUnicode() or + this.getUnescaped() = "0" and result = 0.toUnicode() + or this.isUnicode() and result = this.getUnicode() } /** Holds if this terms name is given by the part following the escape character. */ predicate isIdentityEscape() { - not this.getUnescaped() in ["n", "r", "t", "f", "v"] and not this.isUnicode() + not this.getUnescaped() in ["n", "r", "t", "f", "v", "0"] and not this.isUnicode() } override string getAPrimaryQlClass() { result = "RegExpEscape" } diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 95159d356332..b11fb53dda17 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -318,8 +318,8 @@ regExpNormalCharValue | regexp.cpp:93:19:93:22 | \\x61 | x61 | | regexp.cpp:96:21:96:23 | \\cA | cA | | regexp.cpp:97:22:97:24 | \\cZ | cZ | -| regexp.cpp:100:20:100:21 | \\0 | 0 | -| regexp.cpp:101:21:101:22 | \\0 | 0 | +| regexp.cpp:100:20:100:21 | \\0 | \u0000 | +| regexp.cpp:101:21:101:22 | \\0 | \u0000 | | regexp.cpp:104:21:104:21 | a | a | | regexp.cpp:105:21:105:21 | b | b | | regexp.cpp:106:21:106:21 | c | c | From 40feb22f4ec052698488928b6c0d95cc1e22ffe9 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 18:04:41 +0200 Subject: [PATCH 33/37] C++: Handle `\cX` in `RegExpEscape` and slightly tweak test --- .../semmle/code/cpp/regex/RegexTreeView.qll | 24 ++++++++++++++++++- .../library-tests/regex/locations.expected | 2 +- .../test/library-tests/regex/parse.expected | 6 ++--- cpp/ql/test/library-tests/regex/regexp.cpp | 2 +- .../test/library-tests/regex/regexp.expected | 8 +++---- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 0e9086577c19..1c38a6d63084 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -574,6 +574,7 @@ private module Impl implements RegexTreeViewSig { */ override string getValue() { not this.isUnicode() and + not this.isControl() and this.isIdentityEscape() and result = this.getUnescaped() or @@ -591,11 +592,16 @@ private module Impl implements RegexTreeViewSig { or this.isUnicode() and result = this.getUnicode() + or + this.isControl() and + result = this.getControl() } /** Holds if this terms name is given by the part following the escape character. */ predicate isIdentityEscape() { - not this.getUnescaped() in ["n", "r", "t", "f", "v", "0"] and not this.isUnicode() + not this.getUnescaped() in ["n", "r", "t", "f", "v", "0"] and + not this.isUnicode() and + not this.isControl() } override string getAPrimaryQlClass() { result = "RegExpEscape" } @@ -621,6 +627,22 @@ private module Impl implements RegexTreeViewSig { this.isUnicode() and result = parseHexInt(this.getText().suffix(2)).toUnicode() } + + /** + * Holds if this is a `\cX` escape. + */ + private predicate isControl() { this.getText().prefix(2) = "\\c" } + + /** + * Gets the unicode char for this escape. + * E.g. for `\cA` this returns "a". + */ + private string getControl() { + this.isControl() and + exists(string letter | letter = this.getText().suffix(2).toUpperCase() | + result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(letter).toUnicode() + ) + } } /** diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 1e13b83cd2e7..a9f12c020d79 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -41,7 +41,7 @@ | regexp.cpp:90:19:90:24 | \\u0061 | regexp.cpp | 90 | 19 | 90 | 24 | | regexp.cpp:93:19:93:22 | \\x61 | regexp.cpp | 93 | 19 | 93 | 22 | | regexp.cpp:96:21:96:23 | \\cA | regexp.cpp | 96 | 21 | 96 | 23 | -| regexp.cpp:97:21:97:25 | [\\cZ] | regexp.cpp | 97 | 21 | 97 | 25 | +| regexp.cpp:97:21:97:25 | [\\cz] | regexp.cpp | 97 | 21 | 97 | 25 | | regexp.cpp:100:20:100:21 | \\0 | regexp.cpp | 100 | 20 | 100 | 21 | | regexp.cpp:101:20:101:23 | [\\0] | regexp.cpp | 101 | 20 | 101 | 23 | | regexp.cpp:104:21:104:23 | a*? | regexp.cpp | 104 | 21 | 104 | 23 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index b6c809ad91de..e8d84b277514 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -466,10 +466,10 @@ regexp.cpp: # 96| [RegExpConstant, RegExpEscape] \cA -# 97| [RegExpCharacterClass] [\cZ] -#-----| 0 -> [RegExpConstant, RegExpEscape] \cZ +# 97| [RegExpCharacterClass] [\cz] +#-----| 0 -> [RegExpConstant, RegExpEscape] \cz -# 97| [RegExpConstant, RegExpEscape] \cZ +# 97| [RegExpConstant, RegExpEscape] \cz # 100| [RegExpConstant, RegExpEscape] \0 diff --git a/cpp/ql/test/library-tests/regex/regexp.cpp b/cpp/ql/test/library-tests/regex/regexp.cpp index c26794296179..51986dd59adf 100644 --- a/cpp/ql/test/library-tests/regex/regexp.cpp +++ b/cpp/ql/test/library-tests/regex/regexp.cpp @@ -94,7 +94,7 @@ std::regex r_hex("\\x61"); // control escapes std::regex r_ctrl1("\\cA"); -std::regex r_ctrl2("[\\cZ]"); +std::regex r_ctrl2("[\\cz]"); // NUL escape std::regex r_nul1("\\0"); diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index b11fb53dda17..83631c8a3ab8 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -174,8 +174,8 @@ term | regexp.cpp:90:19:90:24 | \\u0061 | RegExpConstant,RegExpEscape | | regexp.cpp:93:19:93:22 | \\x61 | RegExpConstant,RegExpEscape | | regexp.cpp:96:21:96:23 | \\cA | RegExpConstant,RegExpEscape | -| regexp.cpp:97:21:97:25 | [\\cZ] | RegExpCharacterClass | -| regexp.cpp:97:22:97:24 | \\cZ | RegExpConstant,RegExpEscape | +| regexp.cpp:97:21:97:25 | [\\cz] | RegExpCharacterClass | +| regexp.cpp:97:22:97:24 | \\cz | RegExpConstant,RegExpEscape | | regexp.cpp:100:20:100:21 | \\0 | RegExpConstant,RegExpEscape | | regexp.cpp:101:20:101:23 | [\\0] | RegExpCharacterClass | | regexp.cpp:101:21:101:22 | \\0 | RegExpConstant,RegExpEscape | @@ -316,8 +316,8 @@ regExpNormalCharValue | regexp.cpp:84:39:84:39 | f | f | | regexp.cpp:90:19:90:24 | \\u0061 | a | | regexp.cpp:93:19:93:22 | \\x61 | x61 | -| regexp.cpp:96:21:96:23 | \\cA | cA | -| regexp.cpp:97:22:97:24 | \\cZ | cZ | +| regexp.cpp:96:21:96:23 | \\cA | \u0000 | +| regexp.cpp:97:22:97:24 | \\cz | \u0019 | | regexp.cpp:100:20:100:21 | \\0 | \u0000 | | regexp.cpp:101:21:101:22 | \\0 | \u0000 | | regexp.cpp:104:21:104:21 | a | a | From e6cfe170f87a5c604af729eeaa4330351dc83c3d Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 18:08:08 +0200 Subject: [PATCH 34/37] C++: Handle `\xhh` in `RegExpEscape` --- .../semmle/code/cpp/regex/RegexTreeView.qll | 19 +++++++++++++++++++ .../test/library-tests/regex/regexp.expected | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 1c38a6d63084..6059368e3a0e 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -574,6 +574,7 @@ private module Impl implements RegexTreeViewSig { */ override string getValue() { not this.isUnicode() and + not this.isHex() and not this.isControl() and this.isIdentityEscape() and result = this.getUnescaped() @@ -593,6 +594,9 @@ private module Impl implements RegexTreeViewSig { this.isUnicode() and result = this.getUnicode() or + this.isHex() and + result = this.getHex() + or this.isControl() and result = this.getControl() } @@ -601,6 +605,7 @@ private module Impl implements RegexTreeViewSig { predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f", "v", "0"] and not this.isUnicode() and + not this.isHex() and not this.isControl() } @@ -628,6 +633,20 @@ private module Impl implements RegexTreeViewSig { result = parseHexInt(this.getText().suffix(2)).toUnicode() } + /** + * Holds if this is a hex escape. + */ + private predicate isHex() { this.getText().prefix(2) = "\\x" } + + /** + * Gets the unicode char for this escape. + * E.g. for `\x61` this returns "a". + */ + private string getHex() { + this.isHex() and + result = parseHexInt(this.getText().suffix(2)).toUnicode() + } + /** * Holds if this is a `\cX` escape. */ diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 83631c8a3ab8..65e28914a5c1 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -315,7 +315,7 @@ regExpNormalCharValue | regexp.cpp:84:37:84:37 | a | a | | regexp.cpp:84:39:84:39 | f | f | | regexp.cpp:90:19:90:24 | \\u0061 | a | -| regexp.cpp:93:19:93:22 | \\x61 | x61 | +| regexp.cpp:93:19:93:22 | \\x61 | a | | regexp.cpp:96:21:96:23 | \\cA | \u0000 | | regexp.cpp:97:22:97:24 | \\cz | \u0019 | | regexp.cpp:100:20:100:21 | \\0 | \u0000 | From b30d9d6c84b6f3780c70e046d97223a0a97f5cbd Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 18:10:55 +0200 Subject: [PATCH 35/37] C++: Fix change note category --- .../lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md b/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md index f12261d73365..8c1a4f8339a2 100644 --- a/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md +++ b/cpp/ql/lib/change-notes/2026-07-23-std-regex-ecmascript-parser.md @@ -1,4 +1,4 @@ --- -category: minorAnalysis +category: feature --- * Added a C++ regular-expression parser for the ECMAScript grammar used by `std::regex`. From aca8aa9288e4fa69b0958ee9e3f5408f15ea5b37 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 18:22:03 +0200 Subject: [PATCH 36/37] C++: Fix off-by-one in `\cX` handling --- cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll | 4 ++-- cpp/ql/test/library-tests/regex/regexp.expected | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 6059368e3a0e..9afc27356a2d 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -654,12 +654,12 @@ private module Impl implements RegexTreeViewSig { /** * Gets the unicode char for this escape. - * E.g. for `\cA` this returns "a". + * E.g. for `\cA` this returns `0x01`. */ private string getControl() { this.isControl() and exists(string letter | letter = this.getText().suffix(2).toUpperCase() | - result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(letter).toUnicode() + result = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(letter) + 1).toUnicode() ) } } diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 65e28914a5c1..6e7a4cfc7204 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -316,8 +316,8 @@ regExpNormalCharValue | regexp.cpp:84:39:84:39 | f | f | | regexp.cpp:90:19:90:24 | \\u0061 | a | | regexp.cpp:93:19:93:22 | \\x61 | a | -| regexp.cpp:96:21:96:23 | \\cA | \u0000 | -| regexp.cpp:97:22:97:24 | \\cz | \u0019 | +| regexp.cpp:96:21:96:23 | \\cA | \u0001 | +| regexp.cpp:97:22:97:24 | \\cz | \u001a | | regexp.cpp:100:20:100:21 | \\0 | \u0000 | | regexp.cpp:101:21:101:22 | \\0 | \u0000 | | regexp.cpp:104:21:104:21 | a | a | From e95ae406f41c55af4e5ae3cf447256fd4783a442 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 7 Aug 2026 18:32:43 +0200 Subject: [PATCH 37/37] C++: Fix QL-for-QL errors --- cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index 9afc27356a2d..cadb1e5d12c3 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -622,7 +622,7 @@ private module Impl implements RegexTreeViewSig { /** * Holds if this is a unicode escape. */ - private predicate isUnicode() { this.getText().prefix(2) = "\\u" } + private predicate isUnicode() { this.getText().matches("\\u%") } /** * Gets the unicode char for this escape. @@ -636,7 +636,7 @@ private module Impl implements RegexTreeViewSig { /** * Holds if this is a hex escape. */ - private predicate isHex() { this.getText().prefix(2) = "\\x" } + private predicate isHex() { this.getText().matches("\\x%") } /** * Gets the unicode char for this escape. @@ -650,7 +650,7 @@ private module Impl implements RegexTreeViewSig { /** * Holds if this is a `\cX` escape. */ - private predicate isControl() { this.getText().prefix(2) = "\\c" } + private predicate isControl() { this.getText().matches("\\c%") } /** * Gets the unicode char for this escape.