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

emilles pushed a commit to branch GROOVY-11746
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 64240e42bc65e5d2b94d954e0abc588f69335bb7
Author: Eric Milles <[email protected]>
AuthorDate: Fri Aug 29 14:46:30 2025 -0500

    GROOVY-11746: retain accessible synthetic override
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 10 ++++++---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  3 ++-
 .../groovy/groovy/transform/stc/BugsSTCTest.groovy | 12 +++++++++-
 .../provider/collection/runtime/NamedRecord.groovy | 26 ++++++++++++++++------
 .../provider/collection/runtime/NamedTuple.groovy  | 20 +++++++++++------
 5 files changed, 52 insertions(+), 19 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index c030eb3cca..5b7128988c 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1192,7 +1192,7 @@ public abstract class StaticTypeCheckingSupport {
                 if (toBeRemoved.contains(two)) continue;
                 if (one.getParameters().length == two.getParameters().length) {
                     ClassNode oneDC = one.getDeclaringClass(), twoDC = 
two.getDeclaringClass();
-                    if (oneDC == twoDC || 
isSynthetic(one,two)||isSynthetic(two,one)) { // GROOVY-11341
+                    if (oneDC == twoDC) {
                         if 
(ParameterUtils.parametersEqual(one.getParameters(), two.getParameters())) {
                             ClassNode oneRT = one.getReturnType(), twoRT = 
two.getReturnType();
                             if (isCovariant(oneRT, twoRT)) {
@@ -1201,8 +1201,8 @@ public abstract class StaticTypeCheckingSupport {
                                 toBeRemoved.add(one);
                             }
                         } else {
-                            // imperfect solution to determining if two 
methods are
-                            // equivalent, for example 
String#compareTo(Object) and
+                            // imperfect solution of determining if two 
methods are
+                            // equivalent; for example 
String#compareTo(Object) and
                             // String#compareTo(String) -- in that case, the 
Object
                             // version is marked as synthetic
                             if (isSynthetic(one, two)) {
@@ -1213,6 +1213,10 @@ public abstract class StaticTypeCheckingSupport {
                         }
                     } else if (!oneDC.equals(twoDC)) {
                         if 
(ParameterUtils.parametersEqual(one.getParameters(), two.getParameters())) {
+                            // GROOVY-11341, GROOVY-11746: multi-level 
covariant and synthetic override
+                            if 
(!one.getReturnType().equals(two.getReturnType())) {
+                                
toBeRemoved.add(isCovariant(two.getReturnType(), one.getReturnType()) ? one : 
two);
+                            } else
                             // GROOVY-6882, GROOVY-6970: drop overridden or 
interface equivalent method
                             if (!twoDC.isInterface() ? 
oneDC.isDerivedFrom(twoDC) : oneDC.implementsInterface(twoDC) || // 
GROOVY-10897: concrete vs. abstract
                                                                                
     (!disjoint && !one.isAbstract() && !(two instanceof ExtensionMethodNode))) 
{
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 61b4c7e6bd..2e63acbe50 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -5060,10 +5060,11 @@ trying: for (ClassNode[] signature : signatures) {
             methods = findMethodsWithGenerated(receiver, name);
             if ("call".equals(name) && receiver.isInterface()) {
                 MethodNode sam = findSAM(receiver);
-                if (sam != null) {
+                if (sam != null && !"call".equals(sam.getName())) {
                     MethodNode callMethod = new MethodNode("call", 
sam.getModifiers(), sam.getReturnType(), sam.getParameters(), 
sam.getExceptions(), sam.getCode());
                     callMethod.setDeclaringClass(sam.getDeclaringClass());
                     callMethod.setSourcePosition(sam);
+                    callMethod.setSynthetic(true);
                     methods.add(callMethod);
                 }
             }
diff --git a/src/test/groovy/groovy/transform/stc/BugsSTCTest.groovy 
b/src/test/groovy/groovy/transform/stc/BugsSTCTest.groovy
index db1f68c2a8..884431492a 100644
--- a/src/test/groovy/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/BugsSTCTest.groovy
@@ -1039,7 +1039,7 @@ class BugsSTCTest extends StaticTypeCheckingTestCase {
         'Abstract method m() cannot be called directly'
     }
 
-    // GROOVY-8339, GROOVY-10109, GROOVY-10594
+    // GROOVY-8339, GROOVY-10109, GROOVY-10594, GROOVY-11746
     void testInvokePublicMethodFromInaccessibleBase() {
         assertScript '''
             new StringBuilder().setLength(0)
@@ -1055,6 +1055,16 @@ class BugsSTCTest extends StaticTypeCheckingTestCase {
             String sub = new StringBuilder("Hello World").substring(0,5)
             assert sub == 'Hello'
         '''
+
+        assertScript '''
+            @Grab('org.apache.pdfbox:pdfbox:3.0.5')
+            import org.apache.pdfbox.pdmodel.PDPageContentStream
+            import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject
+            void test(PDPageContentStream stream, PDImageXObject image, float 
x, float y, float width, float height) {
+                stream.drawImage(image, x, y, width, height) // drawImage: 
synthetic in PDPageContentStream for access
+            }
+            assert true
+        '''
     }
 
     void testInvokeSuperMethodFromCovariantOverride() {
diff --git 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedRecord.groovy
 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedRecord.groovy
index d8730bf4c8..8dcd535dc2 100644
--- 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedRecord.groovy
+++ 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedRecord.groovy
@@ -18,6 +18,7 @@
  */
 package org.apache.groovy.ginq.provider.collection.runtime
 
+import groovy.transform.AutoFinal
 import groovy.transform.CompileStatic
 import groovy.transform.stc.POJO
 
@@ -26,26 +27,37 @@ import groovy.transform.stc.POJO
  *
  * @since 4.0.0
  */
-@POJO
 @CompileStatic
+@AutoFinal
+@POJO
 class NamedRecord<E, T> extends NamedTuple<E> {
+
     private static final long serialVersionUID = -2554041223576761912L
-    private SourceRecord<T> sourceRecord
+
     private final List<String> aliasList
+    private SourceRecord<T> sourceRecord
 
     NamedRecord(List<E> elementList, List<String> nameList, List<String> 
aliasList = Collections.emptyList()) {
         super(elementList, nameList)
         this.aliasList = aliasList
     }
 
+    @Override
+    String toString() {
+        return super.toString()
+    }
+
+    @Override
+    def get(String name) {
+        return getAt(name)
+    }
+
     @Override
     def getAt(String name) {
         if (exists(name)) {
-            def value = super.getAt(name)
-            return value
+            return super.get(name)
         }
-
-        return sourceRecord.get(name)
+        return sourceRecord?.get(name)
     }
 
     List<String> getAliasList() {
@@ -53,7 +65,7 @@ class NamedRecord<E, T> extends NamedTuple<E> {
     }
 
     NamedRecord<E, T> sourceRecord(T sr) {
-        this.sourceRecord = new SourceRecord<>(sr, aliasList)
+        sourceRecord = new SourceRecord<>(sr, getAliasList())
         return this
     }
 }
diff --git 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
index 7deb1d0411..5240c2728b 100644
--- 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
+++ 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
@@ -18,20 +18,26 @@
  */
 package org.apache.groovy.ginq.provider.collection.runtime
 
+import groovy.transform.AutoFinal
 import groovy.transform.CompileStatic
 import groovy.transform.PackageScope
 import groovy.transform.stc.POJO
 
+import static groovy.transform.PackageScopeTarget.*
+
 /**
  * Immutable named list to represent list result of GINQ
  *
  * @since 4.0.0
  */
+@PackageScope([CLASS, CONSTRUCTORS, METHODS])
 @CompileStatic
-@PackageScope
+@AutoFinal
 @POJO
 class NamedTuple<E> extends Tuple<E> {
+
     private static final long serialVersionUID = -5067092453136522209L
+
     private final Map<String, E> data
 
     NamedTuple(List<E> elementList, List<String> nameList) {
@@ -54,11 +60,11 @@ class NamedTuple<E> extends Tuple<E> {
         }
     }
 
-    def get(String name) {
-        return getAt(name)
+    public get(String name) {
+        return data.get(name)
     }
 
-    def getAt(String name) {
+    public getAt(String name) {
         return data.get(name)
     }
 
@@ -67,15 +73,15 @@ class NamedTuple<E> extends Tuple<E> {
     }
 
     List<String> getNameList() {
-        return Collections.unmodifiableList(data.keySet().toList())
+        return new ArrayList<>(data.keySet())
     }
 
     @Override
-    String toString() {
+    public String toString() {
         StringJoiner sj = new StringJoiner(', ', '(', ')')
         for (String name : getNameList()) {
             sj.add(name + ':' + this[name])
         }
-        sj.toString()
+        return sj.toString()
     }
 }

Reply via email to