Scott Murphy Heiberg created GROOVY-12697:
---------------------------------------------

             Summary: RegexChecker fails inside the compiler on 
Matcher.group(String) and non-constant group lookups
                 Key: GROOVY-12697
                 URL: https://issues.apache.org/jira/browse/GROOVY-12697
             Project: Groovy
          Issue Type: Bug
    Affects Versions: 6.0.0
            Reporter: Scott Murphy Heiberg


*Environment:* macOS 26.5, JDK 21.0.12 (BellSoft Liberica), Groovy 6.0.0, 
6.0.1-SNAPSHOT (GROOVY_6_0_X at 7c0dcf17dd) and master (d86ea7eb85)

{{groovy.typecheckers.RegexChecker}} fails inside the compiler, with a "General 
error during canonicalization" instead of a diagnostic, as soon as type-checked 
code calls {{Matcher.group(String)}} on a matcher whose regex the checker has 
tracked. The same happens for {{Matcher.group(int)}} with a non-constant index 
and for {{matcher[0][i]}} with a non-constant index.

Reproducer ({{groovy named.groovy}}):

{code:groovy}
import java.util.regex.Matcher
@groovy.transform.TypeChecked(extensions = 'groovy.typecheckers.RegexChecker')
String level(String line) {
    Matcher m = line =~ /(?<level>INFO|WARN|ERROR)/
    m.find() ? m.group('level') : 'none'
}
println level('2026-11-04 WARN disk low')
{code}

Actual output (identical on 6.0.0, 6.0.1-SNAPSHOT and master; the line number 
is from 6.0.0):

{noformat}
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during canonicalization: Cannot get property 'value' on null 
object

java.lang.NullPointerException: Cannot get property 'value' on null object
        at 
org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:99)
        at 
groovy.typecheckers.RegexChecker$2.visitMethodCallExpression(RegexChecker.groovy:290)
        at 
org.codehaus.groovy.ast.expr.MethodCallExpression.visit(MethodCallExpression.java:103)
        ...
        at 
groovy.typecheckers.RegexChecker$_run_closure4.doCall(RegexChecker.groovy:261)
        ...
        at 
org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport.afterVisitMethod(GroovyTypeCheckingExtensionSupport.java:427)
        at 
org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.visitMethod(StaticTypeCheckingVisitor.java:3651)

1 error
{noformat}

Expected: the script compiles and prints {{WARN}}. Without the extension it 
does.

Variants, all with the same {{@TypeChecked(extensions = 
'groovy.typecheckers.RegexChecker')}}:

||variant||6.0.0 / 6.0.1-SNAPSHOT / master||
|{{m.group('level')}}, regex {{/(?<level>INFO\|WARN\|ERROR)/}} via 
{{=~}}|compiler failure (above)|
|{{m.group('level')}}, regex {{/(?<lvl>INFO\|WARN\|ERROR)/}} (no group named 
level)|compiler failure; a diagnostic would be expected|
|{{m.group('level')}}, regex {{/INFO\|WARN\|ERROR/}} (no groups at 
all)|compiler failure; a diagnostic would be expected|
|{{m.group('level')}}, {{Pattern.compile(...).matcher(line)}}|compiler failure|
|{{m.group('level')}}, {{Pattern p = ~/.../; p.matcher(line)}}|compiler failure|
|{{m.group(idx)}} with {{int idx}} a parameter|compiler failure|
|{{m[0][idx]}} with {{int idx}} a parameter|compiler failure|
|{{m.group('level')}} with the regex in a {{String}} parameter (not 
tracked)|compiles, prints WARN|
|{{m.group(1)}}|compiles, prints WARN|
|{{m.group(2)}}|diagnostic "Invalid group count 2 for regex with 1 group" 
(correct)|

Cause: in the {{afterVisitMethod}} visitor, the {{group}} check does 
{{findConstExp(call.arguments.getExpression(0), Integer).value}} and the 
subscript check does {{findConstExp(expression.rightExpression, 
Integer).value}}. {{findConstExp}} returns null when the argument is not an 
{{Integer}} constant (a {{String}} group name, a variable, a parameter, an 
expression), and the {{.value}} dereference raises the NullPointerException 
that the compiler reports as a general error.

Expected behaviour: a non-constant index or name is left to the runtime (no 
report); {{group(String)}} with a constant name that the tracked regex does not 
define is reported as a static type error, in the same way an out-of-range 
constant index already is ("Invalid group count 3 for regex with 2 groups"); 
{{group(String)}} with a name the regex defines compiles; and the compiler 
never fails.

GROOVY-12688 (null-target and null-body guards in FormatStringChecker and 
RegexChecker) does not cover this path; master at d86ea7eb85, which carries it, 
still fails identically. A pull request with regression tests follows.



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

Reply via email to