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 2022/02/26 16:55:30 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-5441: check generics usage for casts, coercions, constructors, array initializers and object initializers
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
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new 49905b5 GROOVY-5441: check generics usage for casts, coercions, constructors, array initializers and object initializers
49905b5 is described below
commit 49905b56ede7cee21010e0d29d6b522a45508caa
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Feb 26 10:48:43 2022 -0600
GROOVY-5441: check generics usage for casts, coercions, constructors,
array initializers and object initializers
3_0_X backport
---
.../groovy/ast/expr/VariableExpression.java | 60 ++++--
.../codehaus/groovy/control/GenericsVisitor.java | 209 ++++++++++-----------
src/test/gls/generics/GenericsUsageTest.groovy | 152 ++++++++++-----
src/test/groovy/bugs/Groovy3731Bug.groovy | 44 -----
4 files changed, 244 insertions(+), 221 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java
index 9043f7a..1dcdd05 100644
--- a/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java
+++ b/src/main/java/org/codehaus/groovy/ast/expr/VariableExpression.java
@@ -50,55 +50,63 @@ public class VariableExpression extends Expression implements Variable {
this.accessedVariable = origin;
}
- public VariableExpression(String variable, ClassNode type) {
- this.variable = variable;
+ public VariableExpression(String name, ClassNode type) {
+ variable = name;
originType = type;
- setType(ClassHelper.getWrapper(type));
+ setType(ClassHelper.isPrimitiveType(type) ? ClassHelper.getWrapper(type) : type);
}
-
+
public VariableExpression(String variable) {
this(variable, ClassHelper.DYNAMIC_TYPE);
}
-
+
public VariableExpression(Variable variable) {
this(variable.getName(), variable.getOriginType());
setAccessedVariable(variable);
setModifiers(variable.getModifiers());
}
+ @Override
public void visit(GroovyCodeVisitor visitor) {
visitor.visitVariableExpression(this);
}
+ @Override
public Expression transformExpression(ExpressionTransformer transformer) {
return this;
}
+ @Override
public String getText() {
return variable;
}
-
+
+ @Override
public String getName() {
return variable;
}
+ @Override
public String toString() {
return super.toString() + "[variable: " + variable + (this.isDynamicTyped() ? "" : " type: " + getType()) + "]";
}
+ @Override
public Expression getInitialExpression() {
return null;
}
+ @Override
public boolean hasInitialExpression() {
return false;
}
-
+
+ @Override
public boolean isInStaticContext() {
- if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isInStaticContext();
+ if (accessedVariable != null && accessedVariable != this) return accessedVariable.isInStaticContext();
return inStaticContext;
}
-
+
public void setInStaticContext(boolean inStaticContext) {
this.inStaticContext = inStaticContext;
}
@@ -108,15 +116,18 @@ public class VariableExpression extends Expression implements Variable {
* the {@link #getAccessedVariable() accessed variable} is ({@link #isClosureSharedVariable() shared},
* this operation is unsafe and may lead to a verify error at compile time. Instead, set the type of
* the {@link #getAccessedVariable() accessed variable}
+ *
* @param cn the type to be set on this variable
*/
- public void setType(ClassNode cn){
+ @Override
+ public void setType(ClassNode cn) {
super.setType(cn);
- isDynamicTyped |= ClassHelper.DYNAMIC_TYPE==cn;
+ isDynamicTyped |= (cn == ClassHelper.DYNAMIC_TYPE);
}
-
+
+ @Override
public boolean isDynamicTyped() {
- if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isDynamicTyped();
+ if (accessedVariable != null && accessedVariable != this) return accessedVariable.isDynamicTyped();
return isDynamicTyped;
}
@@ -127,10 +138,12 @@ public class VariableExpression extends Expression implements Variable {
* def cl = { println str }
* </pre>
* The "str" variable is closure shared.
+ *
* @return true if this variable is used in a closure
*/
+ @Override
public boolean isClosureSharedVariable() {
- if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isClosureSharedVariable();
+ if (accessedVariable != null && accessedVariable != this) return accessedVariable.isClosureSharedVariable();
return closureShare;
}
@@ -141,12 +154,15 @@ public class VariableExpression extends Expression implements Variable {
* </pre>
* The "str" variable is closure shared. The variable expression inside the closure references an
* accessed variable "str" which must have the closure shared flag set.
+ *
* @param inClosure tells if this variable is later referenced in a closure
*/
+ @Override
public void setClosureSharedVariable(boolean inClosure) {
- closureShare = inClosure;
+ closureShare = inClosure;
}
+ @Override
public int getModifiers() {
return modifiers;
}
@@ -154,12 +170,13 @@ public class VariableExpression extends Expression implements Variable {
/**
* For internal use only. This flag is used by compiler internals and should probably
* be converted to a node metadata in future.
+ *
* @param useRef
*/
public void setUseReferenceDirectly(boolean useRef) {
- this.useRef = useRef;
+ this.useRef = useRef;
}
-
+
/**
* For internal use only. This flag is used by compiler internals and should probably
* be converted to a node metadata in future.
@@ -167,19 +184,22 @@ public class VariableExpression extends Expression implements Variable {
public boolean isUseReferenceDirectly() {
return useRef;
}
-
+
+ @Override
public ClassNode getType() {
- if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.getType();
+ if (accessedVariable != null && accessedVariable != this) return accessedVariable.getType();
return super.getType();
}
/**
* Returns the type which was used when this variable expression was created. For example,
* {@link #getType()} may return a boxed type while this method would return the primitive type.
+ *
* @return the type which was used to define this variable expression
*/
+ @Override
public ClassNode getOriginType() {
- if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.getOriginType();
+ if (accessedVariable != null && accessedVariable != this) return accessedVariable.getOriginType();
return originType;
}
diff --git a/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java b/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java
index 95e74cd..07a17a6 100644
--- a/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/GenericsVisitor.java
@@ -25,10 +25,12 @@ import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.transform.trait.Traits;
import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUnboundedWildcard;
@@ -43,142 +45,164 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUnbo
* </ul>
*/
public class GenericsVisitor extends ClassCodeVisitorSupport {
- private final SourceUnit source;
- public GenericsVisitor(SourceUnit source) {
- this.source = source;
- }
+ private final SourceUnit source;
+ @Override
protected SourceUnit getSourceUnit() {
return source;
}
+ public GenericsVisitor(final SourceUnit source) {
+ this.source = source;
+ }
+
+ //--------------------------------------------------------------------------
+
@Override
- public void visitClass(ClassNode node) {
- boolean error = checkWildcard(node);
- if (error) return;
- boolean isAnon = node instanceof InnerClassNode && ((InnerClassNode) node).isAnonymous();
- checkGenericsUsage(node.getUnresolvedSuperClass(false), node.getSuperClass(), isAnon ? true : null);
- ClassNode[] interfaces = node.getInterfaces();
- for (ClassNode anInterface : interfaces) {
- checkGenericsUsage(anInterface, anInterface.redirect());
+ public void visitClass(final ClassNode node) {
+ ClassNode sn = node.getUnresolvedSuperClass(false);
+ if (checkWildcard(sn)) return;
+
+ boolean isAIC = node instanceof InnerClassNode && ((InnerClassNode) node).isAnonymous();
+ checkGenericsUsage(sn, node.getSuperClass(), isAIC ? Boolean.TRUE : null);
+ for (ClassNode face : node.getInterfaces()) {
+ checkGenericsUsage(face);
}
+
+ visitObjectInitializerStatements(node);
node.visitContents(this);
}
@Override
- public void visitField(FieldNode node) {
- ClassNode type = node.getType();
- checkGenericsUsage(type, type.redirect());
+ public void visitField(final FieldNode node) {
+ checkGenericsUsage(node.getType());
+
super.visitField(node);
}
@Override
- public void visitConstructorCallExpression(ConstructorCallExpression call) {
- ClassNode type = call.getType();
- boolean isAnon = type instanceof InnerClassNode && ((InnerClassNode) type).isAnonymous();
- checkGenericsUsage(type, type.redirect(), isAnon);
+ protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
+ for (Parameter p : node.getParameters()) {
+ checkGenericsUsage(p.getType());
+ }
+ if (!isConstructor) {
+ checkGenericsUsage(node.getReturnType());
+ }
+
+ super.visitConstructorOrMethod(node, isConstructor);
}
@Override
- public void visitMethod(MethodNode node) {
- Parameter[] parameters = node.getParameters();
- for (Parameter param : parameters) {
- ClassNode paramType = param.getType();
- checkGenericsUsage(paramType, paramType.redirect());
- }
- ClassNode returnType = node.getReturnType();
- checkGenericsUsage(returnType, returnType.redirect());
- super.visitMethod(node);
+ public void visitConstructorCallExpression(final ConstructorCallExpression expression) {
+ ClassNode type = expression.getType();
+ boolean isAIC = type instanceof InnerClassNode
+ && ((InnerClassNode) type).isAnonymous();
+ checkGenericsUsage(type, type.redirect(), isAIC);
+
+ super.visitConstructorCallExpression(expression);
}
@Override
- public void visitDeclarationExpression(DeclarationExpression expression) {
+ public void visitDeclarationExpression(final DeclarationExpression expression) {
if (expression.isMultipleAssignmentDeclaration()) {
- TupleExpression tExpr = expression.getTupleExpression();
- for (Expression nextExpr : tExpr.getExpressions()) {
- ClassNode declType = nextExpr.getType();
- checkGenericsUsage(declType, declType.redirect());
+ for (Expression e : expression.getTupleExpression()) {
+ checkGenericsUsage(((VariableExpression) e).getOriginType());
}
} else {
- ClassNode declType = expression.getVariableExpression().getType();
- checkGenericsUsage(declType, declType.redirect());
+ checkGenericsUsage(expression.getVariableExpression().getOriginType());
}
+
super.visitDeclarationExpression(expression);
}
- private boolean checkWildcard(ClassNode cn) {
- ClassNode sn = cn.getUnresolvedSuperClass(false);
- if (sn == null) return false;
- GenericsType[] generics = sn.getGenericsTypes();
- if (generics == null) return false;
- boolean error = false;
- for (GenericsType generic : generics) {
- if (generic.isWildcard()) {
- addError("A supertype may not specify a wildcard type", sn);
- error = true;
+ @Override
+ public void visitArrayExpression(final ArrayExpression expression) {
+ checkGenericsUsage(expression.getType());
+
+ super.visitArrayExpression(expression);
+ }
+
+ @Override
+ public void visitCastExpression(final CastExpression expression) {
+ checkGenericsUsage(expression.getType());
+
+ super.visitCastExpression(expression);
+ }
+
+ //--------------------------------------------------------------------------
+
+ private boolean checkWildcard(final ClassNode sn) {
+ boolean wildcard = false;
+ if (sn.getGenericsTypes() != null) {
+ for (GenericsType gt : sn.getGenericsTypes()) {
+ if (gt.isWildcard()) {
+ addError("A supertype may not specify a wildcard type", sn);
+ wildcard = true;
+ }
}
}
- return error;
+ return wildcard;
}
- private void checkGenericsUsage(ClassNode n, ClassNode cn) {
- checkGenericsUsage(n, cn, null);
+ private void checkGenericsUsage(ClassNode cn) {
+ while (cn.isArray())
+ cn = cn.getComponentType();
+ checkGenericsUsage(cn, cn.redirect(), null);
}
- private void checkGenericsUsage(ClassNode n, ClassNode cn, Boolean isAnonInnerClass) {
- if (n.isGenericsPlaceHolder()) return;
- GenericsType[] nTypes = n.getGenericsTypes();
+ private void checkGenericsUsage(final ClassNode cn, final ClassNode rn, final Boolean isAIC) {
+ if (cn.isGenericsPlaceHolder()) return;
GenericsType[] cnTypes = cn.getGenericsTypes();
+ GenericsType[] rnTypes = rn.getGenericsTypes();
// raw type usage is always allowed
- if (nTypes == null) return;
+ if (cnTypes == null) return;
// you can't parameterize a non-generified type
- if (cnTypes == null) {
- String message = "The class " + getPrintName(n) + " (supplied with " + plural("type parameter", nTypes.length) +
- ") refers to the class " + getPrintName(cn) + " which takes no parameters";
- if (nTypes.length == 0) {
+ if (rnTypes == null) {
+ String message = "The class " + cn.toString(false) + " (supplied with " + plural("type parameter", cnTypes.length) +
+ ") refers to the class " + rn.toString(false) + " which takes no parameters";
+ if (cnTypes.length == 0) {
message += " (invalid Diamond <> usage?)";
}
- addError(message, n);
+ addError(message, cn);
return;
}
// parameterize a type by using all of the parameters only
- if (nTypes.length != cnTypes.length) {
- if (Boolean.FALSE.equals(isAnonInnerClass) && nTypes.length == 0) {
+ if (cnTypes.length != rnTypes.length) {
+ if (Boolean.FALSE.equals(isAIC) && cnTypes.length == 0) {
return; // allow Diamond for non-AIC cases from CCE
}
String message;
- if (Boolean.TRUE.equals(isAnonInnerClass) && nTypes.length == 0) {
+ if (Boolean.TRUE.equals(isAIC) && cnTypes.length == 0) {
message = "Cannot use diamond <> with anonymous inner classes";
} else {
- message = "The class " + getPrintName(n) + " (supplied with " + plural("type parameter", nTypes.length) +
- ") refers to the class " + getPrintName(cn) +
- " which takes " + plural("parameter", cnTypes.length);
- if (nTypes.length == 0) {
+ message = "The class " + cn.toString(false) + " (supplied with " + plural("type parameter", cnTypes.length) +
+ ") refers to the class " + rn.toString(false) + " which takes " + plural("parameter", rnTypes.length);
+ if (cnTypes.length == 0) {
message += " (invalid Diamond <> usage?)";
}
}
- addError(message, n);
+ addError(message, cn);
return;
}
- for (int i = 0; i < nTypes.length; i++) {
- ClassNode nType = nTypes[i].getType();
+ for (int i = 0; i < cnTypes.length; i++) {
ClassNode cnType = cnTypes[i].getType();
+ ClassNode rnType = rnTypes[i].getType();
// check nested type parameters
- checkGenericsUsage(nType, nType.redirect());
+ checkGenericsUsage(cnType);
// check bounds: unbounded wildcard (aka "?") is universal substitute
- if (!isUnboundedWildcard(nTypes[i])) {
+ if (!isUnboundedWildcard(cnTypes[i])) {
// check upper bound(s)
- ClassNode[] bounds = cnTypes[i].getUpperBounds();
+ ClassNode[] bounds = rnTypes[i].getUpperBounds();
// first can be class or interface
- boolean valid = nType.isDerivedFrom(cnType) || ((cnType.isInterface() || Traits.isTrait(cnType)) && nType.implementsInterface(cnType));
+ boolean valid = cnType.isDerivedFrom(rnType) || ((rnType.isInterface() || Traits.isTrait(rnType)) && cnType.implementsInterface(rnType));
// subsequent bounds if present can be interfaces
if (valid && bounds != null && bounds.length > 1) {
for (int j = 1; j < bounds.length; j++) {
ClassNode bound = bounds[j];
- if (!nType.implementsInterface(bound)) {
+ if (!cnType.implementsInterface(bound)) {
valid = false;
break;
}
@@ -186,46 +210,13 @@ public class GenericsVisitor extends ClassCodeVisitorSupport {
}
if (!valid) {
- addError("The type " + nTypes[i].getName() + " is not a valid substitute for the bounded parameter <" +
- getPrintName(cnTypes[i]) + ">", nTypes[i]);
+ addError("The type " + cnTypes[i].getName() + " is not a valid substitute for the bounded parameter <" + rnTypes[i] + ">", cnTypes[i]);
}
}
}
}
- private String plural(String orig, int count) {
- return "" + count + " " + (count == 1 ? orig : orig + "s");
- }
-
- private static String getPrintName(GenericsType gt) {
- StringBuilder ret = new StringBuilder(gt.getName());
- ClassNode[] upperBounds = gt.getUpperBounds();
- ClassNode lowerBound = gt.getLowerBound();
- if (upperBounds != null) {
- if (upperBounds.length != 1 || !"java.lang.Object".equals(getPrintName(upperBounds[0]))) {
- ret.append(" extends ");
- for (int i = 0; i < upperBounds.length; i++) {
- ret.append(getPrintName(upperBounds[i]));
- if (i + 1 < upperBounds.length) ret.append(" & ");
- }
- }
- } else if (lowerBound != null) {
- ret.append(" super ").append(getPrintName(lowerBound));
- }
- return ret.toString();
- }
-
- private static String getPrintName(ClassNode cn) {
- StringBuilder ret = new StringBuilder(cn.getName());
- GenericsType[] gts = cn.getGenericsTypes();
- if (gts != null) {
- ret.append("<");
- for (int i = 0; i < gts.length; i++) {
- if (i != 0) ret.append(",");
- ret.append(getPrintName(gts[i]));
- }
- ret.append(">");
- }
- return ret.toString();
+ private static String plural(final String string, final int count) {
+ return "" + count + " " + (count == 1 ? string : string + "s");
}
}
diff --git a/src/test/gls/generics/GenericsUsageTest.groovy b/src/test/gls/generics/GenericsUsageTest.groovy
index 214ab5d..dfef88a 100644
--- a/src/test/gls/generics/GenericsUsageTest.groovy
+++ b/src/test/gls/generics/GenericsUsageTest.groovy
@@ -251,11 +251,12 @@ final class GenericsUsageTest extends CompilableTestSupport {
// GROOVY-3975
void testGenericsForClosureParameters() {
def cl = { List<String> s -> }
- def type = cl.getClass().getMethod("call", List).genericParameterTypes[0]
- assert type.toString().contains("java.util.List<java.lang.String>")
- type = cl.getClass().getMethod("doCall", List).genericParameterTypes[0]
- assert type.toString().contains("java.util.List<java.lang.String>")
+ String type = cl.getClass().getMethod('call', List).genericParameterTypes[0]
+ assert type.contains('java.util.List<java.lang.String>')
+
+ type = cl.getClass().getMethod('doCall', List).genericParameterTypes[0]
+ assert type.contains('java.util.List<java.lang.String>')
}
// GROOVY-4974
@@ -282,79 +283,134 @@ final class GenericsUsageTest extends CompilableTestSupport {
'''
}
- // GROOVY-7865
- void testFriendlyErrorMessageForGenericsArityErrors() {
+ // GROOVY-3731, GROOVY-7865, GROOVY-10033
+ void testFriendlyErrorMessageForGenericsErrors() {
+ // superclass and interfaces
+ shouldFailCompilationWithMessages '''
+ class C extends ArrayList<> { }
+ ''', ["Unexpected input: '<'"]
+ shouldFailCompilationWithMessages '''
+ class C extends ArrayList<? extends Number> { }
+ ''', ['A supertype may not specify a wildcard type']
+ shouldFailCompilationWithMessages '''
+ class C extends ArrayList<String, String> { }
+ ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+ shouldFailCompilationWithMessages '''
+ class C extends HashMap<String> { }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- class MyList extends ArrayList<String, String> {}
+ class C implements Map<> { }
+ ''', ["Unexpected input: '<'"]
+ shouldFailCompilationWithMessages '''
+ class MyMap implements Map<String> { }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ shouldFailCompilationWithMessages '''
+ class C implements List<String, String> { }
''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
- if (CompilerConfiguration.DEFAULT.pluginFactory instanceof AntlrParserPluginFactory) {
- shouldFailCompilationWithMessages '''
- class MyList extends ArrayList<> {}
- ''', ['(supplied with 0 type parameters)', 'which takes 1 parameter', 'invalid Diamond <> usage?']
- } else {
- shouldFailCompilationWithMessages '''
- class MyList extends ArrayList<> {}
- ''', ['Unexpected input: \'<\'']
- }
+ // constructor call
+ assertScript '''
+ List<String> list = new LinkedList<>()
+ '''
+ shouldFailCompilationWithMessage '''
+ new LinkedList<>() { }
+ ''', 'Cannot use diamond <> with anonymous inner classes'
+ shouldFailCompilationWithMessages '''
+ new LinkedList<Integer, String>()
+ ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+ shouldFailCompilationWithMessages '''
+ new LinkedList<String, String>()
+ ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+ shouldFailCompilationWithMessages '''
+ new LinkedList<String, String>() { }
+ ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+ shouldFailCompilationWithMessages '''
+ new Date<Calendar>()
+ ''', ['(supplied with 1 type parameter)', 'which takes no parameters']
+ // constructor declaration
shouldFailCompilationWithMessages '''
- class MyMap extends HashMap<String> {}
+ class C { C(Map<String> m) { } }
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- class MyList implements List<String, String> {}
+ class C { C(Closure<String,Number> c) { } }
''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
- if (CompilerConfiguration.DEFAULT.pluginFactory instanceof AntlrParserPluginFactory) {
- shouldFailCompilationWithMessages '''
- class MyList implements Map<> {}
- ''', ['(supplied with 0 type parameters)', 'which takes 2 parameters', 'invalid Diamond <> usage?']
- } else {
- shouldFailCompilationWithMessages '''
- class MyList implements Map<> {}
- ''', ['Unexpected input: \'<\'']
- }
+ // method declaration
+ shouldFailCompilationWithMessages '''
+ def method(Map<String> map) { }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ shouldFailCompilationWithMessages '''
+ Map<String> method() { }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ shouldFailCompilationWithMessages '''
+ def method(Map<String, Map<String>> map) { }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ shouldFailCompilationWithMessages '''
+ def method(Map<String, Map<String>> map) { }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ // field declaration
+ shouldFailCompilationWithMessages '''
+ class C { Map<String> map }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ shouldFailCompilationWithMessages '''
+ class C { Map<String, Map<String>> map }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ // variable declaration
shouldFailCompilationWithMessages '''
- class MyMap implements Map<String> {}
+ def method() { Map<String> map }
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- List<String> ss = new LinkedList<Integer, String>()
- ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
- shouldFailCompilationWithMessage '''
- List<String> ss = new LinkedList<>(){}
- ''', 'Cannot use diamond <> with anonymous inner classes'
+ def method() { Map<String, Map<String>> map }
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- List<String> ss = new LinkedList<String, String>(){}
- ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+ def (Map<String,String> one, Map<String> two) = [ [:], [:] ]
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- List<String> ss = new LinkedList<String, String>()
- ''', ['supplied with 2 type parameters', 'which takes 1 parameter']
+ Map<String>[][] array = new Map[0][0]
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- def now = new Date<Calendar>()
- ''', ['supplied with 1 type parameter', 'which takes no parameters']
+ Map<String,String>[][] array = new Map<String>[0][]
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- def method(Map<String> map) { map.toString() }
+ class C { { Map<String> m = null } }
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- def method(Map<String, Map<String>> map) { map.toString() }
+ class C { static { Map<String> m = null } }
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- class MyClass { Map<String> map }
+ @groovy.transform.ASTTest(value={
+ Map<String> m = null
+ })
+ class C { }
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+
+ // casting and coercion
shouldFailCompilationWithMessages '''
- class MyClass { Map<String, Map<String>> map }
+ def map = (Map<String>) null
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
shouldFailCompilationWithMessages '''
- def method() { Map<String> map }
+ def map = null as Map<String>
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+ }
+
+ // GROOVY-5441
+ void testCompilationErrorForMismatchedGenericsWithQualifiedTypes() {
shouldFailCompilationWithMessages '''
- def method() { Map<String, Map<String>> map }
+ groovy.lang.Tuple2<Object> tuple
''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
- assertScript '''
- List<String> ss = new LinkedList<>()
- '''
+ shouldFailCompilationWithMessages '''
+ java.util.List<Object,Object> list
+ ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+ shouldFailCompilationWithMessages '''
+ java.util.Map<Object,Object,Object> map
+ ''', ['(supplied with 3 type parameters)', 'which takes 2 parameters']
+ shouldFailCompilationWithMessages '''
+ def (java.util.Map<Object> x, java.util.List<Object,Object> y) = [null,null]
+ ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters',
+ '(supplied with 2 type parameters)', 'which takes 1 parameter']
}
// GROOVY-8990
diff --git a/src/test/groovy/bugs/Groovy3731Bug.groovy b/src/test/groovy/bugs/Groovy3731Bug.groovy
deleted file mode 100644
index 3ba1957..0000000
--- a/src/test/groovy/bugs/Groovy3731Bug.groovy
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package groovy.bugs
-
-import gls.CompilableTestSupport
-
-class Groovy3731Bug extends CompilableTestSupport {
-
- void testWrongGenericsUseInFieldsAndMethods() {
- shouldNotCompile """
- public class G3731A {
- Map<Object> m = [:]
- }
- """
-
- shouldNotCompile """
- public class G3731B {
- void m1(x, Map<Object> y) {}
- }
- """
-
- shouldNotCompile """
- public class G3731C {
- Map<Object> m1(x, y) {null}
- }
- """
- }
-}
\ No newline at end of file