diff --git a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll index f90dc8b66d6..af8d8ae3923 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll @@ -106,6 +106,64 @@ private RegExpTerm seqChild(RegExp re, int start, int end, int i) { // --------------------------------------------------------------------------- // Module Impl (implements RegexTreeViewSig) // --------------------------------------------------------------------------- +/** + * Gets the length of the C++ encoding prefix (`L`, `u`, `U`, `u8`) at the + * start of a string-literal spelling, or 0 if none is present. + * + * `u8` is checked before `u` so that a `u8"..."` literal is not mistakenly + * seen as a `u"..."` literal. + */ +bindingset[spelling] +private int getEncodingPrefixLength(string spelling) { + if spelling.substring(0, 2) = "u8" + then result = 2 + else + if spelling.substring(0, 1) = ["L", "u", "U"] + then result = 1 + else result = 0 +} + +/** + * Gets the number of characters at the start of the raw spelling of the + * string literal `re` that precede its first content character. This is + * the width of the encoding prefix (if any) plus the opening delimiter: + * `"` for a non-raw literal, or `R"delim(` (with a possibly-empty + * user-chosen `delim`) for a raw literal. + * + * For a plain narrow `"..."` literal this returns 1, matching the earlier + * hard-coded gap. + * + * If the spelling cannot be recognized (for example after macro + * expansion), falls back to 1 so that no location becomes empty. + */ +private int getContentOffset(RegExp re) { + result = tryGetContentOffset(re) + or + not exists(tryGetContentOffset(re)) and result = 1 +} + +/** + * Gets the content offset of `re` when its raw spelling matches a + * recognized C++ string-literal form. + */ +private int tryGetContentOffset(RegExp re) { + exists(string spelling, int encLen | + spelling = re.getValueText() and + encLen = getEncodingPrefixLength(spelling) + | + // Raw string: `R"delim(...)delim"`. The content starts one past the + // `(` that terminates the raw prefix; the delimiter is the (possibly + // empty) text between the opening `"` and that `(`. + spelling.charAt(encLen) = "R" and + spelling.charAt(encLen + 1) = "\"" and + result = spelling.indexOf("(", 0, encLen + 2) + 1 + or + // Non-raw string: `"..."`. Content starts one past the opening `"`. + spelling.charAt(encLen) = "\"" and + result = encLen + 1 + ) +} + /** An implementation that satisfies the `RegexTreeViewSig` signature. */ module Impl implements RegexTreeViewSig { // ------------------------------------------------------------------------- @@ -290,18 +348,33 @@ module Impl implements RegexTreeViewSig { /** * Holds if this term is found at the given source location. * - * The location maps back to character offsets within the C++ string literal. - * Because the literal's start column already accounts for the opening `"`, - * we add 1 to skip the quote itself. + * The location maps back to character offsets within the C++ string + * literal. The gap between the literal's start column and its first + * content character depends on the literal's raw spelling: it accounts + * for any encoding prefix (`L`, `u`, `U`, `u8`) and for the opening + * delimiter, which is a single `"` for a plain literal and `R"delim(` + * (with a possibly-empty user-chosen `delim`) for a raw literal. + * `getContentOffset` computes this gap from the raw spelling so that + * every literal form maps to the correct source columns. + * + * This is an approximation that handles single-line literals: for a + * literal whose content spans multiple source lines (for example a raw + * string containing newlines) the reported column is a column within + * the literal's start line, mirroring the documented approximations in + * the Java and Python regex tree views. */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - exists(Location loc | loc = re.getLocation() | + exists(Location loc, int contentOffset | + loc = re.getLocation() and + contentOffset = getContentOffset(re) + | loc.hasLocationInfo(filepath, startline, _, _, _) and - startcolumn = loc.getStartColumn() + start + 1 and + startcolumn = loc.getStartColumn() + contentOffset + start and endline = startline and - endcolumn = loc.getStartColumn() + end // end is exclusive, so -1+1 = 0 offset + // `end` is exclusive, so subtract 1 to point at the last character. + endcolumn = loc.getStartColumn() + contentOffset + end - 1 ) } diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index 3e9c6373c76..67e18b8fb47 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -673,56 +673,56 @@ | b | RegExpNormalChar | test.cpp | 430 | 22 | 430 | 22 | 19 | 2 | 3 | | \\s | RegExpCharacterClassEscape | test.cpp | 431 | 20 | 431 | 21 | 19 | 0 | 2 | | \\s+ | RegExpPlus | test.cpp | 431 | 20 | 431 | 22 | 19 | 0 | 3 | -| a | RegExpConstant | test.cpp | 436 | 20 | 436 | 20 | 19 | 0 | 1 | -| a | RegExpNormalChar | test.cpp | 436 | 20 | 436 | 20 | 19 | 0 | 1 | -| a+ | RegExpPlus | test.cpp | 436 | 20 | 436 | 21 | 19 | 0 | 2 | -| a+b | RegExpSequence | test.cpp | 436 | 20 | 436 | 22 | 19 | 0 | 3 | -| b | RegExpConstant | test.cpp | 436 | 22 | 436 | 22 | 19 | 2 | 3 | -| b | RegExpNormalChar | test.cpp | 436 | 22 | 436 | 22 | 19 | 2 | 3 | -| \\s | RegExpCharacterClassEscape | test.cpp | 437 | 20 | 437 | 21 | 19 | 0 | 2 | -| \\s+ | RegExpPlus | test.cpp | 437 | 20 | 437 | 22 | 19 | 0 | 3 | -| \\s+$ | RegExpSequence | test.cpp | 437 | 20 | 437 | 23 | 19 | 0 | 4 | -| $ | RegExpDollar | test.cpp | 437 | 23 | 437 | 23 | 19 | 3 | 4 | -| \\( | RegExpConstant | test.cpp | 438 | 20 | 438 | 21 | 19 | 0 | 2 | -| \\( | RegExpEscape | test.cpp | 438 | 20 | 438 | 21 | 19 | 0 | 2 | -| \\(([,\\w]+)+\\)$ | RegExpSequence | test.cpp | 438 | 20 | 438 | 33 | 19 | 0 | 14 | -| ([,\\w]+) | RegExpGroup | test.cpp | 438 | 22 | 438 | 29 | 19 | 2 | 10 | -| ([,\\w]+)+ | RegExpPlus | test.cpp | 438 | 22 | 438 | 30 | 19 | 2 | 11 | -| [,\\w] | RegExpCharacterClass | test.cpp | 438 | 23 | 438 | 27 | 19 | 3 | 8 | -| [,\\w]+ | RegExpPlus | test.cpp | 438 | 23 | 438 | 28 | 19 | 3 | 9 | -| , | RegExpConstant | test.cpp | 438 | 24 | 438 | 24 | 19 | 4 | 5 | -| , | RegExpNormalChar | test.cpp | 438 | 24 | 438 | 24 | 19 | 4 | 5 | -| \\w | RegExpCharacterClassEscape | test.cpp | 438 | 25 | 438 | 26 | 19 | 5 | 7 | -| \\) | RegExpConstant | test.cpp | 438 | 31 | 438 | 32 | 19 | 11 | 13 | -| \\) | RegExpEscape | test.cpp | 438 | 31 | 438 | 32 | 19 | 11 | 13 | -| $ | RegExpDollar | test.cpp | 438 | 33 | 438 | 33 | 19 | 13 | 14 | -| a | RegExpConstant | test.cpp | 442 | 20 | 442 | 20 | 19 | 0 | 1 | -| a | RegExpNormalChar | test.cpp | 442 | 20 | 442 | 20 | 19 | 0 | 1 | -| a+ | RegExpPlus | test.cpp | 442 | 20 | 442 | 21 | 19 | 0 | 2 | -| a+b | RegExpSequence | test.cpp | 442 | 20 | 442 | 22 | 19 | 0 | 3 | -| b | RegExpConstant | test.cpp | 442 | 22 | 442 | 22 | 19 | 2 | 3 | -| b | RegExpNormalChar | test.cpp | 442 | 22 | 442 | 22 | 19 | 2 | 3 | -| a | RegExpConstant | test.cpp | 446 | 21 | 446 | 21 | 20 | 0 | 1 | -| a | RegExpNormalChar | test.cpp | 446 | 21 | 446 | 21 | 20 | 0 | 1 | -| a+ | RegExpPlus | test.cpp | 446 | 21 | 446 | 22 | 20 | 0 | 2 | -| a+b | RegExpSequence | test.cpp | 446 | 21 | 446 | 23 | 20 | 0 | 3 | -| b | RegExpConstant | test.cpp | 446 | 23 | 446 | 23 | 20 | 2 | 3 | -| b | RegExpNormalChar | test.cpp | 446 | 23 | 446 | 23 | 20 | 2 | 3 | -| a | RegExpConstant | test.cpp | 447 | 21 | 447 | 21 | 20 | 0 | 1 | -| a | RegExpNormalChar | test.cpp | 447 | 21 | 447 | 21 | 20 | 0 | 1 | -| a+ | RegExpPlus | test.cpp | 447 | 21 | 447 | 22 | 20 | 0 | 2 | -| a+b | RegExpSequence | test.cpp | 447 | 21 | 447 | 23 | 20 | 0 | 3 | -| b | RegExpConstant | test.cpp | 447 | 23 | 447 | 23 | 20 | 2 | 3 | -| b | RegExpNormalChar | test.cpp | 447 | 23 | 447 | 23 | 20 | 2 | 3 | -| a | RegExpConstant | test.cpp | 459 | 20 | 459 | 20 | 19 | 0 | 1 | -| a | RegExpNormalChar | test.cpp | 459 | 20 | 459 | 20 | 19 | 0 | 1 | -| a+ | RegExpPlus | test.cpp | 459 | 20 | 459 | 21 | 19 | 0 | 2 | -| a+b | RegExpSequence | test.cpp | 459 | 20 | 459 | 22 | 19 | 0 | 3 | -| b | RegExpConstant | test.cpp | 459 | 22 | 459 | 22 | 19 | 2 | 3 | -| b | RegExpNormalChar | test.cpp | 459 | 22 | 459 | 22 | 19 | 2 | 3 | -| a | RegExpConstant | test.cpp | 460 | 20 | 460 | 20 | 19 | 0 | 1 | -| a | RegExpNormalChar | test.cpp | 460 | 20 | 460 | 20 | 19 | 0 | 1 | -| a+ | RegExpPlus | test.cpp | 460 | 20 | 460 | 21 | 19 | 0 | 2 | -| a+b | RegExpSequence | test.cpp | 460 | 20 | 460 | 22 | 19 | 0 | 3 | -| b | RegExpConstant | test.cpp | 460 | 22 | 460 | 22 | 19 | 2 | 3 | -| b | RegExpNormalChar | test.cpp | 460 | 22 | 460 | 22 | 19 | 2 | 3 | +| a | RegExpConstant | test.cpp | 436 | 22 | 436 | 22 | 19 | 0 | 1 | +| a | RegExpNormalChar | test.cpp | 436 | 22 | 436 | 22 | 19 | 0 | 1 | +| a+ | RegExpPlus | test.cpp | 436 | 22 | 436 | 23 | 19 | 0 | 2 | +| a+b | RegExpSequence | test.cpp | 436 | 22 | 436 | 24 | 19 | 0 | 3 | +| b | RegExpConstant | test.cpp | 436 | 24 | 436 | 24 | 19 | 2 | 3 | +| b | RegExpNormalChar | test.cpp | 436 | 24 | 436 | 24 | 19 | 2 | 3 | +| \\s | RegExpCharacterClassEscape | test.cpp | 437 | 22 | 437 | 23 | 19 | 0 | 2 | +| \\s+ | RegExpPlus | test.cpp | 437 | 22 | 437 | 24 | 19 | 0 | 3 | +| \\s+$ | RegExpSequence | test.cpp | 437 | 22 | 437 | 25 | 19 | 0 | 4 | +| $ | RegExpDollar | test.cpp | 437 | 25 | 437 | 25 | 19 | 3 | 4 | +| \\( | RegExpConstant | test.cpp | 438 | 22 | 438 | 23 | 19 | 0 | 2 | +| \\( | RegExpEscape | test.cpp | 438 | 22 | 438 | 23 | 19 | 0 | 2 | +| \\(([,\\w]+)+\\)$ | RegExpSequence | test.cpp | 438 | 22 | 438 | 35 | 19 | 0 | 14 | +| ([,\\w]+) | RegExpGroup | test.cpp | 438 | 24 | 438 | 31 | 19 | 2 | 10 | +| ([,\\w]+)+ | RegExpPlus | test.cpp | 438 | 24 | 438 | 32 | 19 | 2 | 11 | +| [,\\w] | RegExpCharacterClass | test.cpp | 438 | 25 | 438 | 29 | 19 | 3 | 8 | +| [,\\w]+ | RegExpPlus | test.cpp | 438 | 25 | 438 | 30 | 19 | 3 | 9 | +| , | RegExpConstant | test.cpp | 438 | 26 | 438 | 26 | 19 | 4 | 5 | +| , | RegExpNormalChar | test.cpp | 438 | 26 | 438 | 26 | 19 | 4 | 5 | +| \\w | RegExpCharacterClassEscape | test.cpp | 438 | 27 | 438 | 28 | 19 | 5 | 7 | +| \\) | RegExpConstant | test.cpp | 438 | 33 | 438 | 34 | 19 | 11 | 13 | +| \\) | RegExpEscape | test.cpp | 438 | 33 | 438 | 34 | 19 | 11 | 13 | +| $ | RegExpDollar | test.cpp | 438 | 35 | 438 | 35 | 19 | 13 | 14 | +| a | RegExpConstant | test.cpp | 442 | 23 | 442 | 23 | 19 | 0 | 1 | +| a | RegExpNormalChar | test.cpp | 442 | 23 | 442 | 23 | 19 | 0 | 1 | +| a+ | RegExpPlus | test.cpp | 442 | 23 | 442 | 24 | 19 | 0 | 2 | +| a+b | RegExpSequence | test.cpp | 442 | 23 | 442 | 25 | 19 | 0 | 3 | +| b | RegExpConstant | test.cpp | 442 | 25 | 442 | 25 | 19 | 2 | 3 | +| b | RegExpNormalChar | test.cpp | 442 | 25 | 442 | 25 | 19 | 2 | 3 | +| a | RegExpConstant | test.cpp | 446 | 22 | 446 | 22 | 20 | 0 | 1 | +| a | RegExpNormalChar | test.cpp | 446 | 22 | 446 | 22 | 20 | 0 | 1 | +| a+ | RegExpPlus | test.cpp | 446 | 22 | 446 | 23 | 20 | 0 | 2 | +| a+b | RegExpSequence | test.cpp | 446 | 22 | 446 | 24 | 20 | 0 | 3 | +| b | RegExpConstant | test.cpp | 446 | 24 | 446 | 24 | 20 | 2 | 3 | +| b | RegExpNormalChar | test.cpp | 446 | 24 | 446 | 24 | 20 | 2 | 3 | +| a | RegExpConstant | test.cpp | 447 | 24 | 447 | 24 | 20 | 0 | 1 | +| a | RegExpNormalChar | test.cpp | 447 | 24 | 447 | 24 | 20 | 0 | 1 | +| a+ | RegExpPlus | test.cpp | 447 | 24 | 447 | 25 | 20 | 0 | 2 | +| a+b | RegExpSequence | test.cpp | 447 | 24 | 447 | 26 | 20 | 0 | 3 | +| b | RegExpConstant | test.cpp | 447 | 26 | 447 | 26 | 20 | 2 | 3 | +| b | RegExpNormalChar | test.cpp | 447 | 26 | 447 | 26 | 20 | 2 | 3 | +| a | RegExpConstant | test.cpp | 459 | 22 | 459 | 22 | 19 | 0 | 1 | +| a | RegExpNormalChar | test.cpp | 459 | 22 | 459 | 22 | 19 | 0 | 1 | +| a+ | RegExpPlus | test.cpp | 459 | 22 | 459 | 23 | 19 | 0 | 2 | +| a+b | RegExpSequence | test.cpp | 459 | 22 | 459 | 24 | 19 | 0 | 3 | +| b | RegExpConstant | test.cpp | 459 | 24 | 459 | 24 | 19 | 2 | 3 | +| b | RegExpNormalChar | test.cpp | 459 | 24 | 459 | 24 | 19 | 2 | 3 | +| a | RegExpConstant | test.cpp | 460 | 24 | 460 | 24 | 19 | 0 | 1 | +| a | RegExpNormalChar | test.cpp | 460 | 24 | 460 | 24 | 19 | 0 | 1 | +| a+ | RegExpPlus | test.cpp | 460 | 24 | 460 | 25 | 19 | 0 | 2 | +| a+b | RegExpSequence | test.cpp | 460 | 24 | 460 | 26 | 19 | 0 | 3 | +| b | RegExpConstant | test.cpp | 460 | 26 | 460 | 26 | 19 | 2 | 3 | +| b | RegExpNormalChar | test.cpp | 460 | 26 | 460 | 26 | 19 | 2 | 3 | diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index 4eb13ff749c..0317db43c65 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -27,7 +27,7 @@ groupNumber | test.cpp:361:19:361:32 | ([[:alpha:]]+) | 1 | | test.cpp:415:21:415:58 | ([[:alpha:]]+[[:digit:]]*[[:space:]]?) | 1 | | test.cpp:415:60:415:85 | ([[:punct:]]\|[[:xdigit:]]) | 2 | -| test.cpp:438:22:438:29 | ([,\\w]+) | 1 | +| test.cpp:438:24:438:31 | ([,\\w]+) | 1 | term | test.cpp:133:20:133:20 | a | RegExpConstant,RegExpNormalChar | | test.cpp:133:20:133:21 | a* | RegExpStar | @@ -530,44 +530,44 @@ term | test.cpp:430:22:430:22 | b | RegExpConstant,RegExpNormalChar | | test.cpp:431:20:431:21 | \\s | RegExpCharacterClassEscape | | test.cpp:431:20:431:22 | \\s+ | RegExpPlus | -| test.cpp:436:20:436:20 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:436:20:436:21 | a+ | RegExpPlus | -| test.cpp:436:20:436:22 | a+b | RegExpSequence | -| test.cpp:436:22:436:22 | b | RegExpConstant,RegExpNormalChar | -| test.cpp:437:20:437:21 | \\s | RegExpCharacterClassEscape | -| test.cpp:437:20:437:22 | \\s+ | RegExpPlus | -| test.cpp:437:20:437:23 | \\s+$ | RegExpSequence | -| test.cpp:437:23:437:23 | $ | RegExpDollar | -| test.cpp:438:20:438:21 | \\( | RegExpConstant,RegExpEscape | -| test.cpp:438:20:438:33 | \\(([,\\w]+)+\\)$ | RegExpSequence | -| test.cpp:438:22:438:29 | ([,\\w]+) | RegExpGroup | -| test.cpp:438:22:438:30 | ([,\\w]+)+ | RegExpPlus | -| test.cpp:438:23:438:27 | [,\\w] | RegExpCharacterClass | -| test.cpp:438:23:438:28 | [,\\w]+ | RegExpPlus | -| test.cpp:438:24:438:24 | , | RegExpConstant,RegExpNormalChar | -| test.cpp:438:25:438:26 | \\w | RegExpCharacterClassEscape | -| test.cpp:438:31:438:32 | \\) | RegExpConstant,RegExpEscape | -| test.cpp:438:33:438:33 | $ | RegExpDollar | -| test.cpp:442:20:442:20 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:442:20:442:21 | a+ | RegExpPlus | -| test.cpp:442:20:442:22 | a+b | RegExpSequence | -| test.cpp:442:22:442:22 | b | RegExpConstant,RegExpNormalChar | -| test.cpp:446:21:446:21 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:446:21:446:22 | a+ | RegExpPlus | -| test.cpp:446:21:446:23 | a+b | RegExpSequence | -| test.cpp:446:23:446:23 | b | RegExpConstant,RegExpNormalChar | -| test.cpp:447:21:447:21 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:447:21:447:22 | a+ | RegExpPlus | -| test.cpp:447:21:447:23 | a+b | RegExpSequence | -| test.cpp:447:23:447:23 | b | RegExpConstant,RegExpNormalChar | -| test.cpp:459:20:459:20 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:459:20:459:21 | a+ | RegExpPlus | -| test.cpp:459:20:459:22 | a+b | RegExpSequence | -| test.cpp:459:22:459:22 | b | RegExpConstant,RegExpNormalChar | -| test.cpp:460:20:460:20 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:460:20:460:21 | a+ | RegExpPlus | -| test.cpp:460:20:460:22 | a+b | RegExpSequence | -| test.cpp:460:22:460:22 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:436:22:436:22 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:436:22:436:23 | a+ | RegExpPlus | +| test.cpp:436:22:436:24 | a+b | RegExpSequence | +| test.cpp:436:24:436:24 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:437:22:437:23 | \\s | RegExpCharacterClassEscape | +| test.cpp:437:22:437:24 | \\s+ | RegExpPlus | +| test.cpp:437:22:437:25 | \\s+$ | RegExpSequence | +| test.cpp:437:25:437:25 | $ | RegExpDollar | +| test.cpp:438:22:438:23 | \\( | RegExpConstant,RegExpEscape | +| test.cpp:438:22:438:35 | \\(([,\\w]+)+\\)$ | RegExpSequence | +| test.cpp:438:24:438:31 | ([,\\w]+) | RegExpGroup | +| test.cpp:438:24:438:32 | ([,\\w]+)+ | RegExpPlus | +| test.cpp:438:25:438:29 | [,\\w] | RegExpCharacterClass | +| test.cpp:438:25:438:30 | [,\\w]+ | RegExpPlus | +| test.cpp:438:26:438:26 | , | RegExpConstant,RegExpNormalChar | +| test.cpp:438:27:438:28 | \\w | RegExpCharacterClassEscape | +| test.cpp:438:33:438:34 | \\) | RegExpConstant,RegExpEscape | +| test.cpp:438:35:438:35 | $ | RegExpDollar | +| test.cpp:442:23:442:23 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:442:23:442:24 | a+ | RegExpPlus | +| test.cpp:442:23:442:25 | a+b | RegExpSequence | +| test.cpp:442:25:442:25 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:446:22:446:22 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:446:22:446:23 | a+ | RegExpPlus | +| test.cpp:446:22:446:24 | a+b | RegExpSequence | +| test.cpp:446:24:446:24 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:447:24:447:24 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:447:24:447:25 | a+ | RegExpPlus | +| test.cpp:447:24:447:26 | a+b | RegExpSequence | +| test.cpp:447:26:447:26 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:459:22:459:22 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:459:22:459:23 | a+ | RegExpPlus | +| test.cpp:459:22:459:24 | a+b | RegExpSequence | +| test.cpp:459:24:459:24 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:460:24:460:24 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:460:24:460:25 | a+ | RegExpPlus | +| test.cpp:460:24:460:26 | a+b | RegExpSequence | +| test.cpp:460:26:460:26 | b | RegExpConstant,RegExpNormalChar | regExpNormalCharValue | test.cpp:133:20:133:20 | a | a | | test.cpp:133:22:133:22 | b | b | @@ -765,20 +765,20 @@ regExpNormalCharValue | test.cpp:430:20:430:20 | a | a | | test.cpp:430:22:430:22 | b | b | | test.cpp:431:20:431:21 | \\s | s | -| test.cpp:436:20:436:20 | a | a | -| test.cpp:436:22:436:22 | b | b | -| test.cpp:437:20:437:21 | \\s | s | -| test.cpp:438:20:438:21 | \\( | ( | -| test.cpp:438:24:438:24 | , | , | -| test.cpp:438:25:438:26 | \\w | w | -| test.cpp:438:31:438:32 | \\) | ) | -| test.cpp:442:20:442:20 | a | a | -| test.cpp:442:22:442:22 | b | b | -| test.cpp:446:21:446:21 | a | a | -| test.cpp:446:23:446:23 | b | b | -| test.cpp:447:21:447:21 | a | a | -| test.cpp:447:23:447:23 | b | b | -| test.cpp:459:20:459:20 | a | a | -| test.cpp:459:22:459:22 | b | b | -| test.cpp:460:20:460:20 | a | a | -| test.cpp:460:22:460:22 | b | b | +| test.cpp:436:22:436:22 | a | a | +| test.cpp:436:24:436:24 | b | b | +| test.cpp:437:22:437:23 | \\s | s | +| test.cpp:438:22:438:23 | \\( | ( | +| test.cpp:438:26:438:26 | , | , | +| test.cpp:438:27:438:28 | \\w | w | +| test.cpp:438:33:438:34 | \\) | ) | +| test.cpp:442:23:442:23 | a | a | +| test.cpp:442:25:442:25 | b | b | +| test.cpp:446:22:446:22 | a | a | +| test.cpp:446:24:446:24 | b | b | +| test.cpp:447:24:447:24 | a | a | +| test.cpp:447:26:447:26 | b | b | +| test.cpp:459:22:459:22 | a | a | +| test.cpp:459:24:459:24 | b | b | +| test.cpp:460:24:460:24 | a | a | +| test.cpp:460:26:460:26 | b | b |