Skip to content

Commit 86df5cf

Browse files
cushonError Prone Team
authored andcommitted
Convert some simple blocks to return switches using yield
PiperOrigin-RevId: 658843015
1 parent 474554a commit 86df5cf

File tree

2 files changed

+62
-27
lines changed

2 files changed

+62
-27
lines changed

core/src/main/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitch.java

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public final class StatementSwitchToExpressionSwitch extends BugChecker
9494
ImmutableSet.of(THROW, EXPRESSION_STATEMENT);
9595
private static final ImmutableSet<Kind> KINDS_RETURN_OR_THROW = ImmutableSet.of(THROW, RETURN);
9696
private static final Pattern FALL_THROUGH_PATTERN =
97-
Pattern.compile("\\bfalls?.?(through|out)\\b", Pattern.CASE_INSENSITIVE);
97+
Pattern.compile("\\bfalls?.?through\\b", Pattern.CASE_INSENSITIVE);
9898
// Default (negative) result for assignment switch conversion analysis. Note that the value is
9999
// immutable.
100100
private static final AssignmentSwitchAnalysisResult DEFAULT_ASSIGNMENT_SWITCH_ANALYSIS_RESULT =
@@ -325,14 +325,21 @@ private static CaseQualifications analyzeCaseForReturnSwitch(
325325
return previousCaseQualifications;
326326
}
327327

328-
// Statement blocks on the RHS are not currently supported
329-
if (!(statements.size() == 1 && KINDS_RETURN_OR_THROW.contains(statements.get(0).getKind()))) {
328+
// Statement blocks on the RHS are not currently supported, except for trivial blocks of
329+
// statements expressions followed by a return or throw
330+
// TODO: handle more complex statement blocks that can be converted using 'yield'
331+
if (statements.isEmpty()) {
332+
return CaseQualifications.SOME_OR_ALL_CASES_DONT_QUALIFY;
333+
}
334+
StatementTree lastStatement = getLast(statements);
335+
if (!statements.subList(0, statements.size() - 1).stream()
336+
.allMatch(statement -> statement.getKind().equals(EXPRESSION_STATEMENT))
337+
|| !KINDS_RETURN_OR_THROW.contains(lastStatement.getKind())) {
330338
return CaseQualifications.SOME_OR_ALL_CASES_DONT_QUALIFY;
331339
}
332340

333-
StatementTree onlyStatement = statements.get(0);
334341
// For this analysis, cases that don't return something can be disregarded
335-
if (!onlyStatement.getKind().equals(RETURN)) {
342+
if (!lastStatement.getKind().equals(RETURN)) {
336343
return previousCaseQualifications;
337344
}
338345

@@ -343,7 +350,7 @@ private static CaseQualifications analyzeCaseForReturnSwitch(
343350
}
344351

345352
// This is the first value-returning case that we are examining
346-
Type returnType = ASTHelpers.getType(((ReturnTree) onlyStatement).getExpression());
353+
Type returnType = ASTHelpers.getType(((ReturnTree) lastStatement).getExpression());
347354
return returnType == null
348355
// Return of void does not qualify
349356
? CaseQualifications.SOME_OR_ALL_CASES_DONT_QUALIFY
@@ -1117,21 +1124,30 @@ private static String transformReturnOrThrowBlock(
11171124
CaseTree caseTree, VisitorState state, List<? extends StatementTree> statements) {
11181125

11191126
StringBuilder transformedBlockBuilder = new StringBuilder();
1120-
int codeBlockStart;
1121-
int codeBlockEnd =
1122-
statements.isEmpty()
1123-
? getBlockEnd(state, caseTree)
1124-
: state.getEndPosition(Streams.findLast(statements.stream()).get());
1125-
1126-
if (statements.size() == 1 && statements.get(0).getKind().equals(RETURN)) {
1127+
int codeBlockEnd = state.getEndPosition(caseTree);
1128+
if (statements.size() > 1) {
1129+
transformedBlockBuilder.append("{\n");
1130+
int codeBlockStart = extractLhsComments(caseTree, state, transformedBlockBuilder);
1131+
int offset = transformedBlockBuilder.length();
1132+
transformedBlockBuilder.append(state.getSourceCode(), codeBlockStart, codeBlockEnd);
1133+
transformedBlockBuilder.append("\n}");
1134+
ReturnTree returnTree = (ReturnTree) getLast(statements);
1135+
int start = getStartPosition(returnTree);
1136+
transformedBlockBuilder.replace(
1137+
offset + start - codeBlockStart,
1138+
offset + start - codeBlockStart + "return".length(),
1139+
"yield");
1140+
} else if (statements.size() == 1 && statements.get(0).getKind().equals(RETURN)) {
11271141
// For "return x;", we want to take source starting after the "return"
11281142
int unused = extractLhsComments(caseTree, state, transformedBlockBuilder);
11291143
ReturnTree returnTree = (ReturnTree) statements.get(0);
1130-
codeBlockStart = getStartPosition(returnTree.getExpression());
1144+
int codeBlockStart = getStartPosition(returnTree.getExpression());
1145+
codeBlockEnd = state.getEndPosition(Streams.findLast(statements.stream()).get());
1146+
transformedBlockBuilder.append(state.getSourceCode(), codeBlockStart, codeBlockEnd);
11311147
} else {
1132-
codeBlockStart = extractLhsComments(caseTree, state, transformedBlockBuilder);
1148+
int codeBlockStart = extractLhsComments(caseTree, state, transformedBlockBuilder);
1149+
transformedBlockBuilder.append(state.getSourceCode(), codeBlockStart, codeBlockEnd);
11331150
}
1134-
transformedBlockBuilder.append(state.getSourceCode(), codeBlockStart, codeBlockEnd);
11351151

11361152
return transformedBlockBuilder.toString();
11371153
}

core/src/test/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitchTest.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3659,34 +3659,53 @@ public void unnecessaryBreaks() {
36593659
}
36603660

36613661
@Test
3662-
public void fallOutComment() {
3662+
public void mixedExpressionsAndYields() {
36633663
assumeTrue(RuntimeVersion.isAtLeast14());
36643664
refactoringHelper
36653665
.addInputLines(
36663666
"Test.java",
36673667
"public class Test {",
3668-
" void f(int x) {",
3668+
" String f(int x) {",
36693669
" switch (x) {",
36703670
" case 0:",
3671-
" System.err.println(\"ZERO\");",
3672-
" break;",
3671+
" return \"ZERO\";",
3672+
" case 1:",
3673+
" return \"ONE\";",
3674+
" case 2: // hello",
3675+
" // world",
3676+
" System.err.println();",
3677+
" System.err.println();",
3678+
" return \"TWO\";",
3679+
" // hello",
3680+
" // world",
36733681
" default:",
3674-
" // fall out",
3682+
" return \"\";",
36753683
" }",
36763684
" }",
36773685
"}")
36783686
.addOutputLines(
36793687
"Test.java",
36803688
"public class Test {",
3681-
" void f(int x) {",
3682-
" switch (x) {",
3683-
" case 0 -> System.err.println(\"ZERO\");",
3684-
" default -> {}",
3685-
" }",
3689+
" String f(int x) {",
3690+
" return switch (x) {",
3691+
" case 0 -> \"ZERO\";",
3692+
" case 1 -> \"ONE\";",
3693+
" case 2 -> {",
3694+
" // hello",
3695+
" // world",
3696+
" System.err.println();",
3697+
" System.err.println();",
3698+
" yield \"TWO\";",
3699+
" }",
3700+
" // hello",
3701+
" // world",
3702+
" default -> \"\";",
3703+
" };",
36863704
" }",
36873705
"}")
36883706
.setArgs(
3689-
ImmutableList.of("-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion"))
3707+
"-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion=true",
3708+
"-XepOpt:StatementSwitchToExpressionSwitch:EnableReturnSwitchConversion=true")
36903709
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);
36913710
}
36923711
}

0 commit comments

Comments
 (0)