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 b351c27bbbc96ca0fb9ae4ea0aff7206dadb070d Author: Eric Milles <[email protected]> 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
