You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2025/07/31 17:42:15 UTC
(groovy) 02/06: GROOVY-8283: STC: field hides getter of super class
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 4445a1e618a081d6899191da50cb11244eb07fb7
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Nov 21 13:44:14 2024 -0600
GROOVY-8283: STC: field hides getter of super class
---
.../transform/stc/StaticTypeCheckingVisitor.java | 18 ++++++---
src/test/groovy/bugs/Groovy8283.groovy | 44 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 5 deletions(-)
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 e9036cb409..b087852c08 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1685,11 +1685,13 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0
else if (field != null && enclosingTypes.contains(current) && storeField(field, pexp, receiverType, visitor, receiver.getData(), !readMode)) {
return true;
}
+ // GROOVY-8283: accessible field shadows getter of super class; GROOVY-11381: inaccessible field stops the loop, so search supers for getter
+ boolean checkUp = field != null && !hasAccessToMember(typeCheckingContext.getEnclosingClassNode(), field.getDeclaringClass(), field.getModifiers());
- MethodNode getter = current.getGetterMethod(isserName);
+ MethodNode getter = getGetterMethod(current, isserName, checkUp);
getter = allowStaticAccessToMember(getter, staticOnly);
if (getter == null) {
- getter = current.getGetterMethod(getterName);
+ getter = getGetterMethod(current, getterName, checkUp);
getter = allowStaticAccessToMember(getter, staticOnly);
}
if (getter != null && ((publicOnly && (!getter.isPublic() || "class".equals(propertyName) || "empty".equals(propertyName)))
@@ -1702,8 +1704,8 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0
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(receiverType)) {
+ // prefer explicit getter/setter for out-of-scope references
+ if (property == null || !enclosingTypes.contains(current)) {
if (readMode) {
if (getter != null) {
ClassNode returnType = inferReturnTypeGenerics(receiverType, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
@@ -1849,6 +1851,12 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0
return foundGetterOrSetter;
}
+ private static MethodNode getGetterMethod(final ClassNode classNode, final String getterName, final boolean searchSupers) {
+ MethodNode getter = classNode.getGetterMethod(getterName, searchSupers);
+ if (getter != null && (getter.getModifiers() & Opcodes.ACC_BRIDGE) != 0) getter = null; // GROOVY-11341
+ return getter;
+ }
+
private static boolean hasAccessToMember(final ClassNode accessor, final ClassNode receiver, final int modifiers) {
if (Modifier.isPublic(modifiers)
|| accessor.equals(receiver)
@@ -2742,7 +2750,7 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0
node.setDeclaringClass(pn.getDeclaringClass());
node.setSynthetic(true);
return node;
- } else if (name.equals(pn.getSetterNameOrDefault()) && !Modifier.isFinal(pn.getModifiers())) {
+ } else if (name.equals(pn.getSetterNameOrDefault()) && !pn.isFinal()) {
MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC | (pn.isStatic() ? Opcodes.ACC_STATIC : 0), VOID_TYPE, new Parameter[]{new Parameter(pn.getType(), pn.getName())}, ClassNode.EMPTY_ARRAY, null);
node.setDeclaringClass(pn.getDeclaringClass());
node.setSynthetic(true);
diff --git a/src/test/groovy/bugs/Groovy8283.groovy b/src/test/groovy/bugs/Groovy8283.groovy
index ec99829e2d..9e75a720b2 100644
--- a/src/test/groovy/bugs/Groovy8283.groovy
+++ b/src/test/groovy/bugs/Groovy8283.groovy
@@ -56,6 +56,50 @@ final class Groovy8283 {
new E().test()
assert new E().foo.class == A // not the field from this perspective
'''
+ assertScript shell, '''import p.*
+ class E extends D {
+ @groovy.transform.ASTTest(phase=org.codehaus.groovy.control.CompilePhase.INSTRUCTION_SELECTION, value={
+ def typeof = { label -> lookup(label)[0].getExpression().getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE).toString(false) }
+
+ assert typeof('implicit' ) == 'p.B'
+ assert typeof('explicit' ) == 'p.B'
+ assert typeof('attribute' ) == 'p.B'
+ assert typeof('methodCall' ) == 'p.A'
+
+ assert typeof('property' ) == 'p.B'
+ assert typeof('attribute2' ) == 'p.B'
+ assert typeof('methodCall2') == 'p.A'
+ })
+ @groovy.transform.TypeChecked
+ void test() {
+ implicit:
+ def a = foo
+ explicit:
+ def b = this.foo
+ attribute:
+ def c = this.@foo
+ methodCall:
+ def d = this.getFoo()
+
+ def that = new E()
+ property:
+ def x = that.foo
+ attribute2:
+ def y = that.@foo
+ methodCall2:
+ def z = that.getFoo()
+ }
+ }
+
+ @groovy.transform.TypeChecked
+ void test() {
+ @groovy.transform.ASTTest(phase=org.codehaus.groovy.control.CompilePhase.INSTRUCTION_SELECTION, value={
+ def type = node.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE)
+ assert type.toString(false) == 'p.A'
+ })
+ def a = new E().foo
+ }
+ '''
}
@Test