You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/06/10 13:39:08 UTC
(groovy) branch master updated: GROOVY-12081: RegexChecker doesn't support String.matches(Pattern|String)
This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 31c92ab18b GROOVY-12081: RegexChecker doesn't support String.matches(Pattern|String)
31c92ab18b is described below
commit 31c92ab18b1e34738eea595649f2e951ac3094bb
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Jun 10 22:54:21 2026 +1000
GROOVY-12081: RegexChecker doesn't support String.matches(Pattern|String)
---
.../groovy/groovy/typecheckers/RegexChecker.groovy | 9 ++++++
.../groovy/typecheckers/RegexCheckerTest.groovy | 36 ++++++++++++++++++++++
2 files changed, 45 insertions(+)
diff --git a/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/RegexChecker.groovy b/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/RegexChecker.groovy
index f105b6af0c..d59168c7cb 100644
--- a/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/RegexChecker.groovy
+++ b/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/RegexChecker.groovy
@@ -148,6 +148,9 @@ class RegexChecker extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
super.visitMethodCallExpression(call)
if (call.objectExpression instanceof ClassExpression) {
checkPatternMethod(call, call.objectExpression.type)
+ } else if (isString(call.receiver) && call.methodAsString == 'matches' && call.arguments.expressions) {
+ def exp = findConstExp(call.arguments.getExpression(0), String)
+ checkRegex(exp, call)
} else if (isPattern(call.receiver) && call.methodAsString == 'matcher') {
def source = call.receiver instanceof VariableExpression ? findTargetVariable(call.receiver) : call.receiver
def groupCount = source?.getNodeMetaData(REGEX_GROUP_COUNT)
@@ -277,6 +280,12 @@ class RegexChecker extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
|| exp.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE) == MATCHER_TYPE
}
+ private boolean isString(Expression exp) {
+ exp.type == STRING_TYPE
+ || exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) == STRING_TYPE
+ || exp.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE) == STRING_TYPE
+ }
+
private boolean isPattern(Expression exp) {
exp.type == PATTERN_TYPE
|| exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) == PATTERN_TYPE
diff --git a/subprojects/groovy-typecheckers/src/test/groovy/groovy/typecheckers/RegexCheckerTest.groovy b/subprojects/groovy-typecheckers/src/test/groovy/groovy/typecheckers/RegexCheckerTest.groovy
index a28a8dda07..119e2f0994 100644
--- a/subprojects/groovy-typecheckers/src/test/groovy/groovy/typecheckers/RegexCheckerTest.groovy
+++ b/subprojects/groovy-typecheckers/src/test/groovy/groovy/typecheckers/RegexCheckerTest.groovy
@@ -241,6 +241,42 @@ final class RegexCheckerTest {
assertScript shell, "assert Pattern.matches(/\\d{4}-\\d{1,2}-\\d{1,2}/, '2020-12-31')"
}
+ @Test
+ void testBadRegexJavaStringMatches() {
+ // GROOVY-12081: the regex argument of String#matches is now checked
+ def err = shouldFail shell, '''
+ assert 'JOHN'.matches('[A-Z+') // unclosed character class
+ '''
+ assert err.message ==~ /(?s).*Bad regex.*: Unclosed character class.*/
+
+ // String-typed receiver via parameter
+ err = shouldFail shell, '''
+ boolean isUpper(String s) { s.matches('[A-Z+') }
+ '''
+ assert err.message ==~ /(?s).*Bad regex.*: Unclosed character class.*/
+
+ // regex supplied via a local constant variable
+ err = shouldFail shell, '''
+ def re = '[A-Z+'
+ assert 'JOHN'.matches(re)
+ '''
+ assert err.message ==~ /(?s).*Bad regex.*: Unclosed character class.*/
+ }
+
+ @Test
+ void testGoodRegexJavaStringMatches() {
+ assertScript shell, '''
+ boolean isUpper(String s) { s.matches('[A-Z]+') }
+ assert isUpper('JOHN')
+ assert !isUpper('John')
+ '''
+ // a matches(String) on a non-String receiver must not be treated as a regex
+ assertScript shell, '''
+ class Foo { boolean matches(String s) { true } }
+ assert new Foo().matches('[A-Z+')
+ '''
+ }
+
@Test
void testBadRegexGroupCount() {
def shouldFailWithGroupCountError = { String script ->