Unify repr of annotations

This commit is contained in:
Clément Fournier
2020-01-25 01:30:26 +01:00
parent 8938756997
commit 97b1870472
27 changed files with 323 additions and 268 deletions

View File

@ -2282,47 +2282,32 @@ void RSIGNEDSHIFT() #void:
/* Annotation syntax follows. */
void Annotation() #void:
void Annotation():
{}
{
LOOKAHEAD( "@" VoidName() "(" ( <IDENTIFIER> "=" | ")" ))
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(<IDENTIFIER> "=")
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=<IDENTIFIER>
{
s.append(t.getImage());
}
( LOOKAHEAD(2) "." t=<IDENTIFIER>
{s.append('.').append(t.getImage());}
t=<IDENTIFIER> { s.append(t.getImage()); }
( LOOKAHEAD(2) "." t=<IDENTIFIER> {s.append('.').append(t.getImage());}
)*
{return s.toString();}
}

View File

@ -4,38 +4,83 @@
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;
/**
* Represents an annotation. This node has three specific syntactic variants,
* represented by nodes that implement this interface.
* Represents an annotation.
*
* <pre class="grammar">
*
* Annotation ::= {@linkplain ASTNormalAnnotation NormalAnnotation}
* | {@linkplain ASTSingleMemberAnnotation SingleMemberAnnotation}
* | {@linkplain ASTMarkerAnnotation MarkerAnnotation}
* Annotation ::= "@" Name {@link ASTAnnotationMemberList AnnotationMemberList}?
*
* </pre>
*/
public interface ASTAnnotation extends TypeNode, ASTMemberValue {
public final class ASTAnnotation extends AbstractJavaTypeNode implements TypeNode, ASTMemberValue, Iterable<ASTMemberValuePair> {
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("\\.");
public String getSimpleName() {
String[] split = getAnnotationName().split("\\.");
return split[split.length - 1];
}
}
/**
* 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<ASTMemberValuePair> getMembers() {
return children(ASTAnnotationMemberList.class).children(ASTMemberValuePair.class);
}
@Override
public Iterator<ASTMemberValuePair> iterator() {
return children(ASTMemberValuePair.class).iterator();
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
@Override
public <T> void jjtAccept(SideEffectingVisitor<T> visitor, T data) {
visitor.visit(this, data);
}
}

View File

@ -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}.
*
* <pre class="grammar">
*
* AnnotationMemberList ::= "(" {@link ASTMemberValuePair MemberValuePair} ( "," {@link ASTMemberValuePair MemberValuePair} )* ")"
* | "(" {@link ASTMemberValuePair ValueShorthand} ")"
* | "(" ")"
*
* </pre>
*/
public final class ASTAnnotationMemberList extends AbstractJavaNode implements Iterable<ASTMemberValuePair> {
ASTAnnotationMemberList(int id) {
super(id);
}
@Override
public ASTAnnotation getParent() {
return (ASTAnnotation) super.getParent();
}
@Override
@SuppressWarnings("unchecked")
public NodeStream<ASTMemberValuePair> children() {
return (NodeStream<ASTMemberValuePair>) super.children();
}
@Override
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
@Override
public <T> void jjtAccept(SideEffectingVisitor<T> visitor, T data) {
visitor.visit(this, data);
}
@Override
public Iterator<ASTMemberValuePair> iterator() {
return children().iterator();
}
}

View File

@ -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<ASTAnnotation> 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<ASTAnnotation> getDeclaredAnnotations() {
return getDimensions().getLastChild().getDeclaredAnnotations();
}
public ASTArrayDimensions getDimensions() {

View File

@ -15,8 +15,11 @@ package net.sourceforge.pmd.lang.java.ast;
*
* @see ASTSingleMemberAnnotation
* @see ASTNormalAnnotation
*
* @deprecated Replaced with {@link ASTAnnotation}
*/
public final class ASTMarkerAnnotation extends AbstractJavaTypeNode implements ASTAnnotation {
@Deprecated
public final class ASTMarkerAnnotation extends AbstractJavaTypeNode {
ASTMarkerAnnotation(int id) {
super(id);

View File

@ -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()}.
*
* <pre class="grammar">
*
* MemberValuePair ::= &lt;IDENTIFIER&gt; "=" {@linkplain ASTMemberValue MemberValue}
* MemberValuePair ::= &lt;IDENTIFIER&gt; "=" {@linkplain ASTMemberValue MemberValue}
*
* ValueShorthand ::= {@linkplain ASTMemberValue MemberValue}
*
* </pre>
*/
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 <T> void jjtAccept(SideEffectingVisitor<T> visitor, T data) {
visitor.visit(this, data);
}
void setShorthand() {
this.isShorthand = true;
}
}

View File

@ -18,8 +18,11 @@ import java.util.Iterator;
*
* @see ASTSingleMemberAnnotation
* @see ASTMarkerAnnotation
* @deprecated Replaced with {@link ASTAnnotation}
*/
public final class ASTNormalAnnotation extends AbstractJavaTypeNode implements ASTAnnotation, Iterable<ASTMemberValuePair> {
@Deprecated
public final class ASTNormalAnnotation extends AbstractJavaTypeNode implements Iterable<ASTMemberValuePair> {
ASTNormalAnnotation(int id) {
super(id);
}

View File

@ -15,8 +15,11 @@ package net.sourceforge.pmd.lang.java.ast;
*
* @see ASTMarkerAnnotation
* @see ASTNormalAnnotation
* @deprecated Replaced with {@link ASTAnnotation}
*/
public final class ASTSingleMemberAnnotation extends AbstractJavaTypeNode implements ASTAnnotation {
@Deprecated
public final class ASTSingleMemberAnnotation extends AbstractJavaTypeNode {
ASTSingleMemberAnnotation(int id) {
super(id);
}

View File

@ -5,8 +5,6 @@
package net.sourceforge.pmd.lang.java.ast;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@ -30,11 +28,6 @@ public final class ASTTypeParameter extends AbstractJavaTypeNode implements Anno
super(id);
}
@Override
public List<ASTAnnotation> getDeclaredAnnotations() {
return children(ASTAnnotation.class).toList();
}
/**
* Returns the name of the type variable introduced by this declaration.
*/

View File

@ -11,6 +11,8 @@ 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 +33,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
*/
public interface AccessNode extends Annotatable {
@Override
default List<ASTAnnotation> getDeclaredAnnotations() {
return getModifiers().children(ASTAnnotation.class).toList();
default NodeStream<ASTAnnotation> getDeclaredAnnotations() {
return getModifiers().children(ASTAnnotation.class);
}

View File

@ -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<ASTAnnotation> 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.
*
* <p>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<ASTAnnotation> 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<String> annotQualifiedNames) {
// TODO use node streams
for (String annotQualifiedName : annotQualifiedNames) {
if (isAnnotationPresent(annotQualifiedName)) {
return true;
}
}
return false;
default NodeStream<ASTAnnotation> 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<TypeNode>) t -> TypeHelper.subclasses(t, type));
}
}

View File

@ -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);

View File

@ -15,25 +15,6 @@ package net.sourceforge.pmd.lang.java.ast;
public class SideEffectingVisitorAdapter<T> implements SideEffectingVisitor<T> {
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);
}
// TODO delegation

View File

@ -38,6 +38,6 @@ public abstract class AbstractIgnoredAnnotationRule extends AbstractJavaRule {
* @return <code>true</code> if the annotation has been found, otherwise <code>false</code>
*/
protected boolean hasIgnoredAnnotation(Annotatable node) {
return node.isAnyAnnotationPresent(getProperty(ignoredAnnotationsDescriptor));
return getProperty(ignoredAnnotationsDescriptor).stream().anyMatch(node::isAnnotationPresent);
}
}

View File

@ -136,6 +136,6 @@ public class AbstractLombokAwareRule extends AbstractIgnoredAnnotationRule {
* @return <code>true</code> if a lombok annotation has been found
*/
protected boolean hasLombokAnnotation(Annotatable node) {
return node.isAnyAnnotationPresent(LOMBOK_ANNOTATIONS);
return LOMBOK_ANNOTATIONS.stream().anyMatch(node::isAnnotationPresent);
}
}

View File

@ -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 {

View File

@ -125,6 +125,7 @@ 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);

View File

@ -73,7 +73,7 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule<AS
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
if (node.isAnnotationPresent("java.lang.Override")) {
if (node.isAnnotationPresent(Override.class)) {
return super.visit(node, data);
}

View File

@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.java.rule.design;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
@ -17,9 +17,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
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.ASTResultType;
import net.sourceforge.pmd.lang.java.ast.TypeNode;
import net.sourceforge.pmd.lang.java.rule.AbstractLombokAwareRule;
import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
public class UseUtilityClassRule extends AbstractLombokAwareRule {
@ -99,29 +100,16 @@ public class UseUtilityClassRule extends AbstractLombokAwareRule {
private boolean hasLombokNoArgsConstructor(ASTClassOrInterfaceDeclaration parent) {
// check if there's a lombok no arg private constructor, if so skip the rest of the rules
ASTAnnotation annotation = parent.getAnnotation("lombok.NoArgsConstructor");
if (annotation != null) {
List<ASTMemberValuePair> 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<ASTName> 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((Predicate<TypeNode>) 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) {

View File

@ -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));
}

View File

@ -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<ASTMarkerAnnotation> {
child<ASTAnnotation> {
it::getAnnotationName shouldBe "F"
it::getSimpleName shouldBe "F"
it::getMemberList shouldBe null
}
}
"@java.lang.Override" should parseAs {
child<ASTMarkerAnnotation> {
child<ASTAnnotation> {
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<ASTSingleMemberAnnotation> {
child<ASTAnnotation> {
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<ASTSingleMemberAnnotation> {
child<ASTAnnotation> {
it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {
child<ASTFieldAccess> {
it::getFieldName shouldBe "PI"
ambiguousName("java.lang.Math")
it::getMemberList shouldBe child {
shorthandMemberValue {
child<ASTMemberValueArrayInitializer> {
child<ASTFieldAccess> {
it::getFieldName shouldBe "PI"
ambiguousName("java.lang.Math")
}
}
}
}
}
}
"@org.F({@Aha, @Oh})" should parseAs {
child<ASTSingleMemberAnnotation> {
child<ASTAnnotation> {
it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {
annotation("Aha")
annotation("Oh")
it::getMemberList shouldBe child {
shorthandMemberValue {
child<ASTMemberValueArrayInitializer> {
annotation("Aha")
annotation("Oh")
}
}
}
}
}
"@org.F(@Oh)" should parseAs {
child<ASTSingleMemberAnnotation> {
child<ASTAnnotation> {
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<ASTNormalAnnotation> {
child<ASTAnnotation> {
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<ASTNormalAnnotation> {
child<ASTAnnotation> {
it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
memberValuePair("a") {
child<ASTMemberValueArrayInitializer> {
child<ASTFieldAccess> {
it::getFieldName shouldBe "PI"
ambiguousName("java.lang.Math")
it::getMemberList shouldBe child {
memberValuePair("a") {
child<ASTMemberValueArrayInitializer> {
fieldAccess("PI") {
ambiguousName("java.lang.Math")
}
}
}
}
memberValuePair("b") {
number()
}
}
}
"@org.F({@Aha, @Oh})" should parseAs {
child<ASTSingleMemberAnnotation> {
it::getAnnotationName shouldBe "org.F"
it::getSimpleName shouldBe "F"
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {
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<ASTSingleMemberAnnotation> {
child<ASTAnnotation> {
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {
child<ASTSingleMemberAnnotation> {
it::getMemberList shouldBe child {
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {}
}
child<ASTSingleMemberAnnotation> {
shorthandMemberValue {
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {
stringLit("\"Beware the ides of March.\"")
}
}
child<ASTSingleMemberAnnotation> {
child<ASTMemberValueArrayInitializer> {
annotation {
it::getMemberValue shouldBe child<ASTMemberValueArrayInitializer> {
stringLit("\"Look both ways\"")
stringLit("\"Before Crossing\"")
it::getMemberList shouldBe child {
shorthandMemberValue {
child<ASTMemberValueArrayInitializer> {}
}
}
}
annotation {
it::getMemberList shouldBe child {
memberValuePair("value") {
it::isShorthand shouldBe false
child<ASTMemberValueArrayInitializer> {
stringLit("\"Beware the ides of March.\"")
}
}
}
}
annotation {
it::getMemberList shouldBe child {
shorthandMemberValue {
child<ASTMemberValueArrayInitializer> {
stringLit("\"Look both ways\"")
stringLit("\"Before Crossing\"")
}
}
}
}
}
}
}

View File

@ -36,7 +36,7 @@ class ASTArrayTypeTest : ParserTestSpec({
arrayType {
it::getElementType shouldBe classType("ArrayTypes")
it::getDeclaredAnnotations shouldBe fromChild<ASTArrayDimensions, List<ASTAnnotation>> {
it::declaredAnnotationsList shouldBe fromChild<ASTArrayDimensions, List<ASTAnnotation>> {
arrayDim { }
arrayDim { }
@ -44,7 +44,7 @@ class ASTArrayTypeTest : ParserTestSpec({
val lst = listOf(annotation("A"))
it::getDeclaredAnnotations shouldBe lst
it::declaredAnnotationsList shouldBe lst
lst
}

View File

@ -56,11 +56,11 @@ class ASTCastExpressionTest : ParserTestSpec({
castExpr {
it::getCastType shouldBe child<ASTIntersectionType> {
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<ASTIntersectionType> {
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<ASTAnnotation>
get() = declaredAnnotations.toList()

View File

@ -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)

View File

@ -102,7 +102,7 @@ class ASTEnumConstantTest : ParserTestSpec({
val c = it
it::getModifiers shouldBe modifiers {
c::getDeclaredAnnotations shouldBe listOf(annotation("C"))
c::declaredAnnotationsList shouldBe listOf(annotation("C"))
}
@ -118,7 +118,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"))
}

View File

@ -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)

View File

@ -472,8 +472,14 @@ fun TreeNodeWrapper<Node, *>.ambiguousName(image: String, contents: NodeSpec<AST
fun TreeNodeWrapper<Node, *>.memberValuePair(name: String, contents: ValuedNodeSpec<ASTMemberValuePair, ASTMemberValue>) =
child<ASTMemberValuePair> {
it::getMemberName shouldBe name
it::getMemberValue shouldBe contents()
it::getName shouldBe name
it::getValue shouldBe contents()
}
fun TreeNodeWrapper<Node, *>.shorthandMemberValue(contents: ValuedNodeSpec<ASTMemberValuePair, ASTMemberValue>) =
memberValuePair("value") {
it::isShorthand shouldBe true
contents()
}