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 <eric.mil...@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