Fix parsing problem with guarded patterns

This commit is contained in:
Clément Fournier
2022-01-29 18:08:25 +01:00
parent 6d5fe7e63c
commit 0be7135122
7 changed files with 73 additions and 20 deletions

View File

@ -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);

View File

@ -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} "&amp;&amp;" {@linkplain ASTConditionalAndExpression ConditionalAndExpression}
* GuardedPattern ::= {@linkplain ASTPattern Pattern} "&amp;&amp;" {@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() {

View File

@ -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();
}

View File

@ -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.

View File

@ -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) {

View File

@ -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) {

View File

@ -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"]