diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 520e435232..249c13d86d 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -243,7 +243,10 @@ options { PARSER_BEGIN(JavaParserImpl) package net.sourceforge.pmd.lang.java.ast; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import java.util.Map; import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.GenericToken; @@ -349,6 +352,18 @@ class JavaParserImpl { } } + private void pushEmptyModifierList() { + ASTModifierList emptyMods = new ASTModifierList(JJTMODIFIERLIST); + + emptyMods.setDeclaredModifiers(Collections.emptySet()); + + JavaccToken tok = getToken(1); + emptyMods.jjtSetFirstToken(tok); + emptyMods.jjtSetLastToken(tok); + + jjtree.pushNode(emptyMods); + } + private void forceTypeContext() { AbstractJavaNode top = jjtree.peekNode(); @@ -819,7 +834,7 @@ private void ModuleDeclLahead() #void: void PackageDeclaration() : {String image;} { - AnnotationList() "package" image=VoidName() { jjtThis.setImage(image); } ";" + ModAnnotationList() "package" image=VoidName() { jjtThis.setImage(image); } ";" } void ImportDeclaration() : @@ -835,62 +850,54 @@ void ImportDeclaration() : * syntax errors for simple modifier mistakes. It will also enable us to give * better error messages. */ -int Modifiers() #void: +void ModifierList(): { - int modifiers = 0; - int numAnnots = 0; + EnumSet modifiers = EnumSet.noneOf(JModifier.class); } { ( LOOKAHEAD(2) ( - "public" { modifiers |= AccessNode.PUBLIC; } - | "static" { modifiers |= AccessNode.STATIC; } - | "protected" { modifiers |= AccessNode.PROTECTED; } - | "private" { modifiers |= AccessNode.PRIVATE; } - | "final" { modifiers |= AccessNode.FINAL; } - | "abstract" { modifiers |= AccessNode.ABSTRACT; } - | "synchronized" { modifiers |= AccessNode.SYNCHRONIZED; } - | "native" { modifiers |= AccessNode.NATIVE; } - | "transient" { modifiers |= AccessNode.TRANSIENT; } - | "volatile" { modifiers |= AccessNode.VOLATILE; } - | "strictfp" { modifiers |= AccessNode.STRICTFP; } - | "default" { modifiers |= AccessNode.DEFAULT; } - | Annotation() {numAnnots++;} + "public" { modifiers.add(JModifier.PUBLIC); } + | "static" { modifiers.add(JModifier.STATIC); } + | "protected" { modifiers.add(JModifier.PROTECTED); } + | "private" { modifiers.add(JModifier.PRIVATE); } + | "final" { modifiers.add(JModifier.FINAL); } + | "abstract" { modifiers.add(JModifier.ABSTRACT); } + | "synchronized" { modifiers.add(JModifier.SYNCHRONIZED); } + | "native" { modifiers.add(JModifier.NATIVE); } + | "transient" { modifiers.add(JModifier.TRANSIENT); } + | "volatile" { modifiers.add(JModifier.VOLATILE); } + | "strictfp" { modifiers.add(JModifier.STRICTFP); } + | "default" { modifiers.add(JModifier.DEFAULT); } + | Annotation() ) )* - { - jjtree.injectRight(numAnnots); - return modifiers; - } + { jjtThis.setDeclaredModifiers(modifiers); } + { jjtree.injectRight(1); } // inject the modifier node in the follower } /* * Declaration syntax follows. */ void TypeDeclaration(): +{} { - int modifiers; -} -{ - modifiers = Modifiers() + ModifierList() ( - ClassOrInterfaceDeclaration(modifiers) + ClassOrInterfaceDeclaration() | - LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(modifiers) + LOOKAHEAD({isKeyword("enum")}) EnumDeclaration() | - AnnotationTypeDeclaration(modifiers) + AnnotationTypeDeclaration() ) } -void ClassOrInterfaceDeclaration(int modifiers): -{ - JavaccToken t = null; - jjtThis.setModifiers(modifiers); -} +void ClassOrInterfaceDeclaration(): +{} { ( "class" | "interface" { jjtThis.setInterface(); } ) - t= { jjtThis.setImage(t.image); } + { setLastTokenImage(jjtThis); } [ TypeParameters() ] [ ExtendsList() ] [ ImplementsList() ] @@ -898,12 +905,10 @@ void ClassOrInterfaceDeclaration(int modifiers): } void ExtendsList(): -{ - boolean extendsMoreThanOne = false; -} +{} { "extends" AnnotatedClassOrInterfaceType() - ( "," AnnotatedClassOrInterfaceType() { extendsMoreThanOne = true; } )* + ( "," AnnotatedClassOrInterfaceType() )* } void ImplementsList(): @@ -913,11 +918,9 @@ void ImplementsList(): ( "," AnnotatedClassOrInterfaceType() )* } -void EnumDeclaration(int modifiers): +void EnumDeclaration(): { - -JavaccToken t; -jjtThis.setModifiers(modifiers); + JavaccToken t; } { t = { @@ -943,7 +946,7 @@ void EnumBody(): void EnumConstant(): {} { - AnnotationList() VariableDeclaratorId() [ ArgumentList() ] [ ClassOrInterfaceBody() #AnonymousClassDeclaration ] + ModAnnotationList() VariableDeclaratorId() [ ArgumentList() ] [ AnonymousClassDeclaration() ] } void TypeParameters(): @@ -966,25 +969,23 @@ void ClassOrInterfaceBody(): } void ClassOrInterfaceBodyDeclaration(): -{ - int modifiers; -} +{} { LOOKAHEAD(["static"] "{" ) Initializer() -| modifiers = Modifiers() - ( LOOKAHEAD(3) ClassOrInterfaceDeclaration(modifiers) - | LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(modifiers) - | LOOKAHEAD( [ TypeParameters() ] "(" ) ConstructorDeclaration(modifiers) - | LOOKAHEAD( Type() (AnnotationList() "[" "]")* ( "," | "=" | ";" ) ) FieldDeclaration(modifiers) - | LOOKAHEAD(2) MethodDeclaration(modifiers) - | LOOKAHEAD(2) AnnotationTypeDeclaration(modifiers) +| ModifierList() + ( LOOKAHEAD(3) ClassOrInterfaceDeclaration() + | LOOKAHEAD({isKeyword("enum")}) EnumDeclaration() + | LOOKAHEAD( [ TypeParameters() ] "(" ) ConstructorDeclaration() + | LOOKAHEAD( Type() (AnnotationList() "[" "]")* ( "," | "=" | ";" ) ) FieldDeclaration() + | LOOKAHEAD(2) MethodDeclaration() + | LOOKAHEAD(2) AnnotationTypeDeclaration() ) | ";" #EmptyDeclaration } -void FieldDeclaration(int modifiers) : -{jjtThis.setModifiers(modifiers);} +void FieldDeclaration() : +{} { Type() VariableDeclarator() ( "," VariableDeclarator() )* ";" } @@ -1027,10 +1028,8 @@ void ArrayInitializer() : "{" [ VariableInitializer() ( LOOKAHEAD(2) "," VariableInitializer() )* ] [ "," ] "}" } -void MethodDeclaration(int modifiers) : -{ - jjtThis.setModifiers(modifiers); -} +void MethodDeclaration() : +{} { [ TypeParameters() ] ResultType() @@ -1049,9 +1048,9 @@ void FormalParameters() : } void FormalParameter() : -{boolean isFinal = false;} +{} { - isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);} + LocalVarModifierList() FormalParamType() ("|" FormalParamType())* // remove this stuff when #2202 is merged VariableIdWithDims() } @@ -1078,13 +1077,11 @@ void VarargsDim() #ArrayTypeDim: } - -void ConstructorDeclaration(int modifiers) : -{jjtThis.setModifiers(modifiers); -JavaccToken t;} +void ConstructorDeclaration() : +{JavaccToken t;} { [ TypeParameters() ] - {jjtThis.setImage(getToken(0).getImage());} FormalParameters() [ ThrowsList() ] + {setLastTokenImage(jjtThis);} FormalParameters() [ ThrowsList() ] ("{" [ LOOKAHEAD(ExplicitConstructorInvocation()) ExplicitConstructorInvocation() ] ( BlockStatement() )* @@ -1159,6 +1156,12 @@ void AnnotationList() #void: (Annotation())* } +void ModAnnotationList() #ModifierList: +{jjtThis.setDeclaredModifiers(Collections.emptySet());} +{ + (Annotation())* +} + int TypeAnnotListNoInject() #void: {int num = 0;} { @@ -1785,21 +1788,24 @@ void LambdaExpression(): void LambdaParameterList(): {} { - VariableDeclaratorId() #LambdaParameter + SimpleLambdaParam() | LOOKAHEAD("(" ("," | ")")) - "(" [ VariableDeclaratorId() #LambdaParameter(true) ( "," VariableDeclaratorId() #LambdaParameter )* ] ")" + "(" [ SimpleLambdaParam() ( "," SimpleLambdaParam())* ] ")" | "(" [ LambdaParameter() ( "," LambdaParameter() )* ] ")" } -void LambdaParameter(): +void SimpleLambdaParam() #LambdaParameter: +{pushEmptyModifierList();} { - boolean isVarType = false; - boolean isFinal = false; + VariableDeclaratorId() } + +void LambdaParameter(): +{} { + LocalVarModifierList() // this weakens the grammar a bit [ - isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);} - isVarType=LambdaParameterType() + LambdaParameterType() ] VariableIdWithDims() } @@ -1864,11 +1870,20 @@ void QualifiedAllocationExpr() #ConstructorCall: [ TypeArguments() ] AnnotatedClassOrInterfaceType() ArgumentList() - [ ClassOrInterfaceBody() #AnonymousClassDeclaration ] + [ AnonymousClassDeclaration() ] } +void AnonymousClassDeclaration(): +{ + pushEmptyModifierList(); +} +{ + ClassOrInterfaceBody() +} + + // this is much weaker than the JLS but since we parse compilable code // the actual terms we get respect the JLS. @@ -1888,7 +1903,7 @@ void UnqualifiedAllocationExpr() #void : ( ArrayDimsAndInits() {isArrayInit=true;} | - ArgumentList() [ ClassOrInterfaceBody() #AnonymousClassDeclaration ] + ArgumentList() [ AnonymousClassDeclaration() ] ) ) {/*Empty unit, important*/} @@ -1977,13 +1992,13 @@ void BlockStatement() #void: // we need to lookahead until the "class" token, // because a method ref may be annotated // -> so Expression, and hence Statement, may start with "@" - LOOKAHEAD(Modifiers() "class") LocalClassDecl() + LOOKAHEAD(ModifierList() "class") LocalClassDecl() | Statement() } void LocalClassDecl() #LocalClassStatement: -{int mods = 0;} +{} { // this preserves the modifiers of the local class. // it allows for modifiers that are forbidden for local classes, @@ -1995,26 +2010,26 @@ void LocalClassDecl() #LocalClassStatement: // In particular, it unfortunately allows local class declarations to start // with a "default" modifier, which introduces an ambiguity with default // switch labels. This is guarded by a custom lookahead around SwitchLabel - mods=Modifiers() ClassOrInterfaceDeclaration(mods) + ModifierList() ClassOrInterfaceDeclaration() } /* * See https://docs.oracle.com/javase/specs/jls/se10/html/jls-14.html#jls-14.4 */ void LocalVariableDeclaration() : -{boolean isFinal = false;} +{} { - isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);} + LocalVarModifierList() LocalVariableType() VariableDeclarator() ( "," VariableDeclarator() )* } -private boolean LocalVarModifierList() #void: -{boolean isFinal = false;} +private void LocalVarModifierList() #ModifierList: +{Set set = Collections.emptySet(); } { - ( "final" {isFinal = true;} | Annotation() )* - {return isFinal;} + ( "final" { set = ASTModifierList.JUST_FINAL; } | Annotation() )* + {jjtThis.setDeclaredModifiers(set);} } void LocalVariableType() #void: @@ -2216,11 +2231,11 @@ void ResourceList(): } void Resource() : -{boolean isFinal = false;} +{} { LOOKAHEAD(("final" | Annotation())* LocalVariableType() VariableDeclaratorId() "=" ) ( - isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);} + LocalVarModifierList() LocalVariableType() VariableDeclarator() ) #LocalVariableDeclaration | @@ -2243,9 +2258,9 @@ void CatchClause() : void CatchParameter(): -{boolean isFinal = false; boolean multi=false;} +{boolean multi=false;} { - isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);} + LocalVarModifierList() ( AnnotatedClassOrInterfaceType() ( "|" AnnotatedClassOrInterfaceType() {multi=true;} )* ) #UnionType(multi) VariableDeclaratorId() } @@ -2359,10 +2374,8 @@ void TypeAnnotation() #void: /* Annotation Types. */ -void AnnotationTypeDeclaration(int modifiers): -{ -jjtThis.setModifiers(modifiers); -} +void AnnotationTypeDeclaration(): +{} { "@" "interface" { setLastTokenImage(jjtThis); } AnnotationTypeBody() @@ -2375,40 +2388,32 @@ void AnnotationTypeBody(): } void AnnotationTypeMemberDeclaration(): +{} { - int modifiers; -} -{ - modifiers = Modifiers() + ModifierList() ( - LOOKAHEAD(Type() "(") AnnotationMethodDeclaration(modifiers) + LOOKAHEAD(Type() "(") AnnotationMethodDeclaration() | - ClassOrInterfaceDeclaration(modifiers) + ClassOrInterfaceDeclaration() | - LOOKAHEAD(3) EnumDeclaration(modifiers) + LOOKAHEAD(3) EnumDeclaration() | - AnnotationTypeDeclaration(modifiers) + AnnotationTypeDeclaration() | - FieldDeclaration(modifiers) + FieldDeclaration() ) | ";" #EmptyDeclaration } -void AnnotationMethodDeclaration(int modifiers) #MethodDeclaration: -{ - JavaccToken t; - jjtThis.setModifiers(modifiers); -} +void AnnotationMethodDeclaration() #MethodDeclaration: +{} { Type() #ResultType - t= + {setLastTokenImage(jjtThis);} ("(" ")") #FormalParameters(true) [ Dims() ] [ DefaultValue() ] ";" - { - jjtThis.setImage(t.image); - } } void DefaultValue(): diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMethodDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMethodDeclaration.java index acc578eb82..3a5b591085 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMethodDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMethodDeclaration.java @@ -12,7 +12,7 @@ import net.sourceforge.pmd.lang.java.ast.MethodLikeNode.MethodLikeKind; * enclosing type is an annotation. */ @Deprecated -public final class ASTAnnotationMethodDeclaration extends AbstractJavaAccessNode { +public final class ASTAnnotationMethodDeclaration extends AbstractJavaNode implements AccessNode { ASTAnnotationMethodDeclaration(int id) { super(id); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java index c04eedb6ce..806fe88270 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationTypeDeclaration.java @@ -4,27 +4,19 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.List; - /** * The declaration of an annotation type. * - *

Note that in constrast to interface types, no {@linkplain ASTExtendsList extends clause} + *

Note that in contrast to interface types, no {@linkplain ASTExtendsList extends clause} * is permitted, and an annotation type cannot be generic. * *

  *
- * AnnotationTypeDeclaration ::= AnnotationTypeModifier*
+ * AnnotationTypeDeclaration ::= {@link ASTModifierList ModifierList}
  *                               "@" "interface"
  *                               <IDENTIFIER>
  *                               {@link ASTAnnotationTypeBody AnnotationTypeBody}
  *
- *
- *
- * AnnotationTypeModifier ::= "public" | "private"  | "protected"
- *                          | "abstract" | "static"
- *                          | {@linkplain ASTAnnotation Annotation}
- *
  * 
* */ @@ -52,10 +44,9 @@ public final class ASTAnnotationTypeDeclaration extends AbstractAnyTypeDeclarati return TypeKind.ANNOTATION; } - @Override - public List getDeclarations() { - return getFirstChildOfType(ASTAnnotationTypeBody.class) - .findChildrenOfType(ASTAnyTypeBodyDeclaration.class); + public boolean isInterface() { + return true; } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java index 6bd2867cac..e08ccb7f44 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java @@ -4,23 +4,19 @@ package net.sourceforge.pmd.lang.java.ast; -import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; - - /** - * An anonymous class declaration. This can occur in a {@linkplain ASTConstructorCall class instance creation expression} + * An anonymous class declaration. This can occur in a {@linkplain ASTConstructorCall class instance creation + * expression} * or in an {@linkplain ASTEnumConstant enum constant declaration}. * * *
  *
- * AnonymousClassDeclaration ::= {@link ASTClassOrInterfaceBody}
+ * AnonymousClassDeclaration ::= {@link ASTModifierList EmptyModifierList} {@link ASTClassOrInterfaceBody}
  *
  * 
*/ -public final class ASTAnonymousClassDeclaration extends AbstractJavaTypeNode implements JavaQualifiableNode { - - private JavaTypeQualifiedName qualifiedName; +public final class ASTAnonymousClassDeclaration extends AbstractAnyTypeDeclaration { ASTAnonymousClassDeclaration(int id) { @@ -34,6 +30,11 @@ public final class ASTAnonymousClassDeclaration extends AbstractJavaTypeNode imp } + @Override + public Visibility getVisibility() { + return Visibility.V_ANONYMOUS; + } + @Override public Object jjtAccept(JavaParserVisitor visitor, Object data) { return visitor.visit(this, data); @@ -45,27 +46,8 @@ public final class ASTAnonymousClassDeclaration extends AbstractJavaTypeNode imp visitor.visit(this, data); } - - /** - * Returns the body of the anonymous class. - */ - public ASTClassOrInterfaceBody getBody() { - return (ASTClassOrInterfaceBody) getChild(0); - } - - - /** - * Returns the qualified name of the anonymous class - * declared by this node. - */ @Override - public JavaTypeQualifiedName getQualifiedName() { - return qualifiedName; + public TypeKind getTypeKind() { + return TypeKind.CLASS; } - - - public void setQualifiedName(JavaTypeQualifiedName qname) { - this.qualifiedName = qname; - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java index 92748c0528..bf514fd7a3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java @@ -4,22 +4,32 @@ package net.sourceforge.pmd.lang.java.ast; +import static net.sourceforge.pmd.lang.java.ast.JModifier.ABSTRACT; + import java.util.Collections; import java.util.List; -import java.util.Locale; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil; import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; /** - * Groups enum, class, annotation and interface declarations. - * - * @author Clément Fournier + * Groups enum, class, annotation and interface declarations under a common + * supertype. */ -public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, AccessNode, JavaNode { +public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, AccessNode, FinalizableNode { + + + /** + * @deprecated Use {@link #getBinaryName()} + */ + @Override + @Deprecated + JavaTypeQualifiedName getQualifiedName(); + /** @@ -47,6 +57,15 @@ public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, Ac // @NotNull String getBinaryName(); + /** + * Returns true if this is an abstract type. Interfaces and annotations + * types are implicitly abstract. + */ + @Override + default boolean isAbstract() { + return hasModifiers(ABSTRACT); + } + /** * Finds the type kind of this declaration. @@ -59,21 +78,31 @@ public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, Ac TypeKind getTypeKind(); + /** + * Returns the enum constants declared by this enum. If this is not + * an enum declaration, returns an empty stream. + */ + default NodeStream getEnumConstants() { + return getFirstChildOfType(ASTEnumBody.class).children(ASTEnumConstant.class); + } + + /** * Retrieves the member declarations (fields, methods, classes, etc.) from the body of this type declaration. * * @return The member declarations declared in this type declaration */ - List getDeclarations(); + default List getDeclarations() { + return getBody().children(ASTAnyTypeBodyDeclaration.class).toList(); + } /** - * @deprecated Use {@link #getBinaryName()} + * Returns the body of this type declaration. */ - @Override - @Deprecated - JavaTypeQualifiedName getQualifiedName(); - + default ASTTypeBody getBody() { + return (ASTTypeBody) getLastChild(); + } default List getTypeParameters() { ASTTypeParameters parameters = getFirstChildOfType(ASTTypeParameters.class); @@ -85,22 +114,65 @@ public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, Ac } /** - * Returns true if this type declaration is nested inside an interface, class or annotation. + * Returns true if this type declaration is nested inside an interface, + * class or annotation. */ default boolean isNested() { - return getParent() instanceof ASTClassOrInterfaceBodyDeclaration - || getParent() instanceof ASTAnnotationTypeMemberDeclaration; + return getParent() instanceof ASTAnyTypeBodyDeclaration; } /** - * Returns true if this is a local class declaration. + * Returns true if the class is declared inside a block other + * than the body of another class, or the top level. Anonymous + * classes are not considered local. Only class declarations + * can be local. Local classes cannot be static. */ default boolean isLocal() { return getParent() instanceof ASTLocalClassStatement; } + + /** + * Returns true if this type is declared at the top-level of a file. + */ + default boolean isTopLevel() { + return getParent() instanceof ASTTypeDeclaration; + } + + + /** + * Returns true if this is an {@linkplain ASTAnonymousClassDeclaration anonymous class declaration}. + */ + default boolean isAnonymous() { + return this instanceof ASTAnonymousClassDeclaration; + } + + + /** + * Returns true if this is an {@linkplain ASTEnumDeclaration enum class declaration}. + */ + default boolean isEnum() { + return this instanceof ASTEnumDeclaration; + } + + /** + * Returns true if this is an interface type declaration (including + * annotation types). This is consistent with {@link Class#isInterface()}. + */ + default boolean isInterface() { + return false; + } + + + /** Returns true if this is an {@linkplain ASTAnnotationTypeDeclaration annotation type declaration}. */ + default boolean isAnnotation() { + return this instanceof ASTAnnotationTypeDeclaration; + } + + + /** * The kind of type this node declares. * @@ -125,27 +197,7 @@ public interface ASTAnyTypeDeclaration extends TypeNode, JavaQualifiableNode, Ac */ @Deprecated enum TypeKind { - CLASS, INTERFACE, ENUM, ANNOTATION; - - - public String getPrintableName() { - return name().toLowerCase(Locale.ROOT); - } - - - public static TypeKind ofClass(Class clazz) { - - if (clazz.isInterface()) { - return INTERFACE; - } else if (clazz.isEnum()) { - return ENUM; - } else if (clazz.isAnnotation()) { - return ANNOTATION; - } else { - return CLASS; - } - - } + CLASS, INTERFACE, ENUM, ANNOTATION } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchParameter.java index 5fccd86d12..268b198569 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchParameter.java @@ -13,11 +13,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; * *
  *
- * CatchParameter ::= ( "final" | {@link ASTAnnotation Annotation} )* {@link ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
+ * CatchParameter ::= {@link ASTModifierList LocalVarModifierList} {@link ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
  *
  * 
*/ -public final class ASTCatchParameter extends AbstractJavaAccessNode implements InternalInterfaces.VariableIdOwner { +public final class ASTCatchParameter extends AbstractJavaNode + implements InternalInterfaces.VariableIdOwner, + FinalizableNode { ASTCatchParameter(int id) { super(id); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java index 172e77f186..5057ea64bc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.java @@ -17,18 +17,13 @@ import net.sourceforge.pmd.util.CollectionUtil; * *
  *
- * ClassOrInterfaceDeclaration ::= ClassModifier*
+ * ClassOrInterfaceDeclaration ::= {@link ASTModifierList ModifierList}
  *                                 ( "class" | "interface" )
  *                                 <IDENTIFIER>
- *                                 {@linkplain ASTTypeParameters TypeParameters}?
- *                                 {@linkplain ASTExtendsList ExtendsList}?
- *                                 {@linkplain ASTImplementsList ImplementsList}?
- *                                 {@linkplain ASTClassOrInterfaceBody ClassOrInterfaceBody}
- *
- *
- * ClassModifier ::=  "public" | "private"  | "protected"
- *                  | "final"  | "abstract" | "static" | "strictfp"
- *                  | {@linkplain ASTAnnotation Annotation}
+ *                                 {@link ASTTypeParameters TypeParameters}?
+ *                                 {@link ASTExtendsList ExtendsList}?
+ *                                 {@link ASTImplementsList ImplementsList}?
+ *                                 {@link ASTClassOrInterfaceBody ClassOrInterfaceBody}
  *
  * 
*/ @@ -45,16 +40,12 @@ public final class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclara return visitor.visit(this, data); } - @Override - public boolean isPackagePrivate() { - return super.isPackagePrivate() && !isLocal(); - } - @Override public void jjtAccept(SideEffectingVisitor visitor, T data) { visitor.visit(this, data); } + @Override public boolean isInterface() { return this.isInterface; } @@ -69,13 +60,6 @@ public final class ASTClassOrInterfaceDeclaration extends AbstractAnyTypeDeclara } - @Override - public List getDeclarations() { - return getFirstChildOfType(ASTClassOrInterfaceBody.class) - .findChildrenOfType(ASTAnyTypeBodyDeclaration.class); - } - - /** * Returns the superclass type node if this node is a class * declaration and explicitly declares an {@code extends} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java index 52ae5a797b..650cd4db30 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java @@ -12,18 +12,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; * *
  *
- * ConstructorDeclaration ::= ConstructorModifier*
+ * ConstructorDeclaration ::= {@link ASTModifierList ModifierList}
  *                            {@link ASTTypeParameters TypeParameters}?
  *                            <IDENTIFIER>
  *                            {@link ASTFormalParameters FormalParameters}
  *                            ({@link ASTThrowsList ThrowsList})?
  *                            {@link ASTBlock Block}
  *
- *
- * ConstructorModifier ::= "public" | "private"  | "protected"
- *                       | {@linkplain ASTAnnotation Annotation}
- *
- *
  * 
*/ public final class ASTConstructorDeclaration extends AbstractMethodOrConstructorDeclaration { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java index 36c3d63fd6..764f16514e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java @@ -11,15 +11,15 @@ import org.checkerframework.checker.nullness.qual.Nullable; * *
  *
- * EnumConstant ::= {@link ASTAnnotation Annotation}* {@link ASTVariableDeclaratorId VariableDeclaratorId} {@linkplain ASTArgumentList ArgumentList}? {@linkplain ASTAnonymousClassDeclaration AnonymousClassDeclaration}?
+ * EnumConstant ::= {@link ASTModifierList AnnotationList} {@link ASTVariableDeclaratorId VariableDeclaratorId} {@linkplain ASTArgumentList ArgumentList}? {@linkplain ASTAnonymousClassDeclaration AnonymousClassDeclaration}?
  *
  * 
*/ public final class ASTEnumConstant extends AbstractJavaNode implements Annotatable, + AccessNode, InternalInterfaces.VariableIdOwner { - ASTEnumConstant(int id) { super(id); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java index 47ebc9fa61..b7931b6f5c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.List; - import net.sourceforge.pmd.lang.ast.Node; /** @@ -18,18 +16,12 @@ import net.sourceforge.pmd.lang.ast.Node; * *
  *
- * EnumDeclaration ::= EnumModifier*
+ * EnumDeclaration ::= {@link ASTModifierList ModifierList}
  *                     "enum"
  *                     <IDENTIFIER>
  *                     {@linkplain ASTImplementsList ImplementsList}?
  *                     {@link ASTEnumBody EnumBody}
  *
- *
- *
- * EnumModifier ::= "public" | "private"  | "protected"
- *                | "strictfp" | "static"
- *                | {@linkplain ASTAnnotation Annotation}
- *
  * 
*/ public final class ASTEnumDeclaration extends AbstractAnyTypeDeclaration { @@ -56,16 +48,8 @@ public final class ASTEnumDeclaration extends AbstractAnyTypeDeclaration { return TypeKind.ENUM; } - /** - * Returns the enum constants declared by this enum. - */ - public List getConstants() { - return getFirstChildOfType(ASTEnumBody.class).findChildrenOfType(ASTEnumConstant.class); - } - @Override - public List getDeclarations() { - return getFirstChildOfType(ASTEnumBody.class) - .findChildrenOfType(ASTAnyTypeBodyDeclaration.class); + public ASTEnumBody getBody() { + return (ASTEnumBody) getLastChild(); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java index 9a3a56e68e..a82c2f1f6e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclaration.java @@ -15,31 +15,21 @@ import net.sourceforge.pmd.lang.java.multifile.signature.JavaFieldSignature; * types (see {@link ASTVariableDeclaratorId#getType()}). The nodes * corresponding to the declared variables are accessible through {@link #iterator()}. * - *

{@link AccessNode} methods take into account the syntactic context of the - * declaration, e.g. {@link #isPublic()} will always return true if the field is - * declared inside an interface, regardless of whether the {@code public} modifier - * was specified or not. If you want to know whether the modifier was explicitly - * stated, use e.g {@link #isSyntacticallyPublic()}. - * *

  *
- * FieldDeclaration ::= FieldModifier* {@linkplain ASTType Type} {@linkplain ASTVariableDeclarator VariableDeclarator} ( "," {@linkplain ASTVariableDeclarator VariableDeclarator} )* ";"
- *
- * FieldModifier ::= "public" | "static"    | "protected" | "private"
- *                 | "final"  | "transient" | "volatile"
- *                 | {@linkplain ASTAnnotation Annotation}
+ * FieldDeclaration ::= {@link ASTModifierList ModifierList} {@linkplain ASTType Type} {@linkplain ASTVariableDeclarator VariableDeclarator} ( "," {@linkplain ASTVariableDeclarator VariableDeclarator} )* ";"
  *
  * 
*/ -public final class ASTFieldDeclaration extends AbstractJavaAccessNode +public final class ASTFieldDeclaration extends AbstractJavaNode implements SignedNode, Iterable, LeftRecursiveNode, + AccessNode, InternalInterfaces.MultiVariableIdOwner { private JavaFieldSignature signature; - ASTFieldDeclaration(int id) { super(id); } @@ -55,86 +45,6 @@ public final class ASTFieldDeclaration extends AbstractJavaAccessNode visitor.visit(this, data); } - - public boolean isSyntacticallyPublic() { - return super.isPublic(); - } - - @Override - public boolean isPublic() { - if (isAnnotationMember() || isInterfaceMember()) { - return true; - } - return super.isPublic(); - } - - public boolean isSyntacticallyStatic() { - return super.isStatic(); - } - - @Override - public boolean isStatic() { - if (isAnnotationMember() || isInterfaceMember()) { - return true; - } - return super.isStatic(); - } - - public boolean isSyntacticallyFinal() { - return super.isFinal(); - } - - @Override - public boolean isFinal() { - if (isAnnotationMember() || isInterfaceMember()) { - return true; - } - return super.isFinal(); - } - - @Override - public boolean isPrivate() { - if (isAnnotationMember() || isInterfaceMember()) { - return false; - } - return super.isPrivate(); - } - - @Override - public boolean isPackagePrivate() { - if (isAnnotationMember() || isInterfaceMember()) { - return false; - } - return super.isPackagePrivate(); - } - - @Override - public boolean isProtected() { - if (isAnnotationMember() || isInterfaceMember()) { - return false; - } - return super.isProtected(); - } - - public boolean isAnnotationMember() { - return getNthParent(2) instanceof ASTAnnotationTypeBody; - } - - public boolean isInterfaceMember() { - if (getNthParent(2) instanceof ASTEnumBody) { - return false; - } - ASTClassOrInterfaceBody classOrInterfaceBody = getFirstParentOfType(ASTClassOrInterfaceBody.class); - if (classOrInterfaceBody == null || classOrInterfaceBody.isAnonymousInnerClass()) { - return false; - } - if (classOrInterfaceBody.getParent() instanceof ASTClassOrInterfaceDeclaration) { - ASTClassOrInterfaceDeclaration n = (ASTClassOrInterfaceDeclaration) classOrInterfaceBody.getParent(); - return n.isInterface(); - } - return false; - } - /** * Gets the variable name of this field. This method searches the first * VariableDeclartorId node and returns its image or null if diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java index 95c2179fe9..40fd575fbb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.VariableIdOwner; import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; @@ -19,13 +20,14 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin * *
  *
- * FormalParameter ::= ( "final" | {@link ASTAnnotation Annotation} )* {@link ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
+ * FormalParameter ::= {@link ASTModifierList LocalVarModifierList} {@link ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
  *
  * 
*/ -public final class ASTFormalParameter extends AbstractJavaAccessTypeNode - implements Annotatable, - InternalInterfaces.VariableIdOwner { +public final class ASTFormalParameter extends AbstractJavaTypeNode + implements FinalizableNode, + Annotatable, + VariableIdOwner { @InternalApi @Deprecated @@ -33,6 +35,12 @@ public final class ASTFormalParameter extends AbstractJavaAccessTypeNode super(id); } + @Override + public Visibility getVisibility() { + return Visibility.V_LOCAL; + } + + /** * Returns true if this node is a varargs parameter. Then, the type * node is an {@link ASTArrayType ArrayType}, and its last dimension diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInfixExpression.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInfixExpression.java index 30022455ab..82aeea752d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInfixExpression.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTInfixExpression.java @@ -35,7 +35,7 @@ import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.BinaryExpressionLike * * */ -public final class ASTInfixExpression extends AbstractJavaExpr implements LeftRecursiveNode, BinaryExpressionLike, AtLeastOneChild { +public final class ASTInfixExpression extends AbstractJavaExpr implements BinaryExpressionLike, AtLeastOneChild { private BinaryOp operator; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpression.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpression.java index 65715b9670..bd9af54dec 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpression.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpression.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.ast.NodeStream; -import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; +import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; /** @@ -20,10 +20,9 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin * * */ -public final class ASTLambdaExpression extends AbstractMethodLikeNode implements ASTExpression { +public final class ASTLambdaExpression extends AbstractJavaExpr implements ASTExpression, MethodLikeNode { - private JavaTypeDefinition typeDefinition; - private int parenDepth; + private JavaOperationQualifiedName qualifiedName; ASTLambdaExpression(int id) { super(id); @@ -43,9 +42,11 @@ public final class ASTLambdaExpression extends AbstractMethodLikeNode implements return !isBlockBody(); } + /** * Returns the body of this lambda if it is a block. */ + @Nullable public ASTBlock getBlockBody() { return NodeStream.of(getLastChild()).filterIs(ASTBlock.class).first(); } @@ -53,40 +54,12 @@ public final class ASTLambdaExpression extends AbstractMethodLikeNode implements /** * Returns the body of this lambda if it is an expression. */ + @Nullable public ASTExpression getExpressionBody() { return NodeStream.of(getLastChild()).filterIs(ASTExpression.class).first(); } - // TODO MethodLikeNode should be removed, and this class extend AbstractJavaExpr - - void bumpParenDepth() { - parenDepth++; - } - - @Override - public int getParenthesisDepth() { - return parenDepth; - } - - @Override - @Nullable - public Class getType() { - return typeDefinition == null ? null : typeDefinition.getType(); - } - - - @Override - @Nullable - public JavaTypeDefinition getTypeDefinition() { - return typeDefinition; - } - - - void setTypeDefinition(JavaTypeDefinition typeDefinition) { - this.typeDefinition = typeDefinition; - } - @Override public boolean isFindBoundary() { @@ -106,6 +79,15 @@ public final class ASTLambdaExpression extends AbstractMethodLikeNode implements } + @Override + public JavaOperationQualifiedName getQualifiedName() { + return qualifiedName; + } + + void setQualifiedName(JavaOperationQualifiedName qualifiedName) { + this.qualifiedName = qualifiedName; + } + @Override public MethodLikeKind getKind() { return MethodLikeKind.LAMBDA; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaParameter.java index 4ed849f462..0cd2a5725e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLambdaParameter.java @@ -15,30 +15,19 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin * *
  *
- * LambdaParameter ::= ( "final" | {@link ASTAnnotation Annotation} )* ( "var" | {@link ASTType Type} ) {@link ASTVariableDeclaratorId VariableDeclaratorId}
- *                   | {@link ASTVariableDeclaratorId VariableDeclaratorId}
+ * LambdaParameter ::= {@link ASTModifierList LocalVarModifierList} ( "var" | {@link ASTType Type} ) {@link ASTVariableDeclaratorId VariableDeclaratorId}
+ *                   | {@link ASTModifierList EmptyModifierList} {@link ASTVariableDeclaratorId VariableDeclaratorId}
  *
  * 
*/ public final class ASTLambdaParameter extends AbstractJavaTypeNode - implements InternalInterfaces.VariableIdOwner { - - private boolean isFinal; + implements InternalInterfaces.VariableIdOwner, + FinalizableNode { ASTLambdaParameter(int id) { super(id); } - - void setFinal(boolean aFinal) { - isFinal = aFinal; - } - - public boolean isFinal() { - return isFinal; - } - - /** * If true, this formal parameter represents one without explicit types. * This can appear as part of a lambda expression with java11 using "var". diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java index 9ac1ae6558..1d61d74ddb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java @@ -15,14 +15,15 @@ package net.sourceforge.pmd.lang.java.ast; * *
  *
- * LocalVariableDeclaration ::= ( "final" | {@linkplain ASTAnnotation Annotation} )* {@linkplain ASTType Type} {@linkplain ASTVariableDeclarator VariableDeclarator} ( "," {@linkplain ASTVariableDeclarator VariableDeclarator} )*
+ * LocalVariableDeclaration ::= {@link ASTModifierList LocalVarModifierList} {@linkplain ASTType Type} {@linkplain ASTVariableDeclarator VariableDeclarator} ( "," {@linkplain ASTVariableDeclarator VariableDeclarator} )*
  *
  * 
*/ // TODO extend AbstractStatement -public final class ASTLocalVariableDeclaration extends AbstractJavaAccessNode +public final class ASTLocalVariableDeclaration extends AbstractJavaNode implements Iterable, ASTStatement, + FinalizableNode, InternalInterfaces.MultiVariableIdOwner { ASTLocalVariableDeclaration(int id) { @@ -40,16 +41,9 @@ public final class ASTLocalVariableDeclaration extends AbstractJavaAccessNode visitor.visit(this, data); } - - /** - * Returns true if the local variables declared by this statement - * are final. - */ @Override - @SuppressWarnings("PMD.UselessOverridingMethod") - public boolean isFinal() { - // TODO unimplement AccessNode, this causes compilation errors because of our current symbol table - return super.isFinal(); + public Visibility getVisibility() { + return Visibility.V_LOCAL; } /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java index d983bf00c4..10e63b484a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.dfa.DFAGraphMethod; @@ -26,7 +25,7 @@ import net.sourceforge.pmd.lang.dfa.DFAGraphMethod; * *
  *
- * MethodDeclaration ::= MethodModifier*
+ * MethodDeclaration ::= {@link ASTModifierList ModifierList}
  *                       {@link ASTTypeParameters TypeParameters}?
  *                       {@link ASTResultType ResultType}
  *                       <IDENTIFIER>
@@ -35,11 +34,6 @@ import net.sourceforge.pmd.lang.dfa.DFAGraphMethod;
  *                       {@link ASTThrowsList ThrowsList}?
  *                       ({@link ASTBlock Block} | ";" )
  *
- *
- * MethodModifier ::= "public" | "private"  | "protected" | "static"
- *                  | "final"  | "abstract" | "native"
- *                  | {@linkplain ASTAnnotation Annotation}
- *
  * 
*/ public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDeclaration implements DFAGraphMethod { @@ -81,67 +75,10 @@ public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDecla } - /** - * Returns true if this method is explicitly modified by - * the {@code public} modifier. - */ - public boolean isSyntacticallyPublic() { - return super.isPublic(); - } - - - /** - * Returns true if this method is explicitly modified by - * the {@code abstract} modifier. - */ - public boolean isSyntacticallyAbstract() { - return super.isAbstract(); - } - - - /** - * Returns true if this method has public visibility. - * Non-private interface members are implicitly public, - * whether they declare the {@code public} modifier or - * not. - */ - @Override - public boolean isPublic() { - // interface methods are public by default, but could be private since java9 - return isInterfaceMember() && !isPrivate() || super.isPublic(); - } - - - /** - * Returns true if this method is abstract, so doesn't - * declare a body. Interface members are - * implicitly abstract, whether they declare the - * {@code abstract} modifier or not. Default interface - * methods are not abstract though, consistently with the - * standard reflection API. - */ - @Override - public boolean isAbstract() { - return isInterfaceMember() && !isDefault() || super.isAbstract(); - } - - - /** - * Returns true if this method declaration is a member of an interface type. - */ - public boolean isInterfaceMember() { - // for a real class/interface the 3rd parent is a ClassOrInterfaceDeclaration, - // for anonymous classes, the parent is e.g. a AllocationExpression - Node potentialTypeDeclaration = getNthParent(3); - - return potentialTypeDeclaration instanceof ASTClassOrInterfaceDeclaration - && ((ASTClassOrInterfaceDeclaration) potentialTypeDeclaration).isInterface() - || potentialTypeDeclaration instanceof ASTAnnotationTypeDeclaration; - } - - /** * Returns true if the result type of this method is {@code void}. + * + * TODO remove, just as simple to write getResultType().isVoid() */ public boolean isVoid() { return getResultType().isVoid(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java index d0f60c2188..9b5555a119 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java @@ -26,7 +26,7 @@ import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; * @see MethodLikeNode * @since 5.8.1 */ -public interface ASTMethodOrConstructorDeclaration extends MethodLikeNode, SignedNode { +public interface ASTMethodOrConstructorDeclaration extends MethodLikeNode, AccessNode, SignedNode { /** @@ -41,6 +41,21 @@ public interface ASTMethodOrConstructorDeclaration extends MethodLikeNode, Signe JavaOperationSignature getSignature(); + + /** + * Returns true if this method is abstract, so doesn't + * declare a body. Interface members are + * implicitly abstract, whether they declare the + * {@code abstract} modifier or not. Default interface + * methods are not abstract though, consistently with the + * standard reflection API. + */ + // TODO is this relevant? + @Override + default boolean isAbstract() { + return hasModifiers(JModifier.ABSTRACT); + } + /** * Returns the formal parameters node of this method or constructor. */ diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java new file mode 100644 index 0000000000..e398aeb852 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java @@ -0,0 +1,246 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import static net.sourceforge.pmd.lang.java.ast.JModifier.ABSTRACT; +import static net.sourceforge.pmd.lang.java.ast.JModifier.DEFAULT; +import static net.sourceforge.pmd.lang.java.ast.JModifier.FINAL; +import static net.sourceforge.pmd.lang.java.ast.JModifier.PRIVATE; +import static net.sourceforge.pmd.lang.java.ast.JModifier.PUBLIC; +import static net.sourceforge.pmd.lang.java.ast.JModifier.STATIC; + +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + +/** + * List of modifiers of a declaration. + * + *

This class keeps track of two modifier sets: the {@linkplain #getExplicitModifiers() explicit} + * one, which is the modifiers that appeared in the source, and the + * {@linkplain #getEffectiveModifiers() effective} one, which includes + * modifiers implicitly given by the context of the node. + * + *

+ *
+ *
+ *
+ * ModifierList         ::= Modifier*
+ *
+ * Modifier             ::= "public" | "private"  | "protected"
+ *                        | "final"  | "abstract" | "static" | "strictfp"
+ *                        | "synchronized" | "native" | "default"
+ *                        | "volatile" | "transient"
+ *                        | {@linkplain ASTAnnotation Annotation}
+ *
+ *
+ * LocalVarModifierList ::= ( "final" | {@link ASTAnnotation Annotation} )*
+ *
+ * AnnotationList       ::= {@link ASTAnnotation Annotation}*
+ *
+ * EmptyModifierList    ::= ()
+ *
+ * 
+ */ +public final class ASTModifierList extends AbstractJavaNode { + + /** Might as well share it. */ + static final Set JUST_FINAL = Collections.singleton(FINAL); + + private Set explicitModifiers; + private Set effectiveModifiers; + + + ASTModifierList(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); + } + + + void setDeclaredModifiers(Set explicit) { + this.explicitModifiers = explicit; + } + + /** + * Returns the set of modifiers written out in the source explicitly. + * The returned set is unmodifiable. + */ + public Set getExplicitModifiers() { + assert explicitModifiers != null : "Parser should have set the explicit modifiers"; + return Collections.unmodifiableSet(explicitModifiers); + } + + /** + * Returns the {@linkplain #getExplicitModifiers() declared modifiers}, + * plus the modifiers that are implicitly bestowed by the context or + * the type of this declaration. E.g. an interface is implicitly abstract, + * while an interface field is implicitly static. + * The returned set is unmodifiable. + */ + public Set getEffectiveModifiers() { + assert explicitModifiers != null : "Parser should have set the explicit modifiers"; + + if (effectiveModifiers == null) { + + EnumSet mods = + explicitModifiers.isEmpty() + ? EnumSet.noneOf(JModifier.class) + : EnumSet.copyOf(explicitModifiers); + + getOwner().jjtAccept(EffectiveModifierVisitor.INSTANCE, mods); + + this.effectiveModifiers = Collections.unmodifiableSet(mods); + + } + + return effectiveModifiers; + } + + public JavaNode getOwner() { + return getParent(); // TODO + } + + /** + * Returns true if the effective modifiers contain all of the mentioned + * modifiers. + * + * @param mod1 First mod + * @param mods Other mods + */ + public boolean hasAll(JModifier mod1, JModifier... mods) { + Set actual = getEffectiveModifiers(); + return actual.contains(mod1) && (mods.length == 0 || actual.containsAll(Arrays.asList(mods))); + } + + /** + * Returns true if the explicit modifiers contain all of the mentioned + * modifiers. + * + * @param mod1 First mod + * @param mods Other mods + */ + public boolean hasAllExplicitly(JModifier mod1, JModifier... mods) { + Set actual = getExplicitModifiers(); + return actual.contains(mod1) && (mods.length == 0 || actual.containsAll(Arrays.asList(mods))); + } + + + /** + * Returns true if the effective modifiers contain any of the mentioned + * modifiers. + * + * @param mod1 First mod + * @param mods Other mods + */ + public boolean hasAny(JModifier mod1, JModifier... mods) { + Set actual = getEffectiveModifiers(); + return actual.contains(mod1) || Arrays.stream(mods).anyMatch(actual::contains); + } + + + /** + * Returns true if the explicit modifiers contain any of the mentioned + * modifiers. + * + * @param mod1 First mod + * @param mods Other mods + */ + public boolean hasAnyExplicitly(JModifier mod1, JModifier... mods) { + Set actual = getExplicitModifiers(); + return actual.contains(mod1) || Arrays.stream(mods).anyMatch(actual::contains); + } + + /** + * Populates effective modifiers from the declared ones. + */ + private static class EffectiveModifierVisitor extends SideEffectingVisitorAdapter> { + + + private static final EffectiveModifierVisitor INSTANCE = new EffectiveModifierVisitor(); + + // TODO strictfp modifier is also implicitly given to descendants + + @Override + public void visit(ASTAnyTypeDeclaration node, Set effective) { + + if (node.getEnclosingType().isInterface()) { + effective.add(PUBLIC); + effective.add(STATIC); + } + + if (node.isInterface() || node.isAnnotation()) { + effective.add(ABSTRACT); + if (!node.isTopLevel()) { + effective.add(STATIC); + } + } else if (node instanceof ASTEnumDeclaration && !node.isTopLevel()) { + effective.add(STATIC); + } + + if (node instanceof ASTEnumDeclaration + && node.getEnumConstants().none(ASTEnumConstant::isAnonymousClass)) { + effective.add(FINAL); + } + } + + + @Override + public void visit(ASTFieldDeclaration node, Set effective) { + if (node.getEnclosingType().isInterface()) { + effective.add(PUBLIC); + effective.add(STATIC); + effective.add(FINAL); + } + } + + @Override + public void visit(ASTLocalVariableDeclaration node, Set effective) { + // resources are implicitly final + if (node.getParent() instanceof ASTResource) { + effective.add(FINAL); + } + } + + + @Override + public void visit(ASTEnumConstant node, Set effective) { + effective.add(PUBLIC); + effective.add(STATIC); + effective.add(FINAL); + } + + @Override + public void visit(ASTAnonymousClassDeclaration node, Set effective) { + // TODO add static modifier in static context + } + + @Override + public void visit(ASTMethodDeclaration node, Set effective) { + + if (node.getEnclosingType().isInterface()) { + + Set declared = node.getModifiers().explicitModifiers; + + if (!declared.contains(PRIVATE)) { + effective.add(PUBLIC); + } + if (!declared.contains(DEFAULT) && !declared.contains(STATIC)) { + effective.add(ABSTRACT); + } + } + } + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclaration.java index 2571e99116..4ef4fb5184 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclaration.java @@ -12,7 +12,7 @@ package net.sourceforge.pmd.lang.java.ast; * *
  *
- * PackageDeclaration ::= "package" Name ";"
+ * PackageDeclaration ::= {@link ASTModifierList AnnotationList} "package" Name ";"
  *
  * 
* 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 dc61b732fb..05f84f456d 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,6 +5,8 @@ package net.sourceforge.pmd.lang.java.ast; +import java.util.List; + import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -28,6 +30,10 @@ public final class ASTTypeParameter extends AbstractJavaTypeNode implements Anno super(id); } + @Override + public List 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/ASTVariableDeclaratorId.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java index 4ff469d79f..948cc151d3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java @@ -43,7 +43,7 @@ import net.sourceforge.pmd.lang.symboltable.NameOccurrence; * */ // @formatter:on -public final class ASTVariableDeclaratorId extends AbstractJavaTypeNode { +public final class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements AccessNode { private VariableNameDeclaration nameDeclaration; @@ -90,6 +90,19 @@ public final class ASTVariableDeclaratorId extends AbstractJavaTypeNode { return children(ASTArrayDimensions.class).first(); } + @Override + public ASTModifierList getModifiers() { + // delegates modifiers + return getModifierOwnerParent().getModifiers(); + } + + private AccessNode getModifierOwnerParent() { + JavaNode parent = getParent(); + if (parent instanceof ASTVariableDeclarator) { + return (AccessNode) parent.getParent(); + } + return (AccessNode) parent; + } /** * Returns true if the declared variable has an array type. @@ -163,6 +176,7 @@ public final class ASTVariableDeclaratorId extends AbstractJavaTypeNode { * Doesn't account for the "effectively-final" nuance. Resource * declarations are implicitly final. */ + @Override public boolean isFinal() { if (isResourceDeclaration()) { // this is implicit even if "final" is not explicitly declared. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java index 7d8c346ddc..70cec819b4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.lang.java.ast; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; @@ -12,9 +11,10 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin /** * Abstract class for type declarations nodes. */ -abstract class AbstractAnyTypeDeclaration extends AbstractJavaAccessTypeNode implements ASTAnyTypeDeclaration, LeftRecursiveNode { +abstract class AbstractAnyTypeDeclaration extends AbstractJavaNode implements ASTAnyTypeDeclaration, LeftRecursiveNode { private JavaTypeQualifiedName qualifiedName; + private JavaTypeDefinition typeDefinition; AbstractAnyTypeDeclaration(int i) { @@ -33,53 +33,8 @@ abstract class AbstractAnyTypeDeclaration extends AbstractJavaAccessTypeNode imp } @Override - public String getSimpleName() { - return getImage(); - } - - @Override - public boolean isFindBoundary() { - return isNested() || isLocal(); - } - - /** - * Returns true if the enclosing type of this type declaration - * is any of the given kinds. If this declaration is a top-level - * declaration, returns false. This won't consider anonymous classes - * until #905 is tackled. TODO 7.0.0 - * - * @param kinds Kinds to test - */ - // TODO 7.0.0 move that up to ASTAnyTypeDeclaration - public final boolean enclosingTypeIsA(TypeKind... kinds) { - - ASTAnyTypeDeclaration parent = getEnclosingTypeDeclaration(); - if (parent == null) { - return false; - } - - for (TypeKind k : kinds) { - if (parent.getTypeKind() == k) { - return true; - } - } - - return false; - } - - - /** - * Returns the enclosing type of this type, if it is nested. - * Otherwise returns null. This won't consider anonymous classes - * until #905 is tackled. TODO 7.0.0 - */ - public final ASTAnyTypeDeclaration getEnclosingTypeDeclaration() { - if (!isNested()) { - return null; - } - Node parent = getNthParent(3); - - return parent instanceof ASTAnyTypeDeclaration ? (ASTAnyTypeDeclaration) parent : null; + public Visibility getVisibility() { + return isLocal() ? Visibility.V_LOCAL : ASTAnyTypeDeclaration.super.getVisibility(); } @Override @@ -91,5 +46,14 @@ abstract class AbstractAnyTypeDeclaration extends AbstractJavaAccessTypeNode imp this.qualifiedName = qualifiedName; this.typeDefinition = JavaTypeDefinition.forClass(qualifiedName.getType()); } + + void setTypeDefinition(JavaTypeDefinition typeDefinition) { + this.typeDefinition = typeDefinition; + } + + @Override + public JavaTypeDefinition getTypeDefinition() { + return typeDefinition; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAccessNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAccessNode.java deleted file mode 100644 index d0ea4d54bd..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAccessNode.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -abstract class AbstractJavaAccessNode extends AbstractJavaNode implements AccessNode { - - private int modifiers; - - AbstractJavaAccessNode(int i) { - super(i); - } - - @Override - public int getModifiers() { - return this.modifiers; - } - - void setModifiers(int modifiers) { - this.modifiers = modifiers; - } - - @Override - public boolean isPublic() { - return isModifier(PUBLIC); - } - - @Override - public boolean isProtected() { - return isModifier(PROTECTED); - } - - @Override - public boolean isPrivate() { - return isModifier(PRIVATE); - } - - - @Override - public boolean isAbstract() { - return isModifier(ABSTRACT); - } - - - @Override - public boolean isStatic() { - return isModifier(STATIC); - } - - - @Override - public boolean isFinal() { - return isModifier(FINAL); - } - - void setFinal(boolean enable) { - setModifier(enable, FINAL); - } - - @Override - public boolean isSynchronized() { - return isModifier(SYNCHRONIZED); - } - - - @Override - public boolean isNative() { - return isModifier(NATIVE); - } - - - @Override - public boolean isTransient() { - return isModifier(TRANSIENT); - } - - - @Override - public boolean isVolatile() { - return isModifier(VOLATILE); - } - - - @Override - public boolean isStrictfp() { - return isModifier(STRICTFP); - } - - - @Override - public boolean isDefault() { - return isModifier(DEFAULT); - } - - - private boolean isModifier(int mask) { - return (modifiers & mask) == mask; - } - - void setModifier(boolean enable, int mask) { - if (enable) { - this.modifiers |= mask; - } else { - this.modifiers &= ~mask; - } - } - - @Override - public boolean isPackagePrivate() { - return !isPrivate() && !isPublic() && !isProtected(); - } -} - diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAccessTypeNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAccessTypeNode.java deleted file mode 100644 index 1a95e96b9c..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaAccessTypeNode.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; - -abstract class AbstractJavaAccessTypeNode extends AbstractJavaAccessNode implements TypeNode { - - /** - * Type definition, used to get the type of the node. - */ - protected JavaTypeDefinition typeDefinition; - - AbstractJavaAccessTypeNode(int i) { - super(i); - } - - @Override - public Class getType() { - if (typeDefinition != null) { - return typeDefinition.getType(); - } - - return null; - } - - @Override - public JavaTypeDefinition getTypeDefinition() { - return typeDefinition; - } - - void setTypeDefinition(JavaTypeDefinition typeDefinition) { - this.typeDefinition = typeDefinition; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodLikeNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodLikeNode.java deleted file mode 100644 index 2294ad9b0f..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodLikeNode.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; - -abstract class AbstractMethodLikeNode extends AbstractJavaAccessNode implements MethodLikeNode { - - private JavaOperationQualifiedName qualifiedName; - - - AbstractMethodLikeNode(int i) { - super(i); - } - - - void setQualifiedName(JavaOperationQualifiedName qualifiedName) { - this.qualifiedName = qualifiedName; - } - - - @Override - @Deprecated - public JavaOperationQualifiedName getQualifiedName() { - return qualifiedName; - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java index ce529d683f..eb7176093a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java @@ -5,12 +5,13 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSignature; +import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; -abstract class AbstractMethodOrConstructorDeclaration extends AbstractMethodLikeNode implements ASTMethodOrConstructorDeclaration, LeftRecursiveNode { +abstract class AbstractMethodOrConstructorDeclaration extends AbstractJavaNode implements ASTMethodOrConstructorDeclaration, LeftRecursiveNode, AccessNode { private JavaOperationSignature signature; - + private JavaOperationQualifiedName qualifiedName; AbstractMethodOrConstructorDeclaration(int i) { super(i); @@ -25,4 +26,17 @@ abstract class AbstractMethodOrConstructorDeclaration extends AbstractMethodLike return signature; } + + + void setQualifiedName(JavaOperationQualifiedName qualifiedName) { + this.qualifiedName = qualifiedName; + } + + + @Override + public JavaOperationQualifiedName getQualifiedName() { + return qualifiedName; + } + + } 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 af85a84bc9..f5918859f0 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 @@ -1,70 +1,250 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ 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; + /** - * This interface captures Java access modifiers. + * A node that owns a {@linkplain ASTModifierList modifier list}. + * + *

{@link AccessNode} methods take into account the syntactic context of the + * declaration, e.g. {@link #isPublic()} will always return true for a field + * declared inside an interface, regardless of whether the {@code public} + * modifier was specified explicitly or not. If you want to know whether + * the modifier was explicitly stated, use {@link #hasExplicitModifiers(JModifier, JModifier...)}. + * + * TODO make modifiers accessible from XPath + * * Ideally we'd have two attributes, eg @EffectiveModifiers and @ExplicitModifiers, + * which would each return a sequence, eg ("public", "static", "final") + * * Ideally we'd have a way to add attributes that are not necessarily + * getters on the node. It makes no sense in the Java API to expose + * those getters on the node, it's more orthogonal to query getModifiers() directly. + * + * TODO rename to ModifierOwner, kept out from PR to reduce diff */ public interface AccessNode extends Annotatable { - int PUBLIC = 0x0001; - int PROTECTED = 0x0002; - int PRIVATE = 0x0004; - int ABSTRACT = 0x0008; - int STATIC = 0x0010; - int FINAL = 0x0020; - int SYNCHRONIZED = 0x0040; - int NATIVE = 0x0080; - int TRANSIENT = 0x0100; - int VOLATILE = 0x0200; - int STRICTFP = 0x1000; - int DEFAULT = 0x2000; + + @Override + default List getDeclaredAnnotations() { + return getModifiers().children(ASTAnnotation.class).toList(); + } - int getModifiers(); + /** + * Returns the node representing the modifier list of this node. + */ + @NonNull + default ASTModifierList getModifiers() { + return children(ASTModifierList.class).firstOrThrow(); + } - boolean isPublic(); + /** + * Returns the visibility corresponding to the {@link ASTModifierList#getEffectiveModifiers() effective modifiers}. + * Eg a public method will have visibility {@link Visibility#V_PUBLIC public}, + * a local class will have visibility {@link Visibility#V_LOCAL local}. + * There cannot be any conflict with {@link #hasModifiers(JModifier, JModifier...)}} on + * well-formed code (e.g. for any {@code n}, {@code (n.getVisibility() == V_PROTECTED) == + * n.hasModifiers(PROTECTED)}) + * + *

TODO a public method of a private class can be considered to be private + * we could probably add another method later on that takes this into account + */ + default Visibility getVisibility() { + Set effective = getModifiers().getEffectiveModifiers(); + if (effective.contains(JModifier.PUBLIC)) { + return Visibility.V_PUBLIC; + } else if (effective.contains(JModifier.PROTECTED)) { + return Visibility.V_PROTECTED; + } else if (effective.contains(JModifier.PRIVATE)) { + return Visibility.V_PRIVATE; + } else { + return Visibility.V_PACKAGE; + } + } - - boolean isProtected(); + /** + * Returns true if this node has all the given modifiers + * either explicitly written or inferred through context. + */ + default boolean hasModifiers(JModifier mod1, JModifier... mod) { + return getModifiers().hasAll(mod1, mod); + } - boolean isPrivate(); + /** + * Returns true if this node has all the given modifiers + * explicitly written in the source. + */ + default boolean hasExplicitModifiers(JModifier mod1, JModifier... mod) { + return getModifiers().hasAllExplicitly(mod1, mod); + } - boolean isAbstract(); + // TODO remove all those, kept only for compatibility with rules + + // these are about effective modifiers - - boolean isStatic(); + @Deprecated + default boolean isFinal() { + return hasModifiers(JModifier.FINAL); + } - boolean isFinal(); + @Deprecated + default boolean isAbstract() { + return hasModifiers(JModifier.ABSTRACT); + } - - boolean isSynchronized(); + @Deprecated + default boolean isStrictfp() { + return hasModifiers(STRICTFP); + } - - boolean isNative(); + @Deprecated + default boolean isSynchronized() { + return hasModifiers(JModifier.SYNCHRONIZED); + } - boolean isTransient(); + @Deprecated + default boolean isNative() { + return hasModifiers(JModifier.NATIVE); + } - boolean isVolatile(); + @Deprecated + default boolean isStatic() { + return hasModifiers(JModifier.STATIC); + } - boolean isStrictfp(); + @Deprecated + default boolean isVolatile() { + return hasModifiers(JModifier.VOLATILE); + } - boolean isPackagePrivate(); + @Deprecated + default boolean isTransient() { + return hasModifiers(JModifier.TRANSIENT); + } - boolean isDefault(); + // these are about visibility + + + @Deprecated + default boolean isPrivate() { + return getVisibility() == Visibility.V_PRIVATE; + } + + + @Deprecated + default boolean isPublic() { + return getVisibility() == Visibility.V_PUBLIC; + } + + + @Deprecated + default boolean isProtected() { + return getVisibility() == Visibility.V_PROTECTED; + } + + + @Deprecated + default boolean isPackagePrivate() { + return getVisibility() == Visibility.V_PACKAGE; + } + + // these are about explicit modifiers + + + @Deprecated + default boolean isSyntacticallyAbstract() { + return hasExplicitModifiers(JModifier.ABSTRACT); + } + + + @Deprecated + default boolean isSyntacticallyPublic() { + return hasExplicitModifiers(JModifier.PUBLIC); + } + + + @Deprecated + default boolean isSyntacticallyStatic() { + return hasExplicitModifiers(JModifier.STATIC); + } + + + @Deprecated + default boolean isSyntacticallyFinal() { + return hasExplicitModifiers(JModifier.FINAL); + } + + + /** + * Represents the visibility of a declaration. + * + *

The ordering of the constants encodes a "contains" relationship, + * ie, given two visibilities {@code v1} and {@code v2}, {@code v1 < v2} + * means that {@code v2} is strictly more permissive than {@code v1}. + */ + enum Visibility { + // Note: constants are prefixed with "V_" to avoid conflicts with JModifier + + /** Special visibility of anonymous classes, even more restricted than local. */ + V_ANONYMOUS("anonymous"), + /** Confined to a local scope, eg method parameters, local variables, local classes. */ + V_LOCAL("local"), + /** File-private. Corresponds to modifier {@link JModifier#PRIVATE}. */ + V_PRIVATE("private"), + /** Package-private. */ + V_PACKAGE("package"), + /** Package-private + visible to subclasses. Corresponds to modifier {@link JModifier#PROTECTED}. */ + V_PROTECTED("protected"), + /** Visible everywhere. Corresponds to modifier {@link JModifier#PUBLIC}. */ + V_PUBLIC("public"); + + private final String myName; + + Visibility(String name) { + this.myName = name; + } + + @Override + public String toString() { + return myName; + } + + /** + * Returns true if this visibility is greater than or equal to + * the parameter. + */ + public boolean isAtLeast(Visibility other) { + return this.compareTo(other) >= 0; + } + + /** + * Returns true if this visibility is strictly lower than the + * parameter. + */ + public boolean isAtMost(Visibility other) { + return this.compareTo(other) < 0; + } + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/FinalizableNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/FinalizableNode.java new file mode 100644 index 0000000000..0dc03a37a2 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/FinalizableNode.java @@ -0,0 +1,21 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +/** + * Package private, though the method is public. + */ +interface FinalizableNode extends AccessNode { + + + /** + * Returns true if this variable, method or class is final (even implicitly). + */ + @Override + default boolean isFinal() { + return hasModifiers(JModifier.FINAL); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java index f0e6538b28..16f7763262 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java @@ -40,25 +40,23 @@ public final class InternalApiBridge { ((AbstractJavaNode) node).comment(comment); } - public static void setModifier(AccessNode node, int modifier) { - ((AbstractJavaAccessNode) node).setModifier(true, modifier); - } - public static void setQname(ASTAnyTypeDeclaration declaration, JavaTypeQualifiedName qualifiedName) { ((AbstractAnyTypeDeclaration) declaration).setQualifiedName(qualifiedName); } public static void setQname(MethodLikeNode node, JavaOperationQualifiedName qualifiedName) { - ((AbstractMethodLikeNode) node).setQualifiedName(qualifiedName); + if (node instanceof ASTLambdaExpression) { + ((ASTLambdaExpression) node).setQualifiedName(qualifiedName); + } else if (node instanceof AbstractMethodOrConstructorDeclaration) { + ((AbstractMethodOrConstructorDeclaration) node).setQualifiedName(qualifiedName); + } } public static void setTypeDefinition(TypeNode node, JavaTypeDefinition definition) { if (node instanceof AbstractJavaTypeNode) { ((AbstractJavaTypeNode) node).setTypeDefinition(definition); - } else if (node instanceof AbstractJavaAccessTypeNode) { - ((AbstractJavaAccessTypeNode) node).setTypeDefinition(definition); - } else if (node instanceof ASTLambdaExpression) { - ((ASTLambdaExpression) node).setTypeDefinition(definition); + } else if (node instanceof AbstractAnyTypeDeclaration) { + ((AbstractAnyTypeDeclaration) node).setTypeDefinition(definition); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java new file mode 100644 index 0000000000..d6c5a8036e --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java @@ -0,0 +1,68 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + + +import java.lang.reflect.Modifier; +import java.util.Locale; + +/** + * A Java modifier. The ordering of constants respects the ordering + * recommended by the JLS. + */ +// Note: the class is named JModifier and not Modifier to avoid conflict +// with java.lang.reflect.Modifier +public enum JModifier { + // for anything + PUBLIC(Modifier.PUBLIC), + PROTECTED(Modifier.PROTECTED), + PRIVATE(Modifier.PRIVATE), + + ABSTRACT(Modifier.ABSTRACT), + STATIC(Modifier.STATIC), + FINAL(Modifier.FINAL), + + // for methods + SYNCHRONIZED(Modifier.SYNCHRONIZED), + NATIVE(Modifier.NATIVE), + DEFAULT(0), + + // not for fields + STRICTFP(Modifier.STRICT), + + // for fields + TRANSIENT(Modifier.TRANSIENT), + VOLATILE(Modifier.VOLATILE); + + + private final String token = name().toLowerCase(Locale.ROOT); + private final int reflect; + + JModifier(int reflect) { + this.reflect = reflect; + } + + /** + * Returns the constant of java.lang.reflect.Modifier that this + * modifier corresponds to. Be aware that {@link #DEFAULT} has + * no equivalent in {@link Modifier}. + */ + public int getReflectMod() { + return reflect; + } + + + /** + * Returns how the modifier is written in source. + */ + public String getToken() { + return token; + } + + @Override + public String toString() { + return getToken(); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/MethodLikeNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/MethodLikeNode.java index bdf4beb27f..478120a080 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/MethodLikeNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/MethodLikeNode.java @@ -21,7 +21,7 @@ import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; * Ultimately this supertype is not useful and can go away. */ @Deprecated -public interface MethodLikeNode extends AccessNode, JavaQualifiableNode, JavaNode { +public interface MethodLikeNode extends JavaQualifiableNode, JavaNode { /** * Returns a token indicating whether this node is a lambda diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java index 28aafac1ac..721ee11f3f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/internal/LanguageLevelChecker.java @@ -40,6 +40,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTTypeArguments; import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.ASTYieldStatement; +import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.SideEffectingVisitorAdapter; @@ -211,11 +212,11 @@ public class LanguageLevelChecker { @Override public void visit(ASTMethodDeclaration node, T data) { - if (node.isDefault()) { + if (node.hasModifiers(JModifier.DEFAULT)) { check(node, RegularLanguageFeature.DEFAULT_METHODS, data); } - if (node.isPrivate() && node.isInterfaceMember()) { + if (node.isPrivate() && node.getEnclosingType().isInterface()) { check(node, RegularLanguageFeature.PRIVATE_METHODS_IN_INTERFACES, data); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaClassMetric.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaClassMetric.java index 44022529f3..deb3ff98f1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaClassMetric.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaClassMetric.java @@ -10,7 +10,6 @@ import java.util.List; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetric; @@ -36,7 +35,7 @@ public abstract class AbstractJavaClassMetric extends AbstractJavaMetric(); super.visit(node, data); - } else if (node instanceof ASTClassOrInterfaceDeclaration - && ((ASTClassOrInterfaceDeclaration) node).isLocal()) { + } else if (node.isLocal()) { super.visit(node, data); } return methodAttributeAccess; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaSignature.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaSignature.java index c3dd860493..36a32ab828 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaSignature.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaSignature.java @@ -30,7 +30,7 @@ public abstract class JavaSignature> implements Signatur /** - * The visibility of a node. + * The visibility of a node. TODO replace with {@link AccessNode.Visibility} */ public enum Visibility { PUBLIC, PACKAGE, PROTECTED, PRIVATE; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java index 457c77695e..9feb0fa546 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/qname/QualifiedNameResolver.java @@ -17,7 +17,6 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnonymousClassDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; @@ -206,9 +205,7 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { @Override public Object visit(ASTAnyTypeDeclaration node, Object data) { int localIndex = NOTLOCAL_PLACEHOLDER; - if (node instanceof ASTClassOrInterfaceDeclaration - && ((ASTClassOrInterfaceDeclaration) node).isLocal()) { - + if (node.isLocal()) { localIndex = getNextIndexFromHistogram(currentLocalIndices.peek(), node.getSimpleName(), 1); } @@ -229,7 +226,7 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { public Object visit(ASTAnonymousClassDeclaration node, Object data) { updateContextForAnonymousClass(); - node.setQualifiedName(contextClassQName()); + InternalApiBridge.setQname(node, contextClassQName()); super.visit(node, data); rollbackClassContext(); @@ -376,7 +373,7 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { return "static"; } else if (parent instanceof ASTFieldDeclaration) { ASTFieldDeclaration field = (ASTFieldDeclaration) parent; - if (field.isStatic() || field.isInterfaceMember()) { + if (field.isStatic() || field.getEnclosingType().isInterface()) { return "static"; } if (innermostEnclosingTypeName.peek().isAnonymousClass()) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index 14767ca038..0f75c2cbe0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -10,7 +10,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration.DeclarationKind; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTInitializer; @@ -50,7 +49,7 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule unnecessaryModifiers, String explanation) { + Set unnecessaryModifiers, String explanation) { if (unnecessaryModifiers.isEmpty()) { return; } @@ -87,7 +88,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { private String getPrintableNodeKind(Node node) { if (node instanceof ASTAnyTypeDeclaration) { return PrettyPrintingUtil.kindName((ASTAnyTypeDeclaration) node); - } else if (node instanceof ASTMethodDeclaration || node instanceof ASTAnnotationMethodDeclaration) { + } else if (node instanceof ASTMethodDeclaration) { return "method"; } else if (node instanceof ASTConstructorDeclaration) { return "constructor"; @@ -100,7 +101,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { } - private String formatUnnecessaryModifiers(Set set) { + private String formatUnnecessaryModifiers(Set set) { // prints in the standard modifier order (sorted by enum constant ordinal), // regardless of the actual order in which we checked return (set.size() > 1 ? "s" : "") + " '" + StringUtils.join(set, " ") + "'"; @@ -110,13 +111,13 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { @Override public Object visit(ASTEnumDeclaration node, Object data) { - if (node.isPublic()) { - checkDeclarationInInterfaceType(data, node, EnumSet.of(Modifier.PUBLIC)); + if (node.hasExplicitModifiers(PUBLIC)) { + checkDeclarationInInterfaceType(data, node, EnumSet.of(PUBLIC)); } - if (node.isStatic()) { + if (node.hasExplicitModifiers(STATIC)) { // a static enum - reportUnnecessaryModifiers(data, node, Modifier.STATIC, "nested enums are implicitly static"); + reportUnnecessaryModifiers(data, node, STATIC, "nested enums are implicitly static"); } return data; @@ -127,7 +128,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { public Object visit(ASTAnnotationTypeDeclaration node, Object data) { if (node.isAbstract()) { // may have several violations, with different explanations - reportUnnecessaryModifiers(data, node, Modifier.ABSTRACT, "annotations types are implicitly abstract"); + reportUnnecessaryModifiers(data, node, ABSTRACT, "annotations types are implicitly abstract"); } @@ -137,45 +138,54 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { } // a public annotation within an interface or annotation - if (node.isPublic() && node.enclosingTypeIsA(TypeKind.INTERFACE, TypeKind.ANNOTATION)) { - reportUnnecessaryModifiers(data, node, Modifier.PUBLIC, "members of " + getPrintableNodeKind(node.getEnclosingTypeDeclaration()) + " types are implicitly public"); + if (node.hasExplicitModifiers(PUBLIC) && isParentInterfaceType(node.getEnclosingType())) { + reportUnnecessaryModifiers(data, node, PUBLIC, + "members of " + getPrintableNodeKind(node.getEnclosingType()) + + " types are implicitly public"); } - if (node.isStatic()) { + if (node.hasExplicitModifiers(STATIC)) { // a static annotation - reportUnnecessaryModifiers(data, node, Modifier.STATIC, "nested annotation types are implicitly static"); + reportUnnecessaryModifiers(data, node, STATIC, "nested annotation types are implicitly static"); } return data; } + // also considers annotations, as should ASTAnyTypeDeclaration do + private boolean isParentInterfaceType(@Nullable ASTAnyTypeDeclaration enclosing) { + return enclosing != null && enclosing.isInterface(); + } + @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isInterface() && node.isAbstract()) { + if (node.isInterface() && node.hasExplicitModifiers(ABSTRACT)) { // an abstract interface - reportUnnecessaryModifiers(data, node, Modifier.ABSTRACT, "interface types are implicitly abstract"); + reportUnnecessaryModifiers(data, node, ABSTRACT, "interface types are implicitly abstract"); } if (!node.isNested()) { return data; } - boolean isParentInterfaceOrAnnotation = node.enclosingTypeIsA(TypeKind.INTERFACE, TypeKind.ANNOTATION); + boolean isParentInterfaceOrAnnotation = isParentInterfaceType(node.getEnclosingType()); // a public class or interface within an interface or annotation - if (node.isPublic() && isParentInterfaceOrAnnotation) { - reportUnnecessaryModifiers(data, node, Modifier.PUBLIC, "members of " + getPrintableNodeKind(node.getEnclosingTypeDeclaration()) + " types are implicitly public"); + if (node.hasExplicitModifiers(PUBLIC) && isParentInterfaceOrAnnotation) { + reportUnnecessaryModifiers(data, node, PUBLIC, + "members of " + getPrintableNodeKind(node.getEnclosingType()) + + " types are implicitly public"); } - if (node.isStatic()) { + if (node.hasExplicitModifiers(STATIC)) { if (node.isInterface()) { // a static interface - reportUnnecessaryModifiers(data, node, Modifier.STATIC, "member interfaces are implicitly static"); + reportUnnecessaryModifiers(data, node, STATIC, "member interfaces are implicitly static"); } else if (isParentInterfaceOrAnnotation) { // a type nested within an interface - reportUnnecessaryModifiers(data, node, Modifier.STATIC, "types nested within an interface type are implicitly static"); + reportUnnecessaryModifiers(data, node, STATIC, "types nested within an interface type are implicitly static"); } } @@ -184,29 +194,30 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { @Override public Object visit(final ASTMethodDeclaration node, Object data) { - Set unnecessary = EnumSet.noneOf(Modifier.class); + Set unnecessary = EnumSet.noneOf(JModifier.class); - if (node.isSyntacticallyPublic()) { - unnecessary.add(Modifier.PUBLIC); + if (node.hasExplicitModifiers(PUBLIC)) { + unnecessary.add(PUBLIC); } - if (node.isSyntacticallyAbstract()) { - unnecessary.add(Modifier.ABSTRACT); + if (node.hasExplicitModifiers(ABSTRACT)) { + unnecessary.add(ABSTRACT); } checkDeclarationInInterfaceType(data, node, unnecessary); - if (node.isFinal()) { + if (node.hasExplicitModifiers(FINAL)) { // If the method is annotated by @SafeVarargs then it's ok if (!isSafeVarargs(node)) { - if (node.isPrivate()) { - reportUnnecessaryModifiers(data, node, Modifier.FINAL, "private methods cannot be overridden"); + if (node.hasModifiers(PRIVATE)) { + reportUnnecessaryModifiers(data, node, FINAL, "private methods cannot be overridden"); } else { - final Node n = node.getNthParent(3); + final ASTAnyTypeDeclaration n = node.getEnclosingType(); // A final method of an anonymous class / enum constant. Neither can be extended / overridden - if (n instanceof ASTAllocationExpression || n instanceof ASTEnumConstant) { - reportUnnecessaryModifiers(data, node, Modifier.FINAL, "an anonymous class cannot be extended"); - } else if (n instanceof ASTClassOrInterfaceDeclaration && ((AccessNode) n).isFinal()) { - reportUnnecessaryModifiers(data, node, Modifier.FINAL, "the method is already in a final class"); + if (n.isAnonymous()) { + reportUnnecessaryModifiers(data, node, FINAL, "an anonymous class cannot be extended"); + } else if (n.isFinal()) { + // notice: enum types are implicitly final if no enum constant declares a body + reportUnnecessaryModifiers(data, node, FINAL, "the method is already in a final class"); } } } @@ -217,8 +228,8 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { @Override public Object visit(final ASTResource node, final Object data) { - if (!node.isConciseResource() && node.asLocalVariableDeclaration().isFinal()) { - reportUnnecessaryModifiers(data, node, Modifier.FINAL, "resource specifications are implicitly final"); + if (!node.isConciseResource() && node.asLocalVariableDeclaration().hasExplicitModifiers(FINAL)) { + reportUnnecessaryModifiers(data, node, FINAL, "resource specifications are implicitly final"); } return data; @@ -226,15 +237,15 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { @Override public Object visit(ASTFieldDeclaration node, Object data) { - Set unnecessary = EnumSet.noneOf(Modifier.class); - if (node.isSyntacticallyPublic()) { - unnecessary.add(Modifier.PUBLIC); + Set unnecessary = EnumSet.noneOf(JModifier.class); + if (node.hasExplicitModifiers(PUBLIC)) { + unnecessary.add(PUBLIC); } - if (node.isSyntacticallyStatic()) { - unnecessary.add(Modifier.STATIC); + if (node.hasExplicitModifiers(STATIC)) { + unnecessary.add(STATIC); } - if (node.isSyntacticallyFinal()) { - unnecessary.add(Modifier.FINAL); + if (node.hasExplicitModifiers(FINAL)) { + unnecessary.add(FINAL); } checkDeclarationInInterfaceType(data, node, unnecessary); @@ -243,9 +254,9 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { @Override public Object visit(ASTConstructorDeclaration node, Object data) { - if (node.getNthParent(2) instanceof ASTEnumBody) { - if (node.isPrivate()) { - reportUnnecessaryModifiers(data, node, Modifier.PRIVATE, "enum constructors are implicitly private"); + if (node.getEnclosingType().isEnum()) { + if (node.hasExplicitModifiers(PRIVATE)) { + reportUnnecessaryModifiers(data, node, PRIVATE, "enum constructors are implicitly private"); } } return data; @@ -257,26 +268,16 @@ public class UnnecessaryModifierRule extends AbstractJavaRule { } - private void checkDeclarationInInterfaceType(Object data, Node fieldOrMethod, Set unnecessary) { + private void checkDeclarationInInterfaceType(Object data, JavaNode fieldOrMethod, Set unnecessary) { // third ancestor could be an AllocationExpression // if this is a method in an anonymous inner class - Node parent = fieldOrMethod.getParent().getParent().getParent(); - if (parent instanceof ASTAnnotationTypeDeclaration - || parent instanceof ASTClassOrInterfaceDeclaration - && ((ASTClassOrInterfaceDeclaration) parent).isInterface()) { - reportUnnecessaryModifiers(data, fieldOrMethod, unnecessary, "the " + getPrintableNodeKind(fieldOrMethod) + " is declared in an " + getPrintableNodeKind(parent) + " type"); + ASTAnyTypeDeclaration parent = fieldOrMethod.getEnclosingType(); + if (parent.isInterface()) { + reportUnnecessaryModifiers(data, fieldOrMethod, unnecessary, + "the " + getPrintableNodeKind(fieldOrMethod) + " is declared in an " + + getPrintableNodeKind(parent) + " type"); } } - private enum Modifier { - PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, ABSTRACT; - - - @Override - public String toString() { - return name().toLowerCase(Locale.ROOT); - } - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java index b5c7d59ea8..aeb95fdb3c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/ConstructorCallsOverridableMethodRule.java @@ -35,6 +35,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; /** @@ -933,7 +934,7 @@ public final class ConstructorCallsOverridableMethodRule extends AbstractJavaRul if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) { // only evaluate if we have an eval package for this class MethodHolder h = new MethodHolder(node); - if (!node.isAbstract() && !node.isPrivate() && !node.isStatic() && !node.isFinal()) { + if (!node.getModifiers().hasAny(JModifier.ABSTRACT, JModifier.PRIVATE, JModifier.STATIC, JModifier.FINAL)) { // Skip abstract methods, have a separate rule for that h.setDangerous(); // this method is overridable h.setCalledMethod(node.getName()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/ClassScope.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/ClassScope.java index 459e065925..ef5df40f49 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/ClassScope.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/ClassScope.java @@ -33,7 +33,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter; import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.symboltable.Applier; import net.sourceforge.pmd.lang.symboltable.ImageFinderFunction; @@ -296,7 +295,7 @@ public class ClassScope extends AbstractJavaScope { private MethodNameDeclaration createBuiltInMethodDeclaration(final String methodName, final String... parameterTypes) { ASTMethodDeclaration methodDeclaration = new ASTMethodDeclaration(0); - InternalApiBridge.setModifier(methodDeclaration, AccessNode.PUBLIC); + // InternalApiBridge.setModifier(methodDeclaration, JModifier.PUBLIC); InternalApiBridge.setScope(methodDeclaration, this); methodDeclaration.setImage(methodName); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclaration.java index e0600a6a09..dcf972dd5e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/VariableNameDeclaration.java @@ -78,11 +78,11 @@ public class VariableNameDeclaration extends AbstractNameDeclaration implements public AccessNode getAccessNodeParent() { if (node.getParent() instanceof ASTFormalParameter) { - return (AccessNode) node.getParent(); + return (ASTFormalParameter) node.getParent(); } else if (node.getParent() instanceof ASTLambdaParameter) { - return (AccessNode) node.getParent().getParent().getParent(); + return (ASTLambdaParameter) node.getParent(); } else if (node.getParent() instanceof ASTEnumConstant) { - return (AccessNode) node.getParent().getParent().getParent(); + return (ASTEnumConstant) node.getParent(); } else if (node.getParent() instanceof ASTCatchParameter) { return (AccessNode) node.getParent(); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java deleted file mode 100644 index 1e00823936..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.lang.java.JavaParsingHelper; - - -/** - * @author Clément Fournier - * @since 6.1.0 - */ -public class ASTClassOrInterfaceDeclarationTest { - - private static final String LOCAL_CLASS_IN_METHOD - = "class Foo { void bar() { class Local {}}}"; - - private static final String NESTED_CLASS_IS_NOT_LOCAL - = "class Foo { class Nested {} void bar() {}}"; - - private static final String LOCAL_CLASS_IN_INITIALIZER - = "class Foo { { class Local {} } }"; - - private static final String LOCAL_CLASS_WITH_MODIFIERS - = "class Foo { { abstract class Local {} } }"; - - private static final String LOCAL_CLASS_WITH_MIXED_MODIFIER_ANNOTATIONS - = "class Foo { { final @F class Local {} } }"; - - private static final String LOCAL_CHILDREN_ARE_NOT_ALWAYS_LOCAL - = "class Foo { { class Local { class Nested {} void bar() {class Local2 {}}}}}"; - - - @Test - public void testLocalInMethod() { - List classes = getClassDecls(LOCAL_CLASS_IN_METHOD); - assertTrue(classes.size() == 2); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); - assertTrue("Local class false-negative", classes.get(1).isLocal()); - } - - - @Test - public void testLocalInInitializer() { - List classes = getClassDecls(LOCAL_CLASS_IN_INITIALIZER); - assertTrue(classes.size() == 2); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); - assertTrue("Local class false-negative", classes.get(1).isLocal()); - } - - - - @Test - public void testLocalAbstractClass() { - List classes = getClassDecls(LOCAL_CLASS_WITH_MODIFIERS); - assertTrue(classes.size() == 2); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); - assertTrue("Local class false-negative", classes.get(1).isLocal()); - assertTrue("Local class should preserve its modifiers", classes.get(1).isAbstract()); - } - - - @Test - public void testLocalClassWithMixedModifiers() { - List classes = getClassDecls(LOCAL_CLASS_WITH_MIXED_MODIFIER_ANNOTATIONS); - assertTrue(classes.size() == 2); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); - assertTrue("Local class false-negative", classes.get(1).isLocal()); - assertTrue("Local class should preserve its modifiers", classes.get(1).isFinal()); - } - - - - @Test - public void testLocalClassVisibility() { - List classes = getClassDecls(LOCAL_CLASS_WITH_MODIFIERS); - assertTrue(classes.size() == 2); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); - assertTrue("Local class false-negative", classes.get(1).isLocal()); - assertFalse("Local class is not public", classes.get(1).isPublic()); - assertFalse("Local class is not private", classes.get(1).isPrivate()); - assertFalse("Local class is not protected", classes.get(1).isProtected()); - assertFalse("Local class is not package-private", classes.get(1).isPackagePrivate()); - } - - - @Test - public void testNestedClassIsNotLocal() { - List classes = getClassDecls(NESTED_CLASS_IS_NOT_LOCAL); - assertTrue(classes.size() == 2); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); - assertFalse("Local class false-positive", classes.get(1).isLocal()); - } - - - @Test - public void testLocalChildrenAreNotAlwaysLocal() { - List classes = getClassDecls(LOCAL_CHILDREN_ARE_NOT_ALWAYS_LOCAL); - assertTrue(classes.size() == 4); - - assertFalse("Local class false-positive", classes.get(0).isLocal()); // class Foo - assertTrue("Local class false-negative", classes.get(1).isLocal()); // class Local - assertFalse("Local class false-positive", classes.get(2).isLocal()); // class Nested - assertTrue("Local class false-negative", classes.get(3).isLocal()); // class Local2 - } - - - private List getClassDecls(String code) { - return JavaParsingHelper.WITH_PROCESSING.getNodes(ASTClassOrInterfaceDeclaration.class, code); - } -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java deleted file mode 100644 index 63aae9f2cd..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.JavaParsingHelper; - -public class AccessNodeTest extends BaseParserTest { - - public static class MyAccessNode extends AbstractJavaAccessNode { - public MyAccessNode(int i) { - super(i); - } - - @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); - } - } - - @Test - public void testModifiersOnClassDecl() { - List ops = java.getNodes(ASTClassOrInterfaceDeclaration.class, TEST1); - assertTrue(ops.get(0).isPublic()); - } - - private static final String TEST1 = "public class Foo {}"; - - @Test - public void testStatic() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not static.", node.isStatic()); - InternalApiBridge.setModifier(node, AccessNode.STATIC); - assertTrue("Node set to static, not static.", node.isStatic()); - } - - @Test - public void testPublic() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not public.", node.isPublic()); - InternalApiBridge.setModifier(node, AccessNode.PUBLIC); - assertTrue("Node set to public, not public.", node.isPublic()); - } - - @Test - public void testProtected() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not protected.", node.isProtected()); - InternalApiBridge.setModifier(node, AccessNode.PROTECTED); - assertTrue("Node set to protected, not protected.", node.isProtected()); - } - - @Test - public void testPrivate() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not private.", node.isPrivate()); - InternalApiBridge.setModifier(node, AccessNode.PRIVATE); - assertTrue("Node set to private, not private.", node.isPrivate()); - } - - @Test - public void testFinal() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not final.", node.isFinal()); - InternalApiBridge.setModifier(node, AccessNode.FINAL); - assertTrue("Node set to final, not final.", node.isFinal()); - } - - @Test - public void testSynchronized() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not synchronized.", node.isSynchronized()); - InternalApiBridge.setModifier(node, AccessNode.SYNCHRONIZED); - assertTrue("Node set to synchronized, not synchronized.", node.isSynchronized()); - } - - @Test - public void testVolatile() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not volatile.", node.isVolatile()); - InternalApiBridge.setModifier(node, AccessNode.VOLATILE); - assertTrue("Node set to volatile, not volatile.", node.isVolatile()); - } - - @Test - public void testTransient() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not transient.", node.isTransient()); - InternalApiBridge.setModifier(node, AccessNode.TRANSIENT); - assertTrue("Node set to transient, not transient.", node.isTransient()); - } - - @Test - public void testNative() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not native.", node.isNative()); - InternalApiBridge.setModifier(node, AccessNode.NATIVE); - assertTrue("Node set to native, not native.", node.isNative()); - } - - @Test - public void testAbstract() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not abstract.", node.isAbstract()); - InternalApiBridge.setModifier(node, AccessNode.ABSTRACT); - assertTrue("Node set to abstract, not abstract.", node.isAbstract()); - } - - @Test - public void testStrict() { - AccessNode node = new MyAccessNode(1); - assertFalse("Node should default to not strict.", node.isStrictfp()); - InternalApiBridge.setModifier(node, AccessNode.STRICTFP); - assertTrue("Node set to strict, not strict.", node.isStrictfp()); - } - - @Test - public void testPackagePrivate() { - AccessNode node = new MyAccessNode(1); - assertTrue("Node should default to package private.", node.isPackagePrivate()); - InternalApiBridge.setModifier(node, AccessNode.PRIVATE); - assertFalse("Node set to private, still package private.", node.isPackagePrivate()); - node = new MyAccessNode(1); - InternalApiBridge.setModifier(node, AccessNode.PUBLIC); - assertFalse("Node set to public, still package private.", node.isPackagePrivate()); - node = new MyAccessNode(1); - InternalApiBridge.setModifier(node, AccessNode.PROTECTED); - assertFalse("Node set to protected, still package private.", node.isPackagePrivate()); - } - - - private static String makeAccessJavaCode(String[] access, String declRest) { - String result = "public class Test { "; - for (String s : access) { - result += s + " "; - } - return result + " " + declRest + " }"; - } - - - public static T getDeclWithModifiers(String[] access, Class target, String declRest) { - ASTCompilationUnit acu = JavaParsingHelper.JUST_PARSE.parse(makeAccessJavaCode(access, declRest)); - - List declarations = acu.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class) - .findDescendantsOfType(target); - - assertEquals("Wrong number of declarations", 1, declarations.size()); - return declarations.get(0); - } -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ClassDeclTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ClassDeclTest.java deleted file mode 100644 index 518a977497..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ClassDeclTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class ClassDeclTest extends BaseParserTest { - - @Test - public void testPublic() { - String[] access = {"public"}; - ASTClassOrInterfaceDeclaration acd = getClassDecl(access); - verifyFlags(acd, true, false, false, false); - } - - @Test - public void testAbstract() { - String[] access = { "abstract" }; - ASTClassOrInterfaceDeclaration acd = getClassDecl(access); - verifyFlags(acd, false, true, false, false); - } - - @Test - public void testFinal() { - String[] access = { "final" }; - ASTClassOrInterfaceDeclaration acd = getClassDecl(access); - verifyFlags(acd, false, false, true, false); - } - - @Test - public void testStrict() { - String[] access = { "strictfp" }; - ASTClassOrInterfaceDeclaration acd = getClassDecl(access); - verifyFlags(acd, false, false, false, true); - } - - @Test - public void testPublicFinal() { - String[] access = { "public", "final" }; - ASTClassOrInterfaceDeclaration acd = getClassDecl(access); - verifyFlags(acd, true, false, true, false); - } - - public void verifyFlags(ASTClassOrInterfaceDeclaration acd, boolean bPublic, boolean bAbstract, boolean bFinal, - boolean bStrict) { - assertEquals("Public: ", bPublic, acd.isPublic()); - assertEquals("Abstract: ", bAbstract, acd.isAbstract()); - assertEquals("Final: ", bFinal, acd.isFinal()); - assertEquals("Strict: ", bStrict, acd.isStrictfp()); - } - - public ASTClassOrInterfaceDeclaration getClassDecl(String[] access) { - return AccessNodeTest.getDeclWithModifiers(access, ASTClassOrInterfaceDeclaration.class, "class Test {}"); - } -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FieldDeclTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FieldDeclTest.java deleted file mode 100644 index c229b53690..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/FieldDeclTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static net.sourceforge.pmd.lang.java.ast.AccessNodeTest.getDeclWithModifiers; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class FieldDeclTest extends BaseParserTest { - - - public ASTFieldDeclaration getFieldDecl(String[] access) { - return getDeclWithModifiers(access, ASTFieldDeclaration.class, "int j;"); - } - - - @Test - public void testPublic() { - String[] access = { "public" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be public.", afd.isPublic()); - } - - @Test - public void testProtected() { - String[] access = { "protected" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be protected.", afd.isProtected()); - } - - @Test - public void testPrivate() { - String[] access = { "private" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be private.", afd.isPrivate()); - } - - @Test - public void testStatic() { - String[] access = { "private", "static" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be static.", afd.isStatic()); - assertTrue("Expecting field to be private.", afd.isPrivate()); - } - - @Test - public void testFinal() { - String[] access = { "public", "final" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be final.", afd.isFinal()); - assertTrue("Expecting field to be public.", afd.isPublic()); - } - - @Test - public void testTransient() { - String[] access = { "private", "transient" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be private.", afd.isPrivate()); - assertTrue("Expecting field to be transient.", afd.isTransient()); - } - - @Test - public void testVolatile() { - String[] access = { "private", "volatile" }; - ASTFieldDeclaration afd = getFieldDecl(access); - assertTrue("Expecting field to be volatile.", afd.isVolatile()); - assertTrue("Expecting field to be private.", afd.isPrivate()); - } -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java index a68f0c776d..82fcdcc54e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java @@ -279,7 +279,7 @@ public class JDKVersionTest { List methods = acu.findDescendantsOfType(ASTMethodDeclaration.class, true); assertEquals(3, methods.size()); for (ASTMethodDeclaration method : methods) { - assertFalse(method.isInterfaceMember()); + assertFalse(method.getEnclosingType().isInterface()); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/MethodDeclTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/MethodDeclTest.java deleted file mode 100644 index 99ae118166..0000000000 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/MethodDeclTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class MethodDeclTest { - - @Test - public void testPublic() { - String[] access = { "public" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be public.", amd.isPublic()); - } - - @Test - public void testPrivate() { - String[] access = { "private" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be private.", amd.isPrivate()); - } - - @Test - public void testProtected() { - String[] access = { "protected" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be protected.", amd.isProtected()); - } - - @Test - public void testFinal() { - String[] access = { "public", "final" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be final.", amd.isFinal()); - assertTrue("Expecting method to be public.", amd.isPublic()); - } - - @Test - public void testSynchronized() { - String[] access = { "public", "synchronized" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be synchronized.", amd.isSynchronized()); - assertTrue("Expecting method to be public.", amd.isPublic()); - } - - @Test - public void testAbstract() { - String[] access = { "public", "abstract" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be abstract.", amd.isAbstract()); - assertTrue("Expecting method to be public.", amd.isPublic()); - } - - @Test - public void testNative() { - String[] access = { "private", "native" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be native.", amd.isNative()); - assertTrue("Expecting method to be private.", amd.isPrivate()); - } - - @Test - public void testStrict() { - String[] access = { "public", "strictfp" }; - ASTMethodDeclaration amd = getMethodDecl(access); - assertTrue("Expecting method to be strict.", amd.isStrictfp()); - assertTrue("Expecting method to be public.", amd.isPublic()); - } - - private ASTMethodDeclaration getMethodDecl(String[] access) { - return AccessNodeTest.getDeclWithModifiers(access, ASTMethodDeclaration.class, "void stuff(){}"); - } -} 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 116a058ffd..91759f5b92 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 @@ -7,6 +7,8 @@ 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.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 @@ -23,7 +25,7 @@ class ASTAnnotationTest : ParserTestSpec({ } } - parserTest("Marker annotations") { + parserTest("Marker annotations", javaVersions = J1_5..Latest) { inContext(AnnotationParsingCtx) { @@ -44,7 +46,7 @@ class ASTAnnotationTest : ParserTestSpec({ } - parserTest("Single-value shorthand") { + parserTest("Single-value shorthand", javaVersions = J1_5..Latest) { inContext(AnnotationParsingCtx) { @@ -94,7 +96,7 @@ class ASTAnnotationTest : ParserTestSpec({ } - parserTest("Normal annotation") { + parserTest("Normal annotation", javaVersions = J1_5..Latest) { inContext(AnnotationParsingCtx) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassTest.kt new file mode 100644 index 0000000000..7db48c82df --- /dev/null +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassTest.kt @@ -0,0 +1,61 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast + +import io.kotlintest.should +import io.kotlintest.shouldBe +import net.sourceforge.pmd.lang.ast.test.shouldBe +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_ANONYMOUS + +class ASTAnonymousClassTest : ParserTestSpec({ + + parserTest("Anon class modifiers") { + + inContext(StatementParsingCtx) { + + """ + new java.lang.Runnable() { + + @Override public void run() { + + } + }; + """ should parseAs { + + exprStatement { + + constructorCall { + it::getTypeNode shouldBe classType("Runnable") + + it::getArguments shouldBe child {} + + it::getAnonymousClassDeclaration shouldBe child { + + it::getModifiers shouldBe modifiers { + it.effectiveModifiers shouldBe emptySet() + it.explicitModifiers shouldBe emptySet() + } + + it should haveVisibility(V_ANONYMOUS) + + it::isAnonymous shouldBe true + it::isInterface shouldBe false + + val anon = it + + child { + child(ignoreChildren = true) { + it::getEnclosingType shouldBe anon + } + } + } + } + } + } + } + } + + +}) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchClauseTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchClauseTest.kt index 5c4691e84e..8a1da5c2f3 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchClauseTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCatchClauseTest.kt @@ -30,6 +30,8 @@ class ASTCatchClauseTest : ParserTestSpec({ it::getBody shouldBe block { } catchClause("ioe") { catchFormal("ioe") { + it::getModifiers shouldBe localVarModifiers { } + it::isMulticatch shouldBe false it::getTypeNode shouldBe classType("IOException") @@ -51,6 +53,8 @@ class ASTCatchClauseTest : ParserTestSpec({ catchFormal("e") { it::isMulticatch shouldBe true + it::getModifiers shouldBe localVarModifiers { } + it::getTypeNode shouldBe unionType { classType("IOException") classType("AssertionError") @@ -76,7 +80,9 @@ class ASTCatchClauseTest : ParserTestSpec({ catchFormal("e") { it::isMulticatch shouldBe true - annotation("B") // not a type annotation + it::getModifiers shouldBe localVarModifiers { + annotation("B") // not a type annotation + } unionType { classType("IOException") diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclTest.kt deleted file mode 100644 index dcecd61f52..0000000000 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - - -package net.sourceforge.pmd.lang.java.ast - -import net.sourceforge.pmd.lang.ast.test.shouldBe - -// TODO merge the java ASTClassOrInterfaceDeclarationTest into this -class ASTClassOrInterfaceDeclTest : ParserTestSpec({ - - parserTest("Local classes") { - - inContext(StatementParsingCtx) { - - """ - @F class Local { - - } - """ should parseAs { - localClassDecl(simpleName = "Local") { - - it::isAbstract shouldBe false - it::isFinal shouldBe false - it::isLocal shouldBe true - it::isNested shouldBe false - - annotation("F") - - typeBody() - } - } - - """ - @F abstract @C class Local { - - } - """ should parseAs { - - localClassDecl(simpleName = "Local") { - it::getDeclaredAnnotations shouldBe listOf( - annotation("F"), - annotation("C") - ) - - it::isAbstract shouldBe true - it::isFinal shouldBe false - it::isLocal shouldBe true - it::isNested shouldBe false - - typeBody() - } - } - } - } - - -}) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.kt new file mode 100644 index 0000000000..3416c9d3e4 --- /dev/null +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.kt @@ -0,0 +1,91 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + + +package net.sourceforge.pmd.lang.java.ast + +import net.sourceforge.pmd.lang.ast.test.shouldBe + +class ASTClassOrInterfaceDeclarationTest : ParserTestSpec({ + + parserTest("Local classes") { + + inContext(StatementParsingCtx) { + + """ + @F class Local { + + } + """ should parseAs { + localClassDecl(simpleName = "Local") { + + it::isAbstract shouldBe false + it::isFinal shouldBe false + it::isLocal shouldBe true + it::isNested shouldBe false + + it::getModifiers shouldBe modifiers { + annotation("F") + } + + typeBody() + } + } + + """ + @F abstract @C class Local { + + } + """ should parseAs { + + localClassDecl(simpleName = "Local") { + + it::getModifiers shouldBe modifiers { + annotation("F") + annotation("C") + } + + it::isAbstract shouldBe true + it::isFinal shouldBe false + it::isLocal shouldBe true + it::isNested shouldBe false + + typeBody() + } + } + + """ + class Local { class Nested {} void bar() {class Local2 {}}} + """ should parseAs { + + localClassDecl(simpleName = "Local") { + + it::getModifiers shouldBe modifiers {} + + it::isLocal shouldBe true + it::isNested shouldBe false + + + it.descendants(ASTClassOrInterfaceDeclaration::class.java) + .first() + .shouldMatchNode { + + it::getModifiers shouldBe modifiers {} + + + it::isLocal shouldBe false + it::isNested shouldBe true + + typeBody() + + } + + typeBody() + } + } + } + } + + +}) 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 c778667741..5b1d9486fb 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 @@ -18,6 +18,8 @@ class ASTConstructorDeclarationTest : ParserTestSpec({ // notice that arity is zero it::getArity shouldBe 0 + it::getModifiers shouldBe modifiers { } + it::getFormalParameters shouldBe formalsList(0) { it::getReceiverParameter shouldBe child { classType("Foo") { @@ -35,6 +37,8 @@ class ASTConstructorDeclarationTest : ParserTestSpec({ it::isVarargs shouldBe false it::getArity shouldBe 1 + it::getModifiers shouldBe modifiers { } + it::getFormalParameters shouldBe formalsList(1) { it::getReceiverParameter shouldBe child { @@ -45,6 +49,7 @@ class ASTConstructorDeclarationTest : ParserTestSpec({ it::toList shouldBe listOf( child { + localVarModifiers { } primitiveType(PrimitiveType.INT) variableId("other") } @@ -64,7 +69,9 @@ class ASTConstructorDeclarationTest : ParserTestSpec({ it::getName shouldBe "Foo" - annotation("OnDecl") + it::getModifiers shouldBe modifiers { + annotation("OnDecl") + } typeParamList { typeParam("T") { 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 3d755f2ceb..25a093abf1 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 @@ -4,18 +4,25 @@ package net.sourceforge.pmd.lang.java.ast +import io.kotlintest.matchers.beEmpty +import io.kotlintest.matchers.collections.shouldContainExactly +import io.kotlintest.should import net.sourceforge.pmd.lang.ast.test.shouldBe +import net.sourceforge.pmd.lang.java.ast.JModifier.* class ASTEnumConstantTest : ParserTestSpec({ parserTest("Enum constants should have a variable declarator id") { "enum Foo { A, B }" should matchToplevelType { + it::getModifiers shouldBe modifiers {} typeBody { enumConstant("A") { it::isAnonymousClass shouldBe false + it::getModifiers shouldBe modifiers { } + it::getVarId shouldBe variableId("A") { it::isEnumConstant shouldBe true it::isField shouldBe false @@ -28,6 +35,8 @@ class ASTEnumConstantTest : ParserTestSpec({ enumConstant("B") { it::isAnonymousClass shouldBe false + it::getModifiers shouldBe modifiers { } + it::getVarId shouldBe variableId("B") { it::isEnumConstant shouldBe true it::isField shouldBe false @@ -45,10 +54,14 @@ class ASTEnumConstantTest : ParserTestSpec({ parserTest("Enum constants should have an anonymous class node") { "enum Foo { B { } }" should matchToplevelType { + it::getModifiers shouldBe modifiers {} + typeBody { enumConstant("B") { it::isAnonymousClass shouldBe true + it::getModifiers shouldBe modifiers { } + it::getVarId shouldBe variableId("B") { it::isEnumConstant shouldBe true it::isField shouldBe false @@ -57,6 +70,8 @@ class ASTEnumConstantTest : ParserTestSpec({ it::getArguments shouldBe null it::getAnonymousClass shouldBe child { + it::getModifiers shouldBe modifiers { } + typeBody() } } @@ -68,11 +83,18 @@ class ASTEnumConstantTest : ParserTestSpec({ parserTest("Enum constants should contain their annotations") { "enum Foo { @C B, @A@a C }" should matchToplevelType { + it::getModifiers shouldBe modifiers {} typeBody { enumConstant("B") { - it::getDeclaredAnnotations shouldBe listOf(annotation("C")) + + val c = it + + it::getModifiers shouldBe modifiers { + c::getDeclaredAnnotations shouldBe listOf(annotation("C")) + } + it::getVarId shouldBe variableId("B") @@ -81,7 +103,14 @@ class ASTEnumConstantTest : ParserTestSpec({ } enumConstant("C") { - it::getDeclaredAnnotations shouldBe listOf(annotation("A"), annotation("a")) + + + val c = it + + it::getModifiers shouldBe modifiers { + c::getDeclaredAnnotations shouldBe listOf(annotation("A"), annotation("a")) + } + it::getVarId shouldBe variableId("C") @@ -96,10 +125,17 @@ class ASTEnumConstantTest : ParserTestSpec({ parserTest("Enum constants with arguments") { "enum Foo { B(\"str\") }" should matchToplevelType { + it::getModifiers shouldBe modifiers {} typeBody { enumConstant("B") { + + it::getModifiers shouldBe modifiers { + it.explicitModifiers should beEmpty() + it.effectiveModifiers.shouldContainExactly(PUBLIC, STATIC, FINAL) + } + it::getVarId shouldBe variableId("B") { it::isEnumConstant shouldBe true it::isField shouldBe false @@ -116,9 +152,14 @@ class ASTEnumConstantTest : ParserTestSpec({ "enum Foo { B(\"str\") { } }" should matchToplevelType { + it::getModifiers shouldBe modifiers {} + typeBody { enumConstant("B") { + + it::getModifiers shouldBe modifiers {} + it::getVarId shouldBe variableId("B") { it::isEnumConstant shouldBe true it::isField shouldBe false @@ -129,6 +170,7 @@ class ASTEnumConstantTest : ParserTestSpec({ } it::getAnonymousClass shouldBe child { + it::getModifiers shouldBe modifiers { } typeBody() } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTExplicitConstructorInvocationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTExplicitConstructorInvocationTest.kt index aec45db20d..4c4378c0ef 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTExplicitConstructorInvocationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTExplicitConstructorInvocationTest.kt @@ -12,6 +12,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { this(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -28,6 +30,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { this(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -52,6 +56,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { super(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -70,6 +76,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { super(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -94,6 +102,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { o.super(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -113,6 +123,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { o.super(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -135,6 +147,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { o.foo().super(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -157,6 +171,10 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "public TabbedPaneLayout() { MetalTabbedPaneUI.this.super(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { + it::getExplicitModifiers shouldBe setOf(JModifier.PUBLIC) + } + child { } it::getBody shouldBe block { child { @@ -189,6 +207,9 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ } """ should matchDeclaration { + it::getModifiers shouldBe modifiers { } + + child { } block { @@ -221,6 +242,8 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { this.name = null; }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + child { } block { @@ -229,6 +252,7 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ } "Foo() { super.name = null; }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } child { } block { @@ -237,6 +261,7 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ } "Foo() { super.foo(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } child { } @@ -247,6 +272,7 @@ class ASTExplicitConstructorInvocationTest : ParserTestSpec({ "Foo() { A.super.foo(); }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } child { } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclarationTest.kt index a7a6a06703..1117567677 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclarationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTFieldDeclarationTest.kt @@ -20,6 +20,8 @@ class ASTFieldDeclarationTest : ParserTestSpec({ "int x @A@B[];" should parseAs { fieldDecl { + it::getModifiers shouldBe modifiers { } + it::isPublic shouldBe false it::isSyntacticallyPublic shouldBe false it::isPackagePrivate shouldBe true @@ -56,12 +58,13 @@ class ASTFieldDeclarationTest : ParserTestSpec({ "@A int x[] = { 2 };" should parseAs { fieldDecl { + it::getModifiers shouldBe modifiers { + it::getExplicitModifiers shouldBe emptySet() + it::getEffectiveModifiers shouldBe setOf(JModifier.PUBLIC, JModifier.STATIC, JModifier.FINAL) - it::isPublic shouldBe true - it::isSyntacticallyPublic shouldBe false + annotation("A") + } - - annotation("A") primitiveType(INT) varDeclarator { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpressionTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpressionTest.kt index 07cbe2230c..36f12048cf 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpressionTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpressionTest.kt @@ -19,12 +19,11 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::isBlockBody shouldBe false it::getParameters shouldBe child { - child { - variableId("a") { - it::isTypeInferred shouldBe true - it::isLambdaParameter shouldBe true - } + simpleLambdaParam("a") { + it::isTypeInferred shouldBe true + it::isLambdaParameter shouldBe true } + } @@ -36,22 +35,19 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::isBlockBody shouldBe false it::getParameters shouldBe child { - child { - variableId("a") { - it::isTypeInferred shouldBe true - it::isLambdaParameter shouldBe true - } - + simpleLambdaParam("a") { + it::isTypeInferred shouldBe true + it::isLambdaParameter shouldBe true } - child { - variableId("b") { - it::isTypeInferred shouldBe true - it::isLambdaParameter shouldBe true - } + + simpleLambdaParam("b") { + it::isTypeInferred shouldBe true + it::isLambdaParameter shouldBe true } } + child(ignoreChildren = true) {} } @@ -60,13 +56,8 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::isBlockBody shouldBe true it::getParameters shouldBe child { - child { - variableId("a") - - } - child { - variableId("b") - } + simpleLambdaParam("a") + simpleLambdaParam("b") } @@ -79,7 +70,9 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::getParameters shouldBe child { lambdaParam { - it::isFinal shouldBe true + it::getModifiers shouldBe modifiers { + it::getExplicitModifiers shouldBe setOf(JModifier.FINAL) + } it::getTypeNode shouldBe primitiveType(INT) @@ -90,7 +83,11 @@ class ASTLambdaExpressionTest : ParserTestSpec({ } } lambdaParam { - annotation() + it::getModifiers shouldBe modifiers { + it::getExplicitModifiers shouldBe setOf() + annotation("F") + } + it::getTypeNode shouldBe classType("List") variableId("b") { it::isFinal shouldBe false @@ -115,7 +112,9 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::getParameters shouldBe child { lambdaParam { - it::isFinal shouldBe true + it::getModifiers shouldBe modifiers { + it::getExplicitModifiers shouldBe setOf(JModifier.FINAL) + } it::getTypeNode shouldBe primitiveType(INT) @@ -134,7 +133,11 @@ class ASTLambdaExpressionTest : ParserTestSpec({ } lambdaParam { - annotation() + it::getModifiers shouldBe modifiers { + it::getExplicitModifiers shouldBe setOf() + annotation("F") + } + it::getTypeNode shouldBe arrayType { classType("List") it::getDimensions shouldBe child { @@ -174,6 +177,7 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::getParameters shouldBe child { lambdaParam { + it::getModifiers shouldBe modifiers {} it::getTypeNode shouldBe arrayType { classType("String") it::getDimensions shouldBe child { @@ -198,6 +202,8 @@ class ASTLambdaExpressionTest : ParserTestSpec({ it::getParameters shouldBe child { lambdaParam { + it::getModifiers shouldBe modifiers {} + it::getTypeNode shouldBe arrayType { classType("String") it::getDimensions shouldBe child { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclarationTest.kt index 2c992b38a2..c0f65887a1 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclarationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclarationTest.kt @@ -20,6 +20,8 @@ class ASTLocalVariableDeclarationTest : ParserTestSpec({ "int x@A[];" should parseAs { localVarDecl { + it::getModifiers shouldBe modifiers { } + primitiveType(INT) varDeclarator { 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 9901387387..429f365744 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,7 +1,12 @@ package net.sourceforge.pmd.lang.java.ast +import io.kotlintest.should +import io.kotlintest.shouldNot import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_PRIVATE +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_PUBLIC +import net.sourceforge.pmd.lang.java.ast.JModifier.* 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_8 @@ -14,36 +19,38 @@ class ASTMethodDeclarationTest : ParserTestSpec({ genClassHeader = "interface Bar" "int foo();" should matchDeclaration(ignoreChildren = true) { - it::isPublic shouldBe true - it::isPrivate shouldBe false - it::isSyntacticallyPublic shouldBe false + it should haveVisibility(V_PUBLIC) + it shouldNot haveExplicitModifier(PUBLIC) + it should haveModifier(PUBLIC) } "public int kk();" should matchDeclaration(ignoreChildren = true) { - it::isPublic shouldBe true - it::isPrivate shouldBe false - it::isSyntacticallyPublic shouldBe true + it should haveVisibility(V_PUBLIC) + it should haveExplicitModifier(PUBLIC) + it should haveModifier(PUBLIC) } "int FOO = 0;" should matchDeclaration(ignoreChildren = true) { - it::isPublic shouldBe true - it::isPrivate shouldBe false - it::isSyntacticallyPublic shouldBe false + it should haveVisibility(V_PUBLIC) + it shouldNot haveExplicitModifier(PUBLIC) + it should haveModifier(PUBLIC) } "public int FOO = 0;" should matchDeclaration(ignoreChildren = true) { - it::isPublic shouldBe true - it::isPrivate shouldBe false - it::isSyntacticallyPublic shouldBe true + it should haveVisibility(V_PUBLIC) + it should haveExplicitModifier(PUBLIC) + it should haveModifier(PUBLIC) } } parserTest("Private methods in interface should be private", J9..Latest) { "private int de() { return 1; }" should matchDeclaration(ignoreChildren = true) { - it::isPublic shouldBe false - it::isPrivate shouldBe true - it::isSyntacticallyPublic shouldBe false + it should haveVisibility(V_PRIVATE) + it shouldNot haveExplicitModifier(PUBLIC) + it shouldNot haveModifier(PUBLIC) + it should haveExplicitModifier(PRIVATE) + it should haveModifier(PRIVATE) } } @@ -53,13 +60,13 @@ class ASTMethodDeclarationTest : ParserTestSpec({ genClassHeader = "interface Bar" "int bar();" should matchDeclaration(ignoreChildren = true) { - it::isDefault shouldBe false - it::isAbstract shouldBe true + it shouldNot haveModifier(DEFAULT) + it should haveModifier(ABSTRACT) } "abstract int bar();" should matchDeclaration(ignoreChildren = true) { - it::isDefault shouldBe false - it::isAbstract shouldBe true + it shouldNot haveModifier(DEFAULT) + it should haveModifier(ABSTRACT) } } @@ -69,8 +76,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ genClassHeader = "interface Bar" "default int kar() { return 1; } " should matchDeclaration(ignoreChildren = true) { - it::isDefault shouldBe true - it::isAbstract shouldBe false + it should haveModifier(DEFAULT) + it shouldNot haveModifier(ABSTRACT) } // default abstract is an invalid combination of modifiers so we won't encounter it in real analysis @@ -85,6 +92,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ it::isVoid shouldBe true it::getArity shouldBe 0 + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() @@ -105,6 +114,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "void bar() throws @Oha IOException, @Aha java.io.@Oha Bar { }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe formalsList(0) @@ -132,11 +143,15 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "void bar(@Oha IOException @Aha ... java) { }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe formalsList(1) { child { - annotation("Oha") + it::getModifiers shouldBe modifiers { + annotation("Oha") + } arrayType { classType("IOException") it::getDimensions shouldBe child { @@ -156,11 +171,15 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "void bar(@Oha IOException []@O[] @Aha ... java) { }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe formalsList(1) { child { - annotation("Oha") + it::getModifiers shouldBe modifiers { + annotation("Oha") + } arrayType { classType("IOException") it::getDimensions shouldBe child { @@ -187,6 +206,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "void bar() [] @O[] { }" should matchDeclaration { + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe formalsList(0) @@ -214,6 +235,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "int bar(Foo f);" shouldNot parse() "public int bar();" should parseAs { annotationMethod { + modifiers { } + resultType { primitiveType(PrimitiveType.INT) } @@ -223,6 +246,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "int bar() default 2;" should parseAs { annotationMethod { + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe resultType { primitiveType(PrimitiveType.INT) } @@ -234,6 +259,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "int bar() @NonZero [];" should parseAs { annotationMethod { + it::getModifiers shouldBe modifiers { } it::getResultType shouldBe resultType { primitiveType(PrimitiveType.INT) } @@ -250,6 +276,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "Override bar() default @Override;" should parseAs { annotationMethod { + it::getModifiers shouldBe modifiers { } it::getResultType shouldBe resultType { classType("Override") } @@ -261,6 +288,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ "Override bar()[] default { @Override };" should parseAs { annotationMethod { + it::getModifiers shouldBe modifiers { } it::getResultType shouldBe resultType { classType("Override") } @@ -294,6 +322,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ // notice that arity is zero it::getArity shouldBe 0 + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe child { @@ -319,6 +349,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({ it::isVoid shouldBe true it::getArity shouldBe 1 + it::getModifiers shouldBe modifiers { } + it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe child { @@ -332,6 +364,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ it::toList shouldBe listOf( child { + localVarModifiers { } primitiveType(PrimitiveType.INT) variableId("other") } @@ -351,7 +384,9 @@ class ASTMethodDeclarationTest : ParserTestSpec({ it::getName shouldBe "bar" - annotation("OnDecl") + it::getModifiers shouldBe modifiers { + annotation("OnDecl") + } it::getTypeParameters shouldBe typeParamList { typeParam("T") { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTStatementsTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTStatementsTest.kt index fd85441346..6f1ad23247 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTStatementsTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTStatementsTest.kt @@ -20,6 +20,8 @@ class ASTStatementsTest : ParserTestSpec({ foreachLoop { it::getVariableId shouldBe fromChild { + it::getModifiers shouldBe modifiers {} + it::getTypeNode shouldBe classType("Integer") fromChild { variableId("i") @@ -43,7 +45,9 @@ class ASTStatementsTest : ParserTestSpec({ foreachLoop { localVarDecl { - annotation("Nullable") + it::getModifiers shouldBe modifiers { + annotation("Nullable") + } classType("Integer") variableDeclarator("i") } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTTryStatementTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTTryStatementTest.kt index 7606035336..fb600eb556 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTTryStatementTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTTryStatementTest.kt @@ -24,7 +24,11 @@ class ASTTryStatementTest : ParserTestSpec({ it::getStableName shouldBe "a" it::getInitializer shouldBe fromChild { - it::isFinal shouldBe false + it::getModifiers shouldBe localVarModifiers { + it::getExplicitModifiers shouldBe emptySet() + it::getEffectiveModifiers shouldBe setOf(JModifier.FINAL) + } + classType("Foo") fromChild { variableId("a") @@ -45,7 +49,10 @@ class ASTTryStatementTest : ParserTestSpec({ it::getStableName shouldBe "a" it::getInitializer shouldBe fromChild { - it::isFinal shouldBe true + it::getModifiers shouldBe localVarModifiers { + it::getExplicitModifiers shouldBe setOf(JModifier.FINAL) + it::getEffectiveModifiers shouldBe setOf(JModifier.FINAL) + } classType("Foo") fromChild { variableId("a") diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt index 53154f42e5..febc2b2404 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt @@ -1,4 +1,5 @@ -import io.kotlintest.shouldBe + +import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.* import net.sourceforge.pmd.lang.java.ast.JavaVersion.* import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest @@ -11,11 +12,10 @@ class Java11Test : ParserTestSpec({ onVersions(J1_8..J10) { "(var x) -> String.valueOf(x)" should matchExpr { - child { - child { - child { - it.image shouldBe "var" - } + it::getParameters shouldBe child { + lambdaParam { + modifiers { } + classType("var") variableId("x") } } @@ -24,18 +24,16 @@ class Java11Test : ParserTestSpec({ } "(var x, var y) -> x + y" should matchExpr { - child { - child { - child { - it.image shouldBe "var" - } + it::getParameters shouldBe child { + lambdaParam { + modifiers { } + classType("var") variableId("x") } - child { - child { - it.image shouldBe "var" - } + lambdaParam { + modifiers { } + classType("var") variableId("y") } } @@ -44,10 +42,12 @@ class Java11Test : ParserTestSpec({ } "(@Nonnull var x) -> String.valueOf(x)" should matchExpr { - child { - child { - annotation("Nonnull") - child(ignoreChildren = true) {} + it::getParameters shouldBe child { + lambdaParam { + modifiers { + annotation("Nonnull") + } + classType("var") variableId("x") } } @@ -58,9 +58,10 @@ class Java11Test : ParserTestSpec({ // var keyword should generate no type after java 11 onVersions(J11..Latest) { "(var x) -> String.valueOf(x)" should matchExpr { - child { - child { - it.isTypeInferred shouldBe true + it::getParameters shouldBe child { + lambdaParam { + modifiers { } + it::isTypeInferred shouldBe true variableId("x") } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaTextAccessTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaTextAccessTest.kt index d66c194fa4..ad71933633 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaTextAccessTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/JavaTextAccessTest.kt @@ -25,6 +25,10 @@ class JavaTextAccessTest : ParserTestSpec({ it.textStr shouldBe "int a = ((3));" + modifiers { + it.textStr shouldBe "" + } + primitiveType(INT) { it.textStr shouldBe "int" } @@ -41,6 +45,10 @@ class JavaTextAccessTest : ParserTestSpec({ it.textStr shouldBe "int a = ((a)).f;" + modifiers { + it.textStr shouldBe "" + } + primitiveType(INT) { it.textStr shouldBe "int" } @@ -62,6 +70,10 @@ class JavaTextAccessTest : ParserTestSpec({ it.textStr shouldBe "int a = ((1 + 2) + f);" + modifiers { + it.textStr shouldBe "" + } + primitiveType(INT) { it.textStr shouldBe "int" } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt index 226fc397a6..2edf369484 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinTestingDsl.kt @@ -4,6 +4,7 @@ import com.github.oowekyala.treeutils.matchers.baseShouldMatchSubtree import com.github.oowekyala.treeutils.printers.KotlintestBeanTreePrinter import io.kotlintest.Matcher import io.kotlintest.Result +import io.kotlintest.matchers.collections.shouldContainAll import io.kotlintest.matchers.string.shouldContain import io.kotlintest.shouldThrow import net.sourceforge.pmd.lang.ast.Node @@ -103,6 +104,13 @@ private val javaImplicitAssertions: Assertions = { } } + if (it is AccessNode) run { + it.modifiers.effectiveModifiers.shouldContainAll(it.modifiers.explicitModifiers) + it.modifiers.effectiveModifiers.shouldContainAtMostOneOf(JModifier.PUBLIC, JModifier.PRIVATE, JModifier.PROTECTED) + it.modifiers.effectiveModifiers.shouldContainAtMostOneOf(JModifier.FINAL, JModifier.ABSTRACT) + it.modifiers.effectiveModifiers.shouldContainAtMostOneOf(JModifier.DEFAULT, JModifier.ABSTRACT) + } + } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParenthesesTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParenthesesTest.kt index cb785c1392..c1bf6d9778 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParenthesesTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParenthesesTest.kt @@ -21,6 +21,7 @@ class ParenthesesTest : ParserTestSpec({ // we use a statement context to avoid the findFirstNodeOnStraightLine skipping parentheses "int a = 3;" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe int(3) { @@ -31,6 +32,7 @@ class ParenthesesTest : ParserTestSpec({ } "int a = (3);" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe int(3) { @@ -41,6 +43,7 @@ class ParenthesesTest : ParserTestSpec({ } "int a = ((3));" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe int(3) { @@ -53,6 +56,7 @@ class ParenthesesTest : ParserTestSpec({ } "int a = ((a)).f;" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe fieldAccess("f") { @@ -69,6 +73,7 @@ class ParenthesesTest : ParserTestSpec({ } } "int a = ((a).f);" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe fieldAccess("f") { @@ -89,6 +94,7 @@ class ParenthesesTest : ParserTestSpec({ // the left parens shouldn't be flattened by AbstractLrBinaryExpr "int a = ((1 + 2) + f);" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe infixExpr(BinaryOp.ADD) { @@ -119,6 +125,7 @@ class ParenthesesTest : ParserTestSpec({ } "int a = (1 + (2 + f));" should matchStmt { + localVarModifiers { } primitiveType(INT) variableDeclarator("a") { it::getInitializer shouldBe infixExpr(BinaryOp.ADD) { 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 df785f7693..cf1d8e5f69 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 @@ -1,9 +1,13 @@ package net.sourceforge.pmd.lang.java.ast import com.github.oowekyala.treeutils.matchers.TreeNodeWrapper +import io.kotlintest.Matcher +import io.kotlintest.Result +import io.kotlintest.matchers.collections.shouldBeEmpty import io.kotlintest.matchers.haveSize import io.kotlintest.matchers.types.shouldBeInstanceOf import io.kotlintest.should +import io.kotlintest.shouldNotBe import net.sourceforge.pmd.lang.ast.GenericToken import net.sourceforge.pmd.lang.ast.Node import net.sourceforge.pmd.lang.ast.test.NodeSpec @@ -15,6 +19,31 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType.* import java.util.* import kotlin.reflect.KCallable +fun > C?.shouldContainAtMostOneOf(vararg expected: T) { + this shouldNotBe null + assert(this!!.intersect(setOf(expected)).size <= 1) { + "$this should contain exactly one of ${expected.toSet()}" + } +} + + +fun haveModifier(mod: JModifier): Matcher = object : Matcher { + override fun test(value: AccessNode): Result = + Result(value.hasModifiers(mod), "Expected $value to have modifier $mod", "Expected $value to not have modifier $mod") +} + +fun haveExplicitModifier(mod: JModifier): Matcher = object : Matcher { + override fun test(value: AccessNode): Result { + return Result(value.hasExplicitModifiers(mod), "Expected $value to have modifier $mod", "Expected $value to not have modifier $mod") + } +} + +fun haveVisibility(vis: AccessNode.Visibility): Matcher = object : Matcher { + override fun test(value: AccessNode): Result = + Result(value.visibility == vis, "Expected $value to have visibility $vis", "Expected $value to not have visibility $vis") +} + + infix fun Optional.shouldBePresent(any: U) { ::isPresent shouldBe true ::get shouldBe any @@ -50,6 +79,19 @@ fun String.addArticle() = when (this[0].toLowerCase()) { } +fun TreeNodeWrapper.modifiers(spec: ValuedNodeSpec = EmptyAssertions) = + child(ignoreChildren = spec == EmptyAssertions, nodeSpec = spec) + +fun TreeNodeWrapper.localVarModifiers(spec: ValuedNodeSpec = EmptyAssertions) = + child(ignoreChildren = spec == EmptyAssertions) { + + // at most "final" + (it.explicitModifiers - JModifier.FINAL).shouldBeEmpty() + (it.effectiveModifiers - JModifier.FINAL).shouldBeEmpty() + + spec() + } + fun TreeNodeWrapper.annotation(spec: ValuedNodeSpec = EmptyAssertions) = child(ignoreChildren = spec == EmptyAssertions, nodeSpec = spec) @@ -89,6 +131,16 @@ fun TreeNodeWrapper.variableId(name: String, otherAssertions: NodeSpec< otherAssertions() } +fun TreeNodeWrapper.simpleLambdaParam(name: String, otherAssertions: NodeSpec = EmptyAssertions) = + child { + it::getModifiers shouldBe modifiers { } + + child(ignoreChildren = otherAssertions == EmptyAssertions) { + it::getVariableName shouldBe name + otherAssertions() + } + } + fun TreeNodeWrapper.variableDeclarator(name: String, spec: NodeSpec = EmptyAssertions) = child { it::getVarId shouldBe variableId(name) @@ -569,6 +621,6 @@ fun TreeNodeWrapper.classDecl(simpleName: String, assertions: NodeSpec< } fun TreeNodeWrapper.typeBody(contents: NodeSpec = EmptyAssertions) = - child { + child(ignoreChildren = contents == EmptyAssertions) { contents() } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt index 6073cd4620..31f411c7e5 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodeExtensions.kt @@ -83,7 +83,7 @@ data class TextRange(val beginPos: TextPosition, val endPos: TextPosition) { // fixme, the end column should be exclusive fun isEmpty(): Boolean = beginPos.line == endPos.line - && beginPos.column - 1 == endPos.column + && beginPos.column == endPos.column fun assertOrdered() { assert(beginPos <= endPos || isEmpty()) { @@ -98,5 +98,6 @@ data class TextRange(val beginPos: TextPosition, val endPos: TextPosition) { operator fun contains(other: TextRange): Boolean = other.beginPos in this && other.endPos in this || this.isEmpty() && other == this + || other.isEmpty() && other.beginPos in this }