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/05/13 16:53:59 UTC

(groovy) 01/01: GROOVY-11663: STC: error for class or trait property expression

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

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

commit 9a273d5a3e0c40be28a53f037182b2e6bb26e889
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue May 13 09:48:07 2025 -0500

    GROOVY-11663: STC: error for class or trait property expression
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 41 ++++++++++++++--------
 .../packageScope/DifferentPackageTest.groovy       |  2 +-
 .../traitx/TraitASTTransformationTest.groovy       |  9 +++--
 3 files changed, 34 insertions(+), 18 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 f375a1ea7d..fa7df3da1e 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -763,28 +763,39 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     @Override
     public void visitPropertyExpression(final PropertyExpression expression) {
-        if (existsProperty(expression, !typeCheckingContext.isTargetOfEnclosingAssignment(expression))) return;
-
-        if (!extension.handleUnresolvedProperty(expression)) {
-            var objectExpression = expression.getObjectExpression();
-            var objectExpressionType = (objectExpression instanceof ClassExpression
-                    ? objectExpression.getType() : wrapTypeIfNecessary(getType(objectExpression)));
-            objectExpressionType = findCurrentInstanceOfClass(objectExpression, objectExpressionType);
-            addStaticTypeError("No such property: " + expression.getPropertyAsString() + " for class: " + prettyPrintTypeName(objectExpressionType), expression);
+        boolean readOnly = !typeCheckingContext.isTargetOfEnclosingAssignment(expression);
+        if (existsProperty(expression, readOnly)
+                || extension.handleUnresolvedProperty(expression)) {
+            return; // resolved or excused
         }
+        recordMissingProperty(expression);
     }
 
     @Override
     public void visitAttributeExpression(final AttributeExpression expression) {
-        if (existsProperty(expression, true)) return;
+        boolean readOnly = true;
+        if (existsProperty(expression, readOnly)
+                || extension.handleUnresolvedAttribute(expression)) {
+            return; // resolved or excused
+        }
+        recordMissingProperty(expression);
+    }
 
-        if (!extension.handleUnresolvedAttribute(expression)) {
-            var objectExpression = expression.getObjectExpression();
-            var objectExpressionType = (objectExpression instanceof ClassExpression
-                    ? objectExpression.getType() : wrapTypeIfNecessary(getType(objectExpression)));
-            objectExpressionType = findCurrentInstanceOfClass(objectExpression, objectExpressionType);
-            addStaticTypeError("No such attribute: " + expression.getPropertyAsString() + " for class: " + prettyPrintTypeName(objectExpressionType), expression);
+    private void recordMissingProperty(final PropertyExpression expression) {
+        var objectExpression = expression.getObjectExpression();
+        var objectExpressionType = findCurrentInstanceOfClass(objectExpression, getType(objectExpression));
+
+        String pattern;
+        if (!isClassClassNodeWrappingConcreteType(objectExpressionType)) {
+            pattern = "No such {0,choice,1#attribute|2#property}: {1} for class: {2}";
+        } else {
+            objectExpressionType = objectExpressionType.getGenericsTypes()[0].getType();
+            pattern = "No such {0,choice,1#attribute|2#property}: {1} for Class or static {0,choice,1#field|2#property} for class: {2}";
         }
+
+        String error = java.text.MessageFormat.format(pattern, expression instanceof AttributeExpression ? 1 : 2, expression.getPropertyAsString(), prettyPrintTypeName(wrapTypeIfNecessary(objectExpressionType)));
+        ASTNode node = expression.getLineNumber() > 0 ? expression : expression.getProperty(); // GROOVY-11663
+        addStaticTypeError(error, node);
     }
 
     @Override
diff --git a/src/test/groovy/org/codehaus/groovy/transform/packageScope/DifferentPackageTest.groovy b/src/test/groovy/org/codehaus/groovy/transform/packageScope/DifferentPackageTest.groovy
index ba1644879e..135dd8d364 100644
--- a/src/test/groovy/org/codehaus/groovy/transform/packageScope/DifferentPackageTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/transform/packageScope/DifferentPackageTest.groovy
@@ -447,6 +447,6 @@ final class DifferentPackageTest {
                 '''
             )
         }
-        assert err =~ /No such property: answer for class: p.One/ // TODO: Cannot access p.One#getAnswer?
+        assert err =~ /No such property: answer for Class or static property for class: p.One/ // TODO: Cannot access p.One#getAnswer?
     }
 }
diff --git a/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index e0bfd2b087..c1dd00f55e 100644
--- a/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -3710,10 +3710,10 @@ final class TraitASTTransformationTest {
         """
     }
 
-    // GROOVY-11641
+    // GROOVY-11641, GROOVY-11663
     @CompileModesTest
     void testTraitAccessToInheritedStaticMethods4(String mode) {
-        shouldFail shell, """
+        var err = shouldFail shell, """
             $mode
             trait Foo {
                 public static final String BANG = '!'
@@ -3739,6 +3739,11 @@ final class TraitASTTransformationTest {
             Main.test1()
             new Main().test2()
         """
+        if (mode.endsWith('Dynamic')) {
+            assert err =~ /MissingPropertyException/
+        } else {
+            assert err =~ /\[Static type checking\] - No such property: BANG for Class or static property for class: Bar/
+        }
     }
 
     // GROOVY-9386