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:47:59 UTC
[groovy] 01/03: 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 GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 1ea6cacc8047e95b5e158808103e30c70fe25610
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)
(cherry picked from commit c7c35b0fc0b54816d52742d90893491efb307c89)
---
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 e9dc5a2..b02361c 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;
@@ -663,7 +662,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);
@@ -683,33 +682,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);
}
}
@@ -1471,26 +1462,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;
@@ -1501,20 +1502,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;
}
@@ -1533,7 +1534,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);
@@ -1558,7 +1559,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);
@@ -1580,24 +1581,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)) {
@@ -1619,15 +1612,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);
@@ -1639,12 +1632,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) {
@@ -1781,19 +1774,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);
@@ -4786,7 +4789,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'
+ }
}