From db1344d5aa010305a813b2f06860f22da02d9d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 20 Jul 2019 13:08:49 +0200 Subject: [PATCH] Separate catch formal from method formal --- pmd-java/etc/grammar/Java.jjt | 20 ++++- .../pmd/lang/java/ast/ASTCatchClause.java | 21 ++++- .../pmd/lang/java/ast/ASTCatchParameter.java | 80 +++++++++++++++++++ .../lang/java/ast/ASTIntersectionType.java | 15 +++- .../pmd/lang/java/ast/ASTResourceList.java | 13 ++- .../pmd/lang/java/ast/ASTTryStatement.java | 9 +-- .../pmd/lang/java/ast/ASTType.java | 14 ++++ .../pmd/lang/java/ast/ASTUnionType.java | 67 ++++++++++++++++ 8 files changed, 227 insertions(+), 12 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchParameter.java create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnionType.java diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index c938b8aed5..7573447fca 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -2980,10 +2980,28 @@ void CatchClause() : {} { "catch" - "(" FormalParameter() ")" + "(" CatchParameter() ")" Block() } + +void CatchParameter() : +{boolean isFinal = false;} +{ + isFinal=LocalVarModifierList() UnionType() VariableDeclaratorId() +} + +// Special type for catch formal parameters +// Eg `IOException | ParseException` +void UnionType() #UnionType(isUnion): +{boolean isUnion=false;} +{ + // Annotations of the first class type belong to the + // catch parameter (the variable) because of syntactic ambiguity + // This is similar to how a local var type works. + ClassOrInterfaceType() ( "|" {isUnion=true; checkForBadMultipleExceptionsCatching();} AnnotatedClassOrInterfaceType() )* +} + void FinallyClause() : {} { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchClause.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchClause.java index 880a73a2f2..4045a7b411 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchClause.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchClause.java @@ -49,15 +49,28 @@ public final class ASTCatchClause extends AbstractJavaNode { * @return True if this node is a multi-catch statement */ public boolean isMulticatchStatement() { - return getCaughtExceptionTypeNodes().size() > 1; // the list is parsed multiple times... + return getFormal().isMultiCatch(); } + /** + * Returns the {@linkplain ASTCatchParameter CatchParameter} node. + */ + public ASTCatchParameter getFormal() { + return (ASTCatchParameter) jjtGetChild(0); + } + + /** + * Returns the ID of the declared variable. + */ + public ASTVariableDeclaratorId getVariableId() { + return getFormal().getVariableId(); + } /** * Returns the Block node of this catch branch. */ public ASTBlock getBlock() { - return getFirstChildOfType(ASTBlock.class); + return (ASTBlock) getLastChild(); } /** @@ -67,7 +80,7 @@ public final class ASTCatchClause extends AbstractJavaNode { */ public List getCaughtExceptionTypeNodes() { // maybe cache the list - return getFirstChildOfType(ASTFormalParameter.class).findChildrenOfType(ASTType.class); + return getFormal().getTypeNode().asList(); } @@ -89,7 +102,7 @@ public final class ASTCatchClause extends AbstractJavaNode { * Returns exception name caught by this catch block. */ public String getExceptionName() { - return getFirstDescendantOfType(ASTVariableDeclaratorId.class).getImage(); + return getVariableId().getVariableName(); } } 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 new file mode 100644 index 0000000000..6926a19214 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCatchParameter.java @@ -0,0 +1,80 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import net.sourceforge.pmd.annotation.InternalApi; + + +/** + * Formal parameter of a {@linkplain ASTCatchStatement catch statement}. + * The type node may be a {@link ASTUnionType union type}, which represents + * multi-catch clauses. + * + * TODO warning suppression + * + *
+ *
+ * CatchParameter ::= ( "final" | {@link ASTAnnotation Annotation} )* {@link ASTType Type} {@link ASTVariableDeclaratorId VariableDeclaratorId}
+ *
+ * 
+ */ +public class ASTCatchParameter extends AbstractJavaTypeNode implements Annotatable { + + private boolean isFinal; + + @InternalApi + @Deprecated + public ASTCatchParameter(int id) { + super(id); + } + + ASTCatchParameter(JavaParser p, int id) { + super(p, id); + } + + + public boolean isFinal() { + return isFinal; + } + + void setFinal(boolean f) { + isFinal = f; + } + + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + + @Override + public void jjtAccept(SideEffectingVisitor visitor, T data) { + visitor.visit(this, data); + } + + + /** + * Returns the declarator ID of this catch parameter. + */ + public ASTVariableDeclaratorId getVariableId() { + return (ASTVariableDeclaratorId) getLastChild(); + } + + /** + * Returns the type node of this formal parameter. This may be + * a {@linkplain ASTUnionType union type}. + */ + public ASTType getTypeNode() { + return getFirstChildOfType(ASTType.class); + } + + /** + * Returns true if this is a multi-catch node. + */ + public boolean isMultiCatch() { + return getTypeNode() instanceof ASTUnionType; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIntersectionType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIntersectionType.java index 5cf6612494..815abbb54e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIntersectionType.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIntersectionType.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.java.ast; import java.util.Iterator; +import java.util.List; /** @@ -24,7 +25,7 @@ import java.util.Iterator; * * */ -public final class ASTIntersectionType extends AbstractJavaTypeNode implements ASTReferenceType, Iterable { +public final class ASTIntersectionType extends AbstractJavaTypeNode implements ASTReferenceType, JSingleChildNode, Iterable { ASTIntersectionType(int id) { super(id); @@ -43,6 +44,18 @@ public final class ASTIntersectionType extends AbstractJavaTypeNode implements A } + @Override + public List asList() { + return findChildrenOfType(ASTType.class); + } + + + @Override + public ASTType jjtGetChild(int index) { + return (ASTType) super.jjtGetChild(index); + } + + @Override public Object jjtAccept(JavaParserVisitor visitor, Object data) { return visitor.visit(this, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResourceList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResourceList.java index a7cf923558..380acf1231 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResourceList.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTResourceList.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.java.ast; import java.util.Iterator; +import java.util.List; /** * A list of resources in a {@linkplain ASTTryStatement try-with-resources}. @@ -15,7 +16,7 @@ import java.util.Iterator; * * */ -public final class ASTResourceList extends AbstractJavaNode implements Iterable { +public final class ASTResourceList extends AbstractJavaNode implements Iterable, JSingleChildNode { private boolean trailingSemi; @@ -38,6 +39,11 @@ public final class ASTResourceList extends AbstractJavaNode implements Iterable< visitor.visit(this, data); } + @Override + public ASTResource jjtGetChild(int index) { + return (ASTResource) super.jjtGetChild(index); + } + void setTrailingSemi() { this.trailingSemi = true; } @@ -54,4 +60,9 @@ public final class ASTResourceList extends AbstractJavaNode implements Iterable< public Iterator iterator() { return children(ASTResource.class).iterator(); } + + public List asList() { + return findChildrenOfType(ASTResource.class); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java index 5c41f95404..73de58f680 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java @@ -9,8 +9,6 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.internal.util.IteratorUtil; - /** * Try statement node. @@ -57,13 +55,13 @@ public final class ASTTryStatement extends AbstractStatement { } @Nullable - public ASTResourceList getResourceList() { + public ASTResourceList getResourceListNode() { return AstImplUtil.getChildAs(this, 0, ASTResourceList.class); } public List getResources() { - ASTResourceList list = getResourceList(); - return list == null ? Collections.emptyList() : IteratorUtil.toList(list.iterator()); + ASTResourceList list = getResourceListNode(); + return list == null ? Collections.emptyList() : list.asList(); } @@ -88,6 +86,7 @@ public final class ASTTryStatement extends AbstractStatement { * * @return The finally statement, or null if there is none */ + @Nullable public ASTFinallyClause getFinallyClause() { return getFirstChildOfType(ASTFinallyClause.class); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java index 0b607c38e6..9681db678c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd.lang.java.ast; +import java.util.Collections; +import java.util.List; + import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.Experimental; @@ -71,6 +74,17 @@ public interface ASTType extends TypeNode, Annotatable { } + /** + * Returns a read-only list of the components of this type. + * Returns a singleton containing this type if this is neither + * a {@linkplain ASTUnionType union type} or a {@linkplain ASTIntersectionType intersection type}. + * In those cases, returns the list of components. + */ + default List asList() { + return Collections.singletonList(this); + } + + default boolean isClassOrInterfaceType() { return this instanceof ASTClassOrInterfaceType; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnionType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnionType.java new file mode 100644 index 0000000000..9ed1a467f0 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTUnionType.java @@ -0,0 +1,67 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import java.util.Iterator; +import java.util.List; + + +/** + * Represents the type node of a multi-catch statement. This node is used + * to make the grammar of {@link ASTCatchStatement CatchStatement} more + * straightforward. Note though, that the Java type system does not feature + * union types per se. The type of this node is defined as the least upper-bound + * of all its components. + * + *
+ *
+ * UnionType ::= {@link ASTClassOrInterfaceType ClassType} ("|" {@link ASTClassOrInterfaceType ClassType})+
+ *
+ * 
+ */ +public final class ASTUnionType extends AbstractJavaTypeNode implements ASTReferenceType, JSingleChildNode, Iterable { + + ASTUnionType(int id) { + super(id); + } + + + ASTUnionType(JavaParser p, int id) { + super(p, id); + } + + + @Override + public String getTypeImage() { + // TODO + return iterator().next().getTypeImage(); + } + + @Override + public List asList() { + return findChildrenOfType(ASTType.class); + } + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + + @Override + public void jjtAccept(SideEffectingVisitor visitor, T data) { + visitor.visit(this, data); + } + + @Override + public Iterator iterator() { + return new NodeChildrenIterator<>(this, ASTClassOrInterfaceType.class); + } + + @Override + public ASTClassOrInterfaceType jjtGetChild(int index) { + return (ASTClassOrInterfaceType) super.jjtGetChild(index); + } +}