diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index f1afd60351..e01e21317f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -138,6 +138,20 @@ public final class StringUtil { return col; } + /** + * Returns the substring following the last occurrence of the + * given character. If the character doesn't occur, returns + * the whole string. This contrasts with {@link StringUtils#substringAfterLast(String, String)}, + * which returns the empty string in that case. + * + * @param str String to cut + * @param c Delimiter + */ + public static String substringAfterLast(String str, int c) { + int i = str.lastIndexOf(c); + return i < 0 ? str : str.substring(i + 1); + } + /** * Formats a double to a percentage, keeping {@code numDecimal} decimal places. * diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index ea55e57d5a..6ceabc1db9 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -2282,47 +2282,32 @@ void RSIGNEDSHIFT() #void: /* Annotation syntax follows. */ -void Annotation() #void: +void Annotation(): {} { - LOOKAHEAD( "@" VoidName() "(" ( "=" | ")" )) - NormalAnnotation() -| - LOOKAHEAD( "@" VoidName() "(" ) - SingleMemberAnnotation() -| - MarkerAnnotation() + "@" jjtThis.name=VoidName() [ AnnotationMemberList() ] } -void AnnotationBase(Node n) #void: -{String name = null;} -{ - "@" name=VoidName() {n.setImage(name);} -} - -void NormalAnnotation(): +void AnnotationMemberList(): {} { - AnnotationBase(jjtThis) "(" [ MemberValuePairs() ] ")" + "(" + ( LOOKAHEAD( "=") + MemberValuePair() ( "," MemberValuePair() )* + | [ ShorthandAnnotationValue() ] + ) + ")" } -void MarkerAnnotation(): -{} +void ShorthandAnnotationValue() #MemberValuePair: { - AnnotationBase(jjtThis) + jjtThis.setImage("value"); + jjtThis.setShorthand(); +} +{ + MemberValue() } -void SingleMemberAnnotation(): -{} -{ - AnnotationBase(jjtThis) "(" MemberValue() ")" -} - -void MemberValuePairs() #void: -{} -{ - MemberValuePair() ( "," MemberValuePair() )* -} void MemberValuePair(): {} @@ -2334,11 +2319,8 @@ void MemberValue() #void: {} { Annotation() - | - MemberValueArrayInitializer() - | - // Constant expression - ConditionalExpression() + | MemberValueArrayInitializer() + | ConditionalExpression() // Constant expression } void MemberValueArrayInitializer(): @@ -2471,12 +2453,8 @@ String VoidName() #void: JavaccToken t; } { - t= - { - s.append(t.getImage()); - } - ( LOOKAHEAD(2) "." t= - {s.append('.').append(t.getImage());} + t= { s.append(t.getImage()); } + ( LOOKAHEAD(2) "." t= {s.append('.').append(t.getImage());} )* {return s.toString();} } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java index 20b8265829..129b30f871 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java @@ -4,38 +4,82 @@ package net.sourceforge.pmd.lang.java.ast; +import java.util.Iterator; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.util.StringUtil; + /** - * Represents an annotation. This node has three specific syntactic variants, - * represented by nodes that implement this interface. + * Represents an annotation. * *
  *
- * Annotation ::= {@linkplain ASTNormalAnnotation NormalAnnotation}
- *              | {@linkplain ASTSingleMemberAnnotation SingleMemberAnnotation}
- *              | {@linkplain ASTMarkerAnnotation MarkerAnnotation}
+ * Annotation ::= "@" Name {@link ASTAnnotationMemberList AnnotationMemberList}?
  *
  * 
*/ -public interface ASTAnnotation extends TypeNode, ASTMemberValue { +public final class ASTAnnotation extends AbstractJavaTypeNode implements TypeNode, ASTMemberValue, Iterable { + + String name; + + ASTAnnotation(int id) { + super(id); + } /** * Returns the name of the annotation as it is used, * eg {@code java.lang.Override} or {@code Override}. */ - default String getAnnotationName() { - return getImage(); + public String getAnnotationName() { + return name; } + @Override + @Deprecated + public String getImage() { + return name; + } /** * Returns the simple name of the annotation. */ - default String getSimpleName() { - String[] split = getImage().split("\\."); - return split[split.length - 1]; + public String getSimpleName() { + return StringUtil.substringAfterLast(getImage(), '.'); } -} + /** + * Returns the list of members, or null if there is none. + */ + public @Nullable ASTAnnotationMemberList getMemberList() { + return children().first(ASTAnnotationMemberList.class); + } + /** + * Returns the stream of explicit members for this annotation. + */ + public NodeStream getMembers() { + return children(ASTAnnotationMemberList.class).children(ASTMemberValuePair.class); + } + + + @Override + public Iterator iterator() { + return children(ASTMemberValuePair.class).iterator(); + } + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + + @Override + public void jjtAccept(SideEffectingVisitor visitor, T data) { + visitor.visit(this, data); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMemberList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMemberList.java new file mode 100644 index 0000000000..2cedd5513c --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMemberList.java @@ -0,0 +1,56 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import java.util.Iterator; + +import net.sourceforge.pmd.lang.ast.NodeStream; + +/** + * Represents the list of {@link ASTMemberValuePair member-value pairs} + * in an {@link ASTAnnotation annotation}. + * + *
+ *
+ * AnnotationMemberList ::= "(" {@link ASTMemberValuePair MemberValuePair} ( "," {@link ASTMemberValuePair MemberValuePair} )* ")"
+ *                        | "(" {@link ASTMemberValuePair ValueShorthand} ")"
+ *                        | "(" ")"
+ *
+ * 
+ */ +public final class ASTAnnotationMemberList extends AbstractJavaNode implements Iterable { + + ASTAnnotationMemberList(int id) { + super(id); + } + + + @Override + public ASTAnnotation getParent() { + return (ASTAnnotation) super.getParent(); + } + + @Override + @SuppressWarnings("unchecked") + public NodeStream children() { + return (NodeStream) super.children(); + } + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + + @Override + public void jjtAccept(SideEffectingVisitor visitor, T data) { + visitor.visit(this, data); + } + + @Override + public Iterator iterator() { + return children().iterator(); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java index f9eb1adb38..4df9a2c670 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.List; +import net.sourceforge.pmd.lang.ast.NodeStream; /** * Represents an array type. @@ -23,11 +23,8 @@ public final class ASTArrayType extends AbstractJavaTypeNode implements ASTRefer @Override - public List getDeclaredAnnotations() { - // an array type's annotations are on its dimensions - // any annotations found before the element type apply to the - // element type - return ((ASTArrayTypeDim) getDimensions().getLastChild()).getDeclaredAnnotations(); + public NodeStream getDeclaredAnnotations() { + return getDimensions().getLastChild().getDeclaredAnnotations(); } public ASTArrayDimensions getDimensions() { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMarkerAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMarkerAnnotation.java deleted file mode 100644 index c680680794..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMarkerAnnotation.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -/** - * Represents an annotation with no declared member, e.g. {@code @Override}. - * - *
- *
- * MarkerAnnotation ::= "@" Name
- *
- * 
- * - * @see ASTSingleMemberAnnotation - * @see ASTNormalAnnotation - */ -public final class ASTMarkerAnnotation extends AbstractJavaTypeNode implements ASTAnnotation { - - ASTMarkerAnnotation(int id) { - super(id); - } - - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java index 0ff2d9e33b..b7b78bdcbb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.ast; /** * Represents the value of a member of an annotation. * This can appear in a {@linkplain ASTMemberValuePair member-value pair}, - * in a {@linkplain ASTSingleMemberAnnotation single-member annotation}, * or in the {@linkplain ASTDefaultValue default clause} of an annotation * method. * diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java index a4afe7c074..3055da7a26 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java @@ -5,38 +5,54 @@ package net.sourceforge.pmd.lang.java.ast; /** - * Represents a single member-value pair in a {@linkplain ASTNormalAnnotation NormalAnnotation}. + * Represents a single pair of member name to value in an annotation. + * This node also represents the shorthand syntax, see {@link #isShorthand()}. * *
  *
- * MemberValuePair ::=  <IDENTIFIER> "=" {@linkplain ASTMemberValue MemberValue}
+ * MemberValuePair ::= <IDENTIFIER> "=" {@linkplain ASTMemberValue MemberValue}
+ *
+ * ValueShorthand  ::= {@linkplain ASTMemberValue MemberValue}
  *
  * 
*/ public final class ASTMemberValuePair extends AbstractJavaNode { + + private boolean isShorthand; + ASTMemberValuePair(int id) { super(id); } /** * Returns the name of the member set by this pair. + * This returns {@code "value"} if this is a shorthand declaration. */ - public String getMemberName() { + public String getName() { return getImage(); } + /** + * Returns true if this is a shorthand for the {@code value} attribute. + * For example, {@code @A("v")} has exactly the same structure as + * {@code @A(value = "v")}, except this attribute returns true for + * the first one only. + */ + public boolean isShorthand() { + return isShorthand; + } /** * Returns the value of the member set by this pair. */ - public ASTMemberValue getMemberValue() { + public ASTMemberValue getValue() { return (ASTMemberValue) getChild(0); } @Override - public ASTNormalAnnotation getParent() { - return (ASTNormalAnnotation) super.getParent(); + public ASTAnnotationMemberList getParent() { + return (ASTAnnotationMemberList) super.getParent(); } @@ -50,4 +66,8 @@ public final class ASTMemberValuePair extends AbstractJavaNode { public void jjtAccept(SideEffectingVisitor visitor, T data) { visitor.visit(this, data); } + + void setShorthand() { + this.isShorthand = true; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePairs.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePairs.java deleted file mode 100644 index 146c7f9c81..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePairs.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import java.util.Iterator; - - -/** - * Represents a list of member values in an {@linkplain ASTNormalAnnotation annotation}. - * - *
- *
- *  MemberValuePairs ::= {@linkplain ASTMemberValuePair MemberValuePair} ( "," {@linkplain ASTMemberValuePair MemberValuePair} )*
- *
- * 
- * - * @deprecated Removed from the tree, added no info - */ -@Deprecated -public final class ASTMemberValuePairs extends AbstractJavaNode implements Iterable { - - ASTMemberValuePairs(int id) { - super(id); - } - - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - - - @Override - public ASTMemberValuePair getChild(int index) { - return (ASTMemberValuePair) super.getChild(index); - } - - - @Override - public ASTNormalAnnotation getParent() { - return (ASTNormalAnnotation) super.getParent(); - } - - - @Override - public Iterator iterator() { - return children(ASTMemberValuePair.class).iterator(); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNormalAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNormalAnnotation.java deleted file mode 100644 index 4ef7407b71..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNormalAnnotation.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import java.util.Iterator; - -/** - * Represents an annotation that with a parenthesized list - * of key-value pairs (possibly empty). - * - *
- *
- * NormalAnnotation ::=  "@" Name "(" ( {@linkplain ASTMemberValuePair MemberValuePair} ( "," {@linkplain ASTMemberValuePair MemberValuePair} )* )? ")"
- *
- * 
- * - * @see ASTSingleMemberAnnotation - * @see ASTMarkerAnnotation - */ -public final class ASTNormalAnnotation extends AbstractJavaTypeNode implements ASTAnnotation, Iterable { - ASTNormalAnnotation(int id) { - super(id); - } - - - @Override - public Iterator iterator() { - return children(ASTMemberValuePair.class).iterator(); - } - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSingleMemberAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSingleMemberAnnotation.java deleted file mode 100644 index 058a0bef0b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSingleMemberAnnotation.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -/** - * Represents an annotation using the shorthand syntax for the default member. - * - *
- *
- * SingleMemberAnnotation ::=  "@" Name "(" {@linkplain ASTMemberValue MemberValue} ")"
- *
- * 
- * - * @see ASTMarkerAnnotation - * @see ASTNormalAnnotation - */ -public final class ASTSingleMemberAnnotation extends AbstractJavaTypeNode implements ASTAnnotation { - ASTSingleMemberAnnotation(int id) { - super(id); - } - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - - - /** - * Returns the value of the default member - * set by this annotation. - */ - public ASTMemberValue getMemberValue() { - return (ASTMemberValue) getChild(0); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java index 2af872d9b2..c3a5bc9f64 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java @@ -5,8 +5,6 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.List; - import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; @@ -32,11 +30,6 @@ public final class ASTTypeParameter extends AbstractTypedSymbolDeclarator getDeclaredAnnotations() { - return children(ASTAnnotation.class).toList(); - } - /** * Returns the name of the type variable introduced by this declaration. */ diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java index f5918859f0..a016ac8e97 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java @@ -6,11 +6,12 @@ package net.sourceforge.pmd.lang.java.ast; import static net.sourceforge.pmd.lang.java.ast.JModifier.STRICTFP; -import java.util.List; import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.lang.ast.NodeStream; + /** * A node that owns a {@linkplain ASTModifierList modifier list}. * @@ -31,10 +32,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; */ public interface AccessNode extends Annotatable { - @Override - default List getDeclaredAnnotations() { - return getModifiers().children(ASTAnnotation.class).toList(); + default NodeStream getDeclaredAnnotations() { + return getModifiers().children(ASTAnnotation.class); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java index 12afe8904f..6a136e705a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java @@ -4,11 +4,9 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.Collection; -import java.util.List; - -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.Predicate; +import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; /** @@ -24,54 +22,25 @@ public interface Annotatable extends JavaNode { /** * Returns all annotations present on this node. */ - default List getDeclaredAnnotations() { - return this.findChildrenOfType(ASTAnnotation.class); - } - - - /** - * Returns the annotation with the given qualified name if it is present, - * otherwise returns null. The argument should be a qualified name, though - * this method will find also usages of an annotation that use the simple - * name if it is in scope. - * - *

E.g. {@code getAnnotation("java.lang.Override")} will find both - * {@code @java.lang.Override} and {@code @Override}. - */ - @Nullable - default ASTAnnotation getAnnotation(String annotQualifiedName) { - // TODO use node streams - List annotations = getDeclaredAnnotations(); - for (ASTAnnotation annotation : annotations) { - if (TypeHelper.isA(annotation, annotQualifiedName)) { - return annotation; - } - } - return null; - } - - - /** - * Returns true if any annotation in the given collection is present, - * using {@link #isAnnotationPresent(String)}, otherwise false. - */ - default boolean isAnyAnnotationPresent(Collection annotQualifiedNames) { - // TODO use node streams - for (String annotQualifiedName : annotQualifiedNames) { - if (isAnnotationPresent(annotQualifiedName)) { - return true; - } - } - return false; + default NodeStream getDeclaredAnnotations() { + return children(ASTAnnotation.class); } /** * Returns true if an annotation with the given qualified name is - * applied to this node. In this case, {@link #getAnnotation(String)} - * will not return null. + * applied to this node. */ default boolean isAnnotationPresent(String annotQualifiedName) { - return getAnnotation(annotQualifiedName) != null; + return getDeclaredAnnotations().any(t -> TypeHelper.isA(t, annotQualifiedName)); + } + + + /** + * Returns true if an annotation with the given type is + * applied to this node. + */ + default boolean isAnnotationPresent(Class type) { + return getDeclaredAnnotations().any((Predicate) t -> TypeHelper.subclasses(t, type)); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java index 8a52ba6ce1..bc8055b38e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java @@ -25,24 +25,6 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class JavaParserVisitorAdapter implements JavaParserVisitor { - public Object visit(ASTAnnotation node, Object data) { - return visit((JavaNode) node, data); - } - - @Override - public Object visit(ASTMarkerAnnotation node, Object data) { - return visit((ASTAnnotation) node, data); - } - - @Override - public Object visit(ASTSingleMemberAnnotation node, Object data) { - return visit((ASTAnnotation) node, data); - } - - @Override - public Object visit(ASTNormalAnnotation node, Object data) { - return visit((ASTAnnotation) node, data); - } public Object visit(ASTType node, Object data) { return visit((JavaNode) node, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java index d1d4e29ca8..2b4970e7c9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java @@ -15,44 +15,22 @@ package net.sourceforge.pmd.lang.java.ast; public class SideEffectingVisitorAdapter implements SideEffectingVisitor { - public void visit(ASTAnnotation node, T data) { - visit((JavaNode) node, data); - } - - @Override - public void visit(ASTSingleMemberAnnotation node, T data) { - visit((ASTAnnotation) node, data); - } - - @Override - public void visit(ASTNormalAnnotation node, T data) { - visit((ASTAnnotation) node, data); - } - - @Override - public void visit(ASTMarkerAnnotation node, T data) { - visit((ASTAnnotation) node, data); - } - - public void visit(ASTMethodOrConstructorDeclaration node, T data) { visit((JavaNode) node, data); } - + @Override public void visit(ASTMethodDeclaration node, T data) { visit((ASTMethodOrConstructorDeclaration) node, data); } - + @Override public void visit(ASTConstructorDeclaration node, T data) { visit((ASTMethodOrConstructorDeclaration) node, data); } - // TODO delegation - public void visit(ASTAnyTypeDeclaration node, T data) { visit((JavaNode) node, data); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java index a6df69870f..81d3335341 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java @@ -38,6 +38,6 @@ public abstract class AbstractIgnoredAnnotationRule extends AbstractJavaRule { * @return true if the annotation has been found, otherwise false */ protected boolean hasIgnoredAnnotation(Annotatable node) { - return node.isAnyAnnotationPresent(getProperty(ignoredAnnotationsDescriptor)); + return getProperty(ignoredAnnotationsDescriptor).stream().anyMatch(node::isAnnotationPresent); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java index 0d95425b52..8c7512bf5c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java @@ -16,7 +16,6 @@ import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTAndExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; @@ -106,9 +105,6 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse // FIXME those are not in sync with JavaParserVisitorAdapter // See #1786 - public Object visit(ASTAnnotation node, Object data) { - return JavaParserVisitor.super.visit(node, data); - } public Object visit(ASTExpression node, Object data) { return JavaParserVisitor.super.visit(node, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java index f6883b3196..2ba13ebc45 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java @@ -136,6 +136,6 @@ public class AbstractLombokAwareRule extends AbstractIgnoredAnnotationRule { * @return true if a lombok annotation has been found */ protected boolean hasLombokAnnotation(Annotatable node) { - return node.isAnyAnnotationPresent(LOMBOK_ANNOTATIONS); + return LOMBOK_ANNOTATIONS.stream().anyMatch(node::isAnnotationPresent); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 2952d14069..b0a92688a6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -9,13 +9,11 @@ import java.util.List; import java.util.Map; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTMemberValuePair; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTNormalAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; @@ -41,7 +39,7 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { @Override public Object visit(ASTMethodDeclaration method, Object data) { if (isJUnitMethod(method, data)) { - if (!isExpectAnnotated(method.getParent())) { + if (!isExpectAnnotated(method)) { Map variables = getVariables(method); Scope classScope = method.getScope().getParent(); @@ -98,10 +96,9 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { for (Map.Entry> entry : decls.entrySet()) { Node parent = entry.getKey().getNode().getParent().getParent().getParent(); - if (parent.hasDescendantOfType(ASTMarkerAnnotation.class) - && parent.getFirstChildOfType(ASTFieldDeclaration.class) != null) { - String annot = parent.getFirstDescendantOfType(ASTMarkerAnnotation.class).getChild(0).getImage(); - if (!"Rule".equals(annot) && !"org.junit.Rule".equals(annot)) { + if (parent.getFirstChildOfType(ASTFieldDeclaration.class) != null) { + ASTAnnotation annot = parent.getFirstDescendantOfType(ASTAnnotation.class); + if (annot == null || !TypeHelper.isA(annot, "org.junit.Rule")) { continue; } @@ -118,20 +115,12 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { /** * Tells if the node contains a Test annotation with an expected exception. */ - private boolean isExpectAnnotated(Node methodParent) { - List annotations = methodParent.findDescendantsOfType(ASTNormalAnnotation.class); - for (ASTNormalAnnotation annotation : annotations) { - ASTName name = annotation.getFirstChildOfType(ASTName.class); - if (name != null && TypeHelper.isA(name, JUNIT4_CLASS_NAME)) { - List memberValues = annotation.findDescendantsOfType(ASTMemberValuePair.class); - for (ASTMemberValuePair pair : memberValues) { - if ("expected".equals(pair.getImage())) { - return true; - } - } - } - } - return false; + private boolean isExpectAnnotated(ASTMethodDeclaration method) { + return method.getDeclaredAnnotations() + .filter(it -> TypeHelper.isA(it, JUNIT4_CLASS_NAME)) + .flatMap(ASTAnnotation::getMembers) + .any(it -> "expected".equals(it.getName())); + } /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java index 38eb211aa9..f85eecc207 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java @@ -5,14 +5,12 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTResultType; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.util.CollectionUtil; @@ -44,17 +42,8 @@ public class LooseCouplingRule extends AbstractJavaRule { return data; } - private boolean methodHasOverride(Node node) { - ASTClassOrInterfaceBodyDeclaration method = node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class); - if (method != null && method.getNumChildren() > 0 && method.getChild(0) instanceof ASTAnnotation) { - ASTMarkerAnnotation marker = method.getFirstDescendantOfType(ASTMarkerAnnotation.class); - if (marker != null && marker.getFirstChildOfType(ASTName.class) != null) { - ASTName name = marker.getFirstChildOfType(ASTName.class); - if (name.getType() == Override.class) { - return true; - } - } - } - return false; + private boolean methodHasOverride(JavaNode node) { + ASTMethodDeclaration method = node.ancestors(ASTMethodDeclaration.class).first(); + return method != null && method.isAnnotationPresent(Override.class); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java index 9076e635de..1b00d0f84d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java @@ -18,7 +18,6 @@ import java.util.Stack; import java.util.logging.Logger; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; @@ -213,11 +212,9 @@ public class MissingOverrideRule extends AbstractJavaRule { return super.visit(node, data); } - for (ASTAnnotation annot : node.getDeclaredAnnotations()) { - if (Override.class.equals(annot.getType())) { - // we assume the compiler has already checked it, so it's correct - return super.visit(node, data); - } + if (node.isAnnotationPresent(Override.class)) { + // we assume the compiler has already checked it, so it's correct + return super.visit(node, data); } try { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java index 0111277c61..a04cfbfbf9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java @@ -18,10 +18,8 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTThrowsList; import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; @@ -125,17 +123,6 @@ public class UnusedFormalParameterRule extends AbstractJavaRule { } private boolean hasOverrideAnnotation(ASTMethodDeclaration node) { - int childIndex = node.getIndexInParent(); - for (int i = 0; i < childIndex; i++) { - Node previousSibling = node.getParent().getChild(i); - List annotations = previousSibling.findDescendantsOfType(ASTMarkerAnnotation.class); - for (ASTMarkerAnnotation annotation : annotations) { - ASTName name = annotation.getFirstChildOfType(ASTName.class); - if (name != null && (name.hasImageEqualTo("Override") || name.hasImageEqualTo("java.lang.Override"))) { - return true; - } - } - } - return false; + return node.isAnnotationPresent(Override.class); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java index b805a0cabc..0fab28c792 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java @@ -73,7 +73,7 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule memberValuePairs = annotation.findDescendantsOfType(ASTMemberValuePair.class); - - for (ASTMemberValuePair memberValuePair : memberValuePairs) { - // to set the access level of a constructor in lombok, you set the access property on the annotation - if ("access".equals(memberValuePair.getImage())) { - List names = memberValuePair.findDescendantsOfType(ASTName.class); - - for (ASTName name : names) { - // check to see if the value of the member value pair ends PRIVATE. This is from the AccessLevel enum in Lombok - if (name.getImage().endsWith("PRIVATE")) { - // if the constructor is found and the accesslevel is private no need to check anything else - return true; - } - } - } - } - } - - return false; + return parent.getDeclaredAnnotations() + .filter(t -> TypeHelper.isA(t, "lombok.NoArgsConstructor")) + .flatMap(ASTAnnotation::getMembers) + // to set the access level of a constructor in lombok, you set the access property on the annotation + .filterMatching(ASTMemberValuePair::getName, "access") + .map(ASTMemberValuePair::getValue) + // This is from the AccessLevel enum in Lombok + // if the constructor is found and the accesslevel is private no need to check anything else + .any(it -> it.getImage().equals("PRIVATE")); } private Node skipAnnotations(Node p) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java index fd5b262987..fab4558260 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTBlock; @@ -19,7 +18,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTName; @@ -31,6 +29,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTResultType; import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -175,20 +174,8 @@ public class UselessOverridingMethodRule extends AbstractJavaRule { return super.visit(node, data); } - if (!ignoreAnnotations) { - ASTClassOrInterfaceBodyDeclaration parent = (ASTClassOrInterfaceBodyDeclaration) node.getParent(); - for (int i = 0; i < parent.getNumChildren(); i++) { - Node n = parent.getChild(i); - if (n instanceof ASTAnnotation) { - if (n.getChild(0) instanceof ASTMarkerAnnotation) { - // @Override is ignored - if ("Override".equals(((ASTName) n.getChild(0).getChild(0)).getImage())) { - continue; - } - } - return super.visit(node, data); - } - } + if (!ignoreAnnotations && node.getDeclaredAnnotations().any(it -> !TypeHelper.isExactlyA(it, Override.class.getName()))) { + return super.visit(node, data); } if (arguments.getNumChildren() == 0) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java index 60dcafd286..f9af7da0ef 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java @@ -19,9 +19,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSignature; @@ -178,13 +176,7 @@ public class CommentRequiredRule extends AbstractCommentRule { private boolean isAnnotatedOverride(ASTMethodDeclaration decl) { - List annotations = decl.getParent().findDescendantsOfType(ASTMarkerAnnotation.class); - for (ASTMarkerAnnotation ann : annotations) { // TODO consider making a method to get the annotations of a method - if (ann.getFirstChildOfType(ASTName.class).getImage().equals("Override")) { - return true; - } - } - return false; + return decl.isAnnotationPresent(Override.class); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java index 5316715051..91a5be5411 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java @@ -104,12 +104,7 @@ final class AnnotationSuppressionUtil { } private static boolean hasSuppressWarningsAnnotationFor(final Annotatable node, Rule rule) { - for (ASTAnnotation a : node.getDeclaredAnnotations()) { - if (annotationSuppresses(a, rule)) { - return true; - } - } - return false; + return node.getDeclaredAnnotations().any(it -> annotationSuppresses(it, rule)); } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt index 91759f5b92..1a9685cd76 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTest.kt @@ -6,9 +6,9 @@ package net.sourceforge.pmd.lang.java.ast import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Earliest +import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest import net.sourceforge.pmd.lang.java.ast.JavaVersion.J1_3 import net.sourceforge.pmd.lang.java.ast.JavaVersion.J1_5 -import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest /** * @author Clément Fournier @@ -30,16 +30,20 @@ class ASTAnnotationTest : ParserTestSpec({ inContext(AnnotationParsingCtx) { "@F" should parseAs { - child { + child { it::getAnnotationName shouldBe "F" it::getSimpleName shouldBe "F" + + it::getMemberList shouldBe null } } "@java.lang.Override" should parseAs { - child { + child { it::getAnnotationName shouldBe "java.lang.Override" it::getSimpleName shouldBe "Override" + + it::getMemberList shouldBe null } } } @@ -51,45 +55,63 @@ class ASTAnnotationTest : ParserTestSpec({ inContext(AnnotationParsingCtx) { "@F(\"ohio\")" should parseAs { - child { + child { it::getAnnotationName shouldBe "F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe stringLit("\"ohio\"") + it::getMemberList shouldBe child { + shorthandMemberValue { + stringLit("\"ohio\"") + } + } } } "@org.F({java.lang.Math.PI})" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe child { - child { - it::getFieldName shouldBe "PI" - ambiguousName("java.lang.Math") + it::getMemberList shouldBe child { + shorthandMemberValue { + child { + child { + it::getFieldName shouldBe "PI" + ambiguousName("java.lang.Math") + } + } } } } } "@org.F({@Aha, @Oh})" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe child { - annotation("Aha") - annotation("Oh") + + it::getMemberList shouldBe child { + shorthandMemberValue { + child { + annotation("Aha") + annotation("Oh") + } + } } } } "@org.F(@Oh)" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe annotation("Oh") + + it::getMemberList shouldBe child { + shorthandMemberValue { + annotation("Oh") + } + } } } } @@ -101,44 +123,37 @@ class ASTAnnotationTest : ParserTestSpec({ inContext(AnnotationParsingCtx) { "@F(a=\"ohio\")" should parseAs { - child { + child { it::getAnnotationName shouldBe "F" it::getSimpleName shouldBe "F" - memberValuePair("a") { - stringLit("\"ohio\"") + + it::getMemberList shouldBe child { + memberValuePair("a") { + stringLit("\"ohio\"") + } } } } "@org.F(a={java.lang.Math.PI}, b=2)" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - memberValuePair("a") { - child { - child { - it::getFieldName shouldBe "PI" - ambiguousName("java.lang.Math") + + it::getMemberList shouldBe child { + memberValuePair("a") { + child { + fieldAccess("PI") { + ambiguousName("java.lang.Math") + } } } - } - memberValuePair("b") { - number() - } - } - } - - "@org.F({@Aha, @Oh})" should parseAs { - child { - it::getAnnotationName shouldBe "org.F" - it::getSimpleName shouldBe "F" - - it::getMemberValue shouldBe child { - annotation("Aha") - annotation("Oh") + memberValuePair("b") { + number() + } } } } @@ -146,28 +161,45 @@ class ASTAnnotationTest : ParserTestSpec({ """ @TestAnnotation({@SuppressWarnings({}), - @SuppressWarnings({"Beware the ides of March.",}), + @SuppressWarnings(value = {"Beware the ides of March.",}), @SuppressWarnings({"Look both ways", "Before Crossing",}), }) """ should parseAs { - child { + child { - it::getMemberValue shouldBe child { - child { + it::getMemberList shouldBe child { - it::getMemberValue shouldBe child {} - } - child { + shorthandMemberValue { - it::getMemberValue shouldBe child { - stringLit("\"Beware the ides of March.\"") - } - } - child { + child { + annotation { - it::getMemberValue shouldBe child { - stringLit("\"Look both ways\"") - stringLit("\"Before Crossing\"") + it::getMemberList shouldBe child { + shorthandMemberValue { + child {} + } + } + } + annotation { + it::getMemberList shouldBe child { + memberValuePair("value") { + it::isShorthand shouldBe false + child { + stringLit("\"Beware the ides of March.\"") + } + } + } + } + annotation { + it::getMemberList shouldBe child { + shorthandMemberValue { + child { + stringLit("\"Look both ways\"") + stringLit("\"Before Crossing\"") + } + } + } + } } } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt index f347079cc6..a67e981a01 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt @@ -36,7 +36,7 @@ class ASTArrayTypeTest : ParserTestSpec({ arrayType { it::getElementType shouldBe classType("ArrayTypes") - it::getDeclaredAnnotations shouldBe fromChild> { + it::declaredAnnotationsList shouldBe fromChild> { arrayDim { } arrayDim { } @@ -44,7 +44,7 @@ class ASTArrayTypeTest : ParserTestSpec({ val lst = listOf(annotation("A")) - it::getDeclaredAnnotations shouldBe lst + it::declaredAnnotationsList shouldBe lst lst } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt index d2f47d13d3..b6e12c936f 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt @@ -56,11 +56,11 @@ class ASTCastExpressionTest : ParserTestSpec({ castExpr { it::getCastType shouldBe child { - it::getDeclaredAnnotations shouldBe emptyList() + it::declaredAnnotationsList shouldBe emptyList() classType("Foo") { // annotations nest on the inner node - it::getDeclaredAnnotations shouldBe listOf(annotation("F")) + it::declaredAnnotationsList shouldBe listOf(annotation("F")) } classType("Bar") @@ -76,15 +76,15 @@ class ASTCastExpressionTest : ParserTestSpec({ castExpr { it::getCastType shouldBe child { - it::getDeclaredAnnotations shouldBe emptyList() + it::declaredAnnotationsList shouldBe emptyList() classType("Foo") { // annotations nest on the inner node - it::getDeclaredAnnotations shouldBe listOf(annotation("F")) + it::declaredAnnotationsList shouldBe listOf(annotation("F")) } classType("Bar") { - it::getDeclaredAnnotations shouldBe listOf(annotation("B"), annotation("C")) + it::declaredAnnotationsList shouldBe listOf(annotation("B"), annotation("C")) } } @@ -140,3 +140,6 @@ class ASTCastExpressionTest : ParserTestSpec({ }) + +val Annotatable.declaredAnnotationsList: List + get() = declaredAnnotations.toList() diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt index 7d3f766039..018b289557 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.java.ast +import io.kotlintest.shouldBe import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType @@ -52,7 +53,7 @@ class ASTConstructorDeclarationTest : ParserTestSpec({ } } - it::toList shouldBe listOf( + it.toList() shouldBe listOf( child { localVarModifiers { } primitiveType(PrimitiveType.INT) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt index 054ebb103c..2ab7282651 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt @@ -170,7 +170,7 @@ class ASTEnumConstantTest : ParserTestSpec({ val c = it it::getModifiers shouldBe modifiers { - c::getDeclaredAnnotations shouldBe listOf(annotation("C")) + c::declaredAnnotationsList shouldBe listOf(annotation("C")) } @@ -186,7 +186,7 @@ class ASTEnumConstantTest : ParserTestSpec({ val c = it it::getModifiers shouldBe modifiers { - c::getDeclaredAnnotations shouldBe listOf(annotation("A"), annotation("a")) + c::declaredAnnotationsList shouldBe listOf(annotation("A"), annotation("a")) } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt index c2f6373742..6b6786b3d7 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt @@ -1,6 +1,7 @@ package net.sourceforge.pmd.lang.java.ast import io.kotlintest.should +import io.kotlintest.shouldBe import io.kotlintest.shouldNot import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType @@ -399,7 +400,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe formalsList(0) { - it::toList shouldBe emptyList() + it.toList() shouldBe emptyList() it::getReceiverParameter shouldBe child { classType("Foo") { @@ -434,7 +435,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ } } - it::toList shouldBe listOf( + it.toList() shouldBe listOf( child { localVarModifiers { } primitiveType(PrimitiveType.INT) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt index 324448d9b5..016fbeecb9 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt @@ -485,8 +485,14 @@ fun TreeNodeWrapper.ambiguousName(image: String, contents: NodeSpec.memberValuePair(name: String, contents: ValuedNodeSpec) = child { - it::getMemberName shouldBe name - it::getMemberValue shouldBe contents() + it::getName shouldBe name + it::getValue shouldBe contents() + } + +fun TreeNodeWrapper.shorthandMemberValue(contents: ValuedNodeSpec) = + memberValuePair("value") { + it::isShorthand shouldBe true + contents() }