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/11/17 18:29:40 UTC
[groovy] branch master updated: GROOVY-10845: STC: `enum` constant initialization
This is an automated email from the ASF dual-hosted git repository.
emilles 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 2095b9eb3b GROOVY-10845: STC: `enum` constant initialization
2095b9eb3b is described below
commit 2095b9eb3b73c5bcf27625395c24fa5210890443
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Nov 17 11:37:08 2022 -0600
GROOVY-10845: STC: `enum` constant initialization
---
.../transform/stc/StaticTypeCheckingVisitor.java | 77 ++++++++++++++--------
.../stc/ArraysAndCollectionsSTCTest.groovy | 2 +-
.../groovy/transform/stc/CoercionSTCTest.groovy | 7 +-
.../transform/stc/ConstructorsSTCTest.groovy | 12 ++--
.../groovy/transform/stc/MethodCallsSTCTest.groovy | 6 +-
src/test/groovy/transform/stc/MiscSTCTest.groovy | 54 ++++++++++++++-
6 files changed, 118 insertions(+), 40 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 44e92b6ce5..0afd5ff3ec 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1430,7 +1430,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* @param node the class node for which we will try to find a matching constructor
* @param arguments the constructor arguments
*/
- protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode source) {
+ protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode origin) {
if (isObjectType(node) || isDynamicTyped(node)) {
// in that case, we are facing a list constructor assigned to a def or object
return null;
@@ -1445,11 +1445,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
// there will be a default hash map constructor added later
return new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{new Parameter(LinkedHashMap_TYPE, "args")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
} else {
- addStaticTypeError("No matching constructor found: " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
+ addNoMatchingMethodError(node, "<init>", arguments, origin);
return null;
}
} else if (constructors.size() > 1) {
- addStaticTypeError("Ambiguous constructor call " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
+ addStaticTypeError("Ambiguous constructor call " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), origin);
return null;
}
return constructors.get(0);
@@ -1945,12 +1945,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- private void visitInitialExpression(final Expression value, final Expression target, final ASTNode position) {
+ private void visitInitialExpression(final Expression value, final Expression target, final ASTNode origin) {
if (value != null) {
ClassNode lType = target.getType();
applyTargetType(lType, value); // GROOVY-9977
- typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, position));
+ typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, origin));
value.visit(this);
ClassNode rType = getType(value);
@@ -2720,43 +2720,54 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
addStaticTypeError("cannot resolve dynamic method name at compile time.", call);
return;
}
+ ClassNode type = call.getOwnerType();
+ if (type.isEnum() && name.equals("$INIT")) { // GROOVY-10845: $INIT(Object[]) delegates to constructor
+ Expression target = typeCheckingContext.getEnclosingBinaryExpression().getLeftExpression();
+ ConstructorCallExpression cce = new ConstructorCallExpression(type, call.getArguments());
+ cce.setSourcePosition(((FieldExpression) target).getField());
+ visitConstructorCallExpression(cce);
+
+ MethodNode init = type.getDeclaredMethods("$INIT").get(0);
+ call.putNodeMetaData(INFERRED_TYPE, init.getReturnType());
+ call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, init);
+ return;
+ }
if (extension.beforeMethodCall(call)) {
extension.afterMethodCall(call);
return;
}
try {
- ClassNode receiver = call.getOwnerType();
ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(call.getArguments());
boolean closuresVisited = false; // visit *after* method has been chosen
- visitMethodCallArguments(receiver, argumentList, closuresVisited, null);
+ visitMethodCallArguments(type, argumentList, closuresVisited, null);
ClassNode[] args = getArgumentTypes(argumentList);
- List<MethodNode> mn = findMethod(receiver, name, args);
+ List<MethodNode> mn = findMethod(type, name, args);
if (!mn.isEmpty()) {
if (mn.size() == 1) {
// GROOVY-8909, GROOVY-8961, GROOVY-9734, GROOVY-9844, GROOVY-9915, et al.
resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0).getParameters());
- typeCheckMethodsWithGenericsOrFail(receiver, args, mn.get(0), call);
+ typeCheckMethodsWithGenericsOrFail(type, args, mn.get(0), call);
}
}
if (mn.isEmpty()) {
- mn = extension.handleMissingMethod(receiver, name, argumentList, args, call);
+ mn = extension.handleMissingMethod(type, name, argumentList, args, call);
}
if (mn.isEmpty()) {
- addNoMatchingMethodError(receiver, name, args, call);
+ addNoMatchingMethodError(type, name, args, call);
} else {
- mn = disambiguateMethods(mn, receiver, args, call);
+ mn = disambiguateMethods(mn, type, args, call);
if (mn.size() != 1) {
addAmbiguousErrorMessage(mn, name, args, call);
} else {
MethodNode directMethodCallCandidate = mn.get(0);
ClassNode returnType = getType(directMethodCallCandidate);
if (returnType.isUsingGenerics() && !returnType.isEnum()) {
- closuresVisited = true; // now visit closure/lambda arguments with selected method
- visitMethodCallArguments(receiver, argumentList, true, directMethodCallCandidate);
+ closuresVisited = true; // visit closure/lambda arguments with selected method
+ visitMethodCallArguments(type, argumentList, true, directMethodCallCandidate);
- ClassNode rt = inferReturnTypeGenerics(receiver, directMethodCallCandidate, argumentList);
+ ClassNode rt = inferReturnTypeGenerics(type, directMethodCallCandidate, argumentList);
if (rt != null && implementsInterfaceOrIsSubclassOf(rt, returnType))
returnType = rt;
}
@@ -2767,7 +2778,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (!closuresVisited) {
- visitMethodCallArguments(receiver, argumentList, true, target);
+ visitMethodCallArguments(type, argumentList, true, target);
}
if (target != null) { Parameter[] params = target.getParameters();
checkClosureMetadata(argumentList.getExpressions(), params);
@@ -5898,29 +5909,41 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
@Override
- public void addError(final String msg, final ASTNode expr) {
- Long err = ((long) expr.getLineNumber()) << 16 + expr.getColumnNumber();
- if ((DEBUG_GENERATED_CODE && expr.getLineNumber() < 0) || !typeCheckingContext.reportedErrors.contains(err)) {
- typeCheckingContext.getErrorCollector().addErrorAndContinue(msg + '\n', expr, getSourceUnit());
+ public void addError(final String msg, final ASTNode node) {
+ Long err = ((long) node.getLineNumber()) << 16 + node.getColumnNumber();
+ if ((DEBUG_GENERATED_CODE && node.getLineNumber() < 0) || !typeCheckingContext.reportedErrors.contains(err)) {
+ typeCheckingContext.getErrorCollector().addErrorAndContinue(msg + '\n', node, getSourceUnit());
typeCheckingContext.reportedErrors.add(err);
}
}
- protected void addStaticTypeError(final String msg, final ASTNode expr) {
- if (expr.getColumnNumber() > 0 && expr.getLineNumber() > 0) {
- addError(StaticTypesTransformation.STATIC_ERROR_PREFIX + msg, expr);
+ protected void addStaticTypeError(final String msg, final ASTNode node) {
+ if (node.getColumnNumber() > 0 && node.getLineNumber() > 0) {
+ addError(StaticTypesTransformation.STATIC_ERROR_PREFIX + msg, node);
} else {
if (DEBUG_GENERATED_CODE) {
- addError(StaticTypesTransformation.STATIC_ERROR_PREFIX + "Error in generated code [" + expr.getText() + "] - " + msg, expr);
+ addError(StaticTypesTransformation.STATIC_ERROR_PREFIX + "Error in generated code [" + node.getText() + "] - " + msg, node);
}
// ignore errors which are related to unknown source locations
// because they are likely related to generated code
}
}
- protected void addNoMatchingMethodError(final ClassNode receiver, final String name, final ClassNode[] args, final Expression call) {
- ClassNode type = isClassClassNodeWrappingConcreteType(receiver) ? receiver.getGenericsTypes()[0].getType() : receiver;
- addStaticTypeError("Cannot find matching method " + prettyPrintTypeName(type) + "#" + toMethodParametersString(name, args) + ". Please check if the declared type is correct and if the method exists.", call);
+ protected void addNoMatchingMethodError(final ClassNode receiver, final String name, ClassNode[] args, final Expression exp) {
+ addNoMatchingMethodError(receiver, name, args, (ASTNode)exp);
+ }
+
+ protected void addNoMatchingMethodError(final ClassNode receiver, final String name, ClassNode[] args, final ASTNode origin) {
+ String error;
+ if ("<init>".equals(name)) {
+ // remove implicit agruments [String, int] from enum constant construction
+ if (receiver.isEnum() && args.length >= 2) args = Arrays.copyOfRange(args, 2, args.length);
+ error = "Cannot find matching constructor " + prettyPrintTypeName(receiver) + toMethodParametersString("", args);
+ } else {
+ ClassNode type = isClassClassNodeWrappingConcreteType(receiver) ? receiver.getGenericsTypes()[0].getType() : receiver;
+ error = "Cannot find matching method " + prettyPrintTypeName(type) + "#" + toMethodParametersString(name, args) + ". Please check if the declared type is correct and if the method exists.";
+ }
+ addStaticTypeError(error, origin);
}
protected void addAmbiguousErrorMessage(final List<MethodNode> foundMethods, final String name, final ClassNode[] args, final Expression expr) {
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 12319efa81..ea66af7699 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -1085,6 +1085,6 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
interface MVM<K, V> extends Map<K, List<V>> { }
MVM map = [:] // no STC error; fails at runtime
''',
- 'No matching constructor found'
+ 'Cannot find matching constructor MVM(java.util.LinkedHashMap)'
}
}
diff --git a/src/test/groovy/transform/stc/CoercionSTCTest.groovy b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
index 35f6a1be2c..7e9c0ac8d2 100644
--- a/src/test/groovy/transform/stc/CoercionSTCTest.groovy
+++ b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
@@ -30,8 +30,7 @@ class CoercionSTCTest extends StaticTypeCheckingTestCase {
} catch (Throwable t) {
def newTrace = []
def clean = newTrace.toArray(newTrace as StackTraceElement[])
- // doing twice, because bug showed that the more you call the array coercion, the more the error
- // gets stupid :
+ // doing twice, because bug showed that the more you call the array coercion, the more the error gets stupid:
// Cannot call java.util.List#toArray([Ljava.lang.Object;) with arguments [[Ljava.lang.StackTraceElement; -> [Ljava.lang.StackTraceElement;]
// Cannot call java.util.List#toArray([[Ljava.lang.Object;) with arguments [[Ljava.lang.StackTraceElement; -> [Ljava.lang.StackTraceElement;]
// Cannot call java.util.List#toArray([[[Ljava.lang.Object;) with arguments [[Ljava.lang.StackTraceElement; -> [Ljava.lang.StackTraceElement;]
@@ -103,10 +102,10 @@ class CoercionSTCTest extends StaticTypeCheckingTestCase {
'''
shouldFailWithMessages '''
Class c = []
- ''', 'No matching constructor found: java.lang.Class()'
+ ''', 'Cannot find matching constructor java.lang.Class()'
shouldFailWithMessages '''
Class c = [:]
- ''', 'No matching constructor found: java.lang.Class(java.util.LinkedHashMap)'
+ ''', 'Cannot find matching constructor java.lang.Class(java.util.LinkedHashMap)'
}
// GROOVY-6803
diff --git a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
index 353b0cc4b2..f7566fa789 100644
--- a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
@@ -39,7 +39,8 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
import java.awt.Dimension
Dimension d = [100]
- ''', 'No matching constructor found: java.awt.Dimension(int)'
+ ''',
+ 'Cannot find matching constructor java.awt.Dimension(int)'
}
void testWrongNumberOfArgumentsWithDefaultConstructor() {
@@ -49,7 +50,8 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
new X("f")
}
println foo()
- ''', 'Cannot find matching method X#<init>(java.lang.String)'
+ ''',
+ 'Cannot find matching constructor X(java.lang.String)'
}
void testCreateArrayWithDefaultConstructor() {
@@ -64,7 +66,8 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
import java.awt.Dimension
Dimension d = ['100','200']
- ''', 'No matching constructor found: java.awt.Dimension(java.lang.String, java.lang.String)'
+ ''',
+ 'Cannot find matching constructor java.awt.Dimension(java.lang.String, java.lang.String)'
}
void testConstructFromListAndVariables() {
@@ -93,7 +96,8 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
import java.awt.Dimension
List args = [100,200]
Dimension d = args // not supported
- ''', 'Cannot assign value of type java.util.ArrayList<java.lang.Integer> to variable of type java.awt.Dimension'
+ ''',
+ 'Cannot assign value of type java.util.ArrayList<java.lang.Integer> to variable of type java.awt.Dimension'
}
void testConstructFromMap() {
diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
index c594abeaea..7172bb6c17 100644
--- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
@@ -389,7 +389,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
def foo() { new Main() }
}
''',
- 'Cannot find matching method Main#<init>()'
+ 'Cannot find matching constructor Main()'
}
// GROOVY-8509
@@ -1257,8 +1257,8 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
}
new C(*['A','B'])
''',
- 'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
- 'Cannot find matching method '
+ 'Cannot find matching constructor C(',
+ 'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time'
}
void testSpreadArgsRestrictedInClosureCall() {
diff --git a/src/test/groovy/transform/stc/MiscSTCTest.groovy b/src/test/groovy/transform/stc/MiscSTCTest.groovy
index 9bde8b8185..fe77518b9f 100644
--- a/src/test/groovy/transform/stc/MiscSTCTest.groovy
+++ b/src/test/groovy/transform/stc/MiscSTCTest.groovy
@@ -205,11 +205,63 @@ class MiscSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10845
+ void testEnumConstructorChecks() {
+ shouldFailWithMessages '''
+ enum E {
+ CONST()
+ E(String s) { }
+ }
+ ''',
+ 'Cannot find matching constructor E()'
+
+ shouldFailWithMessages '''
+ enum E {
+ CONST(new Object())
+ E(String s) { }
+ }
+ ''',
+ 'Cannot find matching constructor E(java.lang.Object)'
+
+ shouldFailWithMessages '''
+ enum E {
+ CONST(new Object())
+ }
+ ''',
+ 'Cannot find matching constructor E(java.lang.Object)'
+
+ assertScript '''
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ def init = node.getDeclaredMethod("<clinit>").code.statements[0] // this.CONST = $INIT("CONST",0,"xx")
+ def dmct = init.expression.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
+ assert dmct != null
+ def type = init.expression.rightExpression.getNodeMetaData(INFERRED_TYPE)
+ assert type == node
+ })
+ enum E {
+ CONST('xx')
+ E(String s) { assert s == 'xx' }
+ }
+ E.CONST
+ '''
+
+ assertScript '''import groovy.transform.stc.SimpleType
+ enum E {
+ CONST({ assert it.toLowerCase() == 'const' })
+ E(@ClosureParams(value=SimpleType, options='java.lang.String') Closure c) {
+ c.call(name())
+ }
+ }
+ E.CONST
+ '''
+ }
+
void testMethodReturnTypeInferenceShouldNotWorkBecauseNotSameSourceUnit() {
shouldFailWithMessages '''
import groovy.transform.stc.MiscSTCTest.MiscSTCTestSupport as A
A.foo().toInteger()
- ''', 'Cannot find matching method java.lang.Object#toInteger()'
+ ''',
+ 'Cannot find matching method java.lang.Object#toInteger()'
}
void testClassLiteralAsArgument() {