You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2021/08/17 16:48:50 UTC
[groovy] 03/04: GROOVY-10027: NamedParam: check against
declared/inferred argument type
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 39917b8b3ac89fbe7bf15139ed09058aee780d76
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Apr 9 14:31:37 2021 -0500
GROOVY-10027: NamedParam: check against declared/inferred argument type
---
.../transform/stc/StaticTypeCheckingVisitor.java | 15 ++--
src/test/groovy/NamedParameterTest.groovy | 80 ++++++++++++++++++----
2 files changed, 70 insertions(+), 25 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 41256b3..7be2912 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2783,21 +2783,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
if (!entries.containsKey(name)) {
if (required) {
- addStaticTypeError("required named arg '" + name + "' not found.", expression);
+ addStaticTypeError("required named param '" + name + "' not found.", expression);
}
- } else {
- Expression supplied = entries.get(name);
- if (isCompatibleType(expectedType, expectedType != null, supplied.getType())) {
- addStaticTypeError("parameter for named arg '" + name + "' has type '" + prettyPrintType(supplied.getType()) +
- "' but expected '" + prettyPrintType(expectedType) + "'.", expression);
+ } else if (expectedType != null) {
+ ClassNode argumentType = getDeclaredOrInferredType(entries.get(name));
+ if (!isAssignableTo(argumentType, expectedType)) {
+ addStaticTypeError("argument for named param '" + name + "' has type '" + prettyPrintType(argumentType) + "' but expected '" + prettyPrintType(expectedType) + "'.", expression);
}
}
}
- private boolean isCompatibleType(final ClassNode expectedType, final boolean b, final ClassNode type) {
- return b && !isAssignableTo(type, expectedType);
- }
-
/**
* This method is responsible for performing type inference on closure argument types whenever code like this is
* found: <code>foo.collect { it.toUpperCase() }</code>.
diff --git a/src/test/groovy/NamedParameterTest.groovy b/src/test/groovy/NamedParameterTest.groovy
index 4ff1553..cc0af57 100644
--- a/src/test/groovy/NamedParameterTest.groovy
+++ b/src/test/groovy/NamedParameterTest.groovy
@@ -25,12 +25,12 @@ import groovy.transform.TypeChecked
import static groovy.NamedParameterHelper.myJavaMethod
-class NamedParameterTest extends GroovyTestCase {
+final class NamedParameterTest extends GroovyTestCase {
void testPassingNamedParametersToMethod() {
someMethod(name:"gromit", eating:"nice cheese", times:2)
}
-
+
protected void someMethod(args) {
assert args.name == "gromit"
assert args.eating == "nice cheese"
@@ -70,12 +70,15 @@ class NamedParameterTest extends GroovyTestCase {
import groovy.transform.TypeChecked
import static groovy.NamedParameterTest.myMethod
+ int getAnswer() { 42 }
+
@TypeChecked
- def method() {
- assert myMethod(foo: 'FOO', bar: 'BAR') == 'foo = FOO, bar = BAR'
- assert myMethod(bar: 'BAR') == 'foo = null, bar = BAR'
- assert myMethod(foo: 'FOO', bar: 45, 442) == 'foo = FOO, bar = 45, num = 442'
- assert myMethod(foo: 'FOO', 542) == 'foo = FOO, bar = null, num = 542'
+ def method() {
+ assert myMethod(foo: 'FOO', bar: 'BAR') == 'foo = FOO, bar = BAR'
+ assert myMethod(bar: 'BAR') == 'foo = null, bar = BAR'
+ assert myMethod(foo: 'FOO', bar: 45, 442) == 'foo = FOO, bar = 45, num = 442'
+ assert myMethod(foo: 'FOO', 542) == 'foo = FOO, bar = null, num = 542'
+ assert myMethod(foo: 'string', bar: answer, 666) == 'foo = string, bar = 42, num = 666' // GROOVY-10027
}
method()
'''
@@ -87,12 +90,12 @@ class NamedParameterTest extends GroovyTestCase {
import static groovy.NamedParameterTest.myMethod
@TypeChecked
- def method() {
+ def method() {
myMethod(foo: 'FOO')
}
method()
'''
- assert message.contains("required named arg 'bar' not found")
+ assert message.contains("required named param 'bar' not found")
}
void testUnknownName() {
@@ -101,7 +104,7 @@ class NamedParameterTest extends GroovyTestCase {
import static groovy.NamedParameterTest.myMethod
@TypeChecked
- def method() {
+ def method() {
myMethod(bar: 'BAR', baz: 'BAZ')
}
method()
@@ -109,20 +112,67 @@ class NamedParameterTest extends GroovyTestCase {
assert message.contains("unexpected named arg: baz")
}
- void testInvalidType() {
+ // GROOVY-10027
+ void testFlowType() {
+ assertScript '''
+ import groovy.transform.TypeChecked
+ import static groovy.NamedParameterTest.myMethod
+
+ @TypeChecked
+ def method(arg) {
+ if (arg instanceof Integer) {
+ myMethod(foo: 'x', bar: arg, 123)
+ }
+ }
+ assert method(42) == 'foo = x, bar = 42, num = 123'
+ '''
+ }
+
+ void testInvalidType1() {
def message = shouldFail '''
import groovy.transform.TypeChecked
import static groovy.NamedParameterTest.myMethod
@TypeChecked
- def method() {
- myMethod(foo: 42, 42)
+ def method() {
+ myMethod(foo:42, -1)
+ }
+ '''
+ assert message.contains("argument for named param 'foo' has type 'int' but expected 'java.lang.String'")
+ }
+
+ void testInvalidType2() {
+ def message = shouldFail '''
+ import groovy.transform.TypeChecked
+ import static groovy.NamedParameterTest.myMethod
+
+ @TypeChecked
+ def method() {
+ int answer = 42
+ myMethod(foo:answer, -1)
+ }
+ '''
+ assert message.contains("argument for named param 'foo' has type 'int' but expected 'java.lang.String'")
+ }
+
+ // GROOVY-10027
+ void testInvalidType3() {
+ def message = shouldFail '''
+ import groovy.transform.TypeChecked
+ import static groovy.NamedParameterTest.myMethod
+
+ int getAnswer() { 42 }
+
+ @TypeChecked
+ def method() {
+ myMethod(foo:answer, -1)
}
- method()
'''
- assert message.contains("parameter for named arg 'foo' has type 'int' but expected 'java.lang.String'")
+ assert message.contains("argument for named param 'foo' has type 'int' but expected 'java.lang.String'")
}
+ //--------------------------------------------------------------------------
+
static String myMethod(@NamedParams([
@NamedParam(value = "foo"),
@NamedParam(value = "bar", type = String, required = true)