Merge branch 'grammar-void-type' into java-grammar

This commit is contained in:
Clément Fournier
2020-08-23 17:55:53 +02:00
15 changed files with 111 additions and 86 deletions

View File

@ -1562,10 +1562,11 @@ void PrimitiveType() :
}
void ResultType() :
void ResultType() #void:
{}
{
"void" | AnnotatedType()
"void" #VoidType
| AnnotatedType()
}
@ -1853,7 +1854,7 @@ void PrimaryPrefix() #void :
| "super" #SuperExpression(true)
("." MemberSelector() | MethodReference())
| UnqualifiedAllocationExpr()
| ("void" "." "class") #ClassLiteral
| ("void" #VoidType "." "class") #ClassLiteral
| LOOKAHEAD(1) // suppress the warning here.
(PrimitiveType() [ Dims() ] ) #ArrayType(>1)
(
@ -2628,7 +2629,7 @@ void AnnotationTypeMemberDeclaration() #void:
void AnnotationMethodDeclaration() #MethodDeclaration:
{}
{
Type() #ResultType
Type()
<IDENTIFIER> { setLastTokenImage(jjtThis); }
("(" ")") #FormalParameters
[ Dims() ]

View File

@ -4,7 +4,7 @@
package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* A class literal. Class literals are {@linkplain ASTPrimaryExpression primary expressions},
@ -12,7 +12,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*
* <pre class="grammar">
*
* ClassLiteral ::= ({@link ASTType Type} | "void") "." "class"
* ClassLiteral ::= {@link ASTType Type} "." "class"
*
* </pre>
*/
@ -26,17 +26,10 @@ public final class ASTClassLiteral extends AbstractJavaExpr implements ASTPrimar
return visitor.visit(this, data);
}
public boolean isVoid() {
return getNumChildren() == 0;
}
/**
* Returns the enclosed type node, or an empty optional if this is void.
* Returns the type node (this may be a {@link ASTVoidType}).
*/
@Nullable
public ASTType getTypeNode() {
return isVoid() ? null : (ASTType) getChild(0);
public @NonNull ASTType getTypeNode() {
return (ASTType) getChild(0);
}
}

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
@ -27,7 +28,7 @@ import net.sourceforge.pmd.lang.rule.xpath.DeprecatedAttribute;
*
* MethodDeclaration ::= {@link ASTModifierList ModifierList}
* {@link ASTTypeParameters TypeParameters}?
* {@link ASTResultType ResultType}
* {@link ASTType Type}
* &lt;IDENTIFIER&gt;
* {@link ASTFormalParameters FormalParameters}
* {@link ASTArrayDimensions ArrayDimensions}?
@ -87,11 +88,9 @@ public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDecla
/**
* 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();
return getResultTypeNode().isVoid();
}
@ -106,11 +105,21 @@ public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDecla
/**
* Returns the result type node of the method.
*
* @deprecated todo When removed from java-grammar, rename the other to this good name
*/
@Deprecated
public ASTResultType getResultType() {
return getFirstChildOfType(ASTResultType.class);
}
/**
* Returns the result type node of the method. This may be a {@link ASTVoidType}.
*/
public @NonNull ASTType getResultTypeNode() {
return getFirstChildOfType(ASTType.class);
}
/**
* Returns the extra array dimensions that may be after the
* formal parameters.

View File

@ -14,7 +14,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* ResultType ::= "void" | {@link ASTType Type}
*
* </pre>
*
* @deprecated This has been replaced by an unwrapped {@link ASTType},
* "void" being represented by {@link ASTVoidType}.
*/
@Deprecated
public final class ASTResultType extends AbstractJavaNode {
ASTResultType(int id) {
@ -41,6 +45,6 @@ public final class ASTResultType extends AbstractJavaNode {
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
throw new UnsupportedOperationException("Node was removed from grammar");
}
}

View File

@ -44,13 +44,17 @@ public interface ASTType extends TypeNode, Annotatable, LeftRecursiveNode {
}
default boolean isPrimitiveType() {
return this instanceof ASTPrimitiveType;
/**
* Returns true if this is the "void" pseudo-type, ie an {@link ASTVoidType}.
*/
default boolean isVoid() {
return this instanceof ASTVoidType;
}
// TODO remove that, there's enough on JTypeMirror
default boolean isReferenceType() {
return !isPrimitiveType();
default boolean isPrimitiveType() {
return this instanceof ASTPrimitiveType;
}

View File

@ -0,0 +1,34 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
/**
* Type node to represent the void pseudo-type. This represents the
* absence of a type, not a type, but it's easier to process that way.
* Can only occur as return type of method declarations, and as the qualifier
* of a {@linkplain ASTClassLiteral class literal}.
*
* <pre class="grammar">
*
* VoidType ::= "void"
*
* </pre>
*/
public final class ASTVoidType extends AbstractJavaTypeNode implements ASTType {
ASTVoidType(int id) {
super(id);
}
@Override
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
@Override
public String getTypeImage() {
return "void";
}
}

View File

@ -184,4 +184,9 @@ public interface JavaParserVisitor extends JavaVisitor<Object, Object> {
return null;
}
@Deprecated
default Object visit(ASTResultType node, Object data) {
return null;
}
}

View File

@ -15,7 +15,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTResultType;
/**
* Signature for an operation.
@ -128,7 +127,7 @@ public final class JavaOperationSignature extends JavaSignature<ASTMethodOrConst
/** Attempts to determine if the method is a getter. */
private static boolean isGetter(ASTMethodDeclaration node, Map<String, String> fieldNames) {
if (node.getArity() != 0 || node.getFirstDescendantOfType(ASTResultType.class).isVoid()) {
if (node.getArity() != 0 || node.isVoid()) {
return false;
}
@ -146,7 +145,7 @@ public final class JavaOperationSignature extends JavaSignature<ASTMethodOrConst
/** Attempts to determine if the method is a setter. */
private static boolean isSetter(ASTMethodDeclaration node, Map<String, String> fieldNames) {
if (node.getArity() != 1 || !node.getFirstDescendantOfType(ASTResultType.class).isVoid()) {
if (node.getArity() != 1 || !node.isVoid()) {
return false;
}

View File

@ -35,6 +35,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression;
import net.sourceforge.pmd.lang.java.ast.ASTResultType;
import net.sourceforge.pmd.lang.java.ast.ASTShiftExpression;
import net.sourceforge.pmd.lang.java.ast.ASTStatement;
import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;

View File

@ -18,7 +18,6 @@ import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTResultType;
import net.sourceforge.pmd.lang.java.ast.ASTType;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule;
@ -107,10 +106,9 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
}
private void checkPrefixedTransformMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
ASTResultType resultType = node.getResultType();
List<String> prefixes = getProperty(TRANSFORM_METHOD_NAMES_PROPERTY);
String[] splitMethodName = StringUtils.splitByCharacterTypeCamelCase(nameOfMethod);
if (resultType.isVoid() && splitMethodName.length > 0
if (node.isVoid() && splitMethodName.length > 0
&& prefixes.contains(splitMethodName[0].toLowerCase(Locale.ROOT))) {
// "To" or any other configured prefix found
addViolationWithMessage(data, node, "Linguistics Antipattern - The transform method ''{0}'' should not return void linguistically",
@ -119,10 +117,9 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
}
private void checkTransformMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
ASTResultType resultType = node.getResultType();
List<String> infixes = getProperty(TRANSFORM_METHOD_NAMES_PROPERTY);
for (String infix : infixes) {
if (resultType.isVoid() && containsWord(nameOfMethod, StringUtils.capitalize(infix))) {
if (node.isVoid() && containsWord(nameOfMethod, StringUtils.capitalize(infix))) {
// "To" or any other configured infix in the middle somewhere
addViolationWithMessage(data, node, "Linguistics Antipattern - The transform method ''{0}'' should not return void linguistically",
new Object[] { nameOfMethod });
@ -133,16 +130,14 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
}
private void checkGetters(ASTMethodDeclaration node, Object data, String nameOfMethod) {
ASTResultType resultType = node.getResultType();
if (hasPrefix(nameOfMethod, "get") && resultType.isVoid()) {
if (hasPrefix(nameOfMethod, "get") && node.isVoid()) {
addViolationWithMessage(data, node, "Linguistics Antipattern - The getter ''{0}'' should not return void linguistically",
new Object[] { nameOfMethod });
}
}
private void checkSetters(ASTMethodDeclaration node, Object data, String nameOfMethod) {
ASTResultType resultType = node.getResultType();
if (hasPrefix(nameOfMethod, "set") && !resultType.isVoid()) {
if (hasPrefix(nameOfMethod, "set") && !node.isVoid()) {
addViolationWithMessage(data, node, "Linguistics Antipattern - The setter ''{0}'' should not return any type except void linguistically",
new Object[] { nameOfMethod });
}
@ -155,9 +150,8 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
}
private void checkBooleanMethods(ASTMethodDeclaration node, Object data, String nameOfMethod) {
ASTResultType resultType = node.getResultType();
ASTType t = node.getResultType().getFirstChildOfType(ASTType.class);
if (!resultType.isVoid() && t != null) {
ASTType t = node.getResultTypeNode();
if (!t.isVoid()) {
for (String prefix : getProperty(BOOLEAN_METHOD_PREFIXES_PROPERTY)) {
if (hasPrefix(nameOfMethod, prefix) && !isBooleanType(t)) {
addViolationWithMessage(data, node, "Linguistics Antipattern - The method ''{0}'' indicates linguistically it returns a boolean, but it returns ''{1}''",
@ -187,7 +181,7 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
@Override
public Object visit(ASTFieldDeclaration node, Object data) {
ASTType type = node.getFirstChildOfType(ASTType.class);
ASTType type = node.getTypeNode();
if (type != null && getProperty(CHECK_FIELDS)) {
List<ASTVariableDeclarator> fields = node.findChildrenOfType(ASTVariableDeclarator.class);
for (ASTVariableDeclarator field : fields) {
@ -199,7 +193,7 @@ public class LinguisticNamingRule extends AbstractIgnoredAnnotationRule {
@Override
public Object visit(ASTLocalVariableDeclaration node, Object data) {
ASTType type = node.getFirstChildOfType(ASTType.class);
ASTType type = node.getTypeNode();
if (type != null && getProperty(CHECK_VARIABLES)) {
List<ASTVariableDeclarator> variables = node.findChildrenOfType(ASTVariableDeclarator.class);
for (ASTVariableDeclarator variable : variables) {

View File

@ -16,7 +16,7 @@ public class UnnecessaryReturnRule extends AbstractJavaRule {
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
if (node.getResultType().isVoid()) {
if (node.isVoid()) {
super.visit(node, data);
}
return data;

View File

@ -27,7 +27,7 @@ public class SingletonClassReturningNewInstanceRule extends AbstractJavaRule {
String localVarName = null;
String returnVariableName = null;
if (node.getResultType().isVoid()) {
if (node.isVoid()) {
return super.visit(node, data);
}

View File

@ -19,7 +19,7 @@ class ASTClassLiteralTest : ParserTestSpec({
"void.class" should parseAs {
classLiteral { null }
classLiteral { voidType() }
}

View File

@ -151,7 +151,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(0)
@ -178,7 +178,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(0)
@ -212,7 +212,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(1) {
child<ASTFormalParameter> {
@ -243,7 +243,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(1) {
child<ASTFormalParameter> {
@ -282,7 +282,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(0)
@ -313,9 +313,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({
annotationMethod {
modifiers { }
resultType {
primitiveType(PrimitiveType.INT)
}
it::getResultTypeNode shouldBe primitiveType(PrimitiveType.INT)
formalsList(0)
}
}
@ -324,9 +323,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({
annotationMethod {
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe resultType {
primitiveType(PrimitiveType.INT)
}
it::getResultTypeNode shouldBe primitiveType(PrimitiveType.INT)
it::getFormalParameters shouldBe formalsList(0)
it::getDefaultClause shouldBe defaultValue { int(2) }
@ -336,9 +334,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({
"int bar() @NonZero [];" should parseAs {
annotationMethod {
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe resultType {
primitiveType(PrimitiveType.INT)
}
it::getResultTypeNode shouldBe primitiveType(PrimitiveType.INT)
it::getFormalParameters shouldBe formalsList(0)
it::getDefaultClause shouldBe null
@ -353,9 +350,8 @@ class ASTMethodDeclarationTest : ParserTestSpec({
"Override bar() default @Override;" should parseAs {
annotationMethod {
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe resultType {
classType("Override")
}
it::getResultTypeNode shouldBe classType("Override")
it::getFormalParameters shouldBe formalsList(0)
it::getDefaultClause shouldBe defaultValue { annotation("Override") }
@ -365,9 +361,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
"Override bar()[] default { @Override };" should parseAs {
annotationMethod {
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe resultType {
classType("Override")
}
it::getResultTypeNode shouldBe classType("Override")
it::getFormalParameters shouldBe formalsList(0)
it::getExtraDimensions shouldBe child {
@ -401,7 +395,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(0) {
it.toList() shouldBe emptyList()
@ -429,7 +423,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({
it::getModifiers shouldBe modifiers { }
it::getResultType shouldBe voidResult()
it::getResultTypeNode shouldBe voidType()
it::getFormalParameters shouldBe formalsList(1) {
@ -473,12 +467,11 @@ class ASTMethodDeclarationTest : ParserTestSpec({
}
}
it::getResultType shouldBe resultType {
classType("Ret") {
annotation("OnType")
}
it::getResultTypeNode shouldBe classType("Ret") {
annotation("OnType")
}
formalsList(0)
block()
}

View File

@ -225,18 +225,6 @@ fun TreeNodeWrapper<Node, *>.lambdaFormals(size: Int? = null, contents: NodeSpec
fun TreeNodeWrapper<Node, *>.formalsList(arity: Int, contents: NodeSpec<ASTFormalParameters> = EmptyAssertions) =
childW(listSpec(arity, contents))
fun TreeNodeWrapper<Node, *>.voidResult() =
child<ASTResultType> {
it::getTypeNode shouldBe null
it::isVoid shouldBe true
}
fun TreeNodeWrapper<Node, *>.resultType(contents: ValuedNodeSpec<ASTResultType, ASTType>) =
child<ASTResultType>(ignoreChildren = contents == EmptyAssertions) {
it::getTypeNode shouldBe contents()
it::isVoid shouldBe false
}
fun TreeNodeWrapper<Node, *>.defaultValue(contents: ValuedNodeSpec<ASTDefaultValue, ASTMemberValue>) =
child<ASTDefaultValue>(ignoreChildren = contents == EmptyAssertions) {
it::getConstant shouldBe contents()
@ -406,6 +394,8 @@ fun TreeNodeWrapper<Node, *>.unionType(contents: NodeSpec<ASTUnionType> = EmptyA
contents()
}
fun TreeNodeWrapper<Node, *>.voidType() = child<ASTVoidType>() {}
fun TreeNodeWrapper<Node, *>.typeExpr(contents: ValuedNodeSpec<ASTTypeExpression, ASTType>) =
child<ASTTypeExpression>(ignoreChildren = contents == EmptyAssertions) {
@ -470,10 +460,8 @@ fun TreeNodeWrapper<Node, *>.textBlock(contents: NodeSpec<ASTStringLiteral> = Em
contents()
}
fun TreeNodeWrapper<Node, *>.classLiteral(contents: ValuedNodeSpec<ASTClassLiteral, ASTType?>) =
fun TreeNodeWrapper<Node, *>.classLiteral(contents: ValuedNodeSpec<ASTClassLiteral, ASTType>) =
child<ASTClassLiteral> {
val tn = it.typeNode
it::isVoid shouldBe (tn == null)
it::getTypeNode shouldBe contents()
}