You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/07/15 03:56:16 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-8999: super.@x should fail for private fields; no get workarounds (port to 2_5_X)

This is an automated email from the ASF dual-hosted git repository.

paulk 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 a85e4a5  GROOVY-8999: super.@x should fail for private fields; no get workarounds (port to 2_5_X)
a85e4a5 is described below

commit a85e4a530a1069e69869637669a8d23378da928d
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Jul 14 12:22:59 2020 -0500

    GROOVY-8999: super.@x should fail for private fields; no get workarounds (port to 2_5_X)
---
 .../groovy/classgen/AsmClassGenerator.java         |   6 +-
 .../groovy/classgen/asm/InvocationWriter.java      |  17 ++-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 133 ++++++++++-----------
 src/test/groovy/ThisAndSuperTest.groovy            | 117 ++++++++++++------
 .../stc/FieldsAndPropertiesSTCTest.groovy          | 113 ++++++++++++-----
 .../stc/TypeCheckingExtensionsTest.groovy          |   3 +-
 .../classgen/asm/sc/bugs/Groovy7300Bug.groovy      |  96 ++++++++-------
 7 files changed, 296 insertions(+), 189 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 2fea225..4887026 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -183,7 +183,7 @@ public class AsmClassGenerator extends ClassGenerator {
     private final Map genericParameterNames;
     private final SourceUnit source;
     private WriterController controller;
-    
+
     public AsmClassGenerator(
             SourceUnit source, GeneratorContext context,
             ClassVisitor classVisitor, String sourceFile
@@ -1039,7 +1039,7 @@ public class AsmClassGenerator extends ClassGenerator {
                     fieldX(field).visit(this);
                     return;
                 }
-                if (isSuperExpression(objectExpression)) {
+                if (isSuperExpression(objectExpression) && !(expression instanceof AttributeExpression)) {
                     if (controller.getCompileStack().isLHS()) {
                         setPropertyOfSuperClass(classNode, expression, mv);
                     } else {
@@ -2208,7 +2208,7 @@ public class AsmClassGenerator extends ClassGenerator {
         return false;
     }
 
-    private static boolean isSuperExpression(Expression expression) {
+    public static boolean isSuperExpression(Expression expression) {
         if (expression instanceof VariableExpression) {
             VariableExpression varExp = (VariableExpression) expression;
             return varExp.getName().equals("super");
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
index 79e9946..1e7741e 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/InvocationWriter.java
@@ -132,10 +132,19 @@ public class InvocationWriter {
             MethodCallerMultiAdapter adapter,
             boolean safe, boolean spreadSafe, boolean implicitThis
     ) {
-        ClassNode cn = controller.getClassNode();
-        if (controller.isInClosure() && !implicitThis && AsmClassGenerator.isThisExpression(receiver)) cn=cn.getOuterClass();
-        makeCall(origin, new ClassExpression(cn), receiver, message, arguments,
-                adapter, safe, spreadSafe, implicitThis);
+        ClassNode sender = controller.getClassNode();
+        if (AsmClassGenerator.isSuperExpression(receiver) || (AsmClassGenerator.isThisExpression(receiver) && !implicitThis)) {
+            while (sender.getOuterClass() != null && sender.getSuperClass() == ClassHelper.CLOSURE_TYPE) {
+                sender = sender.getOuterClass();
+            }
+            if (AsmClassGenerator.isSuperExpression(receiver)) {
+                sender = sender.getSuperClass(); // GROOVY-4035
+                implicitThis = false; // prevent recursion
+                safe = false; // GROOVY-6045
+            }
+        }
+
+        makeCall(origin, new ClassExpression(sender), receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
     }
     
     protected boolean writeDirectMethodCall(MethodNode target, boolean implicitThis,  Expression receiver, TupleExpression args) {
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 89ed0c4..6317fa5 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -193,7 +193,6 @@ import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
 import static org.codehaus.groovy.classgen.AsmClassGenerator.MINIMUM_BYTECODE_VERSION;
-import static org.codehaus.groovy.classgen.AsmClassGenerator.isNullConstant;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
 import static org.codehaus.groovy.syntax.Types.ASSIGNMENT_OPERATOR;
@@ -760,11 +759,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     @Override
     public void visitAttributeExpression(final AttributeExpression expression) {
-        super.visitAttributeExpression(expression);
         if (!existsProperty(expression, true) && !extension.handleUnresolvedAttribute(expression)) {
             Expression objectExpression = expression.getObjectExpression();
-            addStaticTypeError("No such property: " + expression.getPropertyAsString() +
-                    " for class: " + findCurrentInstanceOfClass(objectExpression, objectExpression.getType()), expression);
+            addStaticTypeError("No such attribute: " + expression.getPropertyAsString() +
+                    " for class: " + findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), expression);
         }
     }
 
@@ -1418,15 +1416,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         if (propertyName == null) return false;
 
         Expression objectExpression = pexp.getObjectExpression();
-        final ClassNode objectExpressionType = getType(objectExpression);
-
+        ClassNode objectExpressionType = getType(objectExpression);
+        List<ClassNode> enclosingTypes = typeCheckingContext.getEnclosingClassNodes();
         boolean staticOnlyAccess = isClassClassNodeWrappingConcreteType(objectExpressionType);
         if ("this".equals(propertyName) && staticOnlyAccess) {
             // Outer.this for any level of nesting
             ClassNode outerNode = objectExpressionType.getGenericsTypes()[0].getType();
-            List<ClassNode> candidates = typeCheckingContext.getEnclosingClassNodes();
             ClassNode found = null;
-            for (ClassNode current : candidates) {
+            for (ClassNode current : enclosingTypes) {
                 if (!current.isStaticClass() && current instanceof InnerClassNode && outerNode.equals(current.getOuterClass())) {
                     found = current;
                     break;
@@ -1447,30 +1444,34 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         boolean isAttributeExpression = pexp instanceof AttributeExpression;
         HashSet<ClassNode> handledNodes = new HashSet<ClassNode>();
         for (Receiver<String> receiver : receivers) {
-            ClassNode testClass = receiver.getType();
+            ClassNode receiverType = receiver.getType();
 
-            if (testClass.isArray() && "length".equals(propertyName)) {
+            if (receiverType.isArray() && "length".equals(propertyName)) {
                 storeType(pexp, int_TYPE);
                 if (visitor != null) {
-                    PropertyNode length = new PropertyNode("length", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, int_TYPE, testClass, null, null, null);
+                    PropertyNode length = new PropertyNode("length", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, int_TYPE, receiverType, null, null, null);
                     visitor.visitProperty(length);
                 }
                 return true;
             }
 
             LinkedList<ClassNode> queue = new LinkedList<>();
-            queue.add(testClass);
-            if (isPrimitiveType(testClass)) {
-                queue.add(getWrapper(testClass));
+            queue.add(receiverType);
+            if (isPrimitiveType(receiverType)) {
+                queue.add(getWrapper(receiverType));
             }
             while (!queue.isEmpty()) {
                 ClassNode current = queue.removeFirst();
-                if (handledNodes.contains(current)) continue;
-                handledNodes.add(current);
-                Set<ClassNode> allInterfaces = current.getAllInterfaces();
-                for (ClassNode intf : allInterfaces) {
-                    //TODO: apply right generics here!
-                    queue.add(GenericsUtils.parameterizeType(current, intf));
+                if (!handledNodes.add(current)) continue;
+
+                FieldNode field = current.getDeclaredField(propertyName);
+                if (field == null) {
+                    if (current.getSuperClass() != null) {
+                        queue.addFirst(current.getUnresolvedSuperClass());
+                    }
+                    for (ClassNode face : current.getAllInterfaces()) {
+                        queue.add(GenericsUtils.parameterizeType(current, face));
+                    }
                 }
 
                 // in case of a lookup on Class we look for instance methods on Class
@@ -1482,10 +1483,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 } else {
                     staticOnly = staticOnlyAccess;
                 }
-
-                FieldNode field = current.getDeclaredField(propertyName);
                 field = allowStaticAccessToMember(field, staticOnly);
 
+                // GROOVY-9288, GROOVY-9292, GROOVY-9293
                 if (null != field) {
                     int fieldModifiers = field.getModifiers();
                     if (Modifier.isProtected(fieldModifiers) || isPackagePrivate(fieldModifiers)) {
@@ -1495,17 +1495,21 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     }
                 }
 
-                if (storeField(field, isAttributeExpression, pexp, current, visitor, receiver.getData(), !readMode)) {
+                // skip property/accessor checks for "x.@field"
+                if (storeField(field, isAttributeExpression, pexp, receiverType, visitor, receiver.getData(), !readMode)) {
                     pexp.removeNodeMetaData(READONLY_PROPERTY);
                     return true;
+                } else if (isAttributeExpression) {
+                    continue;
                 }
 
-                boolean isThisExpression = objectExpression instanceof VariableExpression
-                        && ((VariableExpression) objectExpression).isThisExpression()
-                        && objectExpressionType.equals(current);
-
-                if (storeField(field, isThisExpression, pexp, receiver.getType(), visitor, receiver.getData(), !readMode))
+                // skip property/accessor checks for "field", "this.field", "this.with { field }", etc. in declaring class of field
+                if (storeField(field, enclosingTypes.contains(current), pexp, receiverType, visitor, receiver.getData(), !readMode)) {
+                    pexp.removeNodeMetaData(READONLY_PROPERTY);
                     return true;
+                }
+
+                boolean isThisExpression = enclosingTypes.contains(receiverType);
 
                 MethodNode getter = findGetter(current, "get" + capName, pexp.isImplicitThis());
                 getter = allowStaticAccessToMember(getter, staticOnly);
@@ -1515,13 +1519,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 List<MethodNode> setters = findSetters(current, setterName, false);
                 setters = allowStaticAccessToMember(setters, staticOnly);
 
-                // TODO: remove this visit
                 // need to visit even if we only look for a setters for compatibility
                 if (visitor != null && getter != null) visitor.visitMethod(getter);
 
                 PropertyNode propertyNode = current.getProperty(propertyName);
                 propertyNode = allowStaticAccessToMember(propertyNode, staticOnly);
-                //prefer explicit getter or setter over property if receiver is not 'this'
+                // prefer explicit getter or setter over property if receiver is not 'this'
                 boolean checkGetterOrSetter = !isThisExpression || propertyNode == null;
 
                 if (readMode && checkGetterOrSetter) {
@@ -1548,8 +1551,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                                 }
                             }
                         }
-                        //TODO: apply generics on parameter[0]?
-//                                storeType(pexp, setter.getParameters()[0].getType());
                         SetterInfo info = new SetterInfo(current, setterName, setters);
                         BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
                         if (enclosingBinaryExpression != null) {
@@ -1560,26 +1561,25 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             pexp.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
                         }
                         return true;
-                    } else if (getter != null && propertyNode == null) {
-                        pexp.putNodeMetaData(READONLY_PROPERTY, true);
+                    } else if (propertyNode == null) {
+                        if (field != null && hasAccessToField(field, typeCheckingContext.getEnclosingClassNode())) {
+                            pexp.removeNodeMetaData(READONLY_PROPERTY);
+                        } else if (getter != null) {
+                            pexp.putNodeMetaData(READONLY_PROPERTY, Boolean.TRUE);
+                        }
                     }
                 }
-                foundGetterOrSetter = foundGetterOrSetter || !setters.isEmpty() || getter != null;
+                foundGetterOrSetter = (foundGetterOrSetter || !setters.isEmpty() || getter != null);
 
-                if (storeProperty(propertyNode, pexp, current, visitor, receiver.getData())) return true;
+                if (storeProperty(propertyNode, pexp, receiverType, visitor, receiver.getData())) return true;
 
-                if (storeField(field, true, pexp, current, visitor, receiver.getData(), !readMode)) return true;
-                // if the property expression is an attribute expression (o.@attr), then
-                // we stop now, otherwise we must check the parent class
-                if (/*!isAttributeExpression && */current.getSuperClass() != null) {
-                    queue.add(current.getUnresolvedSuperClass());
-                }
+                if (storeField(field, true, pexp, receiverType, visitor, receiver.getData(), !readMode)) return true;
             }
 
             // GROOVY-5568: the property may be defined by DGM
             List<ClassNode> dgmReceivers = new ArrayList<ClassNode>(2);
-            dgmReceivers.add(testClass);
-            if (isPrimitiveType(testClass)) dgmReceivers.add(getWrapper(testClass));
+            dgmReceivers.add(receiverType);
+            if (isPrimitiveType(receiverType)) dgmReceivers.add(getWrapper(receiverType));
             for (ClassNode dgmReceiver : dgmReceivers) {
                 List<MethodNode> methods = findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
                 for (MethodNode m : findDGMMethodsByNameAndArguments(getTransformLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
@@ -1601,17 +1601,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
 
             // GROOVY-7996: check if receiver implements get(String)/set(String,Object) or propertyMissing(String)
-            if (!testClass.isArray() && !isPrimitiveType(getUnwrapper(testClass))
+            if (!receiverType.isArray() && !isPrimitiveType(getUnwrapper(receiverType))
                     && pexp.isImplicitThis() && typeCheckingContext.getEnclosingClosure() != null) {
                 MethodNode mopMethod;
                 if (readMode) {
-                    mopMethod = testClass.getMethod("get", new Parameter[]{new Parameter(STRING_TYPE, "name")});
+                    mopMethod = receiverType.getMethod("get", new Parameter[]{new Parameter(STRING_TYPE, "name")});
                 } else {
-                    mopMethod = testClass.getMethod("set", new Parameter[]{new Parameter(STRING_TYPE, "name"), new Parameter(OBJECT_TYPE, "value")});
+                    mopMethod = receiverType.getMethod("set", new Parameter[]{new Parameter(STRING_TYPE, "name"), new Parameter(OBJECT_TYPE, "value")});
                 }
-                if (mopMethod == null) mopMethod = testClass.getMethod("propertyMissing", new Parameter[]{new Parameter(STRING_TYPE, "propertyName")});
+                if (mopMethod == null) mopMethod = receiverType.getMethod("propertyMissing", new Parameter[]{new Parameter(STRING_TYPE, "propertyName")});
 
-                if (mopMethod != null) {
+                if (mopMethod != null && !mopMethod.isSynthetic()) {
                     pexp.putNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION, Boolean.TRUE);
                     pexp.removeNodeMetaData(DECLARATION_INFERRED_TYPE);
                     pexp.removeNodeMetaData(INFERRED_TYPE);
@@ -1621,16 +1621,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
 
         for (Receiver<String> receiver : receivers) {
-            ClassNode testClass = receiver.getType();
-            ClassNode propertyType = getTypeForMapPropertyExpression(testClass, objectExpressionType, pexp);
+            ClassNode receiverType = receiver.getType();
+            ClassNode propertyType = getTypeForMapPropertyExpression(receiverType, objectExpressionType, pexp);
             if (propertyType == null)
-                propertyType = getTypeForListPropertyExpression(testClass, objectExpressionType, pexp);
-            if (propertyType == null) propertyType = getTypeForSpreadExpression(testClass, objectExpressionType, pexp);
+                propertyType = getTypeForListPropertyExpression(receiverType, objectExpressionType, pexp);
+            if (propertyType == null) propertyType = getTypeForSpreadExpression(receiverType, objectExpressionType, pexp);
             if (propertyType == null) continue;
             if (visitor != null) {
-                // todo : type inference on maps and lists, if possible
-                PropertyNode node = new PropertyNode(propertyName, Opcodes.ACC_PUBLIC, propertyType, receiver.getType(), null, null, null);
-                node.setDeclaringClass(receiver.getType());
+                PropertyNode node = new PropertyNode(propertyName, Opcodes.ACC_PUBLIC, propertyType, receiverType, null, null, null);
+                node.setDeclaringClass(receiverType);
                 visitor.visitProperty(node);
             }
             storeType(pexp, propertyType);
@@ -1638,6 +1637,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (delegationData != null) pexp.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
             return true;
         }
+
         return foundGetterOrSetter;
     }
 
@@ -1646,13 +1646,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private static boolean hasAccessToField(FieldNode field, ClassNode objectExpressionType) {
-        if (field != null) {
-            if (field.isPublic() || field.isProtected()) {
-                return true;
-            }
-            if (!field.isPrivate() && Objects.equals(objectExpressionType.getPackageName(), field.getDeclaringClass().getPackageName())) {
-                return true;
-            }
+        if (field.isPublic() || field.isProtected()) {
+            return true;
+        }
+        if (!field.isPrivate() && Objects.equals(objectExpressionType.getPackageName(), field.getDeclaringClass().getPackageName())) {
+            return true;
         }
         return false;
     }
@@ -1835,7 +1833,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 String setterName = "set" + capName;
                 List<MethodNode> setterMethods = findSetters(current, setterName, false);
                 if (!setterMethods.isEmpty()) {
-//                    storeType(pexp, setterMethod.getParameters()[0].getType());
                     return new SetterInfo(current, setterName, setterMethods);
                 }
                 if (!isAttributeExpression && current.getSuperClass() != null) {
@@ -3277,7 +3274,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             boolean callArgsVisited = false;
             if (isCallOnClosure) {
                 // this is a closure.call() call
-                if (objectExpression == VariableExpression.THIS_EXPRESSION) {
+                if (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()) {
                     // isClosureCall() check verified earlier that a field exists
                     FieldNode field = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name);
                     GenericsType[] genericsTypes = field.getType().getGenericsTypes();
@@ -3676,7 +3673,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     protected boolean isClosureCall(final String name, final Expression objectExpression, final Expression arguments) {
         if (objectExpression instanceof ClosureExpression && ("call".equals(name) || "doCall".equals(name)))
             return true;
-        if (objectExpression == VariableExpression.THIS_EXPRESSION) {
+        if (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()) {
             FieldNode fieldNode = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name);
             if (fieldNode != null) {
                 ClassNode type = fieldNode.getType();
@@ -4688,8 +4685,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             final VariableExpression vexp = (VariableExpression) exp;
             ClassNode selfTrait = isTraitSelf(vexp);
             if (selfTrait != null) return makeSelf(selfTrait);
-            if (vexp == VariableExpression.THIS_EXPRESSION) return makeThis();
-            if (vexp == VariableExpression.SUPER_EXPRESSION) return makeSuper();
+            if (vexp.isThisExpression()) return makeThis();
+            if (vexp.isSuperExpression()) return makeSuper();
             final Variable variable = vexp.getAccessedVariable();
             if (variable instanceof FieldNode) {
                 FieldNode fieldNode = (FieldNode) variable;
diff --git a/src/test/groovy/ThisAndSuperTest.groovy b/src/test/groovy/ThisAndSuperTest.groovy
index c71e895..4513f53 100644
--- a/src/test/groovy/ThisAndSuperTest.groovy
+++ b/src/test/groovy/ThisAndSuperTest.groovy
@@ -18,20 +18,28 @@
  */
 package groovy
 
-class ThisAndSuperTest extends GroovyTestCase {
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+
+final class ThisAndSuperTest {
+
+    @Test
     void testOverwrittenSuperMethod() {
         def helper = new TestForSuperHelper2()
         assert helper.foo() == 2
         assert helper.callFooInSuper() == 1
     }
 
+    @Test
     void testClosureUsingSuperAndThis() {
         def helper = new TestForSuperHelper2()
         assert helper.aClosureUsingThis() == 2
         assert helper.aClosureUsingSuper() == 1
         // accessing private method should not be changed
         // by a public method of the same name and signature!
-        assertEquals "bar", helper.closureUsingPrivateMethod()
+        assert helper.closureUsingPrivateMethod() == "bar"
         assert helper.bar() == "no bar"
 
         assert helper.aField == "I am a field"
@@ -41,6 +49,7 @@ class ThisAndSuperTest extends GroovyTestCase {
         assert helper.aField == 2
     }
 
+    @Test
     void testClosureDelegateAndThis() {
         def map = [:]
         def helper = new TestForSuperHelper2()
@@ -76,6 +85,7 @@ class ThisAndSuperTest extends GroovyTestCase {
         assert map.foo == 1
     }
 
+    @Test
     void testConstructorChain() {
         def helper = new TestForSuperHelper4()
         assert helper.x == 1
@@ -83,6 +93,7 @@ class ThisAndSuperTest extends GroovyTestCase {
         assert helper.x == "Object"
     }
 
+    @Test
     void testChainingForAsType() {
         def helper = new TestForSuperHelper1()
         def ret = helper as Object[]
@@ -94,43 +105,83 @@ class ThisAndSuperTest extends GroovyTestCase {
         }
     }
 
+    @Test
     void testSuperEach() {
         def x = new TestForSuperEach()
         x.each {
             x.res << "I am it: ${it.class.name}"
         }
 
-        assertEquals 3, x.res.size()
-        assertEquals "start each in subclass", x.res[0]
-        assertEquals "I am it: groovy.TestForSuperEach", x.res[1]
-        assertEquals "end of each in subclass", x.res[2]
-    }
-
-// GROOVY-2555
-//    void testCallToAbstractSuperMethodShouldResultInMissingMethod () {
-//        def x = new TestForSuperHelper6()
-//        shouldFail(MissingMethodException) {
-//            x.theMethod()
-//        }
-//    }
-
-    void testDgm() {
-        assertEquals A.empty(), '123'
+        assert x.res.size() == 3
+        assert x.res[0] == "start each in subclass"
+        assert x.res[1] == "I am it: groovy.TestForSuperEach"
+        assert x.res[2] == "end of each in subclass"
     }
 
+    @Test // GROOVY-2555
     void testAbstractSuperMethodShouldBeTreatedLikeMissingMethod() {
-        shouldFail(MissingMethodException) {
-            new TestForSuperHelper6().theMethod()
-        }
-    }
-}
-
-class A {
-    static {
-        A.metaClass.static.empty << {-> '123' }
+        shouldFail MissingMethodException, '''
+            abstract class A {
+                abstract void m()
+            }
+            class B extends A {
+                void m() {
+                    super.m()
+                }
+            }
+            new B().m()
+        '''
+    }
+
+    @Test // GROOVY-8999
+    void testPrivateSuperField1() {
+        def err = shouldFail MissingFieldException, '''
+            abstract class A {
+                private x = 1
+                def getX() { 2 }
+            }
+            class B extends A {
+                private x = 3
+                def m() { super.@x }
+            }
+            assert new B().m() == 1
+        '''
+
+        assert err =~ /No such field: x for class: A/
+    }
+
+    @Test // GROOVY-8999
+    void testPrivateSuperField2() {
+        def err = shouldFail MissingFieldException, '''
+            abstract class A {
+                private x = 1
+                def getX() { 2 }
+                void setX(x) { this.x = 3 }
+            }
+            class B extends A {
+                private x = 4
+                def m() { super.@x = 5; return x }
+            }
+            assert new B().m() == 2
+        '''
+
+        assert err =~ /No such field: x for class: A/
+    }
+
+    // https://github.com/apache/groovy/commit/b62e4d3165b4d899a3b6c71dba2858c9362b2e1b
+    @Test // TODO: Does this belong in another test suite?
+    void testStaticMetaClassClosure() {
+        assertScript '''
+            class A {
+            }
+            A.metaClass.static.something << { -> '123' }
+
+            assert A.something() == '123'
+        '''
     }
 }
 
+//------------------------------------------------------------------------------
 
 class TestForSuperEach {
     def res = []
@@ -191,14 +242,4 @@ class TestForSuperHelper4 extends TestForSuperHelper3 {
     TestForSuperHelper4(Object j) {
         super(j)
     }
-}
-
-abstract class TestForSuperHelper5 {
-    abstract void theMethod()
-}
-
-class TestForSuperHelper6 extends TestForSuperHelper5 {
-    void theMethod() {
-        super.theMethod()
-    }
-}
+}
\ No newline at end of file
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index 20e7b9c..1b9dd86 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -18,6 +18,8 @@
  */
 package groovy.transform.stc
 
+import groovy.transform.NotYetImplemented
+
 /**
  * Unit tests for static type checking : fields and properties.
  */
@@ -99,14 +101,14 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testShouldComplainAboutMissingField() {
+    void testShouldComplainAboutMissingProperty() {
         shouldFailWithMessages '''
             Object o = new Object()
             o.x = 0
         ''', 'No such property: x for class: java.lang.Object'
     }
 
-    void testShouldComplainAboutMissingField2() {
+    void testShouldComplainAboutMissingProperty2() {
         shouldFailWithMessages '''
             class A {
             }
@@ -115,19 +117,73 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         ''', 'No such property: x for class: A'
     }
 
-    void testFieldWithInheritance() {
+    void testShouldComplainAboutMissingAttribute() {
+        shouldFailWithMessages '''
+            Object o = new Object()
+            o.@x = 0
+        ''', 'No such attribute: x for class: java.lang.Object'
+    }
+
+    void testShouldComplainAboutMissingAttribute2() {
+        shouldFailWithMessages '''
+            class A {
+            }
+            A a = new A()
+            a.@x = 0
+        ''', 'No such attribute: x for class: A'
+    }
+
+    void testShouldComplainAboutMissingAttribute3() {
+        shouldFailWithMessages '''
+            class A {
+                def getX() { }
+            }
+            A a = new A()
+            println a.@x
+        ''', 'No such attribute: x for class: A'
+    }
+
+    void testShouldComplainAboutMissingAttribute4() {
+        shouldFailWithMessages '''
+            class A {
+                def setX(x) { }
+            }
+            A a = new A()
+            a.@x = 0
+        ''', 'No such attribute: x for class: A'
+    }
+
+    @NotYetImplemented
+    void testShouldComplainAboutMissingAttribute5() {
+        shouldFailWithMessages '''
+            class A {
+                private x
+            }
+            class B extends A {
+                void test() {
+                    this.@x
+                }
+            }
+        ''', 'The field A.x is not accessible'
+    }
+
+    void testPropertyWithInheritance() {
         assertScript '''
             class A {
                 int x
             }
             class B extends A {
             }
+
             B b = new B()
+            assert b.x == 0
+
             b.x = 2
+            assert b.x == 2
         '''
     }
 
-    void testFieldTypeWithInheritance() {
+    void testPropertyTypeWithInheritance() {
         shouldFailWithMessages '''
             class A {
                 int x
@@ -139,7 +195,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         ''', 'Cannot assign value of type java.lang.String to variable of type int'
     }
 
-    void testFieldWithInheritanceFromAnotherSourceUnit() {
+    void testPropertyWithInheritanceFromAnotherSourceUnit() {
         assertScript '''
             class B extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass {
             }
@@ -148,7 +204,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testFieldWithInheritanceFromAnotherSourceUnit2() {
+    void testPropertyWithInheritanceFromAnotherSourceUnit2() {
         shouldFailWithMessages '''
             class B extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass {
             }
@@ -157,7 +213,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         ''', 'Cannot assign value of type java.lang.String to variable of type int'
     }
 
-    void testFieldWithSuperInheritanceFromAnotherSourceUnit() {
+    void testPropertyWithSuperInheritanceFromAnotherSourceUnit() {
         assertScript '''
             class B extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass2 {
             }
@@ -616,29 +672,30 @@ new FooWorker().doSomething()'''
     }
 
     void testShouldFailWithIncompatibleGenericTypes() {
-        shouldFailWithMessages '''public class Foo {
-
-    private List<String> names;
+        shouldFailWithMessages '''\
+            public class Foo {
+                private List<String> names;
 
-    public List<String> getNames() {
-        return names;
-    }
-
-    public void setNames(List<String> names) {
-        this.names = names;
-    }
-}
+                public List<String> getNames() {
+                    return names;
+                }
 
-class FooWorker {
+                public void setNames(List<String> names) {
+                    this.names = names;
+                }
+            }
 
-    public void doSomething() {
-        new Foo().with {
-            names = new ArrayList<Integer>()
-        }
-    }
-}
+            class FooWorker {
+                public void doSomething() {
+                    new Foo().with {
+                        names = new ArrayList<Integer>()
+                    }
+                }
+            }
 
-new FooWorker().doSomething()''', 'Cannot assign value of type java.util.ArrayList <Integer> to variable of type java.util.List <String>'
+            new FooWorker().doSomething()
+        ''',
+        'Cannot assign value of type java.util.ArrayList <Integer> to variable of type java.util.List <String>'
     }
 
     void testAICAsStaticProperty() {
@@ -804,7 +861,7 @@ import org.codehaus.groovy.ast.stmt.AssertStatement
     void testImplicitPropertyOfDelegateShouldNotPreferField() {
         assertScript '''
             Calendar.instance.with {
-                Date d1 = time
+                Date d1 = time // Date getTime() vs. long time
             }
         '''
     }
@@ -893,4 +950,4 @@ import org.codehaus.groovy.ast.stmt.AssertStatement
 
     static class BaseClass2 extends BaseClass {
     }
-}
+}
\ No newline at end of file
diff --git a/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy b/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
index 2ae60e4..bb4793d 100644
--- a/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
+++ b/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
@@ -20,7 +20,6 @@ package groovy.transform.stc
 
 import org.codehaus.groovy.control.MultipleCompilationErrorsException
 import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
-import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport
 
 /**
  * Units tests for type checking extensions.
@@ -235,7 +234,7 @@ class TypeCheckingExtensionsTest extends StaticTypeCheckingTestCase {
         extension = null
         shouldFailWithMessages '''
             'str'.@FOO
-        ''', 'No such property: FOO for class: java.lang.String'
+        ''', 'No such attribute: FOO for class: java.lang.String'
 
         extension = 'groovy/transform/stc/UnresolvedAttributeTestExtension.groovy'
         assertScript '''
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300Bug.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300Bug.groovy
index 33a3cbd..d1af453 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300Bug.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300Bug.groovy
@@ -16,64 +16,68 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
-
-
-
-
-
 package org.codehaus.groovy.classgen.asm.sc.bugs
 
+import groovy.transform.NotYetImplemented
 import groovy.transform.stc.StaticTypeCheckingTestCase
 import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport
 
-class Groovy7300Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
-    void testShouldNotThrowStackOverflow() {
-        assertScript '''
-class A {
-    private String field1 = 'test'
+final class Groovy7300Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
 
-    String getField1() {
-        return this.field1
-   }
-}
-
-class B extends A {
-    @Override
-    String getField1() {
-        super.field1
+    void testUseSuperToBypassOverride1() {
+        assertScript '''
+            abstract class A {
+                protected x = 1
+                def getX() { 2 }
+            }
+            class B extends A {
+                @Override
+                def getX() { super.x }
+            }
+            assert new B().getX() == 1 // TODO: Why use A#x and not A#getX?
+        '''
     }
-}
-
-B b = new B()
 
-assert b.field1 == 'test'
-'''
+    void testUseSuperToBypassOverride1a() {
+        assertScript '''
+            abstract class A {
+                protected x = 1
+                def getX() { 2 }
+            }
+            class B extends A {
+                @Override
+                def getX() { super.@x }
+            }
+            assert new B().getX() == 1
+        '''
     }
 
-    void testShouldNotThrowStackOverflowWithSuper() {
+    void testUseSuperToBypassOverride2() {
         assertScript '''
-class A {
-    private String field1 = 'test'
-
-    void setField1(String val) { field1 = val }
-
-    String getField1() {
-        return this.field1
-   }
-}
-
-class B extends A {
-    @Override
-    String getField1() {
-        super.field1 = 'test 2'
-        super.field1
+            abstract class A {
+                private x = 1
+                def getX() { 2 }
+            }
+            class B extends A {
+                @Override
+                def getX() { super.x }
+            }
+            assert new B().getX() == 2
+        '''
     }
-}
-
-B b = new B()
 
-assert b.field1 == 'test 2'
-'''
+    @NotYetImplemented
+    void testUseSuperToBypassOverride2a() {
+        shouldFailWithMessages '''
+            abstract class A {
+                private x = 1
+                def getX() { 2 }
+            }
+            class B extends A {
+                @Override
+                def getX() { super.@x }
+            }
+            assert false
+        ''', 'The field A.x is not accessible'
     }
 }