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()
         '''
-
     }
 }