Cleanup
This commit is contained in:
@ -1802,6 +1802,7 @@ void FormalParameter() :
|
||||
VariableDeclaratorId()
|
||||
}
|
||||
|
||||
|
||||
void ConstructorDeclaration(int modifiers) :
|
||||
{jjtThis.setModifiers(modifiers);
|
||||
JavaccToken t;}
|
||||
@ -1927,15 +1928,10 @@ void AnnotatedClassOrInterfaceType() #void:
|
||||
* See https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#jls-UnannType
|
||||
*/
|
||||
void Type() #void:
|
||||
{}
|
||||
{
|
||||
JavaccToken t;
|
||||
}
|
||||
{
|
||||
// lookahead to catch arrays of primitive types.
|
||||
// we can't lookahead for just PrimitiveType() "["
|
||||
// because the "[" may be preceded by annotations
|
||||
LOOKAHEAD(PrimitiveType() ArrayTypeDim() | <IDENTIFIER>) ReferenceType()
|
||||
| PrimitiveType()
|
||||
PrimitiveType() [ LOOKAHEAD(2) Dims() #ArrayType(2) ]
|
||||
| ClassOrInterfaceType() [ LOOKAHEAD(2) Dims() #ArrayType(2) ]
|
||||
}
|
||||
|
||||
void Dims() #ArrayDimensions:
|
||||
@ -2522,7 +2518,6 @@ void LambdaParameterList():
|
||||
void LambdaParameter():
|
||||
{
|
||||
boolean isVarType = false;
|
||||
ASTLambdaParameter me = jjtThis;
|
||||
boolean isFinal = false;
|
||||
}
|
||||
{
|
||||
@ -2985,22 +2980,12 @@ void CatchClause() :
|
||||
}
|
||||
|
||||
|
||||
void CatchParameter() :
|
||||
{boolean isFinal = false;}
|
||||
void CatchParameter():
|
||||
{boolean isFinal = false; boolean multi=false;}
|
||||
{
|
||||
isFinal=LocalVarModifierList() UnionType() VariableDeclaratorId()
|
||||
{jjtThis.setFinal(isFinal);}
|
||||
}
|
||||
|
||||
// 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;} AnnotatedClassOrInterfaceType() )*
|
||||
isFinal=LocalVarModifierList() {jjtThis.setFinal(isFinal);}
|
||||
( AnnotatedClassOrInterfaceType() ( "|" AnnotatedClassOrInterfaceType() {multi=true;} )* ) #UnionType(multi)
|
||||
VariableDeclaratorId()
|
||||
}
|
||||
|
||||
void FinallyClause() :
|
||||
|
@ -4,17 +4,12 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* A "catch" clause of a {@linkplain ASTTryStatement try statement}.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
* CatchClause ::= "catch" "(" {@link ASTFormalParameter FormalParameter} ")" {@link ASTBlock Block}
|
||||
* CatchClause ::= "catch" "(" {@link ASTCatchParameter CatchParameter} ")" {@link ASTBlock Block}
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
@ -39,73 +34,15 @@ public final class ASTCatchClause extends AbstractJavaNode {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this node is a multi-catch statement,
|
||||
* that is, it catches several unrelated exception types
|
||||
* at the same time. Such a block can be declared like the
|
||||
* following for example:
|
||||
*
|
||||
* <p>{@code catch (IllegalStateException | IllegalArgumentException e) {}}
|
||||
*
|
||||
* @return True if this node is a multi-catch statement
|
||||
*/
|
||||
public boolean isMulticatchStatement() {
|
||||
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 (ASTBlock) getLastChild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of type nodes denoting the exception types
|
||||
* caught by this catch block. The returned list has at least
|
||||
* one element.
|
||||
*/
|
||||
public List<ASTType> getCaughtExceptionTypeNodes() {
|
||||
ASTType typeNode = getFormal().getTypeNode();
|
||||
return typeNode instanceof ASTUnionType
|
||||
? typeNode.findChildrenOfType(ASTType.class)
|
||||
: Collections.singletonList(typeNode);
|
||||
/** Returns the catch parameter. */
|
||||
public ASTCatchParameter getParameter() {
|
||||
return (ASTCatchParameter) getFirstChild();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the list of exception types caught by this catch block.
|
||||
* Any of these can be null, if they couldn't be resolved. This can
|
||||
* happen if the auxclasspath is not correctly set.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Class<? extends Exception>> getCaughtExceptionTypes() {
|
||||
List<Class<? extends Exception>> result = new ArrayList<>();
|
||||
for (ASTType type : getCaughtExceptionTypeNodes()) {
|
||||
result.add((Class<? extends Exception>) type.getType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns exception name caught by this catch block.
|
||||
*/
|
||||
public String getExceptionName() {
|
||||
return getVariableId().getVariableName();
|
||||
/** Returns the body of this catch branch. */
|
||||
public ASTBlock getBody() {
|
||||
return getFirstChildOfType(ASTBlock.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
/*
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
|
||||
/**
|
||||
* Formal parameter of a {@linkplain ASTCatchStatement catch statement}.
|
||||
* The type node may be a {@link ASTUnionType union type}, which represents
|
||||
* multi-catch clauses.
|
||||
* Formal parameter of a {@linkplain ASTCatchClause catch clause}
|
||||
* to represent the declared exception variable.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
@ -15,7 +17,7 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public class ASTCatchParameter extends AbstractJavaTypeNode implements Annotatable {
|
||||
public class ASTCatchParameter extends AbstractJavaNode implements InternalInterfaces.VariableIdOwner {
|
||||
|
||||
private boolean isFinal;
|
||||
|
||||
@ -28,14 +30,6 @@ public class ASTCatchParameter extends AbstractJavaTypeNode implements Annotatab
|
||||
}
|
||||
|
||||
|
||||
public boolean isFinal() {
|
||||
return isFinal;
|
||||
}
|
||||
|
||||
void setFinal(boolean f) {
|
||||
isFinal = f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
|
||||
return visitor.visit(this, data);
|
||||
@ -47,28 +41,43 @@ public class ASTCatchParameter extends AbstractJavaTypeNode implements Annotatab
|
||||
visitor.visit(this, data);
|
||||
}
|
||||
|
||||
/** Returns the name of the variable. */
|
||||
public String getName() {
|
||||
return getVariableId().getVariableName();
|
||||
public boolean isFinal() {
|
||||
return isFinal;
|
||||
}
|
||||
|
||||
/** Returns the declarator ID of this catch parameter. */
|
||||
public ASTVariableDeclaratorId getVariableId() {
|
||||
void setFinal(boolean aFinal) {
|
||||
isFinal = aFinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a multi-catch parameter,
|
||||
* that is, it catches several unrelated exception types
|
||||
* at the same time. For example:
|
||||
*
|
||||
* <pre>catch (IllegalStateException | IllegalArgumentException e) {}</pre>
|
||||
*/
|
||||
public boolean isMulticatch() {
|
||||
return getTypeNode() instanceof ASTUnionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ASTVariableDeclaratorId getVarId() {
|
||||
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 the name of this parameter. */
|
||||
public String getName() {
|
||||
return getVarId().getVariableName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this is a multi-catch node.
|
||||
* Returns the type node of this catch parameter. May be a
|
||||
* {@link ASTUnionType UnionType}.
|
||||
*/
|
||||
public boolean isMultiCatch() {
|
||||
return getTypeNode() instanceof ASTUnionType;
|
||||
public ASTType getTypeNode() {
|
||||
return children(ASTType.class).first();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin
|
||||
* FormalParameter ::= ( "final" | {@link ASTAnnotation Annotation} )* {@link ASTType Type} [ "..." ] {@link ASTVariableDeclaratorId VariableDeclaratorId}
|
||||
* </pre>
|
||||
*/
|
||||
public class ASTFormalParameter extends AbstractJavaAccessTypeNode implements Dimensionable, Annotatable {
|
||||
public final class ASTFormalParameter extends AbstractJavaAccessTypeNode implements Dimensionable, Annotatable {
|
||||
|
||||
private boolean isVarargs;
|
||||
|
||||
|
@ -24,7 +24,10 @@ import java.util.Iterator;
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public final class ASTIntersectionType extends AbstractJavaTypeNode implements ASTReferenceType, JSingleChildNode<ASTType>, Iterable<ASTType> {
|
||||
public final class ASTIntersectionType extends AbstractJavaTypeNode
|
||||
implements ASTReferenceType,
|
||||
InternalInterfaces.AtLeastOneChildOfType<ASTType>,
|
||||
Iterable<ASTType> {
|
||||
|
||||
ASTIntersectionType(int id) {
|
||||
super(id);
|
||||
|
@ -16,7 +16,9 @@ import java.util.List;
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public final class ASTResourceList extends AbstractJavaNode implements Iterable<ASTResource>, JSingleChildNode<ASTResource> {
|
||||
public final class ASTResourceList extends AbstractJavaNode
|
||||
implements Iterable<ASTResource>,
|
||||
InternalInterfaces.AtLeastOneChildOfType<ASTResource> {
|
||||
|
||||
private boolean trailingSemi;
|
||||
|
||||
|
@ -69,7 +69,7 @@ public final class ASTTryStatement extends AbstractStatement {
|
||||
* Returns the body of this try statement.
|
||||
*/
|
||||
public ASTBlock getBody() {
|
||||
return (ASTBlock) jjtGetChild(1);
|
||||
return children(ASTBlock.class).first();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,9 +4,6 @@
|
||||
|
||||
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;
|
||||
|
@ -4,14 +4,19 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.AtLeastOneChildOfType;
|
||||
|
||||
|
||||
/**
|
||||
* Represents the type node of a multi-catch statement. This node is used
|
||||
* to make the grammar of {@link ASTCatchStatement CatchStatement} more
|
||||
* to make the grammar of {@link ASTCatchParameter CatchParameter} 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
|
||||
* union types at all. The type of this node is defined as the least upper-bound
|
||||
* of all its components.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
@ -20,7 +25,10 @@ import java.util.Iterator;
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
public final class ASTUnionType extends AbstractJavaTypeNode implements ASTReferenceType, JSingleChildNode<ASTClassOrInterfaceType>, Iterable<ASTClassOrInterfaceType> {
|
||||
public final class ASTUnionType extends AbstractJavaTypeNode
|
||||
implements ASTReferenceType,
|
||||
AtLeastOneChildOfType<ASTClassOrInterfaceType>,
|
||||
Iterable<ASTClassOrInterfaceType> {
|
||||
|
||||
ASTUnionType(int id) {
|
||||
super(id);
|
||||
@ -34,8 +42,7 @@ public final class ASTUnionType extends AbstractJavaTypeNode implements ASTRefer
|
||||
|
||||
@Override
|
||||
public String getTypeImage() {
|
||||
// TODO
|
||||
return iterator().next().getTypeImage();
|
||||
return children(ASTClassOrInterfaceType.class).toStream().map(ASTClassOrInterfaceType::getTypeImage).collect(Collectors.joining(" | "));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,9 +56,17 @@ public final class ASTUnionType extends AbstractJavaTypeNode implements ASTRefer
|
||||
visitor.visit(this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of component types.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public List<ASTClassOrInterfaceType> getComponents() {
|
||||
return (List<ASTClassOrInterfaceType>) (List) Arrays.asList(children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ASTClassOrInterfaceType> iterator() {
|
||||
return new NodeChildrenIterator<>(this, ASTClassOrInterfaceType.class);
|
||||
return children(ASTClassOrInterfaceType.class).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,4 +92,38 @@ final class InternalInterfaces {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tags a node that has at least one child, then some methods never
|
||||
* return null.
|
||||
*/
|
||||
interface AtLeastOneChildOfType<T extends JavaNode> extends JavaNode {
|
||||
|
||||
@Override
|
||||
T jjtGetChild(int index);
|
||||
|
||||
|
||||
/** Returns the first child of this node, never null. */
|
||||
@Override
|
||||
@NonNull
|
||||
default T getFirstChild() {
|
||||
assert jjtGetNumChildren() > 0;
|
||||
return jjtGetChild(0);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the last child of this node, never null. */
|
||||
@Override
|
||||
@NonNull
|
||||
default T getLastChild() {
|
||||
assert jjtGetNumChildren() > 0;
|
||||
return jjtGetChild(jjtGetNumChildren() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
interface VariableIdOwner extends JavaNode {
|
||||
|
||||
/** Returns the id of the declared variable. */
|
||||
ASTVariableDeclaratorId getVarId();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ public class LanguageLevelChecker<T> {
|
||||
|
||||
@Override
|
||||
public void visit(ASTCatchClause node, T data) {
|
||||
if (node.isMulticatchStatement()) {
|
||||
if (node.getParameter().isMulticatch()) {
|
||||
check(node, RegularLanguageFeature.COMPOSITE_CATCH_CLAUSES, data);
|
||||
}
|
||||
visitChildren(node, data);
|
||||
|
@ -16,7 +16,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTType;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
|
||||
|
||||
@ -30,7 +29,7 @@ public class IdenticalCatchBranchesRule extends AbstractJavaRule {
|
||||
|
||||
|
||||
private boolean areEquivalent(ASTCatchClause st1, ASTCatchClause st2) {
|
||||
return hasSameSubTree(st1.getBlock(), st2.getBlock(), st1.getExceptionName(), st2.getExceptionName());
|
||||
return hasSameSubTree(st1.getBody(), st2.getBody(), st1.getParameter().getName(), st2.getParameter().getName());
|
||||
}
|
||||
|
||||
|
||||
@ -71,17 +70,7 @@ public class IdenticalCatchBranchesRule extends AbstractJavaRule {
|
||||
|
||||
// Gets the representation of the set of catch statements as a single multicatch
|
||||
private String getCaughtExceptionsAsString(ASTCatchClause stmt) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
final String delim = " | ";
|
||||
for (ASTType type : stmt.getCaughtExceptionTypeNodes()) {
|
||||
sb.append(type.getTypeImage()).append(delim);
|
||||
}
|
||||
|
||||
// remove the last delimiter
|
||||
sb.replace(sb.length() - 3, sb.length(), "");
|
||||
return sb.toString();
|
||||
return stmt.getParameter().getTypeNode().getTypeImage();
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,22 +22,15 @@ class ASTCatchClauseTest : ParserTestSpec({
|
||||
importedTypes += IOException::class.java
|
||||
|
||||
"try { } catch (IOException ioe) { }" should matchStmt<ASTTryStatement> {
|
||||
block()
|
||||
it::getBody shouldBe block { }
|
||||
catchClause("ioe") {
|
||||
it::isMulticatchStatement shouldBe false
|
||||
it::getExceptionName shouldBe "ioe"
|
||||
|
||||
var types: List<ASTClassOrInterfaceType>? = null
|
||||
|
||||
catchFormal("ioe") {
|
||||
types = listOf(classType("IOException"))
|
||||
it::isMulticatch shouldBe false
|
||||
it::getTypeNode shouldBe classType("IOException")
|
||||
|
||||
variableId("ioe")
|
||||
}
|
||||
|
||||
block()
|
||||
|
||||
it::getCaughtExceptionTypeNodes shouldBe types!!
|
||||
it::getBody shouldBe block { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,26 +41,49 @@ class ASTCatchClauseTest : ParserTestSpec({
|
||||
importedTypes += IOException::class.java
|
||||
|
||||
"try { } catch (IOException | AssertionError e) { }" should matchStmt<ASTTryStatement> {
|
||||
block()
|
||||
it::getBody shouldBe block { }
|
||||
catchClause("e") {
|
||||
it::isMulticatchStatement shouldBe true
|
||||
it::getExceptionName shouldBe "e"
|
||||
|
||||
var types: List<ASTClassOrInterfaceType>? = null
|
||||
|
||||
catchFormal("e") {
|
||||
unionType {
|
||||
val t1 = classType("IOException")
|
||||
val t2 = classType("AssertionError")
|
||||
it::isMulticatch shouldBe true
|
||||
|
||||
types = listOf(t1, t2)
|
||||
it::getTypeNode shouldBe unionType {
|
||||
classType("IOException")
|
||||
classType("AssertionError")
|
||||
}
|
||||
|
||||
variableId("e")
|
||||
}
|
||||
|
||||
block()
|
||||
|
||||
it::getCaughtExceptionTypeNodes shouldBe types!!
|
||||
it::getBody shouldBe block { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parserTest("Test annotated multicatch", javaVersions = J1_8..Latest) {
|
||||
|
||||
importedTypes += IOException::class.java
|
||||
|
||||
"try { } catch (@B IOException | @A AssertionError e) { }" should matchStmt<ASTTryStatement> {
|
||||
it::getBody shouldBe block { }
|
||||
catchClause("e") {
|
||||
catchFormal("e") {
|
||||
it::isMulticatch shouldBe true
|
||||
|
||||
annotation("B") // not a type annotation
|
||||
|
||||
unionType {
|
||||
classType("IOException")
|
||||
classType("AssertionError") {
|
||||
annotation("A")
|
||||
}
|
||||
}
|
||||
|
||||
variableId("e")
|
||||
}
|
||||
|
||||
block {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,9 +56,9 @@ fun TreeNodeWrapper<Node, *>.annotation(name: String, spec: NodeSpec<ASTAnnotati
|
||||
spec()
|
||||
}
|
||||
|
||||
fun TreeNodeWrapper<Node, *>.catchClause(name:String, spec: NodeSpec<ASTCatchClause> = EmptyAssertions) =
|
||||
fun TreeNodeWrapper<Node, *>.catchClause(name: String, spec: NodeSpec<ASTCatchClause> = EmptyAssertions) =
|
||||
child<ASTCatchClause> {
|
||||
it::getExceptionName shouldBe name
|
||||
it.parameter::getName shouldBe name
|
||||
spec()
|
||||
}
|
||||
|
||||
@ -280,6 +280,7 @@ fun TreeNodeWrapper<Node, *>.classType(simpleName: String, contents: NodeSpec<AS
|
||||
it::getSimpleName shouldBe simpleName
|
||||
contents()
|
||||
}
|
||||
|
||||
fun TreeNodeWrapper<Node, *>.unionType(contents: NodeSpec<ASTUnionType> = EmptyAssertions) =
|
||||
child<ASTUnionType>(ignoreChildren = contents == EmptyAssertions) {
|
||||
contents()
|
||||
|
Reference in New Issue
Block a user