Merge branch 'pr-2282' into java-grammar

[java] Use single node for annotations
This commit is contained in:
Andreas Dangel
2020-02-20 11:08:16 +01:00
35 changed files with 345 additions and 537 deletions

View File

@ -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.
*

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,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.
*
* <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("\\.");
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<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

@ -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}.
*
* <pre class="grammar">
*
* MarkerAnnotation ::= "@" Name
*
* </pre>
*
* @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 <T> void jjtAccept(SideEffectingVisitor<T> visitor, T data) {
visitor.visit(this, data);
}
}

View File

@ -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.
*

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

@ -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}.
*
* <pre class="grammar">
*
* MemberValuePairs ::= {@linkplain ASTMemberValuePair MemberValuePair} ( "," {@linkplain ASTMemberValuePair MemberValuePair} )*
*
* </pre>
*
* @deprecated Removed from the tree, added no info
*/
@Deprecated
public final class ASTMemberValuePairs extends AbstractJavaNode implements Iterable<ASTMemberValuePair> {
ASTMemberValuePairs(int id) {
super(id);
}
@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 ASTMemberValuePair getChild(int index) {
return (ASTMemberValuePair) super.getChild(index);
}
@Override
public ASTNormalAnnotation getParent() {
return (ASTNormalAnnotation) super.getParent();
}
@Override
public Iterator<ASTMemberValuePair> iterator() {
return children(ASTMemberValuePair.class).iterator();
}
}

View File

@ -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).
*
* <pre class="grammar">
*
* NormalAnnotation ::= "@" Name "(" ( {@linkplain ASTMemberValuePair MemberValuePair} ( "," {@linkplain ASTMemberValuePair MemberValuePair} )* )? ")"
*
* </pre>
*
* @see ASTSingleMemberAnnotation
* @see ASTMarkerAnnotation
*/
public final class ASTNormalAnnotation extends AbstractJavaTypeNode implements ASTAnnotation, Iterable<ASTMemberValuePair> {
ASTNormalAnnotation(int id) {
super(id);
}
@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

@ -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.
*
* <pre class="grammar">
*
* SingleMemberAnnotation ::= "@" Name "(" {@linkplain ASTMemberValue MemberValue} ")"
*
* </pre>
*
* @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 <T> void jjtAccept(SideEffectingVisitor<T> 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);
}
}

View File

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

@ -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<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,44 +15,22 @@ 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);
}
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);
}

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

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

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

@ -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<String, VariableNameDeclaration> variables = getVariables(method);
Scope classScope = method.getScope().getParent();
@ -98,10 +96,9 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule {
for (Map.Entry<NameDeclaration, List<NameOccurrence>> 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<ASTNormalAnnotation> 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<ASTMemberValuePair> 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()));
}
/**

View File

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

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

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

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,6 @@ package net.sourceforge.pmd.lang.java.rule.design;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
@ -17,9 +16,9 @@ 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.rule.AbstractLombokAwareRule;
import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper;
public class UseUtilityClassRule extends AbstractLombokAwareRule {
@ -99,29 +98,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(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

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

View File

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

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
}

Some files were not shown because too many files have changed in this diff Show More