Paul King created GROOVY-12707:
----------------------------------

             Summary: RegexChecker misreports named groups for regexes in 
COMMENTS mode
                 Key: GROOVY-12707
                 URL: https://issues.apache.org/jira/browse/GROOVY-12707
             Project: Groovy
          Issue Type: Bug
            Reporter: Paul King


h2. Problem

GROOVY-12697 added a compile-time check that {{Matcher.group(String)}} names a 
group the regex defines. The checker finds the names by scanning the regex text 
for {{(?<name>}} openers. That scan doesn't follow the JDK's parsing rules, 
which goes wrong both ways:

* *Valid code is rejected.* In COMMENTS mode ({{(?x)}}) the JDK allows 
whitespace between {{(}} and {{?}}, and allows a {{#}} comment inside a group 
name. The scan misses those names and reports a correctly named group as 
missing.
* *Errors are missed.* Text that only looks like a group opener, such as 
{{(?<a>)}} inside {{\Q...\E}}, is counted as a group name, so a lookup of that 
name is not reported and fails at runtime instead.

Reproducer, with {{@TypeChecked(extensions = 
'groovy.typecheckers.RegexChecker')}}:

{code:groovy}
Matcher m = 'foobar' =~ /(?x)( ?<first>...)/
assert m.find()
assert m.group('first') == 'foo'
{code}

Actual:
{noformat}
[Static type checking] - Invalid group name 'first' for regex with no named 
groups
{noformat}

Expected: the script compiles and the assertions pass, as they do without the 
extension.

||Regex||Lookup||Actual||Expected||
|{{/(?x)( ?<first>...)/}}|{{group('first')}}|"Invalid group name 'first' for 
regex with no named groups"|compiles|
|{{"(?x)(?<fi#c\nrst>...)"}}|{{group('first')}}|"Invalid group name 'first' for 
regex with no named groups"|compiles|
|{{/\Q(?<a>)\E(?<b>...)/}}|{{group('a')}}|compiles, then 
{{IllegalArgumentException: No group with name <a>}} at runtime|"Invalid group 
name 'a' for regex with named group b"|

GROOVY-12697 is on master but not yet released, so no release is affected. If 
GROOVY-12697 is backported to GROOVY_6_0_X, this fix should go with it.

h2. Proposed fix

Ask the JDK whether the regex defines the name, rather than scanning the text. 
A named backreference to an undefined group is a compile error ("named 
capturing group <x> does not exist"), so compiling the regex with {{"\n\\k<" + 
name + ">"}} added to the end answers the question exactly. The {{\n}} ends a 
trailing COMMENTS-mode comment. This works on JDK 17, so 
{{Pattern.namedGroups()}} (JDK 20) is not needed.

Checked on JDK 17 against 15 regexes, with exact results for each:
* names after whitespace or comments in COMMENTS mode ({{( ?<first>}}, 
{{(?<fi#c\nrst>}});
* a trailing {{# comment}};
* openers inside {{\Q...\E}}, inside a character class, or after an escaped 
{{\(}}, which correctly don't count;
* scoped {{(?x:...)}} and {{(?-x)}};
* the COMMENTS flag passed to {{Pattern.compile}}.

Two cases can only report a name as defined, never as missing. Both only miss 
an error and never reject valid code:
* an unterminated {{\Q}} quotes the added backreference;
* with {{Pattern.LITERAL}} the added text is literal. A LITERAL regex has no 
groups at all.

The error message keeps listing the defined names ("for regex with named groups 
first, second"). The scan stays only to find the candidate names for that list, 
and each one is confirmed with the same JDK test before it is listed, so names 
from quoted text or character classes no longer appear in the message.

{{Matcher.start(String)}} and {{Matcher.end(String)}} take a group name in the 
same way and should get the same check.

h2. Tests

* Each row of the table above.
* The existing named-group tests from GROOVY-12697 keep passing, including 
whitespace inside the angle brackets in COMMENTS mode.
* The message lists only the names the regex defines; for example, a regex with 
{{(?<a>)}} inside {{\Q...\E}} lists only the real groups.
* {{start('name')}} and {{end('name')}}: a defined name compiles, and an 
undefined one is reported.
* The unterminated {{\Q}} and {{Pattern.LITERAL}} cases compile, and are left 
to the runtime.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to