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