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 ed63edb33f GROOVY-11823: replace inner class propertyMissing with
metaclass impl
ed63edb33f is described below
commit ed63edb33fa81b660493e8924487a05ce0735a65
Author: Eric Milles <[email protected]>
AuthorDate: Tue Feb 3 12:06:39 2026 -0600
GROOVY-11823: replace inner class propertyMissing with metaclass impl
GROOVY-4737, GROOVY-4862, GROOVY-5875, GROOVY-6580, GROOVY-6581,
GROOVY-7024, GROOVY-8050, GROOVY-11198
---
src/main/java/groovy/lang/MetaClassImpl.java | 153 +++++++++-
.../java/org/codehaus/groovy/ast/ModuleNode.java | 12 +
.../groovy/classgen/EnumCompletionVisitor.java | 89 ------
.../classgen/InnerClassCompletionVisitor.java | 271 +----------------
.../groovy/classgen/InnerClassVisitorHelper.java | 2 +-
.../stc/GroovyTypeCheckingExtensionSupport.java | 31 +-
src/test/groovy/bugs/Groovy7938Bug.groovy | 70 -----
src/test/groovy/gls/enums/EnumTest.groovy | 17 ++
.../groovy/gls/innerClass/InnerClassTest.groovy | 325 ++++++++++++++++-----
src/test/groovy/groovy/ClosureTest.groovy | 285 +++++++-----------
src/test/groovy/groovy/PropertyTest.groovy | 18 --
.../stc/AnonymousInnerClassSTCTest.groovy | 19 +-
.../packageScope/PackageScopeTransformTest.groovy | 6 +-
.../groovy/nio/extensions/NioExtensionsTest.groovy | 10 +-
.../groovy/typecheckers/FormatStringChecker.groovy | 16 +-
.../groovy/groovy/typecheckers/RegexChecker.groovy | 55 ++--
.../groovy/typecheckers/CheckingVisitor.groovy | 23 +-
17 files changed, 622 insertions(+), 780 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java
b/src/main/java/groovy/lang/MetaClassImpl.java
index 066ee504b6..ffeb8069c0 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -380,7 +380,7 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
inheritInterfaceNewMetaMethods(interfaces);
- if (isGroovyObject) {
+ if (isGroovyObject()) {
metaMethodIndex.copyMethodsToSuper(); // methods -->
methodsForSuper
connectMultimethods(superClasses, firstGroovySuper);
removeMultimethodsOverloadedWithPrivateMethods();
@@ -662,7 +662,7 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
if (firstGroovy == null) {
- return isGroovyObject ? theCachedClass.getCachedSuperClass() :
theCachedClass;
+ return isGroovyObject() ? theCachedClass.getCachedSuperClass() :
theCachedClass;
}
// Closure for closures and GroovyObjectSupport for extenders
(including Closure)
if (firstGroovy.getTheClass() == GroovyObjectSupport.class) {
@@ -1217,7 +1217,59 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
return transformedMetaMethod.doMethodInvoke(object, arguments);
}
- return invokePropertyOrMissing(sender, object, methodName,
originalArguments, fromInsideClass, isCallToSuper);
+ try {
+ return invokePropertyOrMissing(sender, object, methodName,
originalArguments, fromInsideClass, isCallToSuper);
+ } catch (MissingMethodException mme) {
+ if (!isCallToSuper) {
+ return invokeOuterMethod(sender, object, methodName,
originalArguments, mme); // GROOVY-11823
+ }
+ throw mme;
+ }
+ }
+
+ private Object invokeOuterMethod(final Class<?> sender, final Object
object, final String methodName, final Object[] arguments, final
MissingMethodException mme) {
+ if (sender == theClass ? isGroovyObject() :
GroovyObject.class.isAssignableFrom(sender)) {
+ var outerClass = getNonClosureOuter(sender); // check outer class
nesting of call site
+ if (outerClass != null && (sender == theClass ||
sender.isAssignableFrom(theClass))) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ Object target = getOuterReference(sender, object);
+ try {
+ return omc.invokeMethod(outerClass, target, methodName,
arguments, false, false);
+ } catch (MissingMethodException e) {
+ mme.addSuppressed(e);
+ }
+ }
+ }
+ throw mme;
+ }
+
+ private Object getOuterReference(final Class<?> innerClass, final Object
object) {
+ Object outer = null;
+ // non-static inner class may have outer class reference available in
this$0 field
+ if (!(object instanceof Class) && (innerClass.getModifiers() &
Opcodes.ACC_STATIC) == 0) {
+ try {
+ innerClass.getDeclaredField("this$0");
+ outer = getAttribute(object,"this$0");
+ if (outer instanceof GeneratedClosure) {
+ outer = ((Closure<?>) outer).getThisObject(); // skip
closure(s)
+ }
+ } catch (NoSuchFieldException ignored) {
+ // an AIC is non-static but may not have this$0
+ } catch (Throwable t) {
+ throw new GroovyRuntimeException(t);
+ }
+ }
+ if (outer == null) {
+ outer = getNonClosureOuter(innerClass);
+ }
+ return outer;
+ }
+
+ private static Class<?> getNonClosureOuter(Class<?> c) {
+ do { c = c.getEnclosingClass();
+ } while (c != null && GeneratedClosure.class.isAssignableFrom(c));
+
+ return c;
}
private MetaMethod getMetaMethod(final Class<?> sender, final Object
object, final String methodName, final boolean isCallToSuper, final Object[]
arguments) {
@@ -1487,7 +1539,22 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
}
- return invokeStaticMissingMethod(theClass, methodName,
nonNullArguments);
+ try {
+ return invokeStaticMissingMethod(theClass, methodName,
nonNullArguments);
+ } catch (MissingMethodException missing) {
+ if (isGroovyObject()) { // GROOVY-11823, et al.
+ var outerClass = getNonClosureOuter(theClass);
+ if (outerClass != null && object instanceof Class) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ try {
+ return omc.invokeStaticMethod(outerClass, methodName,
arguments);
+ } catch (MissingMethodException mme) {
+ missing.addSuppressed(mme);
+ }
+ }
+ }
+ throw missing;
+ }
}
private Object invokeStaticMissingMethod(final Class<?> sender, final
String methodName, final Object[] arguments) {
@@ -1495,7 +1562,7 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
if (metaMethod != null) {
return metaMethod.invoke(sender, new Object[]{methodName,
arguments});
}
- throw new MissingMethodException(methodName, sender, arguments, true);
+ throw new MissingMethodExceptionNoStack(methodName, sender, arguments,
true);
}
private MetaMethod pickStaticMethod(final String methodName, final
Class<?>[] argumentTypes) throws MethodSelectionException {
@@ -1876,19 +1943,55 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
//----------------------------------------------------------------------
// missing property protocol
//----------------------------------------------------------------------
- return invokeMissingProperty(object, name, null, true);
+ try {
+ return invokeMissingProperty(object, name, null, true);
+ } catch (MissingPropertyException mpe) {
+ return getOuterProperty(sender, object, name, mpe);
+ }
}
private Object getClassProperty(final Class<?> sender, final Class<?>
receiver, final String name) throws MissingPropertyException {
try {
MetaClass cmc = registry.getMetaClass(Class.class);
return cmc.getProperty(Class.class, receiver, name, false, false);
- } catch (MissingPropertyException ignore) {
+ } catch (MissingPropertyException ignored) {
+ }
+
+ try {
// try $static_propertyMissing / throw MissingPropertyException
return invokeStaticMissingProperty(receiver, name, null, true);
+ } catch (MissingPropertyException missing) {
+ if (isGroovyObject()) { // GROOVY-11823, et al.
+ var outerClass = getNonClosureOuter(theClass);
+ if (outerClass != null && sender.isNestmateOf(outerClass)) {
+ try {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ return omc.getProperty(sender, outerClass, name,
false, false);
+ } catch (MissingPropertyException mpe) {
+ missing.addSuppressed(mpe);
+ }
+ }
+ }
+ throw missing;
}
}
+ private Object getOuterProperty(final Class<?> sender, final Object
object, final String name, final MissingPropertyException mpe) {
+ if (sender == theClass ? isGroovyObject() :
GroovyObject.class.isAssignableFrom(sender)) {
+ var outerClass = getNonClosureOuter(sender); // check outer class
nesting of call site
+ if (outerClass != null && (sender == theClass ||
sender.isAssignableFrom(theClass))) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ Object target = getOuterReference(sender, object);
+ try {
+ return omc.getProperty(outerClass, target, name, false,
false);
+ } catch (MissingPropertyException e) {
+ mpe.addSuppressed(e);
+ }
+ }
+ }
+ throw mpe;
+ }
+
public MetaProperty getEffectiveGetMetaProperty(final Class sender, final
Object object, final String name, final boolean useSuper) {
//----------------------------------------------------------------------
@@ -2014,7 +2117,11 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
return new ReadOnlyMetaProperty(name) {
@Override
public Object getProperty(final Object receiver) {
- return invokeMissingProperty(receiver, getName(), null, true);
+ try {
+ return invokeMissingProperty(receiver, getName(), null,
true);
+ } catch (MissingPropertyException mpe) {
+ return getOuterProperty(sender, receiver, getName(), mpe);
+ }
}
};
}
@@ -2754,11 +2861,33 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
if (mp != null) {
throw new ReadOnlyPropertyException(name, theClass);
}
- if (!isStatic) {
- invokeMissingProperty(object, name, newValue, false);
- } else {
- invokeStaticMissingProperty(object, name, newValue, false);
+ try {
+ if (!isStatic) {
+ invokeMissingProperty(object, name, newValue, false);
+ } else {
+ invokeStaticMissingProperty(object, name, newValue, false);
+ }
+ } catch (MissingPropertyException e) {
+ if (!useSuper) setOuterProperty(sender, object, name, newValue,
e); else throw e; // GROOVY-11823
+ }
+ }
+
+ private void setOuterProperty(Class<?> sender, final Object object, final
String name, final Object newValue, final MissingPropertyException mpe) {
+ if (sender == null) sender = theClass; // GROOVY-11745
+ if (sender == theClass ? isGroovyObject() :
GroovyObject.class.isAssignableFrom(sender)) {
+ var outerClass = getNonClosureOuter(sender); // check outer class
nesting of call site
+ if (outerClass != null && (sender == theClass ||
sender.isAssignableFrom(theClass))) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ Object target = getOuterReference(sender, object);
+ try {
+ omc.setProperty(outerClass, target, name, newValue, false,
false);
+ return;
+ } catch (MissingPropertyException e) {
+ mpe.addSuppressed(e);
+ }
+ }
}
+ throw mpe;
}
private MetaProperty getMetaProperty(final Class<?> clazz, final String
name, final boolean useSuper, final boolean useStatic) {
diff --git a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
index 9ba8598b6a..6c0760ba69 100644
--- a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
@@ -19,6 +19,7 @@
package org.codehaus.groovy.ast;
import org.apache.groovy.ast.tools.ClassNodeUtils;
+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.ListExpression;
@@ -452,6 +453,17 @@ public class ModuleNode extends ASTNode {
methodNode.addAnnotations(existingMain.getAnnotations());
}
ClassNodeUtils.addGeneratedMethod(classNode, methodNode, true);
+
+ new CodeVisitorSupport() {
+ @Override
+ public void visitConstructorCallExpression(final
ConstructorCallExpression cce) {
+ if (cce.isUsingAnonymousInnerClass()) { // GROOVY-11846
+ cce.getType().setEnclosingMethod(methodNode);
+ }
+ super.visitConstructorCallExpression(cce);
+ }
+ }
+ .visit(statementBlock);
} else {
fields.forEach(classNode::addField);
classNode.addAnnotations(existingRun.getAnnotations());
diff --git
a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
index 87b8fa71d3..1d0d9653ae 100644
--- a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
@@ -23,7 +23,6 @@ import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.CodeVisitorSupport;
import org.codehaus.groovy.ast.ConstructorNode;
-import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.CastExpression;
@@ -44,16 +43,7 @@ import java.util.List;
import static java.util.stream.Collectors.toList;
import static
org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
-import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
-import static
org.codehaus.groovy.transform.sc.StaticCompilationVisitor.isStaticallyCompiled;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
-import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
-import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
/**
@@ -94,11 +84,6 @@ public class EnumCompletionVisitor extends
ClassCodeVisitorSupport {
for (ConstructorNode ctor : nonSyntheticConstructors(enumClass)) {
transformConstructor(ctor);
}
-
- var outerClass = enumClass.getOuterClass(); // GROOVY-7024
- if (outerClass != null && !isStaticallyCompiled(enumClass)) {
- addOuterClassDispatch((InnerClassNode) enumClass, outerClass);
- }
}
/**
@@ -207,78 +192,4 @@ public class EnumCompletionVisitor extends
ClassCodeVisitorSupport {
private static List<ConstructorNode> nonSyntheticConstructors(final
ClassNode cn) {
return cn.getDeclaredConstructors().stream().filter(c ->
!c.isSynthetic()).collect(toList());
}
-
- private void addOuterClassDispatch(final InnerClassNode innerClass, final
ClassNode outerClass) {
- var visitor = new InnerClassCompletionVisitor(null, sourceUnit);
-
- visitor.addMissingHandler(
- innerClass,
- "methodMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
-
InnerClassVisitorHelper.setMethodDispatcherCode(methodBody, classX(outerClass),
parameters);
- }
- );
-
- visitor.addMissingHandler(
- innerClass,
- "$static_methodMissing",
- ACC_PUBLIC | ACC_STATIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
-
InnerClassVisitorHelper.setMethodDispatcherCode(methodBody, classX(outerClass),
parameters);
- }
- );
-
- //
-
- visitor.addMissingHandler(
- innerClass,
- "propertyMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
-
InnerClassVisitorHelper.setPropertyGetterDispatcher(methodBody,
classX(outerClass), parameters);
- }
- );
-
- visitor.addMissingHandler(
- innerClass,
- "$static_propertyMissing",
- ACC_PUBLIC | ACC_STATIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
-
InnerClassVisitorHelper.setPropertyGetterDispatcher(methodBody,
classX(outerClass), parameters);
- }
- );
-
- //
-
- visitor.addMissingHandler(
- innerClass,
- "propertyMissing",
- ACC_PUBLIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- (methodBody, parameters) -> {
-
InnerClassVisitorHelper.setPropertySetterDispatcher(methodBody,
classX(outerClass), parameters);
- }
- );
-
- visitor.addMissingHandler(
- innerClass,
- "$static_propertyMissing",
- ACC_PUBLIC | ACC_STATIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- (methodBody, parameters) -> {
-
InnerClassVisitorHelper.setPropertySetterDispatcher(methodBody,
classX(outerClass), parameters);
- }
- );
- }
}
diff --git
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index fb70379836..399a82d438 100644
---
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -18,56 +18,31 @@
*/
package org.codehaus.groovy.classgen;
-import groovy.transform.CompileStatic;
-import groovy.transform.stc.POJO;
-import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
-import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
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.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.TryCatchStatement;
-import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.SourceUnit;
-import org.objectweb.asm.MethodVisitor;
-import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
-import java.util.function.BiConsumer;
-import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.hasAnnotation;
import static
org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.getMethod;
import static
org.apache.groovy.ast.tools.ConstructorNodeUtils.getFirstIfSpecialConstructorCall;
import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
-import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.catchS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorThisX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static org.codehaus.groovy.transform.trait.Traits.isTrait;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -75,12 +50,6 @@ import static org.objectweb.asm.Opcodes.ACC_MANDATED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
-import static org.objectweb.asm.Opcodes.ACONST_NULL;
-import static org.objectweb.asm.Opcodes.ALOAD;
-import static org.objectweb.asm.Opcodes.ARETURN;
-import static org.objectweb.asm.Opcodes.CHECKCAST;
-import static org.objectweb.asm.Opcodes.GETFIELD;
-import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
@@ -104,25 +73,13 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
if (node.isEnum() || node.isInterface() ||
isTrait(node.getOuterClass())) return;
- // if the class has an inner class, add methods to support private
member access
- if (node.getInnerClasses().hasNext()) {
- addDispatcherMethods(node);
- }
-
if (node instanceof InnerClassNode innerClass) {
- thisField = node.getField("this$0");
+ thisField = node.getDeclaredField("this$0");
if (innerClass.getVariableScope() == null &&
node.getDeclaredConstructors().isEmpty()) {
// add empty default constructor
addGeneratedConstructor(innerClass, ACC_PUBLIC,
Parameter.EMPTY_ARRAY, null, null);
}
-
super.visitClass(node);
-
- boolean innerPojo = hasAnnotation(node, new ClassNode(POJO.class))
- && hasAnnotation(node, new ClassNode(CompileStatic.class));
- if (!innerPojo) {
- addMopMethods(innerClass);
- }
}
}
@@ -156,227 +113,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
}
}
- private static String getTypeDescriptor(ClassNode node, boolean isStatic) {
- return BytecodeHelper.getTypeDescription(getClassNode(node, isStatic));
- }
-
- private static String getInternalName(ClassNode node, boolean isStatic) {
- return BytecodeHelper.getClassInternalName(getClassNode(node,
isStatic));
- }
-
- private static void addDispatcherMethods(ClassNode classNode) {
- final int objectDistance = getObjectDistance(classNode);
-
- // since we added an anonymous inner class we should also
- // add the dispatcher methods
-
- // add method dispatcher
- BlockStatement block = new BlockStatement();
- MethodNode method = classNode.addSyntheticMethod(
- "this$dist$invoke$" + objectDistance,
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- ClassNode.EMPTY_ARRAY,
- block
- );
- setMethodDispatcherCode(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
-
- // add property setter
- block = new BlockStatement();
- method = classNode.addSyntheticMethod(
- "this$dist$set$" + objectDistance,
- ACC_PUBLIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- ClassNode.EMPTY_ARRAY,
- block
- );
- setPropertySetterDispatcher(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
-
- // add property getter
- block = new BlockStatement();
- method = classNode.addSyntheticMethod(
- "this$dist$get$" + objectDistance,
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- ClassNode.EMPTY_ARRAY,
- block
- );
- setPropertyGetterDispatcher(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
- }
-
- private void getThis(final MethodVisitor mv, final String
classInternalName, final String outerClassDescriptor, final String
innerClassInternalName) {
- mv.visitVarInsn(ALOAD, 0);
- if (thisField != null && CLOSURE_TYPE.equals(thisField.getType())) {
- mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
BytecodeHelper.getTypeDescription(CLOSURE_TYPE));
- mv.visitMethodInsn(INVOKEVIRTUAL,
BytecodeHelper.getClassInternalName(CLOSURE_TYPE), "getThisObject",
"()Ljava/lang/Object;", false);
- mv.visitTypeInsn(CHECKCAST, innerClassInternalName);
- } else {
- mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
outerClassDescriptor);
- }
- }
-
- private void addMopMethods(final InnerClassNode node) {
- final boolean isStatic = isStatic(node);
- final ClassNode outerClass = node.getOuterClass();
- final int outerClassDistance = getObjectDistance(outerClass);
- final String classInternalName =
BytecodeHelper.getClassInternalName(node);
- final String outerClassInternalName = getInternalName(outerClass,
isStatic);
- final String outerClassDescriptor = getTypeDescriptor(outerClass,
isStatic);
-
- addMissingHandler(node,
- "methodMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
- if (isStatic) {
- setMethodDispatcherCode(methodBody,
classX(outerClass), parameters);
- } else {
- methodBody.addStatement(
- new BytecodeSequence(new BytecodeInstruction()
{
- @Override
- public void visit(final MethodVisitor mv) {
- getThis(mv, classInternalName,
outerClassDescriptor, outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitVarInsn(ALOAD, 2);
- mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$invoke$" + outerClassDistance,
"(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", false);
- mv.visitInsn(ARETURN);
- }
- })
- );
- }
- }
- );
-
- addMissingHandler(node,
- "$static_methodMissing",
- ACC_PUBLIC | ACC_STATIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
- setMethodDispatcherCode(methodBody, classX(outerClass),
parameters);
- }
- );
-
- ClassNode[] nameValueTypes = {STRING_TYPE, OBJECT_TYPE};
- MethodNode propertyMissing = getMethod(node, "propertyMissing", (m) ->
!m.isStatic() && !m.isPrivate()
- &&
Arrays.equals(Arrays.stream(m.getParameters()).map(Parameter::getType).toArray(),nameValueTypes));
- ClassNode returnType = propertyMissing != null ?
propertyMissing.getReturnType() : VOID_TYPE; // GROOVY-11822
-
- addMissingHandler(node,
- "propertyMissing",
- ACC_PUBLIC,
- returnType,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- (methodBody, parameters) -> {
- if (isStatic) {
- setPropertySetterDispatcher(methodBody,
classX(outerClass), parameters);
- } else {
- methodBody.addStatement(
- new BytecodeSequence(new BytecodeInstruction()
{
- @Override
- public void visit(final MethodVisitor mv) {
- getThis(mv, classInternalName,
outerClassDescriptor, outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitVarInsn(ALOAD, 2);
- mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$set$" + outerClassDistance,
"(Ljava/lang/String;Ljava/lang/Object;)V", false);
- if
(!ClassHelper.isPrimitiveVoid(returnType)) mv.visitInsn(ACONST_NULL);
- BytecodeHelper.doReturn(mv,
returnType);
- }
- })
- );
- }
- }
- );
-
- addMissingHandler(node,
- "$static_propertyMissing",
- ACC_PUBLIC | ACC_STATIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- (methodBody, parameters) -> {
- setPropertySetterDispatcher(methodBody,
classX(outerClass), parameters);
- }
- );
-
- addMissingHandler(node,
- "propertyMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
- if (isStatic) {
- setPropertyGetterDispatcher(methodBody,
classX(outerClass), parameters);
- } else {
- methodBody.addStatement(
- new BytecodeSequence(new BytecodeInstruction()
{
- @Override
- public void visit(final MethodVisitor mv) {
- getThis(mv, classInternalName,
outerClassDescriptor, outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$get$" + outerClassDistance,
"(Ljava/lang/String;)Ljava/lang/Object;", false);
- mv.visitInsn(ARETURN);
- }
- })
- );
- }
- }
- );
-
- addMissingHandler(node,
- "$static_propertyMissing",
- ACC_PUBLIC | ACC_STATIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
- setPropertyGetterDispatcher(methodBody,
classX(outerClass), parameters);
- }
- );
- }
-
- /* */ void addMissingHandler(final InnerClassNode innerClass, final
String methodName, final int modifiers,
- final ClassNode returnType, final Parameter[] parameters, final
BiConsumer<BlockStatement, Parameter[]> consumer) {
- MethodNode method = innerClass.getDeclaredMethod(methodName,
parameters);
- if (method == null) {
- // try {
- // <consumer dispatch>
- // } catch (MissingMethodException notFound) {
- // throw new MissingMethodException(notFound.method, this,
notFound.arguments)
- // }
- Parameter catchParam = param(OBJECT_TYPE, "notFound"); // dummy
type
- ClassNode exceptionT;
- Expression newException;
- Expression selfType = varX("this");
- if ((modifiers & ACC_STATIC) == 0) selfType = callX(selfType,
"getClass");
- if (methodName.endsWith("methodMissing")) {
- exceptionT =
ClassHelper.make(groovy.lang.MissingMethodException.class);
- newException = ctorX(exceptionT,
args(propX(varX(catchParam),"method"), selfType,
propX(varX(catchParam),"arguments")));
- } else {
- exceptionT =
ClassHelper.make(groovy.lang.MissingPropertyException.class);
- newException = ctorX(exceptionT, args(propX(varX(catchParam),
"property"), selfType, propX(varX(catchParam), "cause")));
- }
- catchParam.setType(exceptionT);
- catchParam.setOriginType(exceptionT);
- BlockStatement handleMissing = block();
- consumer.accept(handleMissing, parameters);
- TryCatchStatement tryCatch = tryCatchS(handleMissing);
- tryCatch.addCatch(catchS(catchParam, throwS(newException)));
-
- innerClass.addSyntheticMethod(methodName, modifiers, returnType,
parameters, ClassNode.EMPTY_ARRAY, tryCatch);
-
- // if there is a user-defined method, add compiler error and
continue
- } else if (isStatic(innerClass) && (method.getModifiers() &
ACC_SYNTHETIC) == 0) {
- addError("\"" + methodName + "\" implementations are not supported
on static inner classes as " +
- "a synthetic version of \"" + methodName + "\" is added during
compilation for the purpose " +
- "of outer class delegation.",
- method);
- }
- }
-
- private void addThisReference(ConstructorNode node) {
+ private void addThisReference(final ConstructorNode node) {
if (!shouldHandleImplicitThisForInnerClass(classNode)) return;
// add "this$0" field init
@@ -428,7 +165,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
node.setCode(block);
}
- private boolean shouldImplicitlyPassThisZero(ConstructorCallExpression
cce) {
+ private boolean shouldImplicitlyPassThisZero(final
ConstructorCallExpression cce) {
boolean pass = false;
if (cce.isThisCall()) {
pass = true;
@@ -447,7 +184,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
return pass;
}
- private String getUniqueName(Parameter[] params, ConstructorNode node) {
+ private String getUniqueName(final Parameter[] params, final
ConstructorNode node) {
String namePrefix = "$p";
outer:
for (int i = 0; i < 100; i++) {
diff --git
a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
index 32c5395164..e507af6416 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
@@ -97,7 +97,7 @@ public abstract class InnerClassVisitorHelper extends
ClassCodeVisitorSupport {
protected static boolean shouldHandleImplicitThisForInnerClass(final
ClassNode cn) {
final int explicitOrImplicitStatic = Opcodes.ACC_ENUM |
Opcodes.ACC_INTERFACE | Opcodes.ACC_RECORD | Opcodes.ACC_STATIC;
- return (cn.getModifiers() & explicitOrImplicitStatic) == 0 && (cn
instanceof InnerClassNode && !((InnerClassNode) cn).isAnonymous())
+ return (cn.getModifiers() & explicitOrImplicitStatic) == 0 && (cn
instanceof InnerClassNode inner && !inner.isAnonymous())
&& cn.getAnnotations().stream().noneMatch(aNode ->
"groovy.transform.RecordType".equals(aNode.getClassNode().getName())); //
GROOVY-11600
}
}
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
b/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
index 90f204f79e..e837def17d 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/GroovyTypeCheckingExtensionSupport.java
@@ -488,47 +488,46 @@ public class GroovyTypeCheckingExtensionSupport extends
AbstractTypeCheckingExte
* @see <a
href="https://docs.groovy-lang.org/latest/html/documentation/#_a_dsl_for_type_checking">Groovy
Language Documentation</a>
*/
public abstract static class TypeCheckingDSL extends Script {
+
private GroovyTypeCheckingExtensionSupport extension;
@Override
- public Object getProperty(final String property) {
+ public Object getProperty(final String name) {
try {
- return InvokerHelper.getProperty(extension, property);
+ return InvokerHelper.getProperty(extension, name);
} catch (Exception e) {
- return super.getProperty(property);
+ return super.getProperty(name);
}
}
@Override
- public void setProperty(final String property, final Object newValue) {
+ public void setProperty(final String name, final Object value) {
try {
- InvokerHelper.setProperty(extension, property, newValue);
+ InvokerHelper.setProperty(extension, name, value);
} catch (Exception e) {
- super.setProperty(property, newValue);
+ super.setProperty(name, value);
}
}
- @Override
- public Object invokeMethod(final String name, final Object args) {
- if (name.startsWith("is") && name.endsWith("Expression") && args
instanceof Object[] && ((Object[]) args).length == 1) {
- String type = name.substring(2);
- Object target = ((Object[]) args)[0];
+ public Object methodMissing(final String name, final Object args) {
+ if (name.startsWith("is") && name.endsWith("Expression") && args
instanceof Object[] array && array.length == 1) {
+ Object target = array[0];
if (target == null) return Boolean.FALSE;
try {
- Class<?> typeClass =
Class.forName("org.codehaus.groovy.ast.expr." + type);
- return typeClass.isAssignableFrom(target.getClass());
+ Class<?> type =
Class.forName("org.codehaus.groovy.ast.expr." + name.substring(2));
+ return type.isAssignableFrom(target.getClass());
} catch (ClassNotFoundException e) {
return Boolean.FALSE;
}
}
- if (args instanceof Object[] argsArray && ((Object[]) args).length
== 1 && ((Object[]) args)[0] instanceof Closure) {
+ if (args instanceof Object[] array && array.length == 1 &&
array[0] instanceof Closure closure) {
String methodName = METHOD_ALIASES.get(name);
if (methodName == null) {
return InvokerHelper.invokeMethod(extension, name, args);
}
- List<Closure> closures =
extension.eventHandlers.computeIfAbsent(methodName, k -> new
LinkedList<Closure>());
- closures.add((Closure) argsArray[0]);
+ var closures =
extension.eventHandlers.computeIfAbsent(methodName, k -> new LinkedList<>());
+ closures.add(closure);
return null;
} else {
return InvokerHelper.invokeMethod(extension, name, args);
diff --git a/src/test/groovy/bugs/Groovy7938Bug.groovy
b/src/test/groovy/bugs/Groovy7938Bug.groovy
deleted file mode 100644
index a7476f4f60..0000000000
--- a/src/test/groovy/bugs/Groovy7938Bug.groovy
+++ /dev/null
@@ -1,70 +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 bugs
-
-import groovy.test.GroovyTestCase
-
-class Groovy7938Bug extends GroovyTestCase {
- void testClassUsageInInterfaceDef() {
- assertScript """
- class Outer {
- static Integer fooCount = 0
- Integer barCount = 0
- static void incFoo() { fooCount++ }
- void incBar() { barCount++ }
- static class Nested {
- static void nestedIncFoo() { incFoo() }
- static class NestedNested {
- static void nestedNestedIncFoo() { incFoo() }
- }
- }
- Inner innerFactory() { new Inner() }
- class Inner {
- InnerInner innerInnerFactory() { new InnerInner() }
- void innerIncFoo() { incFoo() }
- static void staticInnerIncFoo() { incFoo() }
- class InnerInner {
- void innerInnerIncFoo() { incFoo() }
- static void staticInnerInnerIncFoo() { incFoo() }
- }
- }
- }
- Outer.incFoo()
- Outer.Nested.nestedIncFoo()
- Outer.Nested.NestedNested.nestedNestedIncFoo()
- assert Outer.fooCount == 3
- new Outer().with {
- incBar()
- incFoo()
- innerFactory().with {
- incBar()
- innerIncFoo()
- staticInnerIncFoo()
- innerInnerFactory().with {
- incBar()
- innerInnerIncFoo()
- staticInnerInnerIncFoo()
- }
- }
- assert barCount == 3
- assert fooCount == 8
- }
- """
- }
-}
diff --git a/src/test/groovy/gls/enums/EnumTest.groovy
b/src/test/groovy/gls/enums/EnumTest.groovy
index d0e1255d3d..c490ea04d6 100644
--- a/src/test/groovy/gls/enums/EnumTest.groovy
+++ b/src/test/groovy/gls/enums/EnumTest.groovy
@@ -338,6 +338,23 @@ class EnumTest extends CompilableTestSupport {
'''
}
+ void testInnerEnumInitWithUnqualifiedOuterClassValue() {
+ assertScript '''
+ class C {
+ private static int ONE() { 1 }
+ enum E {
+ FOO(1 + ONE())
+ final value
+ E(value) {
+ this.value = value
+ }
+ }
+ }
+
+ assert C.E.FOO.value == 2
+ '''
+ }
+
// GROOVY-3693
void testStaticFieldInitValuesInAStaticBlock() {
// trigger enum class load to test it - asserts are present in the enum
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index ed46f15eda..f8b44e9c89 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -413,7 +413,8 @@ final class InnerClassTest {
void testStaticInnerClass2() {
assertScript '''
class A {
- static class B {}
+ static class B {
+ }
}
assert A.declaredClasses.length == 1
@@ -427,11 +428,12 @@ final class InnerClassTest {
class A {
static class B {
String p
+ String getQ() { WHY }
}
B m() {
return [p:'x'] // calls
ScriptBytecodeAdapter.castToType([p:'x'], A$B.class)
}
- static final String q = 'y'
+ private static final String WHY = 'y'
}
o = new A().m()
@@ -601,14 +603,14 @@ final class InnerClassTest {
@Test
void testUsageOfOuterField() {
assertScript '''
- interface Run {
+ interface A {
def run()
}
- class Foo {
+ class C {
private x = 1
def foo() {
- def runner = new Run() {
+ def runner = new A() {
def run() { return x }
}
runner.run()
@@ -616,24 +618,25 @@ final class InnerClassTest {
void x(y) { x = y }
}
- def foo = new Foo()
- assert foo.foo() == 1
- foo.x(2)
- assert foo.foo() == 2
+
+ def c = new C()
+ assert c.foo() == 1
+ c.x(2)
+ assert c.foo() == 2
'''
}
@Test
void testUsageOfOuterField2() {
assertScript '''
- interface Run {
+ interface A {
def run()
}
- class Foo {
+ class C {
private static x = 1
static foo() {
- def runner = new Run() {
+ def runner = new A() {
def run() { return x }
}
runner.run()
@@ -641,39 +644,16 @@ final class InnerClassTest {
static x(y) { x = y }
}
- assert Foo.foo() == 1
- Foo.x(2)
- assert Foo.foo() == 2
- '''
- }
- @Test
- void testUsageOfOuterField3() {
- assertScript '''
- interface X {
- def m()
- }
-
- class A {
- def pm = "pm"
-
- def bar(x) {x().m()}
- def foo() {
- bar { ->
- return new X() {
- def m() { pm }
- }
- }
- }
- }
- def a = new A()
- assert "pm" == a.foo()
+ assert C.foo() == 1
+ C.x(2)
+ assert C.foo() == 2
'''
}
// GROOVY-6141
@Test
- void testUsageOfOuterField4() {
+ void testUsageOfOuterField3() {
assertScript '''
class A {
def x = 1
@@ -704,7 +684,7 @@ final class InnerClassTest {
// GROOVY-9189
@Test
- void testUsageOfOuterField5() {
+ void testUsageOfOuterField4() {
assertScript '''
interface Run {
def run()
@@ -720,6 +700,7 @@ final class InnerClassTest {
static x(y) { x = y }
}
+
assert Foo.foo() == 1
Foo.x(2)
assert Foo.foo() == 2
@@ -728,7 +709,7 @@ final class InnerClassTest {
// GROOVY-9168
@Test
- void testUsageOfOuterField6() {
+ void testUsageOfOuterField5() {
assertScript '''
class A {
// AIC in this position can use static
properties:
@@ -747,7 +728,7 @@ final class InnerClassTest {
// GROOVY-9501
@Test
- void testUsageOfOuterField7() {
+ void testUsageOfOuterField6() {
assertScript '''
class Main extends Outer {
static main(args) {
@@ -783,7 +764,7 @@ final class InnerClassTest {
}
@Test // inner class is static instead of final
- void testUsageOfOuterField8() {
+ void testUsageOfOuterField7() {
assertScript '''
class Main extends Outer {
static main(args) {
@@ -820,7 +801,7 @@ final class InnerClassTest {
// GROOVY-9569
@Test
- void testUsageOfOuterField9() {
+ void testUsageOfOuterField8() {
assertScript '''
class Main extends Outer {
static main(args) {
@@ -857,7 +838,7 @@ final class InnerClassTest {
}
@Test
- void testUsageOfOuterField10() {
+ void testUsageOfOuterField9() {
assertScript '''
class Outer {
static final String OUTER_CONSTANT = 'Constant Value'
@@ -881,7 +862,7 @@ final class InnerClassTest {
// GROOVY-5259
@Test
- void testUsageOfOuterField11() {
+ void testUsageOfOuterField10() {
assertScript '''
class Base {
Base(String string) {
@@ -913,7 +894,7 @@ final class InnerClassTest {
}
@Test
- void testUsageOfOuterField12() {
+ void testUsageOfOuterField11() {
def err = shouldFail '''
class C {
int count
@@ -934,15 +915,32 @@ final class InnerClassTest {
// GROOVY-8050
@Test
- void testUsageOfOuterField13() {
- assertScript '''
+ void testUsageOfOuterField12() {
+ def err = shouldFail '''
class Outer {
class Inner {
}
def p = 1
}
- def i = new Outer.Inner(new Outer())
- assert i.p == 1
+
+ new Outer.Inner(new Outer()).p
+ '''
+ assert err =~ /MissingPropertyException: No such property: p for
class: Outer.Inner/
+ }
+
+ @NotYetImplemented @Test
+ void testUsageOfOuterField13() {
+ assertScript '''
+ class Outer {
+ interface Inner {
+ default i() {
+ 'i' + o
+ }
+ }
+ private static o = 'o'
+ }
+
+ assert (new Outer.Inner() {}).i() == 'io'
'''
}
@@ -1054,28 +1052,27 @@ final class InnerClassTest {
@Test
void testUsageOfOuterFieldOverridden() {
assertScript '''
- interface Run {
+ interface A {
def run()
}
- class Foo {
- private x = 1
-
- def foo() {
- def runner = new Run() {
+ class B {
+ def test() {
+ def runner = new A() {
def run() { return x } // <-- dynamic variable
}
runner.run()
}
-
+ private x = 1
void setX(val) { x = val }
}
- class Bar extends Foo {
- def x = 'string' // hides 'foo.@x' and overrides
'foo.setX(val)'
+ class C extends B {
+ def x = 'string' // hides 'B.@x' and overrides 'B.setX(val)'
}
- def bar = new Bar()
- assert bar.foo() == 'string'
- bar.x = 'new string'
- assert bar.foo() == 'new string'
+
+ def c = new C()
+ assert c.test() == 1
+ c.x = 'new string'
+ assert c.test() == 1
'''
}
@@ -1095,6 +1092,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1116,6 +1114,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1136,6 +1135,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1157,6 +1157,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1254,6 +1255,75 @@ final class InnerClassTest {
'''
}
+ // GROOVY-7938
+ @Test
+ void testUsageOfOuterMethod9() {
+ assertScript '''
+ class Outer {
+ Integer barCount = 0
+ static Integer fooCount = 0
+ void incBar() { barCount++ }
+ static void incFoo() { fooCount++ }
+ Inner innerFactory() { new Inner() }
+
+ static class Nested {
+ static void nestedIncFoo() { incFoo() }
+ static class NestedNested {
+ static void nestedNestedIncFoo() { incFoo() }
+ }
+ }
+ class Inner {
+ void innerIncFoo() { incFoo() }
+ static void staticInnerIncFoo() { incFoo() }
+ InnerInner innerInnerFactory() { new InnerInner() }
+
+ class InnerInner {
+ void innerInnerIncFoo() { incFoo() }
+ static void staticInnerInnerIncFoo() { incFoo() }
+ }
+ }
+ }
+
+ Outer.incFoo()
+ Outer.Nested.nestedIncFoo()
+ Outer.Nested.NestedNested.nestedNestedIncFoo()
+ assert Outer.fooCount == 3
+
+ new Outer().with {
+ incBar()
+ incFoo()
+ innerFactory().with {
+ incBar()
+ innerIncFoo()
+ staticInnerIncFoo()
+ innerInnerFactory().with {
+ incBar()
+ innerInnerIncFoo()
+ staticInnerInnerIncFoo()
+ }
+ }
+ assert barCount == 3
+ assert fooCount == 8
+ }
+ '''
+ }
+
+ @NotYetImplemented @Test
+ void testUsageOfOuterMethod10() {
+ assertScript '''
+ class Outer {
+ interface Inner {
+ default i() {
+ 'i' + o()
+ }
+ }
+ private static o() { 'o' }
+ }
+
+ assert (new Outer.Inner() {}).i() == 'io'
+ '''
+ }
+
@Test
void testUsageOfOuterMethodOverridden() {
assertScript '''
@@ -1965,10 +2035,12 @@ final class InnerClassTest {
@Test
void testEnclosingMethodIsSet2() {
assertScript '''
- import groovy.transform.ASTTest
+ import groovy.transform.*
import org.codehaus.groovy.ast.expr.*
import static org.codehaus.groovy.classgen.Verifier.*
+ @Field Object result
+
@ASTTest(phase=CLASS_GENERATION, value={
def init =
node.parameters[0].getNodeMetaData(INITIAL_EXPRESSION)
assert init instanceof MapExpression
@@ -2144,6 +2216,7 @@ final class InnerClassTest {
inner.inner()
}
}
+
assert new Outer().test() == 1
'''
}
@@ -2165,17 +2238,18 @@ final class InnerClassTest {
}
}
}
+
new Outer().obj.toString()
'''
}
// GROOVY-8274
@Test
- void testMissingMethodHandling() {
+ void testMethodMissing1() {
assertScript '''
class Outer {
class Inner {
- def methodMissing(String name, args) {
+ def methodMissing(String name, Object args) {
return name
}
}
@@ -2194,6 +2268,68 @@ final class InnerClassTest {
'''
}
+ @Test
+ void testMethodMissing2() {
+ assertScript '''
+ class Outer {
+ class Inner {
+ def methodMissing(String name, Object args) {
+ return 42
+ }
+ def propertyMissing(String name) {
+ return 42
+ }
+ }
+ }
+
+ def i = new Outer.Inner(new Outer())
+ assert i.foo() == 42
+ assert i.foobar == 42
+ '''
+ }
+
+ @Test
+ void testMethodMissing3() {
+ assertScript '''
+ class Outer {
+ static class Inner {
+ def methodMissing(String name, Object args) {
+ return 42
+ }
+ def propertyMissing(String name) {
+ return 42
+ }
+ }
+ }
+
+ def i = new Outer.Inner()
+ assert i.foo() == 42
+ assert i.foobar == 42
+ '''
+ }
+
+ @Test
+ void testMethodMissing4() {
+ assertScript '''
+ class Outer {
+ static class Inner {
+ def methodMissing(String name, Object args) {
+ return 42
+ }
+ def propertyMissing(String name) {
+ return 42
+ }
+ }
+ static class Other extends Inner {
+ }
+ }
+
+ def o = new Outer.Other()
+ assert o.foo() == 42
+ assert o.foobar == 42
+ '''
+ }
+
// GROOVY-6831
@Test
void testNestedPropertyHandling() {
@@ -2255,6 +2391,7 @@ final class InnerClassTest {
def err = shouldFail """
class Upper {
$returnType propertyMissing(String name, Object value) {
+ throw new MissingPropertyException(name, getClass())
}
}
class Outer {
@@ -2266,6 +2403,60 @@ final class InnerClassTest {
assert err =~ /No such property: missing for class: Outer.Inner/
}
+ // GROOVY-11823
+ @Test
+ void testNestedPropertyHandling5() {
+ assertScript '''
+ class Upper {
+ Object propertyMissing(String name) {
+ if (name == 'fizz') return 'buzz'
+ throw new MissingPropertyException(name, getClass())
+ }
+ }
+ class Outer {
+ static class Inner extends Upper {
+ }
+ }
+ def inner = new Outer.Inner()
+ assert inner.fizz == 'buzz'
+ '''
+ }
+
+ // GROOVY-9618
+ @Test
+ void testNestedPropertyHandling6() {
+ assertScript '''
+ class Super {
+ public static X = 1
+ static getX() { 2 }
+ }
+ class Outer extends Super {
+ static class Inner {
+ def m() { X }
+ }
+ }
+
+ assert new Outer.Inner().m() == 2
+ '''
+
+ def err = shouldFail '''
+ class Outer {
+ public static X = 1
+ static getX() { 2 }
+ static class Inner {
+ }
+ }
+ class Other extends Outer.Inner {
+ def m() {
+ X // can't read super outer this way
+ }
+ }
+
+ new Other().m()
+ '''
+ assert err =~ /MissingPropertyException: No such property: X for
class: Other/
+ }
+
// GROOVY-7312
@Test
void testInnerClassOfInterfaceIsStatic() {
diff --git a/src/test/groovy/groovy/ClosureTest.groovy
b/src/test/groovy/groovy/ClosureTest.groovy
index 829645151d..7797016645 100644
--- a/src/test/groovy/groovy/ClosureTest.groovy
+++ b/src/test/groovy/groovy/ClosureTest.groovy
@@ -18,8 +18,9 @@
*/
package groovy
-import org.codehaus.groovy.control.MultipleCompilationErrorsException
-import org.junit.Test
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
import static groovy.test.GroovyAssert.assertScript
import static groovy.test.GroovyAssert.shouldFail
@@ -484,72 +485,6 @@ final class ClosureTest {
'''
}
- @Test
- void testStaticInnerClassOwnerWithPropertyMissingImplementation() {
- def err = shouldFail MultipleCompilationErrorsException, '''
- class ClosureTestA {
- static class ClosureTestB {
- def propertyMissing(String myName, Object myValue) {
- return myValue
- }
-
- def propertyMissing(String myName) {
- return 42
- }
-
- def methodMissing(String myName, Object myArgs) {
- return 42
- }
- }
- }
- '''
-
- assert err.message.contains('"methodMissing" implementations are not
supported on static inner classes as a synthetic version of "methodMissing" is
added during compilation for the purpose of outer class delegation.')
- assert err.message.contains('"propertyMissing" implementations are not
supported on static inner classes as a synthetic version of "propertyMissing"
is added during compilation for the purpose of outer class delegation.')
- }
-
- @Test
- void testInnerClassOwnerWithPropertyMissingImplementation() {
- assertScript '''
- class ClosureTestA {
- class ClosureTestB {
- def propertyMissing(String myName, Object myValue) {
- return myValue
- }
-
- def propertyMissing(String myName) {
- return 42
- }
-
- def methodMissing(String myName, Object myArgs) {
- return 42
- }
- }
- }
-
- def a = new ClosureTestA()
- def b = new ClosureTestA.ClosureTestB(a)
- '''
- }
-
- @Test
- void testStaticInnerClassHierarchyWithMethodMissing() {
- def err = shouldFail MultipleCompilationErrorsException, '''
- class ClosureTestA {
- static class ClosureTestB {
- def methodMissing(String myName, Object myArgs) {
- return 42
- }
- }
-
- static class ClosureTestB1 extends ClosureTestB {
- }
- }
- '''
-
- assert err.message.contains('"methodMissing" implementations are not
supported on static inner classes as a synthetic version of "methodMissing" is
added during compilation for the purpose of outer class delegation.')
- }
-
// GROOVY-10943
@Test
void testClosureUnderscorePlaceholder() {
@@ -564,150 +499,148 @@ final class ClosureTest {
}
// GROOVY-2433, GROOVY-3073, GROOVY-9987, GROOVY-11128
- @Test
- void testClosureAccessToEnclosingClassPrivateMethod() {
- for (who in ['this.', 'owner.', 'thisObject.', '']) {
- assertScript """
- class C {
- def getIds() {
- populateIds()
- }
- def populateIds = { ->
- ${who}sort([ 1, 5, 3, 4, 2 ])
- }
- private sort(list) {
- list.sort{ one, two -> one <=> two }
- }
+ @ParameterizedTest
+ @ValueSource(strings=['this.', 'owner.', 'thisObject.', ''])
+ void testClosureAccessToEnclosingClassPrivateMethod(String who) {
+ assertScript """
+ class C {
+ def getIds() {
+ populateIds()
}
+ def populateIds = { ->
+ ${who}sort([ 1, 5, 3, 4, 2 ])
+ }
+ private sort(list) {
+ list.sort{ one, two -> one <=> two }
+ }
+ }
- class D extends C {
- void test() {
- assert ids == [1,2,3,4,5]
- }
+ class D extends C {
+ void test() {
+ assert ids == [1,2,3,4,5]
}
+ }
- new D().test()
- """
+ new D().test()
+ """
- assertScript """
- class C {
- protected String protectedMethod() {
- def closure = { ->
- ${who}privateMethod()
- }
- closure()
- }
- private String privateMethod() {
- 'hello world'
+ assertScript """
+ class C {
+ protected String protectedMethod() {
+ def closure = { ->
+ ${who}privateMethod()
}
+ closure()
+ }
+ private String privateMethod() {
+ 'hello world'
}
+ }
- class D extends C {
- void test() {
- def result = protectedMethod()
- assert result == 'hello world'
- }
+ class D extends C {
+ void test() {
+ def result = protectedMethod()
+ assert result == 'hello world'
}
+ }
- new D().test()
- """
+ new D().test()
+ """
- assertScript """
- class C {
- def publicMethod() {
- [1].each {
- ${who}privateStaticMethod()
- }
- }
- private static privateStaticMethod() {
- 'hello world'
+ assertScript """
+ class C {
+ def publicMethod() {
+ [1].each {
+ ${who}privateStaticMethod()
}
}
+ private static privateStaticMethod() {
+ 'hello world'
+ }
+ }
- class D extends C {
- void test() {
- publicMethod()
- }
+ class D extends C {
+ void test() {
+ publicMethod()
}
+ }
- new D().test()
- """
- }
+ new D().test()
+ """
}
// GROOVY-3142, GROOVY-5438, GROOVY-6335, GROOVY-11128
- @Test
- void testClosureAccessToEnclosingClassPrivateField() {
- for (who in ['this.@', 'this.', 'owner.', 'thisObject.', '']) {
- assertScript """
- class C {
- String data
- C(arg) {
- arg.each() { ${who}data = it } // MissingFieldException
- }
+ @ParameterizedTest
+ @ValueSource(strings=['this.@', 'this.', 'owner.', 'thisObject.', ''])
+ void testClosureAccessToEnclosingClassPrivateField(String who) {
+ assertScript """
+ class C {
+ String data
+ C(arg) {
+ arg.each() { ${who}data = it } // MissingFieldException
}
+ }
- class D extends C {
- D(arg) {
- super(arg)
- }
+ class D extends C {
+ D(arg) {
+ super(arg)
}
+ }
- new D(["test"])
- """
+ new D(["test"])
+ """
- assertScript """
- class C {
- private String data
- C(arg) {
- arg.each() { ${who}data = it } //
ReadOnlyPropertyException
- }
- String getData() { this.@data }
- private void setData(String value) { this.@data = value }
+ assertScript """
+ class C {
+ private String data
+ C(arg) {
+ arg.each() { ${who}data = it } // ReadOnlyPropertyException
}
+ String getData() { this.@data }
+ private void setData(String value) { this.@data = value }
+ }
- class D extends C {
- D(arg) {
- super(arg)
- }
+ class D extends C {
+ D(arg) {
+ super(arg)
}
+ }
- new D(["test"])
- """
+ new D(["test"])
+ """
- assertScript """
- class C {
- private String string = 'foo'
- def test(List<String> strings) {
- strings.collect { ${who}string + it }
- }
+ assertScript """
+ class C {
+ private String string = 'foo'
+ def test(List<String> strings) {
+ strings.collect { ${who}string + it }
}
+ }
- def result = new C().test(['bar','baz'])
- assert result == ['foobar','foobaz']
+ def result = new C().test(['bar','baz'])
+ assert result == ['foobar','foobaz']
- class D extends C {
- }
+ class D extends C {
+ }
+
+ result = new D().test(['bar','baz'])
+ assert result == ['foobar','foobaz']
+ """
- result = new D().test(['bar','baz'])
- assert result == ['foobar','foobaz']
- """
-
- assertScript """
- @groovy.util.logging.Log
- class C {
- void test() {
- 1.times {
- ${who}log.info('sth')
- }
+ assertScript """
+ @groovy.util.logging.Log
+ class C {
+ void test() {
+ 1.times {
+ ${who}log.info('sth')
}
}
+ }
- class D extends C {
- }
+ class D extends C {
+ }
- new D().test()
- """
- }
+ new D().test()
+ """
}
}
diff --git a/src/test/groovy/groovy/PropertyTest.groovy
b/src/test/groovy/groovy/PropertyTest.groovy
index 761fcde041..2b2547912e 100644
--- a/src/test/groovy/groovy/PropertyTest.groovy
+++ b/src/test/groovy/groovy/PropertyTest.groovy
@@ -831,24 +831,6 @@ final class PropertyTest {
'''
}
- // GROOVY-9618
- @Test
- void testPropertyAndStaticUppercaseFieldPriority() {
- assertScript '''
- class A {
- public static X = 1
- static getX() { 2 }
- static class B { }
- }
- class C extends A.B {
- def test() {
- X
- }
- }
- assert new C().test() == 2
- '''
- }
-
//--------------------------------------------------------------------------
static class Base {
diff --git
a/src/test/groovy/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
b/src/test/groovy/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
index 8ae6966ba1..653aebf263 100644
--- a/src/test/groovy/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy
@@ -176,23 +176,24 @@ class AnonymousInnerClassSTCTest extends
StaticTypeCheckingTestCase {
// GROOVY-6904
void testAICInClosure() {
assertScript '''
- interface X {
+ interface A {
def m()
}
-
- class A {
- Object pm = "pm"
- def bar(Closure<? extends X> x) {x().m()}
+ class C {
+ def bar(Closure<? extends A> closure) {
+ closure().m()
+ }
def foo() {
bar { ->
- return new X() {
- def m() { pm }
+ return new A() {
+ def m() { p }
}
}
}
+ final String p = 'p'
}
- def a = new A()
- assert a.foo() == "pm"
+
+ assert new C().foo() == 'p'
'''
}
diff --git
a/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
b/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
index 12f3a5e6e6..7add367f14 100644
---
a/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
+++
b/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
@@ -19,7 +19,7 @@
package org.codehaus.groovy.transform.packageScope
import groovy.transform.CompileStatic
-import org.junit.Test
+import org.junit.jupiter.api.Test
import static groovy.test.GroovyAssert.assertScript
@@ -191,7 +191,7 @@ final class PackageScopeTransformTest {
import groovy.transform.PackageScope
@CompileStatic
- class Test {
+ class Outer {
@PackageScope static final String S = 'S'
protected static final String T = 'T'
private static final String U = 'U'
@@ -202,7 +202,7 @@ final class PackageScopeTransformTest {
}
}
- assert new Test.Inner().method() == 'STU'
+ assert new Outer.Inner().method() == 'STU'
'''
}
}
diff --git
a/subprojects/groovy-nio/src/test/groovy/org/apache/groovy/nio/extensions/NioExtensionsTest.groovy
b/subprojects/groovy-nio/src/test/groovy/org/apache/groovy/nio/extensions/NioExtensionsTest.groovy
index e6b30f3c48..ed7b2cadeb 100644
---
a/subprojects/groovy-nio/src/test/groovy/org/apache/groovy/nio/extensions/NioExtensionsTest.groovy
+++
b/subprojects/groovy-nio/src/test/groovy/org/apache/groovy/nio/extensions/NioExtensionsTest.groovy
@@ -659,13 +659,9 @@ class NioExtensionsTest extends Specification {
def writable = path.filterLine { it.startsWith('a') ||
it.startsWith('g') || it.startsWith('d') }
def sw = new StringWriter()
writable.writeTo(sw)
- def result = sw.toString()
then:
- result.contains('alpha')
- result.contains('gamma')
- result.contains('delta')
- !result.contains('beta')
+ sw.toString() ==
['alpha','gamma','delta',''].join(System.lineSeparator())
}
def testFilterLineWithCharset() {
@@ -679,7 +675,7 @@ class NioExtensionsTest extends Specification {
writable.writeTo(sw)
then:
- sw.toString().trim() == 'alpha\ngamma'
+ sw.toString() == ['alpha','gamma',''].join(System.lineSeparator())
}
def testFilterLineToWriter() {
@@ -692,7 +688,7 @@ class NioExtensionsTest extends Specification {
path.filterLine(sw) { it.contains('2') || it.contains('4') }
then:
- sw.toString().trim() == 'line2\nline4'
+ sw.toString() == ['line2','line4',''].join(System.lineSeparator())
}
def testCreateParentDirectories() {
diff --git
a/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/FormatStringChecker.groovy
b/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/FormatStringChecker.groovy
index 893cab1ae0..a93af52c30 100644
---
a/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/FormatStringChecker.groovy
+++
b/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/FormatStringChecker.groovy
@@ -68,8 +68,10 @@ import static org.codehaus.groovy.ast.ClassHelper.makeCached
*/
@Incubating
class FormatStringChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
+
private static final ClassNode LOCALE_TYPE = makeCached(Locale)
private static final ClassNode FORMATTER_TYPE = makeCached(Formatter)
+
private String formatSpecifier = /%(\d+\$)?([-#+
0,(\<]*)?(\d+)?(\.\d+)?([tT])?([a-zA-Z%])/
private List<ASTNode> formatMethods = [
macro(CompilePhase.SEMANTIC_ANALYSIS) { String.format(a)
}.withConstraints { varargPlaceholder a },
@@ -95,13 +97,13 @@ class FormatStringChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckin
makeVisitor().checkFormatterMethod(call)
}
}
+
afterVisitMethod { MethodNode method ->
- def visitor = makeVisitor()
- method.code.visit(visitor)
+ method.code.visit(makeVisitor())
}
}
- private makeVisitor() {
+ private CheckingVisitor makeVisitor() {
new CheckingVisitor() {
@Override
void visitMethodCallExpression(MethodCallExpression call) {
@@ -145,7 +147,7 @@ class FormatStringChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckin
}
}
- void checkFormatStringTypes(Expression expression,
List<Expression> args, Expression target) {
+ private void checkFormatStringTypes(Expression expression,
List<Expression> args, Expression target) {
int next = 0
int prevIndex = -1
expression.value.eachMatch(formatSpecifier) { spec ->
@@ -254,14 +256,14 @@ class FormatStringChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckin
}
}
- void checkBadFlags(flagList, conversion, Expression target, String
badFlags) {
+ private void checkBadFlags(flagList, conversion, Expression
target, String badFlags) {
def mismatched = flagList?.findAll { badFlags.contains(it)
}?.join()
if (mismatched) {
addStaticTypeError("FormatFlagsConversionMismatch:
Conversion = $conversion, Flags = '$mismatched'", target)
}
}
- void checkFormatStringConstantArgs(ConstantExpression
formatString, args, Expression target) {
+ private void checkFormatStringConstantArgs(ConstantExpression
formatString, args, Expression target) {
try {
new Formatter().format(formatString.value, *args)
} catch (IllegalFormatException ex) {
@@ -275,7 +277,7 @@ class FormatStringChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckin
void visitDeclarationExpression(DeclarationExpression decl) {
super.visitDeclarationExpression(decl)
if (decl.variableExpression != null) {
- if (isConstantExpression(decl.rightExpression)) {
+ if (decl.rightExpression instanceof ConstantExpression) {
localConstVars.put(decl.variableExpression,
decl.rightExpression)
}
}
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 09e6e6a16d..f53ef4f3ed 100644
---
a/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/RegexChecker.groovy
+++
b/subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/RegexChecker.groovy
@@ -25,12 +25,14 @@ import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.expr.BinaryExpression
import org.codehaus.groovy.ast.expr.BitwiseNegationExpression
+import org.codehaus.groovy.ast.expr.ClassExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.expr.DeclarationExpression
import org.codehaus.groovy.ast.expr.Expression
import org.codehaus.groovy.ast.expr.MethodCall
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.syntax.Types
import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport
import org.codehaus.groovy.transform.stc.StaticTypesMarker
@@ -100,14 +102,15 @@ import static
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.checkC
*/
@Incubating
class RegexChecker extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
- private static final String REGEX_GROUP_COUNT = RegexChecker.simpleName +
'_INFERRED_GROUP_COUNT'
- private static final String REGEX_MATCHER_RESULT_TYPE =
RegexChecker.simpleName + '_MATCHER_RESULT_INFERRED_TYPE'
+
private static final ClassNode MATCHER_TYPE = ClassHelper.make(Matcher)
+ private static final String REGEX_GROUP_COUNT =
RegexChecker.getSimpleName() + '_INFERRED_GROUP_COUNT'
+ private static final String REGEX_MATCHER_RESULT_TYPE =
RegexChecker.getSimpleName() + '_MATCHER_RESULT_INFERRED_TYPE'
@Override
Object run() {
beforeVisitMethod { MethodNode method ->
- def visitor = new CheckingVisitor() {
+ method.code.visit(new CheckingVisitor() {
@Override
void visitBitwiseNegationExpression(BitwiseNegationExpression
expression) {
super.visitBitwiseNegationExpression(expression)
@@ -122,7 +125,7 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
def exp = findConstExp(expression.rightExpression,
String)
checkRegex(exp, expression)
} else if (expression.operation.type ==
Types.LEFT_SQUARE_BRACKET) {
- if (isVariableExpression(expression.leftExpression)) {
+ if (expression.leftExpression instanceof
VariableExpression) {
def var =
findTargetVariable(expression.leftExpression)
def groupCount =
var?.getNodeMetaData(REGEX_GROUP_COUNT)
if (groupCount != null) {
@@ -140,7 +143,7 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
@Override
void visitMethodCallExpression(MethodCallExpression call) {
super.visitMethodCallExpression(call)
- if (isClassExpression(call.objectExpression)) {
+ if (call.objectExpression instanceof ClassExpression) {
checkPatternMethod(call, call.objectExpression.type)
} else if (isPattern(call.receiver) && call.methodAsString
== 'matcher') {
def var = findTargetVariable(call.receiver)
@@ -169,7 +172,7 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
void visitDeclarationExpression(DeclarationExpression decl) {
super.visitDeclarationExpression(decl)
if (decl.variableExpression != null) {
- if (isConstantExpression(decl.rightExpression)) {
+ if (decl.rightExpression instanceof
ConstantExpression) {
localConstVars.put(decl.variableExpression,
decl.rightExpression)
}
def groupCount =
decl.rightExpression.getNodeMetaData(REGEX_GROUP_COUNT)
@@ -178,15 +181,13 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
}
}
}
-
- }
- method.code.visit(visitor)
+ })
}
incompatibleAssignment { lhsType, rhsType, expr ->
- if (isBinaryExpression(expr) && isAssignment(expr.operation.type))
{
+ if (expr instanceof BinaryExpression &&
isAssignment(expr.operation.type)) {
def from = expr.rightExpression
- if (isBinaryExpression(from) && from.operation.type ==
Types.LEFT_SQUARE_BRACKET && getType(from.leftExpression) == MATCHER_TYPE) {
+ if (from instanceof BinaryExpression && from.operation.type ==
Types.LEFT_SQUARE_BRACKET && getType(from.leftExpression) == MATCHER_TYPE) {
ClassNode inferred =
from.getNodeMetaData(REGEX_MATCHER_RESULT_TYPE)
if (inferred) {
handled = true
@@ -200,9 +201,9 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
}
}
- methodNotFound { receiverType, name, argList, argTypes, call ->
+ methodNotFound { receiverType, name, argList, argTypes, MethodCall
call ->
def receiver = call.receiver
- if (isBinaryExpression(receiver) && receiver.operation.type ==
Types.LEFT_SQUARE_BRACKET && getType(receiver.leftExpression) == MATCHER_TYPE) {
+ if (receiver instanceof BinaryExpression &&
receiver.operation.type == Types.LEFT_SQUARE_BRACKET &&
getType(receiver.leftExpression) == MATCHER_TYPE) {
ClassNode inferred =
receiver.getNodeMetaData(REGEX_MATCHER_RESULT_TYPE)
if (inferred) {
makeDynamic(call, inferred)
@@ -211,12 +212,12 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
}
afterVisitMethod { MethodNode method ->
- def visitor = new CheckingVisitor() {
+ method.code.visit(new CheckingVisitor() {
@Override
void visitDeclarationExpression(DeclarationExpression decl) {
super.visitDeclarationExpression(decl)
if (decl.variableExpression != null) {
- if (isConstantExpression(decl.rightExpression)) {
+ if (decl.rightExpression instanceof
ConstantExpression) {
localConstVars.put(decl.variableExpression,
decl.rightExpression)
}
def groupCount =
decl.rightExpression.getNodeMetaData(REGEX_GROUP_COUNT)
@@ -236,7 +237,7 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
}
}
super.visitMethodCallExpression(call)
- if (isVariableExpression(call.objectExpression) &&
call.methodAsString == 'group' && isMatcher(call.receiver) &&
call.arguments.expressions) {
+ if (call.objectExpression instanceof VariableExpression &&
call.methodAsString == 'group' && isMatcher(call.receiver) &&
call.arguments.expressions) {
def var = findTargetVariable(call.receiver)
def maxCnt = var?.getNodeMetaData(REGEX_GROUP_COUNT)
if (maxCnt != null) {
@@ -261,21 +262,22 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
}
}
}
- }
- method.code.visit(visitor)
+ })
}
}
- private boolean isMatcher(Expression obj) {
- obj.type == MATCHER_TYPE ||
- obj.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) ==
MATCHER_TYPE ||
- obj.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE) ==
MATCHER_TYPE
+
//--------------------------------------------------------------------------
+
+ private boolean isMatcher(Expression exp) {
+ exp.type == MATCHER_TYPE
+ || exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) ==
MATCHER_TYPE
+ || exp.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE) ==
MATCHER_TYPE
}
- private boolean isPattern(Expression obj) {
- obj.type == PATTERN_TYPE ||
- obj.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) ==
PATTERN_TYPE ||
- obj.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE) ==
PATTERN_TYPE
+ private boolean isPattern(Expression exp) {
+ exp.type == PATTERN_TYPE
+ || exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) ==
PATTERN_TYPE
+ || exp.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE) ==
PATTERN_TYPE
}
private void checkRegex(ConstantExpression regex, Expression target) {
@@ -290,5 +292,4 @@ class RegexChecker extends
GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {
addStaticTypeError('Bad regex' + additional + ex.message, target)
}
}
-
}
diff --git
a/subprojects/groovy-typecheckers/src/main/groovy/org/apache/groovy/typecheckers/CheckingVisitor.groovy
b/subprojects/groovy-typecheckers/src/main/groovy/org/apache/groovy/typecheckers/CheckingVisitor.groovy
index 1bd63804b5..f7b285fc1d 100644
---
a/subprojects/groovy-typecheckers/src/main/groovy/org/apache/groovy/typecheckers/CheckingVisitor.groovy
+++
b/subprojects/groovy-typecheckers/src/main/groovy/org/apache/groovy/typecheckers/CheckingVisitor.groovy
@@ -18,6 +18,7 @@
*/
package org.apache.groovy.typecheckers
+import groovy.transform.AutoFinal
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassCodeVisitorSupport
import org.codehaus.groovy.ast.FieldNode
@@ -27,10 +28,15 @@ import org.codehaus.groovy.ast.expr.Expression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.control.SourceUnit
-@CompileStatic
-@SuppressWarnings('Instanceof')
+@AutoFinal @CompileStatic
class CheckingVisitor extends ClassCodeVisitorSupport {
- protected final Map<Expression, Expression> localConstVars = new
HashMap<>()
+
+ @Override
+ protected SourceUnit getSourceUnit() {
+ null
+ }
+
+ protected final Map<Expression,Expression> localConstVars = [:]
protected Expression findConstExp(Expression exp, Class type) {
if (exp instanceof ConstantExpression &&
type.isAssignableFrom(exp.value.getClass())) {
@@ -48,16 +54,11 @@ class CheckingVisitor extends ClassCodeVisitorSupport {
null
}
- @Override
- protected SourceUnit getSourceUnit() {
- null
- }
-
- static Variable findTargetVariable(final VariableExpression ve) {
- Variable accessedVariable = ve.accessedVariable
+ protected Variable findTargetVariable(VariableExpression ve) {
+ def accessedVariable = ve.accessedVariable
if (accessedVariable != null && accessedVariable != ve) {
if (accessedVariable instanceof VariableExpression) {
- return findTargetVariable((VariableExpression)
accessedVariable)
+ return findTargetVariable(accessedVariable)
}
return accessedVariable
}