Fix parsing problem with guarded patterns
This commit is contained in:
@ -1717,8 +1717,8 @@ void GuardedPatternCondition() #void:
|
||||
void PrimaryPattern() #void:
|
||||
{}
|
||||
{
|
||||
TypePattern()
|
||||
| "(" Pattern() ")" { AstImplUtil.bumpParenDepth((ASTPattern) jjtree.peekNode()); }
|
||||
"(" Pattern() ")" { AstImplUtil.bumpParenDepth((ASTPattern) jjtree.peekNode()); }
|
||||
| LOOKAHEAD({true}) TypePattern()
|
||||
}
|
||||
|
||||
void TypePattern():
|
||||
@ -1734,17 +1734,14 @@ void InstanceOfExpression() #void:
|
||||
LOOKAHEAD(1)
|
||||
("instanceof"
|
||||
(
|
||||
AnnotatedRefType() [ VariableDeclaratorId() #TypePattern(2) ]
|
||||
|
|
||||
LOOKAHEAD("final" | "@") PrimaryPattern()
|
||||
|
|
||||
LOOKAHEAD("(") Pattern()
|
||||
LOOKAHEAD(1, "(" | "final" | "@") PrimaryPattern()
|
||||
| LOOKAHEAD(1) AnnotatedRefType() [ VariableDeclaratorId() #TypePattern(2) ]
|
||||
)
|
||||
{
|
||||
jjtThis.setOp(BinaryOp.INSTANCEOF);
|
||||
AbstractJavaNode top = jjtree.popNode();
|
||||
if (top instanceof ASTPattern) {
|
||||
if (top.getNumChildren() == 2) {
|
||||
if (top instanceof ASTTypePattern) {
|
||||
insertEmptyModifierListWithAnnotations(top, (AbstractJavaNode) top.getChild(0));
|
||||
}
|
||||
top = new ASTPatternExpression((ASTPattern) top);
|
||||
|
@ -7,17 +7,16 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
/**
|
||||
* A guarded pattern (JDK17 Preview). This can be found
|
||||
* in {@link ASTSwitchLabel}s.
|
||||
* A guarded pattern (JDK17 Preview). This can be found in {@link ASTSwitchLabel}s.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
* GuardedPattern ::= {@linkplain ASTPattern Pattern} "&&" {@linkplain ASTConditionalAndExpression ConditionalAndExpression}
|
||||
* GuardedPattern ::= {@linkplain ASTPattern Pattern} "&&" {@linkplain ASTExpression Expression}
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="https://openjdk.java.net/jeps/406">JEP 406: Pattern Matching for switch (Preview)</a>
|
||||
*/
|
||||
*/
|
||||
@Experimental
|
||||
public final class ASTGuardedPattern extends AbstractJavaNode implements ASTPattern {
|
||||
|
||||
@ -32,12 +31,14 @@ public final class ASTGuardedPattern extends AbstractJavaNode implements ASTPatt
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
|
||||
/** Returns the guarded pattern. */
|
||||
public ASTPattern getPattern() {
|
||||
return (ASTPattern) getChild(0);
|
||||
}
|
||||
|
||||
public JavaNode getGuard() {
|
||||
return getChild(1);
|
||||
/** Returns the boolean expression guarding the pattern. */
|
||||
public ASTExpression getGuard() {
|
||||
return (ASTExpression) getChild(1);
|
||||
}
|
||||
|
||||
void bumpParenDepth() {
|
||||
|
@ -9,6 +9,7 @@ import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.internal.util.AssertionUtil;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTGuardedPattern;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPattern;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPatternExpression;
|
||||
@ -86,9 +87,13 @@ final class PatternBindingsUtil {
|
||||
|
||||
static BindSet collectBindings(ASTPattern pattern) {
|
||||
if (pattern instanceof ASTTypePattern) {
|
||||
return BindSet.EMPTY.addBinding(((ASTTypePattern) pattern).getVarId());
|
||||
return BindSet.whenTrue(HashTreePSet.singleton(((ASTTypePattern) pattern).getVarId()));
|
||||
} else if (pattern instanceof ASTGuardedPattern) {
|
||||
BindSet patternBindings = collectBindings(((ASTGuardedPattern) pattern).getPattern());
|
||||
BindSet guardBindings = bindersOfExpr(((ASTGuardedPattern) pattern).getGuard());
|
||||
return patternBindings.union(guardBindings);
|
||||
} else {
|
||||
throw AssertionUtil.shouldNotReachHere("no other instances of pattern should exist");
|
||||
throw AssertionUtil.shouldNotReachHere("no other instances of pattern should exist: " + pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +110,13 @@ final class PatternBindingsUtil {
|
||||
private final PSet<ASTVariableDeclaratorId> trueBindings;
|
||||
private final PSet<ASTVariableDeclaratorId> falseBindings;
|
||||
|
||||
public BindSet union(BindSet bindSet) {
|
||||
return new BindSet(
|
||||
trueBindings.plusAll(bindSet.trueBindings),
|
||||
falseBindings.plusAll(bindSet.falseBindings)
|
||||
);
|
||||
}
|
||||
|
||||
static PSet<ASTVariableDeclaratorId> noBindings() {
|
||||
return HashTreePSet.empty();
|
||||
}
|
||||
|
@ -470,6 +470,15 @@ public final class SymbolTableResolver {
|
||||
return node.getRightOperand().acceptVisitor(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(ASTGuardedPattern node, @NonNull ReferenceCtx ctx) {
|
||||
BindSet bindSet = PatternBindingsUtil.bindersOfPattern(node.getPattern());
|
||||
int pushed = pushOnStack(f.localVarSymTable(top(), enclosing(), bindSet.getTrueBindings()));
|
||||
setTopSymbolTableAndVisit(node.getGuard(), ctx);
|
||||
popStack(pushed);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(ASTConditionalExpression node, @NonNull ReferenceCtx ctx) {
|
||||
// need to account for pattern bindings.
|
||||
|
@ -27,6 +27,28 @@ class ASTInstanceOfExpressionTest : ParserTestSpec({
|
||||
}
|
||||
}
|
||||
|
||||
parserTest("Instanceof with pattern") {
|
||||
|
||||
inContext(ExpressionParsingCtx) {
|
||||
|
||||
"o instanceof (String s && s.length() > 4)" should parseAs {
|
||||
infixExpr(BinaryOp.INSTANCEOF) {
|
||||
it::getLeftOperand shouldBe variableAccess("o")
|
||||
it::getRightOperand shouldBe patternExpr {
|
||||
guardedPattern {
|
||||
it::getPattern shouldBe typePattern {
|
||||
modifiers()
|
||||
classType("String")
|
||||
variableId("s")
|
||||
}
|
||||
it::getGuard shouldBe infixExpr(BinaryOp.GT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parserTest("InstanceofExpression cannot test primitive types") {
|
||||
|
||||
inContext(ExpressionParsingCtx) {
|
||||
|
@ -400,6 +400,19 @@ fun TreeNodeWrapper<Node, *>.typeExpr(contents: ValuedNodeSpec<ASTTypeExpression
|
||||
it::getTypeNode shouldBe contents()
|
||||
}
|
||||
|
||||
fun TreeNodeWrapper<Node, *>.patternExpr(contents: ValuedNodeSpec<ASTPatternExpression, ASTPattern>) =
|
||||
child<ASTPatternExpression>(ignoreChildren = contents == EmptyAssertions) {
|
||||
it::getPattern shouldBe contents()
|
||||
}
|
||||
fun TreeNodeWrapper<Node, *>.typePattern(contents: NodeSpec<ASTTypePattern>) =
|
||||
child<ASTTypePattern>(ignoreChildren = contents == EmptyAssertions) {
|
||||
contents()
|
||||
}
|
||||
fun TreeNodeWrapper<Node, *>.guardedPattern(contents: NodeSpec<ASTGuardedPattern>) =
|
||||
child<ASTGuardedPattern>(ignoreChildren = contents == EmptyAssertions) {
|
||||
contents()
|
||||
}
|
||||
|
||||
|
||||
fun TreeNodeWrapper<Node, *>.arrayType(contents: NodeSpec<ASTArrayType> = EmptyAssertions) =
|
||||
child<ASTArrayType>(ignoreChildren = contents == EmptyAssertions) {
|
||||
|
@ -119,7 +119,7 @@
|
||||
| | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"]
|
||||
| | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"]
|
||||
| | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | | +- ArgumentList[@Size = "0"]
|
||||
| | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "2", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "2.0", @ValueAsFloat = "2.0", @ValueAsInt = "2", @ValueAsLong = "2"]
|
||||
| | +- Block[@Size = "1", @containsComment = "false"]
|
||||
@ -145,7 +145,7 @@
|
||||
| | | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"]
|
||||
| | | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"]
|
||||
| | | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | | +- ArgumentList[@Size = "0"]
|
||||
| | | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "3", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "3.0", @ValueAsFloat = "3.0", @ValueAsInt = "3", @ValueAsLong = "3"]
|
||||
| | +- Block[@Size = "1", @containsComment = "false"]
|
||||
@ -161,14 +161,13 @@
|
||||
| | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "o", @Name = "o", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | +- PatternExpression[@CompileTimeConstant = "false", @Expression = "true", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | +- GuardedPattern[@ParenthesisDepth = "1"]
|
||||
| | +- ModifierList[]
|
||||
| | +- TypePattern[@Abstract = "false", @EffectiveVisibility = "package", @Final = "false", @Native = "false", @PackagePrivate = "true", @ParenthesisDepth = "0", @Private = "false", @Protected = "false", @Public = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @Visibility = "package", @Volatile = "false"]
|
||||
| | | +- ModifierList[]
|
||||
| | | +- ClassOrInterfaceType[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "true", @FullyQualified = "false", @PrimitiveType = "false", @ReferenceToClassSameCompilationUnit = "false", @SimpleName = "String", @TypeImage = "String"]
|
||||
| | | +- VariableDeclaratorId[@Abstract = "false", @ArrayType = "false", @EffectiveVisibility = "local", @EnumConstant = "false", @ExceptionBlockParameter = "false", @Field = "false", @Final = "false", @ForLoopVariable = "false", @ForeachVariable = "false", @FormalParameter = "false", @Image = "s", @LambdaParameter = "false", @LocalVariable = "false", @Name = "s", @Native = "false", @PackagePrivate = "false", @PatternBinding = "true", @Private = "false", @Protected = "false", @Public = "false", @RecordComponent = "false", @ResourceDeclaration = "false", @Static = "false", @Strictfp = "false", @Synchronized = "false", @SyntacticallyAbstract = "false", @SyntacticallyFinal = "false", @SyntacticallyPublic = "false", @SyntacticallyStatic = "false", @Transient = "false", @TypeInferred = "false", @VariableName = "s", @Visibility = "local", @Volatile = "false"]
|
||||
| | +- InfixExpression[@CompileTimeConstant = "false", @Expression = "true", @Operator = ">", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | +- MethodCall[@CompileTimeConstant = "false", @Expression = "true", @Image = "length", @MethodName = "length", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | +- AmbiguousName[@ArrayDepth = "0", @ArrayType = "false", @ClassOrInterfaceType = "false", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false", @PrimitiveType = "false", @TypeImage = "s"]
|
||||
| | | +- VariableAccess[@AccessType = "READ", @CompileTimeConstant = "false", @Expression = "true", @Image = "s", @Name = "s", @ParenthesisDepth = "0", @Parenthesized = "false"]
|
||||
| | | +- ArgumentList[@Size = "0"]
|
||||
| | +- NumericLiteral[@Base = "10", @BooleanLiteral = "false", @CharLiteral = "false", @CompileTimeConstant = "true", @DoubleLiteral = "false", @Expression = "true", @FloatLiteral = "false", @Image = "4", @IntLiteral = "true", @Integral = "true", @LongLiteral = "false", @NullLiteral = "false", @NumericLiteral = "true", @ParenthesisDepth = "0", @Parenthesized = "false", @StringLiteral = "false", @ValueAsDouble = "4.0", @ValueAsFloat = "4.0", @ValueAsInt = "4", @ValueAsLong = "4"]
|
||||
| +- Block[@Size = "1", @containsComment = "false"]
|
||||
|
Reference in New Issue
Block a user