[ 
https://issues.apache.org/jira/browse/GROOVY-11884?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18069175#comment-18069175
 ] 

ASF GitHub Bot commented on GROOVY-11884:
-----------------------------------------

Copilot commented on code in PR #2407:
URL: https://github.com/apache/groovy/pull/2407#discussion_r3003915697


##########
src/main/java/org/codehaus/groovy/ast/AnnotationNode.java:
##########
@@ -159,9 +164,39 @@ public boolean isTargetAllowed(final int target) {
             return 0x4FF; // GROOVY-11838: JLS 9.6.4.1
         });
 
+        // check @ExtendedTarget for Groovy-specific element types
+        allowedTargets |= 
classNode.redirect().getNodeMetaData(ExtendedTarget.class, (k) -> {
+            for (AnnotationNode an : classNode.getAnnotations()) {
+                if 
("groovy.lang.annotation.ExtendedTarget".equals(an.getClassNode().getName())) {
+                    Expression member = an.getMember("value");
+                    int bits = 0;
+                    if (member instanceof ListExpression list) {
+                        for (Expression e : list.getExpressions()) {
+                            bits |= extendedElementTypeBits(e);
+                        }
+                    } else {
+                        bits |= extendedElementTypeBits(member);
+                    }
+                    return bits;
+                }
+            }
+            return 0;
+        });
+
         return (target & allowedTargets) == target;
     }
 
+    private static int extendedElementTypeBits(final Expression e) {
+        if (e instanceof PropertyExpression item) {
+            String name = item.getPropertyAsString();
+            return switch (ExtendedElementType.valueOf(name)) {
+                case IMPORT -> IMPORT_TARGET;
+                case LOOP   -> STATEMENT_TARGET;
+            };

Review Comment:
   `extendedElementTypeBits` calls `ExtendedElementType.valueOf(name)` without 
guarding against `name == null` or unknown values. If an invalid enum constant 
is used in `@ExtendedTarget(...)`, the compiler may throw 
`IllegalArgumentException`/`NPE` here (potentially masking the existing 
validation error) instead of reporting a clean compilation error. Consider 
handling null/invalid names (e.g., check for null and catch 
`IllegalArgumentException`) and returning 0 or producing a targeted error 
message.
   ```suggestion
               if (name == null) {
                   return 0;
               }
               try {
                   return switch (ExtendedElementType.valueOf(name)) {
                       case IMPORT -> IMPORT_TARGET;
                       case LOOP   -> STATEMENT_TARGET;
                   };
               } catch (IllegalArgumentException ignore) {
                   // unknown ExtendedElementType value; no extended element 
type bits
                   return 0;
               }
   ```



##########
src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java:
##########
@@ -221,6 +232,32 @@ public void visitMethod(final MethodNode node) {
         }
     }
 
+    @Override
+    protected void visitStatementAnnotations(final Statement statement) {
+        for (AnnotationNode annotation : statement.getStatementAnnotations()) {
+            ErrorCollector errorCollector = new 
ErrorCollector(source.getConfiguration());
+            AnnotationVisitor visitor = new AnnotationVisitor(source, 
errorCollector);
+            AnnotationNode visited = visitor.visit(annotation);
+            source.getErrorCollector().addCollectorContents(errorCollector);
+
+            if (!visited.isTargetAllowed(STATEMENT_TARGET)) {
+                addError("Annotation @" + visited.getClassNode().getName() + " 
is not allowed on element "
+                        + AnnotationNode.targetToName(STATEMENT_TARGET), 
visited);
+            }
+        }
+    }

Review Comment:
   This PR adds new validation for statement annotations via 
`visitStatementAnnotations`, but the test additions only cover import-level 
annotations. Consider adding a focused test that (1) rejects a 
non-`@ExtendedTarget(LOOP)` annotation on a loop and (2) accepts an annotation 
that declares `@ExtendedTarget(LOOP)`, to ensure this new validation path is 
exercised and doesn’t regress.



##########
src/main/java/org/codehaus/groovy/classgen/ExtendedVerifier.java:
##########
@@ -43,23 +45,27 @@
 import org.objectweb.asm.Opcodes;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;

Review Comment:
   Unused import: `java.util.Collections` is added but not referenced in this 
file. Please remove it to keep the import list clean and avoid warnings.





> Close gaps in Groovy annotation validation
> ------------------------------------------
>
>                 Key: GROOVY-11884
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11884
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>              Labels: breaking
>
> Groovy lets you have annotations like @Grab on various nodes and various 
> statements including imports. Targeting imports is not possible in Java, but 
> this is part of Groovy's stance of providing more flexibility.
> Groovy also does validation that annotations don't appear in incorrect 
> places. Currently there is a gap in our validation - we ignore things like 
> checking what things can be on imports, e.g. the following is currently valid 
> but totally ignored:
> {code}
> @Deprecated
> import java.lang.String
> {code}
> As well as @Grab, @Newify and @BaseScript can also appear on imports.
> With the new incubating AST transforms on loops, @Invariant can now appear on 
> loops.
> I propose we create some additional classes to capture these extended 
> annotation targets and close the gaps in validation. Something like:
> {code}
> @Retention(RetentionPolicy.RUNTIME)
> @Target(ElementType.ANNOTATION_TYPE)
> public @interface ExtendedTarget {
>     ExtendedElementType[] value();
> }
> {code}
> And:
> {code}
> public enum ExtendedElementType {
>     /** Import statement. */
>     IMPORT,
>     /** Loop statement ({@code for}, {@code while}, {@code do-while}). */
>     LOOP
> }
> {code}
> And then things like @BaseScript would change from this:
> {code}
> @Retention(RetentionPolicy.SOURCE)
> @Target({ElementType.LOCAL_VARIABLE, ElementType.PACKAGE, ElementType.TYPE 
> /*, ElementType.IMPORT*/})
> @GroovyASTTransformationClass("org.codehaus.groovy.transform.BaseScriptASTTransformation")
> public @interface BaseScript {
>     Class value() default Script.class;
> }
> {code}
> to this:
> {code}
> @Retention(RetentionPolicy.SOURCE)
> @Target({ElementType.LOCAL_VARIABLE, ElementType.PACKAGE, ElementType.TYPE})
> @ExtendedTarget(ExtendedElementType.IMPORT)
> @GroovyASTTransformationClass("org.codehaus.groovy.transform.BaseScriptASTTransformation")
> public @interface BaseScript {
>     Class value() default Script.class;
> }
> {code}
> This would be a breaking change if any codebases are relying on Groovy's 
> leniency, e.g. maybe they had a convention of putting @Deprecated on an 
> import if they were hoping to eventually get rid of the related dependency. 
> We don't know anyone doing this, but in such cases, folks would need to now 
> make and use an annotation with @ExtendedTarget.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to