Skip to content

Commit

Permalink
Parse multiple Patterns in a case statement
Browse files Browse the repository at this point in the history
Second part of JEP 443.
Change the parser so that it can parse a list of Patterns in a case.
Change GuardedPattern AST (internal and and API) so that it has a list
of patterns as its child instead of just one.
Modify some tests that use record patterns with a variable
(eg. `MyPoint(int x, int y) p` vs `MyPoint(int x, int y)`),
since this syntax was only ever used in the preview version
of the feature and is no longer allowed.

Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 committed Jan 18, 2024
1 parent 35bf360 commit 3680e69
Show file tree
Hide file tree
Showing 51 changed files with 2,192 additions and 2,373 deletions.
211 changes: 109 additions & 102 deletions org.eclipse.jdt.core.compiler.batch/grammar/java.g

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2576,9 +2576,14 @@ public interface IProblem {
*/
int IllegalRecordPattern = TypeRelated + 1941;

/**
* @since 3.37
*/
int CaseLabelWithPatternMustHaveExactlyOnePattern = Internal + 1942;

/**
* @since 3.35
*/
int SyntheticAccessorNotEnclosingMethod = MethodRelated + 1990;

}
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,14 @@ private Expression getFirstValidExpression(BlockScope scope, SwitchStatement swi
if (e instanceof Pattern) {
scope.problemReporter().validateJavaFeatureSupport(JavaFeature.PATTERN_MATCHING_IN_SWITCH,
e.sourceStart, e.sourceEnd);
if (this.constantExpressions.length > 1) {
scope.problemReporter().illegalCaseConstantCombination(e);
return e;
if (this.constantExpressions.length > 1 || e instanceof GuardedPattern gp && gp.patterns.length > 1) {
PatternVariableCounter myVisitor = new PatternVariableCounter();
this.traverse(myVisitor, scope);
if (myVisitor.numVars > 0) {
scope.problemReporter().illegalCaseLabelWithMultiplePatterns(this);
}
}
return e;
} else if (e instanceof NullLiteral) {
scope.problemReporter().validateJavaFeatureSupport(JavaFeature.PATTERN_MATCHING_IN_SWITCH,
e.sourceStart, e.sourceEnd);
Expand Down Expand Up @@ -559,4 +563,24 @@ public LocalDeclaration getLocalDeclaration() {
return patternVariableIntroduced;
}

private class PatternVariableCounter extends ASTVisitor {

public int numVars = 0;

@Override
public boolean visit(TypePattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}

@Override
public boolean visit(RecordPattern pattern, BlockScope scope) {
if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$
this.numVars++;
}
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,49 @@

public class GuardedPattern extends Pattern {

public Pattern primaryPattern;
public Pattern[] patterns;
public Expression condition;
int thenInitStateIndex1 = -1;
int thenInitStateIndex2 = -1;
public int restrictedIdentifierStart = -1; // used only for 'when' restricted keyword.

public GuardedPattern(Pattern primaryPattern, Expression conditionalAndExpression) {
this.primaryPattern = primaryPattern;
public GuardedPattern(Pattern patterns[], Expression conditionalAndExpression) {
this.patterns = patterns;
this.condition = conditionalAndExpression;
this.sourceStart = primaryPattern.sourceStart;
if (patterns.length > 0) {
this.sourceStart = this.patterns[0].sourceStart;
} else {
this.sourceStart = conditionalAndExpression.sourceStart;
}
this.sourceEnd = conditionalAndExpression.sourceEnd;
}
@Override
public void collectPatternVariablesToScope(LocalVariableBinding[] variables, BlockScope scope) {
this.primaryPattern.collectPatternVariablesToScope(variables, scope);
addPatternVariablesWhenTrue(this.primaryPattern.getPatternVariablesWhenTrue());
for (Pattern pattern : this.patterns) {
pattern.collectPatternVariablesToScope(variables, scope);
addPatternVariablesWhenTrue(pattern.getPatternVariablesWhenTrue());
}
this.condition.collectPatternVariablesToScope(getPatternVariablesWhenTrue(), scope);
addPatternVariablesWhenTrue(this.condition.getPatternVariablesWhenTrue());
}

@Override
public LocalDeclaration getPatternVariable() {
return this.primaryPattern.getPatternVariable();
if (this.patterns.length == 1) {
return this.patterns[0].getPatternVariable();
}
return null;
}

@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
flowInfo = this.primaryPattern.analyseCode(currentScope, flowContext, flowInfo);
this.thenInitStateIndex1 = currentScope.methodScope().recordInitializationStates(flowInfo);
if (this.patterns.length >= 1) {
flowInfo = this.patterns[0].analyseCode(currentScope, flowContext, flowInfo);
this.thenInitStateIndex1 = currentScope.methodScope().recordInitializationStates(flowInfo);
}
for (int i = 1; i < this.patterns.length; i++) {
flowInfo = this.patterns[i].analyseCode(currentScope, flowContext, flowInfo);
}
FlowInfo mergedFlow = this.condition.analyseCode(currentScope, flowContext, flowInfo);
mergedFlow = mergedFlow.safeInitsWhenTrue();
this.thenInitStateIndex2 = currentScope.methodScope().recordInitializationStates(mergedFlow);
Expand All @@ -66,7 +80,9 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) {
this.thenTarget = new BranchLabel(codeStream);
this.elseTarget = new BranchLabel(codeStream);
this.primaryPattern.generateOptimizedBoolean(currentScope, codeStream, this.thenTarget, this.elseTarget);
for (Pattern pattern : this.patterns ) {
pattern.generateOptimizedBoolean(currentScope, codeStream, this.thenTarget, this.elseTarget);
}
Constant cst = this.condition.optimizedBooleanConstant();

setGuardedElseTarget(currentScope, this.elseTarget);
Expand Down Expand Up @@ -105,11 +121,19 @@ public boolean isAlwaysTrue() {
}
@Override
public boolean coversType(TypeBinding type) {
return this.primaryPattern.coversType(type) && isAlwaysTrue();
if (!isAlwaysTrue()) {
return false;
}
for (Pattern pattern : this.patterns) {
if (pattern.coversType(type)) {
return true;
}
}
return false;
}
@Override
public Pattern primary() {
return this.primaryPattern;
return this.patterns.length >= 1 ? this.patterns[0] : null;
}

@Override
Expand All @@ -119,16 +143,21 @@ public void resolve(BlockScope scope) {

@Override
public boolean dominates(Pattern p) {
if (isAlwaysTrue())
return this.primaryPattern.dominates(p);
if (isAlwaysTrue()) {
for (Pattern pattern : this. patterns) {
if (pattern.dominates(p)) {
return true;
}
}
}
return false;
}

@Override
public TypeBinding resolveType(BlockScope scope) {
if (this.resolvedType != null || this.primaryPattern == null)
if (this.resolvedType != null || this.primary() == null)
return this.resolvedType;
this.resolvedType = this.primaryPattern.resolveType(scope);
this.resolvedType = this.primary().resolveType(scope);
// The following call (as opposed to resolveType() ensures that
// the implicitConversion code is set properly and thus the correct
// unboxing calls are generated.
Expand Down Expand Up @@ -158,30 +187,37 @@ public boolean visit(
return false;
}
}, scope);
return this.resolvedType = this.primaryPattern.resolvedType;
return this.resolvedType = this.primary().resolvedType;
}

@Override
public TypeBinding resolveAtType(BlockScope scope, TypeBinding u) {
if (this.resolvedType == null || this.primaryPattern == null)
if (this.resolvedType == null || this.primary() == null)
return null;
if (this.primaryPattern.coversType(u))
return this.primaryPattern.resolveAtType(scope, u);
if (this.primary().coversType(u))
return this.primary().resolveAtType(scope, u);

return this.resolvedType; //else leave the pattern untouched for now.
}

@Override
public StringBuilder printExpression(int indent, StringBuilder output) {
this.primaryPattern.print(indent, output).append(" when "); //$NON-NLS-1$
for (int i = 0; i < this.patterns.length; i++) {
this.patterns[i].print(indent, output);
if (i < this.patterns.length - 1) {
output.append(", "); //$NON-NLS-1$
}
}
output.append(" when "); //$NON-NLS-1$
return this.condition.print(indent, output);
}

@Override
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
if (this.primaryPattern != null)
this.primaryPattern.traverse(visitor, scope);
for (Pattern pattern : this.patterns) {
pattern.traverse(visitor, scope);
}
if (this.condition != null)
this.condition.traverse(visitor, scope);
}
Expand All @@ -190,28 +226,32 @@ public void traverse(ASTVisitor visitor, BlockScope scope) {
@Override
public void suspendVariables(CodeStream codeStream, BlockScope scope) {
codeStream.removeNotDefinitelyAssignedVariables(scope, this.thenInitStateIndex1);
this.primaryPattern.suspendVariables(codeStream, scope);
if (this.primary() != null) {
this.primary().suspendVariables(codeStream, scope);
}
}
@Override
public void resumeVariables(CodeStream codeStream, BlockScope scope) {
codeStream.addDefinitelyAssignedVariables(scope, this.thenInitStateIndex2);
this.primaryPattern.resumeVariables(codeStream, scope);
if (this.primary() != null) {
this.primary().resumeVariables(codeStream, scope);
}
}
@Override
public void resolveWithExpression(BlockScope scope, Expression expression) {
this.primaryPattern.resolveWithExpression(scope, expression);
this.primary().resolveWithExpression(scope, expression);
}
@Override
protected boolean isPatternTypeCompatible(TypeBinding other, BlockScope scope) {
return this.primaryPattern.isPatternTypeCompatible(other, scope);
return this.primary().isPatternTypeCompatible(other, scope);
}
@Override
public void wrapupGeneration(CodeStream codeStream) {
this.primaryPattern.wrapupGeneration(codeStream);
this.primary().wrapupGeneration(codeStream);
}
@Override
protected void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
BranchLabel falseLabel) {
this.primaryPattern.generatePatternVariable(currentScope, codeStream, trueLabel, falseLabel);
this.primary().generatePatternVariable(currentScope, codeStream, trueLabel, falseLabel);
}
}
Loading

0 comments on commit 3680e69

Please sign in to comment.