Skip to content

Commit 353722c

Browse files
SONARJAVA-5015 Improve the tolerance to syntax errors when parsing switch expressions (#4926)
1 parent 3456a1c commit 353722c

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

java-frontend/src/main/java/org/sonar/java/model/JParser.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,19 @@ public class JParser {
258258

259259
private static final Logger LOG = LoggerFactory.getLogger(JParser.class);
260260

261-
private static final Predicate<IProblem> IS_SYNTAX_ERROR = error -> (error.getID() & IProblem.Syntax) != 0;
261+
private static final Set<Integer> WRONGLY_CATEGORIZED_AS_SYNTAX_ERROR = Set.of(
262+
// Accept missing default clause, it may be due to missing semantic information of the switch expression,
263+
// in this case, an enum fully covered with the switch cases will be seen as something that is not an enum
264+
// when it is unknown, and the parser will wrongly consider the missing default clause as a syntax error.
265+
IProblem.SwitchExpressionsYieldMissingDefaultCase,
266+
// Accept missing default clause, it may be due the switch expression being an enum from a wrong dependency.
267+
// In this case, the parser will wrongly consider the missing default clause as a syntax error.
268+
IProblem.SwitchExpressionsYieldMissingEnumConstantCase
269+
);
270+
271+
private static final Predicate<IProblem> IS_SYNTAX_ERROR = error -> ((error.getID() & IProblem.Syntax) != 0) &&
272+
!WRONGLY_CATEGORIZED_AS_SYNTAX_ERROR.contains(error.getID());
273+
262274
private static final Predicate<IProblem> IS_UNDEFINED_TYPE_ERROR = error -> (error.getID() & IProblem.UndefinedType) != 0;
263275

264276
/**

java-frontend/src/test/java/org/sonar/java/model/JParserSemanticTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import org.sonar.plugins.java.api.tree.TypeCastTree;
8989
import org.sonar.plugins.java.api.tree.TypeTree;
9090
import org.sonar.plugins.java.api.tree.VariableTree;
91+
import org.sonar.plugins.java.api.tree.YieldStatementTree;
9192

9293
import static org.assertj.core.api.Assertions.assertThat;
9394
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@@ -559,6 +560,36 @@ void expression_switch() {
559560
}
560561
}
561562

563+
@Test
564+
void switch_expression_with_yield_of_unknown_identifier_without_default_clause() {
565+
SwitchExpressionTreeImpl switchExpression = (SwitchExpressionTreeImpl) expression("switch (unknownIdentifier) { case A -> 0; case B -> 1; }");
566+
assertThat(switchExpression.expression().symbolType().isUnknown()).isTrue();
567+
// the expression type of the full switch expression the should have been int or unknown
568+
// instead of java.lang.Object, it is probably a limitation in the JDT parser when it face a missing default clause error
569+
assertThat(switchExpression.symbolType().isUnknown()).isFalse();
570+
assertThat(switchExpression.symbolType().fullyQualifiedName()).isEqualTo("java.lang.Object");
571+
assertThat(switchExpression.cases().get(0).body().get(0)).isInstanceOf(YieldStatementTree.class);
572+
}
573+
574+
@Test
575+
void switch_expression_of_unknown_identifier_without_default_clause() {
576+
SwitchExpressionTreeImpl switchExpression = (SwitchExpressionTreeImpl) expression("switch (unknownIdentifier) { case A: return 0; case B: return 1; }");
577+
assertThat(switchExpression.expression().symbolType().isUnknown()).isTrue();
578+
assertThat(switchExpression.symbolType().isUnknown()).isTrue();
579+
assertThat(switchExpression.cases().get(0).body().get(0)).isInstanceOf(ReturnStatementTree.class);
580+
}
581+
582+
@Test
583+
void switch_expression_of_enum_without_default_clause() {
584+
CompilationUnitTree cu = test("class C { Object m(java.time.DayOfWeek x) { return switch (x) { case MONDAY: return 0; case TUESDAY: return 1; } ; } }");
585+
ClassTree c = (ClassTree) cu.types().get(0);
586+
MethodTree m = (MethodTree) c.members().get(0);
587+
ReturnStatementTree s = (ReturnStatementTree) Objects.requireNonNull(m.block()).body().get(0);
588+
SwitchExpressionTreeImpl switchExpression = (SwitchExpressionTreeImpl) s.expression();
589+
assertThat(switchExpression.expression().symbolType().isUnknown()).isFalse();
590+
assertThat(switchExpression.symbolType().isUnknown()).isTrue();
591+
}
592+
562593
/**
563594
* Pattern Matching for instanceof
564595
* (Preview in Java 14) https://openjdk.java.net/jeps/305

0 commit comments

Comments
 (0)