implements PR014
This commit is contained in:
parent
c0bccba092
commit
4f32913577
@ -416,7 +416,14 @@ public final class PbsAst {
|
||||
Span span();
|
||||
}
|
||||
|
||||
public enum WildcardSwitchKind {
|
||||
DEFAULT,
|
||||
UNDERSCORE,
|
||||
RECOVERY
|
||||
}
|
||||
|
||||
public record WildcardSwitchPattern(
|
||||
WildcardSwitchKind kind,
|
||||
Span span) implements SwitchPattern {
|
||||
}
|
||||
|
||||
|
||||
@ -522,12 +522,16 @@ final class PbsExprParser {
|
||||
|
||||
private PbsAst.SwitchPattern parseSwitchPattern() {
|
||||
if (cursor.match(PbsTokenKind.DEFAULT)) {
|
||||
return new PbsAst.WildcardSwitchPattern(span(cursor.previous().start(), cursor.previous().end()));
|
||||
return new PbsAst.WildcardSwitchPattern(
|
||||
PbsAst.WildcardSwitchKind.DEFAULT,
|
||||
span(cursor.previous().start(), cursor.previous().end()));
|
||||
}
|
||||
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && "_".equals(cursor.peek().lexeme())) {
|
||||
final var wildcard = cursor.advance();
|
||||
return new PbsAst.WildcardSwitchPattern(span(wildcard.start(), wildcard.end()));
|
||||
return new PbsAst.WildcardSwitchPattern(
|
||||
PbsAst.WildcardSwitchKind.UNDERSCORE,
|
||||
span(wildcard.start(), wildcard.end()));
|
||||
}
|
||||
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER)
|
||||
@ -545,7 +549,9 @@ final class PbsExprParser {
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_INVALID_SWITCH_FORM, "Invalid switch pattern");
|
||||
cursor.advance();
|
||||
return new PbsAst.WildcardSwitchPattern(span(token.start(), token.end()));
|
||||
return new PbsAst.WildcardSwitchPattern(
|
||||
PbsAst.WildcardSwitchKind.RECOVERY,
|
||||
span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseLiteralPatternExpression() {
|
||||
|
||||
@ -850,17 +850,37 @@ final class PbsFlowExpressionAnalyzer {
|
||||
|
||||
final var seenPatterns = new HashSet<String>();
|
||||
boolean hasWildcard = false;
|
||||
PbsAst.WildcardSwitchKind wildcardKind = null;
|
||||
int wildcardCount = 0;
|
||||
TypeView armType = null;
|
||||
|
||||
for (final var arm : switchExpr.arms()) {
|
||||
final var patternKey = switchPatternKey(arm.pattern());
|
||||
if (arm.pattern() instanceof PbsAst.WildcardSwitchPattern) {
|
||||
if (arm.pattern() instanceof PbsAst.WildcardSwitchPattern wildcardPattern) {
|
||||
hasWildcard = true;
|
||||
} else if (!seenPatterns.add(patternKey)) {
|
||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
||||
PbsSemanticsErrors.E_SEM_SWITCH_DUPLICATE_PATTERN.name(),
|
||||
"Duplicate switch pattern",
|
||||
arm.pattern().span());
|
||||
if (wildcardPattern.kind() != PbsAst.WildcardSwitchKind.RECOVERY) {
|
||||
wildcardCount++;
|
||||
if (wildcardCount == 1) {
|
||||
wildcardKind = wildcardPattern.kind();
|
||||
} else if (wildcardKind != wildcardPattern.kind()) {
|
||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
||||
PbsSemanticsErrors.E_SEM_SWITCH_DUPLICATE_PATTERN.name(),
|
||||
"Switch wildcard arms cannot mix 'default' and '_'",
|
||||
arm.pattern().span());
|
||||
} else {
|
||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
||||
PbsSemanticsErrors.E_SEM_SWITCH_DUPLICATE_PATTERN.name(),
|
||||
"Duplicate switch wildcard pattern",
|
||||
arm.pattern().span());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final var patternKey = switchPatternKey(arm.pattern());
|
||||
if (!seenPatterns.add(patternKey)) {
|
||||
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics,
|
||||
PbsSemanticsErrors.E_SEM_SWITCH_DUPLICATE_PATTERN.name(),
|
||||
"Duplicate switch pattern",
|
||||
arm.pattern().span());
|
||||
}
|
||||
}
|
||||
|
||||
final var patternType = switchPatternType(arm.pattern(), model, diagnostics);
|
||||
|
||||
@ -48,7 +48,7 @@ class PbsExprParserTest {
|
||||
final var source = """
|
||||
fn flow(cond: bool, state: int) -> int {
|
||||
let a: int = if cond { 1 } else { 2 };
|
||||
let b: int = switch state { default: { 0 } };
|
||||
let b: int = switch state { default: { 0 }, _: { 1 } };
|
||||
let c: int = handle run apply () { Err.fail -> Other.fail, _ -> { 1 } };
|
||||
return 0;
|
||||
}
|
||||
@ -69,7 +69,15 @@ class PbsExprParserTest {
|
||||
|
||||
final var switchExpr = assertInstanceOf(PbsAst.SwitchExpr.class,
|
||||
assertInstanceOf(PbsAst.LetStatement.class, body.get(1)).initializer());
|
||||
assertEquals(2, switchExpr.arms().size());
|
||||
final var defaultPattern = assertInstanceOf(PbsAst.WildcardSwitchPattern.class,
|
||||
switchExpr.arms().getFirst().pattern());
|
||||
assertEquals(PbsAst.WildcardSwitchKind.DEFAULT, defaultPattern.kind());
|
||||
assertInstanceOf(PbsAst.IntLiteralExpr.class, switchExpr.arms().getFirst().block().tailExpression());
|
||||
final var underscorePattern = assertInstanceOf(PbsAst.WildcardSwitchPattern.class,
|
||||
switchExpr.arms().get(1).pattern());
|
||||
assertEquals(PbsAst.WildcardSwitchKind.UNDERSCORE, underscorePattern.kind());
|
||||
assertInstanceOf(PbsAst.IntLiteralExpr.class, switchExpr.arms().get(1).block().tailExpression());
|
||||
|
||||
final var handle = assertInstanceOf(PbsAst.HandleExpr.class,
|
||||
assertInstanceOf(PbsAst.LetStatement.class, body.get(2)).initializer());
|
||||
|
||||
@ -5,6 +5,7 @@ import p.studio.compiler.pbs.PbsFrontendCompiler;
|
||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class PbsSemanticsControlFlowTest {
|
||||
@ -39,4 +40,46 @@ class PbsSemanticsControlFlowTest {
|
||||
assertTrue(diagnostics.stream().anyMatch(d ->
|
||||
d.getCode().equals(PbsSemanticsErrors.E_SEM_BARE_METHOD_EXTRACTION.name())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectDuplicateSwitchWildcardArms() {
|
||||
final var source = """
|
||||
fn flow(state: int) -> int {
|
||||
let value: int = switch state {
|
||||
default: { 1 },
|
||||
default: { 2 },
|
||||
};
|
||||
return value;
|
||||
}
|
||||
""";
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
|
||||
new PbsFrontendCompiler().compileFile(new FileId(0), source, diagnostics);
|
||||
|
||||
final var duplicateWildcardCount = diagnostics.stream()
|
||||
.filter(d -> d.getCode().equals(PbsSemanticsErrors.E_SEM_SWITCH_DUPLICATE_PATTERN.name()))
|
||||
.count();
|
||||
assertEquals(1, duplicateWildcardCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectMixedSwitchWildcardSpellings() {
|
||||
final var source = """
|
||||
fn flow(state: int) -> int {
|
||||
let value: int = switch state {
|
||||
default: { 1 },
|
||||
_: { 2 },
|
||||
};
|
||||
return value;
|
||||
}
|
||||
""";
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
|
||||
new PbsFrontendCompiler().compileFile(new FileId(0), source, diagnostics);
|
||||
|
||||
final var duplicateWildcardCount = diagnostics.stream()
|
||||
.filter(d -> d.getCode().equals(PbsSemanticsErrors.E_SEM_SWITCH_DUPLICATE_PATTERN.name()))
|
||||
.count();
|
||||
assertEquals(1, duplicateWildcardCount);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user