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/22 22:26:26 UTC
[groovy] branch GROOVY_2_5_X updated: GROOVY-9168: emit more specific error for use of uninitialized this
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new c383e9e GROOVY-9168: emit more specific error for use of uninitialized this
c383e9e is described below
commit c383e9ecad465a01a4b18d4254782a604055694a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Jul 14 02:17:55 2019 +0800
GROOVY-9168: emit more specific error for use of uninitialized this
Conflicts:
src/main/java/org/codehaus/groovy/classgen/Verifier.java
src/main/java/org/codehaus/groovy/control/StaticVerifier.java
src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
---
.../groovy/classgen/InnerClassVisitorHelper.java | 11 +-
.../org/codehaus/groovy/classgen/Verifier.java | 208 +++++++++++++--------
.../groovy/classgen/VerifierCodeVisitor.java | 11 +-
.../codehaus/groovy/control/StaticVerifier.java | 84 +--------
.../groovy/tools/javac/JavaStubGenerator.java | 13 +-
src/test/gls/innerClass/InnerClassTest.groovy | 198 ++++++++++++++++++--
.../groovy/bugs/ConstructorParameterBug.groovy | 47 ++---
src/test/groovy/bugs/ConstructorThisCallBug.groovy | 80 ++++----
.../{Groovy5259Bug.groovy => Groovy5259.groovy} | 23 +--
9 files changed, 428 insertions(+), 247 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
index 4b67f23..bc9292a 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
@@ -28,7 +28,6 @@ import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.FieldExpression;
import org.codehaus.groovy.ast.expr.GStringExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
@@ -44,6 +43,10 @@ import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.List;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+
public abstract class InnerClassVisitorHelper extends ClassCodeVisitorSupport {
protected static void setPropertyGetterDispatcher(BlockStatement block, Expression thiz, Parameter[] parameters) {
@@ -120,11 +123,7 @@ public abstract class InnerClassVisitorHelper extends ClassCodeVisitorSupport {
}
protected static void addFieldInit(Parameter p, FieldNode fn, BlockStatement block) {
- VariableExpression ve = new VariableExpression(p);
- FieldExpression fe = new FieldExpression(fn);
- block.addStatement(new ExpressionStatement(
- new BinaryExpression(fe, Token.newSymbol(Types.ASSIGN, -1, -1), ve)
- ));
+ block.addStatement(assignS(fieldX(fn), varX(p)));
}
protected static boolean shouldHandleImplicitThisForInnerClass(ClassNode cn) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index c76e987..3c461c6 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -20,6 +20,7 @@ package org.codehaus.groovy.classgen;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
import groovy.lang.MetaClass;
import groovy.transform.Generated;
import groovy.transform.Internal;
@@ -31,6 +32,7 @@ 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.DynamicVariable;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.GroovyClassVisitor;
@@ -47,7 +49,6 @@ import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
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.FieldExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
@@ -56,7 +57,6 @@ import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.ast.tools.GenericsUtils;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.classgen.asm.MopWriter;
import org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.ClassNodeSkip;
@@ -93,13 +93,16 @@ import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
import static org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
import static org.apache.groovy.ast.tools.MethodNodeUtils.getPropertyName;
import static org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics;
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;
/**
* Verifies the AST node and adds any default AST code before bytecode generation occurs.
- *
+ * <p>
* Checks include:
* <ul>
* <li>Methods with duplicate signatures</li>
@@ -168,6 +171,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
metaClassField =
node.addField("metaClass", ACC_PRIVATE | ACC_TRANSIENT | ACC_SYNTHETIC, ClassHelper.METACLASS_TYPE,
new BytecodeExpression(ClassHelper.METACLASS_TYPE) {
+ @Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
@@ -200,11 +204,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
return null;
}
- /**
- * walk the class
- *
- * @param node the node to visit
- */
+ @Override
public void visitClass(final ClassNode node) {
this.classNode = node;
@@ -362,6 +362,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
Parameter.EMPTY_ARRAY,
ClassNode.EMPTY_ARRAY,
new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
@@ -419,6 +420,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
Parameter.EMPTY_ARRAY,
ClassNode.EMPTY_ARRAY,
new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
Label nullLabel = new Label();
/*
@@ -468,6 +470,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
} else {
List list = new ArrayList();
list.add(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
/*
* the code is (meta class is stored in 1):
@@ -508,6 +511,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
ClassHelper.OBJECT_TYPE, INVOKE_METHOD_PARAMS,
ClassNode.EMPTY_ARRAY,
new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
@@ -533,6 +537,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
GET_PROPERTY_PARAMS,
ClassNode.EMPTY_ARRAY,
new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
@@ -557,6 +562,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
SET_PROPERTY_PARAMS,
ClassNode.EMPTY_ARRAY,
new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
@@ -600,73 +606,118 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
private static void checkReturnInObjectInitializer(List<Statement> init) {
- CodeVisitorSupport cvs = new CodeVisitorSupport() {
+ GroovyCodeVisitor visitor = new CodeVisitorSupport() {
@Override
public void visitClosureExpression(ClosureExpression expression) {
// return is OK in closures in object initializers
}
-
+ @Override
public void visitReturnStatement(ReturnStatement statement) {
throw new RuntimeParserException("'return' is not allowed in object initializer", statement);
}
};
- for (Statement stm : init) {
- stm.visit(cvs);
- }
- }
-
- public void visitConstructor(ConstructorNode node) {
- CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
- boolean firstMethodCall = true;
- String type = null;
-
- public void visitMethodCallExpression(MethodCallExpression call) {
- if (!firstMethodCall) return;
- firstMethodCall = false;
- String name = call.getMethodAsString();
- // the name might be null if the method name is a GString for example
- if (name == null) return;
- if (!name.equals("super") && !name.equals("this")) return;
- type = name;
- call.getArguments().visit(this);
- type = null;
- }
+ for (Statement stmt : init) {
+ stmt.visit(visitor);
+ }
+ }
+
+ @Override
+ public void visitConstructor(final ConstructorNode node) {
+ Statement stmt = node.getCode();
+ if (stmt != null) {
+ stmt.visit(new VerifierCodeVisitor(getClassNode()));
+ // check for uninitialized-this references
+ stmt.visit(new CodeVisitorSupport() {
+ @Override
+ public void visitClosureExpression(ClosureExpression ce) {
+ boolean oldInClosure = inClosure;
+ inClosure = true;
+ super.visitClosureExpression(ce);
+ inClosure = oldInClosure;
+ }
- public void visitConstructorCallExpression(ConstructorCallExpression call) {
- if (!call.isSpecialCall()) return;
- type = call.getText();
- call.getArguments().visit(this);
- type = null;
- }
+ @Override
+ public void visitConstructorCallExpression(ConstructorCallExpression cce) {
+ boolean oldIsSpecialConstructorCall = inSpecialConstructorCall;
+ inSpecialConstructorCall |= cce.isSpecialCall();
+ super.visitConstructorCallExpression(cce);
+ inSpecialConstructorCall = oldIsSpecialConstructorCall;
+ }
- public void visitVariableExpression(VariableExpression expression) {
- if (type == null) return;
- String name = expression.getName();
- if (!name.equals("this") && !name.equals("super")) return;
- throw new RuntimeParserException("cannot reference " + name + " inside of " + type + "(....) before supertype constructor has been called", expression);
- }
- };
- Statement s = node.getCode();
- if (s == null) {
- return;
- } else {
- s.visit(new VerifierCodeVisitor(this));
+ @Override
+ public void visitMethodCallExpression(MethodCallExpression mce) {
+ if (inSpecialConstructorCall && isThisObjectExpression(mce)) {
+ MethodNode methodTarget = mce.getMethodTarget();
+ if (methodTarget == null || !(methodTarget.isStatic() || classNode.getOuterClasses().contains(methodTarget.getDeclaringClass()))) {
+ if (!mce.isImplicitThis()) {
+ throw newVariableError(mce.getObjectExpression().getText(), mce.getObjectExpression());
+ } else {
+ throw newVariableError(mce.getMethodAsString(), mce.getMethod());
+ }
+ }
+ mce.getMethod().visit(this);
+ mce.getArguments().visit(this);
+ } else {
+ super.visitMethodCallExpression(mce);
+ }
+ }
+
+ @Override
+ public void visitVariableExpression(VariableExpression ve) {
+ // before this/super ctor call completes, only params and static or outer members are accessible
+ if (inSpecialConstructorCall && (ve.isThisExpression() || ve.isSuperExpression() || isNonStaticMemberAccess(ve))) {
+ throw newVariableError(ve.getName(), ve.getLineNumber() > 0 ? ve : node); // TODO: context for default argument
+ }
+ }
+
+ //
+
+ private boolean inClosure, inSpecialConstructorCall;
+
+ private boolean isNonStaticMemberAccess(VariableExpression ve) {
+ Variable variable = ve.getAccessedVariable();
+ return !inClosure && variable != null && !isStatic(variable.getModifiers())
+ && !(variable instanceof DynamicVariable) && !(variable instanceof Parameter);
+ }
+
+ private boolean isThisObjectExpression(MethodCallExpression mce) {
+ if (mce.isImplicitThis()) {
+ return true;
+ } else if (mce.getObjectExpression() instanceof VariableExpression) {
+ VariableExpression var = (VariableExpression) mce.getObjectExpression();
+ return var.isThisExpression() || var.isSuperExpression();
+ } else {
+ return false;
+ }
+ }
+
+ private GroovyRuntimeException newVariableError(String name, ASTNode node) {
+ RuntimeParserException rpe = new RuntimeParserException("Cannot reference '" + name +
+ "' before supertype constructor has been called. Possible causes:\n" +
+ "You attempted to access an instance field, method, or property.\n" +
+ "You attempted to construct a non-static inner class.", node);
+ rpe.setModule(getClassNode().getModule());
+ return rpe;
+ }
+ });
}
- s.visit(checkSuper);
}
+ @Override
public void visitMethod(MethodNode node) {
- //GROOVY-3712 - if it's an MOP method, it's an error as they aren't supposed to exist before ACG is invoked
+ // GROOVY-3712 - if it's an MOP method, it's an error as they aren't supposed to exist before ACG is invoked
if (MopWriter.isMopMethod(node.getName())) {
- throw new RuntimeParserException("Found unexpected MOP methods in the class node for " + classNode.getName() +
- "(" + node.getName() + ")", classNode);
+ throw new RuntimeParserException("Found unexpected MOP methods in the class node for " + classNode.getName() + "(" + node.getName() + ")", classNode);
}
- this.methodNode = node;
+
adjustTypesIfStaticMainMethod(node);
+ this.methodNode = node;
addReturnIfNeeded(node);
- Statement statement;
- statement = node.getCode();
- if (statement != null) statement.visit(new VerifierCodeVisitor(this));
+
+ Statement stmt = node.getCode();
+ if (stmt != null) {
+ stmt.visit(new VerifierCodeVisitor(getClassNode()));
+ }
}
private static void adjustTypesIfStaticMainMethod(MethodNode node) {
@@ -690,6 +741,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
adder.visitMethod(node);
}
+ @Override
public void visitField(FieldNode node) {
}
@@ -703,6 +755,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
return true;
}
+ @Override
public void visitProperty(PropertyNode node) {
String name = node.getName();
FieldNode field = node.getField();
@@ -796,7 +849,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
/**
- * Creates a new helper method for each combination of default parameter expressions
+ * Creates a new method for each combination of default parameter expressions.
*/
protected void addDefaultParameterMethods(final ClassNode node) {
List methods = new ArrayList(node.getMethods());
@@ -822,20 +875,20 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
if (argument instanceof ClosureExpression) {
final List<Parameter> newMethodNodeParameters = Arrays.asList(newParams);
- CodeVisitorSupport visitor = new CodeVisitorSupport() {
+ GroovyCodeVisitor visitor = new CodeVisitorSupport() {
@Override
public void visitVariableExpression(VariableExpression expression) {
Variable v = expression.getAccessedVariable();
if (!(v instanceof Parameter)) return;
Parameter param = (Parameter) v;
- if (param.hasInitialExpression() && code.getVariableScope().getDeclaredVariable(param.getName()) == null && !newMethodNodeParameters.contains(param)) {
-
+ VariableScope variableScope = code.getVariableScope();
+ if (param.hasInitialExpression() && variableScope.getDeclaredVariable(param.getName()) == null && !newMethodNodeParameters.contains(param)) {
VariableExpression localVariable = new VariableExpression(param.getName(), ClassHelper.makeReference());
- DeclarationExpression declarationExpression = new DeclarationExpression(localVariable, Token.newSymbol(Types.EQUAL, -1, -1), new ConstructorCallExpression(ClassHelper.makeReference(), param.getInitialExpression()));
+ localVariable.setInStaticContext(variableScope.isInStaticContext());
+ variableScope.putDeclaredVariable(localVariable);
- code.addStatement(new ExpressionStatement(declarationExpression));
- code.getVariableScope().putDeclaredVariable(localVariable);
+ code.addStatement(declS(localVariable, ctorX(localVariable.getType(), param.getInitialExpression())));
}
}
};
@@ -873,6 +926,9 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
});
}
+ /**
+ * Creates a new constructor for each combination of default parameter expressions.
+ */
protected void addDefaultParameterConstructors(final ClassNode node) {
List methods = new ArrayList(node.getDeclaredConstructors());
addDefaultParameters(methods, new DefaultArgsAction() {
@@ -904,11 +960,10 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
/**
- * Creates a new helper method for each combination of default parameter expressions
+ * Creates a new helper method for each combination of default parameter expressions.
*/
protected void addDefaultParameters(Iterable<? extends MethodNode> methods, DefaultArgsAction action) {
- for (Object next : methods) {
- MethodNode method = (MethodNode) next;
+ for (MethodNode method : methods) {
if (method.hasDefaultValue()) {
addDefaultParameters(action, method);
}
@@ -1097,9 +1152,9 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
/*
- * when InnerClassVisitor adds this.this$0 = $p$n, it adds it as a BlockStatement having that
- * ExpressionStatement
- */
+ * When InnerClassVisitor adds <code>this.this$0 = $p$n</code>, it adds it
+ * as a BlockStatement having that ExpressionStatement.
+ */
private Statement getImplicitThis$0StmtIfInnerClass(List<Statement> otherStatements) {
if (!(classNode instanceof InnerClassNode)) return null;
for (Statement stmt : otherStatements) {
@@ -1154,7 +1209,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
if (expr.getOperation().getType() == Types.ASSIGN
&& expr.getLeftExpression() instanceof FieldExpression
&& expr.getLeftExpression().getType().equals(ClassHelper.REFERENCE_TYPE)
- && (((FieldExpression) expr.getLeftExpression()).getField().getModifiers() & Opcodes.ACC_SYNTHETIC) != 0
+ && (((FieldExpression) expr.getLeftExpression()).getField().getModifiers() & ACC_SYNTHETIC) != 0
/* also could check if the right expression is a variable expression that references ctor parameter */) {
localVariableReferences.add(stmt);
it.remove();
@@ -1169,7 +1224,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
Expression expression = fieldNode.getInitialExpression();
if (expression != null) {
final FieldExpression fe = new FieldExpression(fieldNode);
- if (fieldNode.getType().equals(ClassHelper.REFERENCE_TYPE) && ((fieldNode.getModifiers() & Opcodes.ACC_SYNTHETIC) != 0)) {
+ if (fieldNode.getType().equals(ClassHelper.REFERENCE_TYPE) && ((fieldNode.getModifiers() & ACC_SYNTHETIC) != 0)) {
fe.setUseReferenceDirectly(true);
}
ExpressionStatement statement =
@@ -1210,7 +1265,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
/**
- * Capitalizes the start of the given bean property name
+ * Capitalizes the start of the given bean property name.
*/
public static String capitalize(String name) {
return MetaClassHelper.capitalize(name);
@@ -1232,6 +1287,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
protected Statement createSetterBlock(PropertyNode propertyNode, final FieldNode field) {
return new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
if (field.isStatic()) {
BytecodeHelper.load(mv, field.getType(), 0);
@@ -1247,7 +1303,6 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
public void visitGenericType(GenericsType genericsType) {
-
}
public static Long getTimestampFromFieldName(String fieldName) {
@@ -1374,7 +1429,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
if (!equalParameters && !equalParametersWithGenerics(overridingMethod, oldMethod, genericsSpec)) return null;
// correct to method level generics for the overriding method
- genericsSpec = GenericsUtils.addMethodGenerics(overridingMethod, genericsSpec);
+ genericsSpec = addMethodGenerics(overridingMethod, genericsSpec);
// return type
ClassNode mr = overridingMethod.getReturnType();
@@ -1444,6 +1499,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
List instructions = new ArrayList(1);
instructions.add(
new BytecodeInstruction() {
+ @Override
public void visit(MethodVisitor mv) {
mv.visitVarInsn(ALOAD, 0);
Parameter[] para = oldMethod.getParameters();
@@ -1540,7 +1596,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
private static boolean moveOptimizedConstantsInitialization(final ClassNode node) {
if (node.isInterface() && !Traits.isTrait(node)) return false;
- final int mods = Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC;
+ final int mods = ACC_STATIC | ACC_SYNTHETIC | ACC_PUBLIC;
String name = SWAP_INIT;
BlockStatement methodCode = new BlockStatement();
diff --git a/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java b/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
index 2cb9fc2..2fe6a17 100644
--- a/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
@@ -29,19 +29,18 @@ import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.syntax.RuntimeParserException;
-import org.objectweb.asm.Opcodes;
/**
* Performs various checks on code inside methods and constructors
* including checking for valid field, variables names etc. that
* would otherwise lead to invalid code.
*/
-public class VerifierCodeVisitor extends CodeVisitorSupport implements Opcodes {
+public class VerifierCodeVisitor extends CodeVisitorSupport {
- private final Verifier verifier;
+ private final ClassNode classNode;
- VerifierCodeVisitor(Verifier verifier) {
- this.verifier = verifier;
+ public VerifierCodeVisitor(ClassNode classNode) {
+ this.classNode = classNode;
}
public void visitForLoop(ForStatement expression) {
@@ -72,7 +71,7 @@ public class VerifierCodeVisitor extends CodeVisitorSupport implements Opcodes {
public void visitConstructorCallExpression(ConstructorCallExpression call) {
ClassNode callType = call.getType();
- if (callType.isEnum() && !callType.equals(verifier.getClassNode())) {
+ if (callType.isEnum() && !callType.equals(classNode)) {
throw new RuntimeParserException("Enum constructor calls are only allowed inside the enum class", call);
}
}
diff --git a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
index daeb2e5..f2eb2e7 100644
--- a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
+++ b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
@@ -20,26 +20,17 @@ package org.codehaus.groovy.control;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.CodeVisitorSupport;
import org.codehaus.groovy.ast.DynamicVariable;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
-
-import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
/**
* Checks for dynamic variables in static contexts.
@@ -70,7 +61,7 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
@Override
public void visitConstructorCallExpression(ConstructorCallExpression cce) {
boolean oldIsSpecialConstructorCall = inSpecialConstructorCall;
- inSpecialConstructorCall = cce.isSpecialCall();
+ inSpecialConstructorCall |= cce.isSpecialCall();
super.visitConstructorCallExpression(cce);
inSpecialConstructorCall = oldIsSpecialConstructorCall;
}
@@ -81,37 +72,13 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
methodNode = node;
super.visitConstructorOrMethod(node, isConstructor);
if (isConstructor) {
- final Set<String> exceptions = new HashSet<>();
- for (final Parameter param : node.getParameters()) {
- exceptions.add(param.getName());
+ for (Parameter param : node.getParameters()) {
if (param.hasInitialExpression()) {
- param.getInitialExpression().visit(new CodeVisitorSupport() {
- @Override
- public void visitVariableExpression(VariableExpression ve) {
- if (exceptions.contains(ve.getName())) return;
- if (ve.getAccessedVariable() instanceof DynamicVariable || !ve.isInStaticContext()) {
- addVariableError(ve);
- }
- }
-
- @Override
- public void visitMethodCallExpression(MethodCallExpression call) {
- Expression objectExpression = call.getObjectExpression();
- if (objectExpression instanceof VariableExpression) {
- VariableExpression ve = (VariableExpression) objectExpression;
- if (ve.isThisExpression()) {
- addError("Can't access instance method '" + call.getMethodAsString() + "' for a constructor parameter default value", param);
- return;
- }
- }
- super.visitMethodCallExpression(call);
- }
-
- @Override
- public void visitClosureExpression(ClosureExpression expression) {
- //skip contents, because of dynamic scope
- }
- });
+ // initial expression will be argument to special constructor call
+ boolean oldIsSpecialConstructorCall = inSpecialConstructorCall;
+ inSpecialConstructorCall = true;
+ param.getInitialExpression().visit(this);
+ inSpecialConstructorCall = oldIsSpecialConstructorCall;
}
}
}
@@ -119,43 +86,6 @@ public class StaticVerifier extends ClassCodeVisitorSupport {
}
@Override
- public void visitMethodCallExpression(MethodCallExpression mce) {
- if (inSpecialConstructorCall && !isInnerClass(methodNode.getDeclaringClass())) {
- Expression objectExpression = mce.getObjectExpression();
- if (objectExpression instanceof VariableExpression) {
- VariableExpression ve = (VariableExpression) objectExpression;
- if (ve.isThisExpression()) {
- addError("Can't access instance method '" + mce.getMethodAsString() + "' before the class is constructed", mce);
- return;
- }
- }
- }
- super.visitMethodCallExpression(mce);
- }
-
- @Override // TODO: dead code?
- public void visitPropertyExpression(PropertyExpression pe) {
- if (!inClosure && !inSpecialConstructorCall) {
- for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
- if (it instanceof PropertyExpression) continue;
- if (it instanceof VariableExpression) {
- VariableExpression ve = (VariableExpression) it;
- if (ve.isThisExpression() || ve.isSuperExpression()) return;
- if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;
- if (methodNode != null && methodNode.isStatic()) {
- FieldNode fieldNode = getDeclaredOrInheritedField(methodNode.getDeclaringClass(), ve.getName());
- if (fieldNode != null && fieldNode.isStatic()) return;
- }
- Variable v = ve.getAccessedVariable();
- if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;
- addVariableError(ve);
- }
- return;
- }
- }
- }
-
- @Override
public void visitVariableExpression(VariableExpression ve) {
if (ve.getAccessedVariable() instanceof DynamicVariable && (ve.isInStaticContext() || inSpecialConstructorCall) && !inClosure) {
// GROOVY-5687: interface constants not visible to implementing sub-class in static context
diff --git a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index a62d385..246503a 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -47,6 +47,7 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.classgen.FinalVariableAnalyzer;
import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.classgen.VerifierCodeVisitor;
import org.codehaus.groovy.control.ResolveVisitor;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.tools.Utilities;
@@ -182,8 +183,11 @@ public class JavaStubGenerator {
}
@Override
- protected FinalVariableAnalyzer.VariableNotFinalCallback getFinalVariablesCallback() {
- return null;
+ public void visitConstructor(ConstructorNode node) {
+ Statement stmt = node.getCode();
+ if (stmt != null) {
+ stmt.visit(new VerifierCodeVisitor(getClassNode()));
+ }
}
@Override
@@ -244,6 +248,11 @@ public class JavaStubGenerator {
protected void addDefaultConstructor(ClassNode node) {
// not required for stub generation
}
+
+ @Override
+ protected FinalVariableAnalyzer.VariableNotFinalCallback getFinalVariablesCallback() {
+ return null;
+ }
};
int origNumConstructors = classNode.getDeclaredConstructors().size();
verifier.visitClass(classNode);
diff --git a/src/test/gls/innerClass/InnerClassTest.groovy b/src/test/gls/innerClass/InnerClassTest.groovy
index a0434dc..b140cd2 100644
--- a/src/test/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/gls/innerClass/InnerClassTest.groovy
@@ -1066,20 +1066,21 @@ final class InnerClassTest {
}
@Test // GROOVY-5989
- void testReferenceToOuterClassNestedInterface() {
+ void testResolveInnerOfSuperType1() {
assertScript '''
- interface Koo { class Inner { } }
+ interface I { class C { } }
- class Usage implements Koo {
- static class MyInner extends Inner { }
+ class Outer implements I {
+ static class Inner extends C { }
}
- assert new Usage() != null
+ new Outer()
+ new Outer.Inner()
'''
}
@Test // GROOVY-5754
- void testResolveInnerOfSuperType1() {
+ void testResolveInnerOfSuperType2() {
assertScript '''
interface I { class C { } }
@@ -1091,8 +1092,177 @@ final class InnerClassTest {
'''
}
+ @Test // GROOVY-8364
+ void testResolveInnerOfSuperType3() {
+ assertScript '''
+ abstract class A { static class C { } }
+
+ class B extends A {
+ static m() {
+ C
+ }
+ }
+
+ assert B.m() == A.C
+ '''
+ }
+
+ @Test // GROOVY-8364
+ void testResolveInnerOfSuperType4() {
+ assertScript '''
+ abstract class A { interface I { } }
+
+ class B extends A {
+ static m() {
+ I
+ }
+ }
+
+ assert B.m() == A.I
+ '''
+ }
+
+ @CompileDynamic @Test // GROOVY-8364
+ void testResolveInnerOfSuperType5() {
+ def config = new CompilerConfiguration(
+ targetDirectory: File.createTempDir(),
+ jointCompilationOptions: [stubDir: File.createTempDir()]
+ )
+ def parentDir = File.createTempDir()
+ try {
+ new File(parentDir, 'p').mkdir()
+ new File(parentDir, 'q').mkdir()
+
+ def a = new File(parentDir, 'p/A.Java')
+ a.write '''
+ package p;
+ public abstract class A {
+ public interface I { }
+ }
+ '''
+ def b = new File(parentDir, 'q/B.groovy')
+ b.write '''
+ package q
+ import p.A
+ class B extends A {
+ static m() {
+ I
+ }
+ }
+ '''
+
+ def loader = new GroovyClassLoader(this.class.classLoader)
+ def cu = new JavaAwareCompilationUnit(config, loader)
+ cu.addSources(a, b)
+ cu.compile()
+
+ assert loader.loadClass('q.B').m() instanceof Class
+ } finally {
+ config.jointCompilationOptions.stubDir.deleteDir()
+ config.targetDirectory.deleteDir()
+ parentDir.deleteDir()
+ }
+ }
+
+ @CompileDynamic @Test // GROOVY-8359
+ void testResolveInnerOfSuperType6() {
+ def config = new CompilerConfiguration(
+ targetDirectory: File.createTempDir(),
+ jointCompilationOptions: [stubDir: File.createTempDir()]
+ )
+ def parentDir = File.createTempDir()
+ try {
+ new File(parentDir, 'p').mkdir()
+ new File(parentDir, 'q').mkdir()
+
+ def a = new File(parentDir, 'p/A.Java')
+ a.write '''
+ package p;
+ public abstract class A {
+ public interface I { }
+ }
+ '''
+ def b = new File(parentDir, 'q/B.groovy')
+ b.write '''
+ package q
+ import p.A
+ class B extends A {
+ static m() {
+ I
+ }
+ }
+ '''
+
+ def loader = new GroovyClassLoader(this.class.classLoader)
+ def cu = new JavaAwareCompilationUnit(config, loader)
+ cu.addSources(a)
+ cu.compile()
+
+ loader = new GroovyClassLoader(this.class.classLoader)
+ cu = new JavaAwareCompilationUnit(config, loader)
+ cu.addSources(b)
+ cu.compile()
+
+ assert loader.loadClass('q.B').m() instanceof Class
+ } finally {
+ config.jointCompilationOptions.stubDir.deleteDir()
+ config.targetDirectory.deleteDir()
+ parentDir.deleteDir()
+ }
+ }
+
+ @Test // GROOVY-8358
+ void testResolveInnerOfSuperType7() {
+ assertScript '''
+ class Outer implements I {
+ static class Inner extends C {
+ static usage() {
+ new T() // whoami?
+ }
+ }
+ }
+
+ class C implements H { }
+
+ interface H {
+ static class T {}
+ }
+
+ interface I {
+ static class T {}
+ }
+
+ assert Outer.Inner.usage() instanceof H.T
+ '''
+ }
+
+ @Test // GROOVY-8358
+ void testResolveInnerOfSuperType8() {
+ assertScript '''
+ class C implements H { } // moved ahead of Outer
+
+ class Outer implements I {
+ static class Inner extends C {
+ static usage() {
+ new T() // whoami?
+ }
+ }
+ }
+
+ interface H {
+ static class T {}
+ }
+
+ interface I {
+ static class T {}
+ }
+
+ assert Outer.Inner.usage() instanceof H.T
+ '''
+ }
+
@Test // GROOVY-9642
- void testResolveInnerOfSuperType2() {
+ void testResolveInnerOfSuperType9() {
assertScript '''
class C {
interface I {}
@@ -1112,7 +1282,7 @@ final class InnerClassTest {
}
@Test
- void testResolveInnerOfSuperType3() {
+ void testResolveInnerOfSuperType10() {
assertScript '''
abstract class A {
static class B {}
@@ -1127,7 +1297,7 @@ final class InnerClassTest {
}
@Test
- void testResolveInnerOfSuperType4() {
+ void testResolveInnerOfSuperType10a() {
assertScript '''
abstract class A {
static class B {}
@@ -1142,7 +1312,7 @@ final class InnerClassTest {
}
@CompileDynamic @Test // GROOVY-8715
- void testResolveInnerOfSuperType5() {
+ void testResolveInnerOfSuperType10b() {
def config = new CompilerConfiguration(
targetDirectory: File.createTempDir(),
jointCompilationOptions: [stubDir: File.createTempDir()]
@@ -1186,7 +1356,6 @@ final class InnerClassTest {
import groovy.transform.ASTTest
import org.codehaus.groovy.ast.expr.*
import static org.codehaus.groovy.classgen.Verifier.*
- import static org.codehaus.groovy.control.CompilePhase.*
class A {
@ASTTest(phase=CLASS_GENERATION, value={
@@ -1451,9 +1620,7 @@ final class InnerClassTest {
@Test // GROOVY-7312
void testInnerClassOfInterfaceIsStatic2() {
assertScript '''
- import java.lang.reflect.Modifier
import groovy.transform.ASTTest
- import org.codehaus.groovy.control.CompilePhase
import org.objectweb.asm.Opcodes
@ASTTest(phase = CLASS_GENERATION, value = {
@@ -1541,7 +1708,7 @@ final class InnerClassTest {
'''
}
- @Test @NotYetImplemented // GROOVY-9168
+ @Test // GROOVY-9168
void testReferenceToUninitializedThis4() {
def err = shouldFail '''
class Outer {
@@ -1559,7 +1726,7 @@ final class InnerClassTest {
assert err =~ / Cannot reference 'this' before supertype constructor has been called. /
}
- @Test @NotYetImplemented // GROOVY-9168
+ @Test // GROOVY-9168
void testReferenceToUninitializedThis5() {
def err = shouldFail '''
class Outer {
@@ -1581,7 +1748,6 @@ final class InnerClassTest {
import java.util.concurrent.Callable
import org.codehaus.groovy.ast.expr.*
import static org.codehaus.groovy.classgen.Verifier.*
- import static org.codehaus.groovy.control.CompilePhase.*
class A {
@ASTTest(phase=CLASS_GENERATION, value={
diff --git a/src/test/groovy/bugs/ConstructorParameterBug.groovy b/src/test/groovy/bugs/ConstructorParameterBug.groovy
index c881b96..e73e1ee 100644
--- a/src/test/groovy/bugs/ConstructorParameterBug.groovy
+++ b/src/test/groovy/bugs/ConstructorParameterBug.groovy
@@ -18,37 +18,42 @@
*/
package groovy.bugs
-import gls.CompilableTestSupport
+import org.junit.Test
-class ConstructorParameterBug extends CompilableTestSupport {
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+final class ConstructorParameterBug {
+
+ @Test
void testParamWithDefaultCallingStaticMethod() {
assertScript '''
- class StaticDefault {
- def name
- StaticDefault(name = baz()) {
- this.name = name
- }
- private static baz() {
- 'baz'
+ class StaticDefault {
+ def name
+ StaticDefault(name = baz()) {
+ this.name = name
+ }
+ private static baz() {
+ 'baz'
+ }
}
- }
- assert 'baz' == new StaticDefault().name
+ assert 'baz' == new StaticDefault().name
'''
}
+ @Test
void testParamWithDefaultCallingInstanceMethod() {
- def msg = shouldFail '''
- class InstanceDefault {
- def name
- InstanceDefault(name = baz()) {
- this.name = name
- }
- private baz() {
- 'baz'
+ def err = shouldFail '''
+ class InstanceDefault {
+ def name
+ InstanceDefault(name = baz()) {
+ this.name = name
+ }
+ private baz() {
+ 'baz'
+ }
}
- }
'''
- assert msg.contains("Can't access instance method 'baz' for a constructor parameter default value")
+ assert err =~ / Cannot reference 'baz' before supertype constructor has been called. /
}
}
diff --git a/src/test/groovy/bugs/ConstructorThisCallBug.groovy b/src/test/groovy/bugs/ConstructorThisCallBug.groovy
index e6faf9e..bf48d44 100644
--- a/src/test/groovy/bugs/ConstructorThisCallBug.groovy
+++ b/src/test/groovy/bugs/ConstructorThisCallBug.groovy
@@ -18,32 +18,14 @@
*/
package groovy.bugs
-class ConstructorThisCallBug extends GroovyTestCase {
- // GROOVY-7014
- void testThisCallingInstanceMethod() {
- def msg = shouldFail '''
- class Base {
- String getData() { return "ABCD" }
- Base() { this(getData()) }
- Base(String arg) {}
- }
- '''
- assert msg.contains("Can't access instance method 'getData' before the class is constructed")
- }
+import org.junit.Test
- void testNestedClassThisCallingInstanceMethod() {
- def msg = shouldFail '''
- class Base {
- static class Nested {
- String getData() { return "ABCD" }
- Nested() { this(getData()) }
- Nested(String arg) {}
- }
- }
- '''
- assert msg.contains("Can't access instance method 'getData' before the class is constructed")
- }
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+final class ConstructorThisCallBug {
+
+ @Test
void testThisCallingStaticMethod() {
assertScript '''
class Base {
@@ -57,7 +39,8 @@ class ConstructorThisCallBug extends GroovyTestCase {
'''
}
- void testNestedThisCallingStaticMethod() {
+ @Test
+ void testNestedClassThisCallingStaticMethod() {
assertScript '''
class Base {
static class Nested {
@@ -72,7 +55,8 @@ class ConstructorThisCallBug extends GroovyTestCase {
'''
}
- void testInnerClassSuperCallingInstanceMethod() {
+ @Test
+ void testNestedClassSuperCallingStaticMethod() {
assertScript '''
class Parent {
String str
@@ -82,11 +66,10 @@ class ConstructorThisCallBug extends GroovyTestCase {
static String a
private class Inner extends Parent {
- Inner() { super(myA()) }
+ Inner() { super(getA()) }
}
String test() { new Inner().str }
- String myA() { a }
}
def o = new Outer()
Outer.a = 'ok'
@@ -94,8 +77,37 @@ class ConstructorThisCallBug extends GroovyTestCase {
'''
}
- void testInnerClassSuperCallingStaticProperty() {
- assertScript '''
+ //
+
+ @Test // GROOVY-7014
+ void testThisCallingInstanceMethod() {
+ def err = shouldFail '''
+ class Base {
+ String getData() { return "ABCD" }
+ Base() { this(getData()) }
+ Base(String arg) {}
+ }
+ '''
+ assert err =~ / Cannot reference 'getData' before supertype constructor has been called. /
+ }
+
+ @Test
+ void testNestedClassThisCallingInstanceMethod() {
+ def err = shouldFail '''
+ class Base {
+ static class Nested {
+ String getData() { return "ABCD" }
+ Nested() { this(getData()) }
+ Nested(String arg) {}
+ }
+ }
+ '''
+ assert err =~ / Cannot reference 'getData' before supertype constructor has been called. /
+ }
+
+ @Test
+ void testNestedClassSuperCallingInstanceMethod() {
+ def err = shouldFail '''
class Parent {
String str
Parent(String s) { str = s }
@@ -104,18 +116,22 @@ class ConstructorThisCallBug extends GroovyTestCase {
static String a
private class Inner extends Parent {
- Inner() { super(getA()) }
+ Inner() { super(myA()) }
}
String test() { new Inner().str }
+ String myA() { a }
}
def o = new Outer()
Outer.a = 'ok'
assert o.test() == 'ok'
'''
+ assert err =~ / Cannot reference 'myA' before supertype constructor has been called. /
}
- // GROOVY-994
+ //
+
+ @Test // GROOVY-994
void testCallA() {
assert new ConstructorCallA("foo").toString() == 'foo'
assert new ConstructorCallA(9).toString() == '81'
diff --git a/src/test/groovy/bugs/Groovy5259Bug.groovy b/src/test/groovy/bugs/Groovy5259.groovy
similarity index 92%
rename from src/test/groovy/bugs/Groovy5259Bug.groovy
rename to src/test/groovy/bugs/Groovy5259.groovy
index 267fe2f..1b4bc28 100644
--- a/src/test/groovy/bugs/Groovy5259Bug.groovy
+++ b/src/test/groovy/bugs/Groovy5259.groovy
@@ -18,9 +18,15 @@
*/
package groovy.bugs
-class Groovy5259Bug extends GroovyTestCase {
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+
+final class Groovy5259 {
+
+ @Test
void testInnerClassAccessingOuterClassConstant() {
- // using a script because the bug is a compiler error
assertScript '''
class InnerAccessOuter {
static final String OUTER_CONSTANT = 'Constant Value'
@@ -41,13 +47,11 @@ class Groovy5259Bug extends GroovyTestCase {
}
new InnerAccessOuter().testInnerClassAccessOuter()
'''
-
}
+ @Test
void testInnerClassWithWrongCallToSuperAccessingOuterClassConstant() {
- // using a script because the bug is a compiler error
- shouldFail {
- assertScript '''
+ shouldFail '''
class InnerAccessOuter {
protected static final String OUTER_CONSTANT = 'Constant Value'
@@ -69,11 +73,10 @@ class Groovy5259Bug extends GroovyTestCase {
}
new InnerAccessOuter().testInnerClassAccessOuter()
'''
- }
}
+ @Test
void testInnerClassWithSuperClassAccessingOuterClassConstant() {
- // using a script because the bug is a compiler error
assertScript '''
class Base {
Base(String str) {}
@@ -96,11 +99,10 @@ class Groovy5259Bug extends GroovyTestCase {
}
new InnerAccessOuter().testInnerClassAccessOuter()
'''
-
}
+ @Test
void testInnerClassWithSuperClassAccessingSuperOuterClassConstant() {
- // using a script because the bug is a compiler error
assertScript '''
class Base {
Base(String str) {}
@@ -125,6 +127,5 @@ class Groovy5259Bug extends GroovyTestCase {
}
new InnerAccessOuter().testInnerClassAccessOuter()
'''
-
}
}