You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/07/02 23:36:04 UTC

[groovy] branch master updated: GROOVY-8999: super.@x should fail for private fields -- no getX() or setX() workaround(closes #1291)

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

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new c7c35b0  GROOVY-8999: super.@x should fail for private fields -- no getX() or setX() workaround(closes #1291)
c7c35b0 is described below

commit c7c35b0fc0b54816d52742d90893491efb307c89
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Jul 3 07:35:40 2020 +0800

    GROOVY-8999: super.@x should fail for private fields -- no getX() or setX() workaround(closes #1291)
---
 src/main/java/groovy/lang/MetaClassImpl.java       |   4 +-
 .../groovy/classgen/AsmClassGenerator.java         |  10 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 123 +++++++++++----------
 .../groovy/transform/stc/TypeCheckingContext.java  |   6 +
 src/test/groovy/ThisAndSuperTest.groovy            | 116 ++++++++++++-------
 .../stc/FieldsAndPropertiesSTCTest.groovy          |  83 ++++++++++++--
 .../stc/TypeCheckingExtensionsTest.groovy          |   3 +-
 .../groovy/classgen/asm/sc/bugs/Groovy7300.groovy  |  77 ++++++-------
 8 files changed, 262 insertions(+), 160 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 5dda2f5..85a321a 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -2936,7 +2936,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             }
         }
 
-        throw new MissingFieldException(attribute, theClass);
+        throw new MissingFieldException(attribute, !useSuper ? theClass : theClass.getSuperclass());
     }
 
     /**
@@ -2976,7 +2976,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             }
         }
 
-        throw new MissingFieldException(attribute, theClass);
+        throw new MissingFieldException(attribute, !useSuper ? theClass : theClass.getSuperclass());
     }
 
     /**
diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
index eb181d8..e8fd5ca 100644
--- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -141,8 +141,8 @@ public class AsmClassGenerator extends ClassGenerator {
     // fields
     public  static final MethodCallerMultiAdapter setField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setField", false, false);
     public  static final MethodCallerMultiAdapter getField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getField", false, false);
-  //private static final MethodCallerMultiAdapter setFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setFieldOnSuper", false, false);
-  //private static final MethodCallerMultiAdapter getFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getFieldOnSuper", false, false);
+    private static final MethodCallerMultiAdapter setFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setFieldOnSuper", false, false);
+    private static final MethodCallerMultiAdapter getFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getFieldOnSuper", false, false);
     public  static final MethodCallerMultiAdapter setGroovyObjectField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setGroovyObjectField", false, false);
     public  static final MethodCallerMultiAdapter getGroovyObjectField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getGroovyObjectField", false, false);
 
@@ -1126,8 +1126,6 @@ public class AsmClassGenerator extends ClassGenerator {
                 if (fieldNode != null) {
                     fieldX(fieldNode).visit(this);
                     visited = true;
-                } else if (isSuperExpression(objectExpression)) {
-                    visited = tryPropertyOfSuperClass(expression, name);
                 }
             }
         }
@@ -1135,9 +1133,9 @@ public class AsmClassGenerator extends ClassGenerator {
         if (!visited) {
             MethodCallerMultiAdapter adapter;
             if (controller.getCompileStack().isLHS()) {
-                adapter = isGroovyObject(objectExpression) ? setGroovyObjectField : setField;
+                adapter = isSuperExpression(objectExpression) ? setFieldOnSuper : isGroovyObject(objectExpression) ? setGroovyObjectField : setField;
             } else {
-                adapter = isGroovyObject(objectExpression) ? getGroovyObjectField : getField;
+                adapter = isSuperExpression(objectExpression) ? getFieldOnSuper : isGroovyObject(objectExpression) ? getGroovyObjectField : getField;
             }
             visitAttributeOrProperty(expression, adapter);
         }
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 d9bda18..cc0d98b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -127,7 +127,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
@@ -664,7 +663,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     private boolean tryVariableExpressionAsProperty(final VariableExpression vexp, final String dynName) {
         PropertyExpression pexp = thisPropX(true, dynName);
-        if (visitPropertyExpressionSilent(pexp, vexp)) {
+        if (existsProperty(pexp, !typeCheckingContext.isTargetOfEnclosingAssignment(vexp))) {
             vexp.copyNodeMetaData(pexp.getObjectExpression());
             for (Object key : new Object[]{IMPLICIT_RECEIVER, READONLY_PROPERTY, PV_FIELDS_ACCESS, PV_FIELDS_MUTATION, DECLARATION_INFERRED_TYPE, DIRECT_METHOD_CALL_TARGET}) {
                 Object val = pexp.getNodeMetaData(key);
@@ -684,33 +683,25 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return false;
     }
 
-    private boolean visitPropertyExpressionSilent(final PropertyExpression pe, final Expression lhsPart) {
-        return existsProperty(pe, !isLHSOfEnclosingAssignment(lhsPart));
-    }
-
     @Override
-    public void visitPropertyExpression(final PropertyExpression pexp) {
-        if (visitPropertyExpressionSilent(pexp, pexp)) return;
+    public void visitPropertyExpression(final PropertyExpression expression) {
+        if (existsProperty(expression, !typeCheckingContext.isTargetOfEnclosingAssignment(expression))) return;
 
-        if (!extension.handleUnresolvedProperty(pexp)) {
-            Expression objectExpression = pexp.getObjectExpression();
-            addStaticTypeError("No such property: " + pexp.getPropertyAsString() + " for class: " +
-                    findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), pexp);
+        if (!extension.handleUnresolvedProperty(expression)) {
+            Expression objectExpression = expression.getObjectExpression();
+            addStaticTypeError("No such property: " + expression.getPropertyAsString() + " for class: " +
+                    findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), expression);
         }
     }
 
-    private boolean isLHSOfEnclosingAssignment(final Expression expression) {
-        return Optional.ofNullable(typeCheckingContext.getEnclosingBinaryExpression())
-            .filter(be -> be.getLeftExpression() == expression && isAssignment(be.getOperation().getType())).isPresent();
-    }
-
     @Override
     public void visitAttributeExpression(final AttributeExpression expression) {
-        super.visitAttributeExpression(expression);
-        if (!existsProperty(expression, true) && !extension.handleUnresolvedAttribute(expression)) {
+        if (existsProperty(expression, true)) return;
+
+        if (!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);
         }
     }
 
@@ -1472,26 +1463,36 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         addReceivers(receivers, makeOwnerList(objectExpression), pexp.isImplicitThis());
 
         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;
             }
 
-            Queue<ClassNode> queue = new LinkedList<>();
-            queue.add(testClass);
-            if (isPrimitiveType(testClass)) {
-                queue.add(getWrapper(testClass));
+            LinkedList<ClassNode> queue = new LinkedList<>();
+            queue.add(receiverType);
+            if (isPrimitiveType(receiverType)) {
+                queue.add(getWrapper(receiverType));
             }
             while (!queue.isEmpty()) {
                 ClassNode current = queue.remove();
                 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
                 // as well, since in case of a static property access we have the class
                 // itself in the list of receivers already;
@@ -1502,20 +1503,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     staticOnly = staticOnlyAccess;
                 }
 
-                FieldNode field = current.getDeclaredField(propertyName);
                 field = allowStaticAccessToMember(field, staticOnly);
 
                 // skip property/accessor checks for "x.@field"
-                if (field != null && pexp instanceof AttributeExpression) {
-                    if (storeField(field, pexp, current, visitor, receiver.getData(), !readMode)) {
+                if (pexp instanceof AttributeExpression) {
+                    if (field != null && storeField(field, pexp, receiverType, visitor, receiver.getData(), !readMode)) {
                         pexp.removeNodeMetaData(READONLY_PROPERTY);
                         return true;
                     }
+                    continue;
                 }
 
                 // skip property/accessor checks for "field", "this.field", "this.with { field }", etc. in declaring class of field
                 if (field != null && enclosingTypes.contains(current)) {
-                    if (storeField(field, pexp, receiver.getType(), visitor, receiver.getData(), !readMode)) {
+                    if (storeField(field, pexp, receiverType, visitor, receiver.getData(), !readMode)) {
                         pexp.removeNodeMetaData(READONLY_PROPERTY);
                         return true;
                     }
@@ -1534,7 +1535,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 PropertyNode property = current.getProperty(propertyName);
                 property = allowStaticAccessToMember(property, staticOnly);
                 // prefer explicit getter or setter over property if receiver is not 'this'
-                if (property == null || !enclosingTypes.contains(testClass)) {
+                if (property == null || !enclosingTypes.contains(receiverType)) {
                     if (readMode) {
                         if (getter != null) {
                             ClassNode returnType = inferReturnTypeGenerics(current, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
@@ -1559,7 +1560,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                                     }
                                 }
                             }
-                            SetterInfo info = new SetterInfo(current, "set" + capName, setters);
+                            SetterInfo info = new SetterInfo(current, getSetterName(propertyName), setters);
                             BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
                             if (enclosingBinaryExpression != null) {
                                 putSetterInfo(enclosingBinaryExpression.getLeftExpression(), info);
@@ -1581,24 +1582,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
                 foundGetterOrSetter = (foundGetterOrSetter || !setters.isEmpty() || getter != null);
 
-                if (property != null && storeProperty(property, pexp, current, visitor, receiver.getData())) return true;
-
-                if (field != null && storeField(field, pexp, current, visitor, receiver.getData(), !readMode)) return true;
+                if (property != null && storeProperty(property, pexp, receiverType, visitor, receiver.getData())) return true;
 
-                // check the super types
-                if (current.getSuperClass() != null) {
-                    queue.add(current.getUnresolvedSuperClass());
-                }
-                for (ClassNode face : current.getAllInterfaces()) {
-                    queue.add(GenericsUtils.parameterizeType(current, face));
-                }
+                if (field != null && storeField(field, pexp, receiverType, visitor, receiver.getData(), !readMode)) return true;
             }
 
             // GROOVY-5568: the property may be defined by DGM
             List<ClassNode> dgmReceivers = new ArrayList<>(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(getSourceUnit().getClassLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
                 for (MethodNode method : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
@@ -1620,15 +1613,15 @@ 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 && !mopMethod.isSynthetic()) {
                     pexp.putNodeMetaData(DYNAMIC_RESOLUTION, Boolean.TRUE);
@@ -1640,12 +1633,12 @@ 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);
+                propertyType = getTypeForListPropertyExpression(receiverType, objectExpressionType, pexp);
             if (propertyType == null)
-                propertyType = getTypeForSpreadExpression(testClass, objectExpressionType, pexp);
+                propertyType = getTypeForSpreadExpression(receiverType, objectExpressionType, pexp);
             if (propertyType == null)
                 continue;
             if (visitor != null) {
@@ -1782,19 +1775,29 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return member;
     }
 
-    private void storeWithResolve(final ClassNode typeToResolve, final ClassNode receiver, final ClassNode declaringClass, final boolean isStatic, final PropertyExpression expressionToStoreOn) {
+    private void storeWithResolve(final ClassNode typeToResolve, final ClassNode receiver, final ClassNode declaringClass, final boolean isStatic, final Expression expressionToStoreOn) {
         ClassNode type = typeToResolve;
-        if (getGenericsWithoutArray(type) != null) {
+        if (missesGenericsTypes(type)) {
             Map<GenericsTypeName, GenericsType> resolvedPlaceholders = resolvePlaceHoldersFromDeclaration(receiver, declaringClass, null, isStatic);
             type = resolveGenericsWithContext(resolvedPlaceholders, type);
         }
-        storeInferredTypeForPropertyExpression(expressionToStoreOn, type);
-        storeType(expressionToStoreOn, type);
+        if (expressionToStoreOn instanceof PropertyExpression) {
+            storeInferredTypeForPropertyExpression((PropertyExpression) expressionToStoreOn, type);
+        } else {
+            storeType(expressionToStoreOn, type);
+        }
     }
 
     private boolean storeField(final FieldNode field, final PropertyExpression expressionToStoreOn, final ClassNode receiver, final ClassCodeVisitorSupport visitor, final String delegationData, final boolean lhsOfAssignment) {
         if (visitor != null) visitor.visitField(field);
         checkOrMarkPrivateAccess(expressionToStoreOn, field, lhsOfAssignment);
+
+        if (expressionToStoreOn instanceof AttributeExpression) { // TODO: expand to include PropertyExpression
+            if (!hasAccessToField(isSuperExpression(expressionToStoreOn.getObjectExpression()) ? typeCheckingContext.getEnclosingClassNode() : receiver, field)) {
+                addStaticTypeError("The field " + field.getDeclaringClass().getNameWithoutPackage() + "." + field.getName() + " is not accessible", expressionToStoreOn.getProperty());
+            }
+        }
+
         storeWithResolve(field.getOriginType(), receiver, field.getDeclaringClass(), field.isStatic(), expressionToStoreOn);
         if (delegationData != null) {
             expressionToStoreOn.putNodeMetaData(IMPLICIT_RECEIVER, delegationData);
@@ -4787,7 +4790,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             Variable variable = vexp.getAccessedVariable();
             if (variable instanceof FieldNode) {
                 FieldNode fieldNode = (FieldNode) variable;
-                checkOrMarkPrivateAccess(vexp, fieldNode, isLHSOfEnclosingAssignment(vexp));
+                checkOrMarkPrivateAccess(vexp, fieldNode, typeCheckingContext.isTargetOfEnclosingAssignment(vexp));
                 return getType(fieldNode);
             }
             if (variable != vexp && variable instanceof VariableExpression) {
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/TypeCheckingContext.java b/src/main/java/org/codehaus/groovy/transform/stc/TypeCheckingContext.java
index 19a12f5..8966210 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/TypeCheckingContext.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/TypeCheckingContext.java
@@ -225,6 +225,12 @@ public class TypeCheckingContext {
         return Collections.unmodifiableList(enclosingBinaryExpressions);
     }
 
+    public boolean isTargetOfEnclosingAssignment(final Expression expression) {
+        return Optional.ofNullable(getEnclosingBinaryExpression()).filter(be ->
+            be.getLeftExpression() == expression && StaticTypeCheckingSupport.isAssignment(be.getOperation().getType())
+        ).isPresent();
+    }
+
     /**
      * Pushes a closure expression into the closure expression stack.
      */
diff --git a/src/test/groovy/ThisAndSuperTest.groovy b/src/test/groovy/ThisAndSuperTest.groovy
index 087756d..b948b76 100644
--- a/src/test/groovy/ThisAndSuperTest.groovy
+++ b/src/test/groovy/ThisAndSuperTest.groovy
@@ -18,22 +18,28 @@
  */
 package groovy
 
-import groovy.test.GroovyTestCase
+import org.junit.Test
 
-class ThisAndSuperTest extends GroovyTestCase {
+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"
@@ -43,6 +49,7 @@ class ThisAndSuperTest extends GroovyTestCase {
         assert helper.aField == 2
     }
 
+    @Test
     void testClosureDelegateAndThis() {
         def map = [:]
         def helper = new TestForSuperHelper2()
@@ -78,6 +85,7 @@ class ThisAndSuperTest extends GroovyTestCase {
         assert map.foo == 1
     }
 
+    @Test
     void testConstructorChain() {
         def helper = new TestForSuperHelper4()
         assert helper.x == 1
@@ -85,6 +93,7 @@ class ThisAndSuperTest extends GroovyTestCase {
         assert helper.x == "Object"
     }
 
+    @Test
     void testChainingForAsType() {
         def helper = new TestForSuperHelper1()
         def ret = helper as Object[]
@@ -96,43 +105,84 @@ 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()
-        }
-    }
-
-    static 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 }
+            }
+            new B().m()
+        '''
+
+        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 }
+            }
+            new B().m()
+        '''
+
+        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 = []
 
@@ -193,13 +243,3 @@ class TestForSuperHelper4 extends TestForSuperHelper3 {
         super(j)
     }
 }
-
-abstract class TestForSuperHelper5 {
-    abstract void theMethod()
-}
-
-class TestForSuperHelper6 extends TestForSuperHelper5 {
-    void theMethod() {
-        super.theMethod()
-    }
-}
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index 3b39eff..5db8505 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.test.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,86 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         ''', 'No such property: x for class: A'
     }
 
-    void testFieldWithInheritance() {
+    @NotYetImplemented
+    void testShouldComplainAboutMissingProperty3() {
+        shouldFailWithMessages '''
+            class A {
+                private x
+            }
+            class B extends A {
+                void test() {
+                    this.x
+                }
+            }
+        ''', 'The field A.x is not accessible'
+    }
+
+    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'
+    }
+
+    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 +208,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 +217,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testFieldWithInheritanceFromAnotherSourceUnit2() {
+    void testPropertyWithInheritanceFromAnotherSourceUnit2() {
         shouldFailWithMessages '''
             class B extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass {
             }
@@ -157,7 +226,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 {
             }
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/Groovy7300.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300.groovy
index 3fba642..58908dd 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7300.groovy
@@ -23,72 +23,59 @@ import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport
 
 final class Groovy7300 extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
 
-    void testShouldNotThrowStackOverflow() {
+    void testUseSuperToBypassOverride1() {
         assertScript '''
-            class A {
-                private String field1 = 'test'
-
-                String getField1() {
-                    return this.field1
-               }
+            abstract class A {
+                protected x = 1
+                def getX() { 2 }
             }
-
             class B extends A {
                 @Override
-                String getField1() {
-                    super.field1
-                }
+                def getX() { super.x }
             }
-
-            B b = new B()
-
-            assert b.field1 == 'test'
+            assert new B().getX() == 1 // TODO: Why use A#x and not A#getX?
         '''
     }
 
-    void testShouldNotThrowStackOverflowWithSuper1() {
+    void testUseSuperToBypassOverride1a() {
         assertScript '''
-            class A {
-                private String field1 = 'test'
-
-                void setField1(String val) { field1 = val }
-
-                String getField1() {
-                    return this.field1
-               }
+            abstract class A {
+                protected x = 1
+                def getX() { 2 }
             }
-
             class B extends A {
                 @Override
-                String getField1() {
-                    super.field1 = 'test 2'
-                    super.field1
-                }
+                def getX() { super.@x }
             }
-
-            B b = new B()
-
-            assert b.field1 == 'test 2'
+            assert new B().getX() == 1
         '''
     }
 
-    void testShouldNotThrowStackOverflowWithSuper2() {
+    void testUseSuperToBypassOverride2() {
         assertScript '''
-            class A {
-                private String field = 'value'
-                String getField() { return field }
-                void setField(String value) { field = value }
+            abstract class A {
+                private x = 1
+                def getX() { 2 }
             }
-
             class B extends A {
                 @Override
-                String getField() {
-                    super.@field = 'reset'
-                    return super.field
-                }
+                def getX() { super.x }
             }
-
-            assert new B().field == 'reset'
+            assert new B().getX() == 2
         '''
     }
+
+    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'
+    }
 }