diff --git a/annotation/pom.xml b/annotation/pom.xml
index fbbba7c5524..9411bcb2b60 100644
--- a/annotation/pom.xml
+++ b/annotation/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
@BugPattern annotation
diff --git a/annotations/pom.xml b/annotations/pom.xml
index 62ebfe88a40..5d66e1f6860 100644
--- a/annotations/pom.xml
+++ b/annotations/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
error-prone annotations
diff --git a/annotations/src/main/java/module-info.java b/annotations/src/main/java/module-info.java
new file mode 100644
index 00000000000..3dccd229104
--- /dev/null
+++ b/annotations/src/main/java/module-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 The Error Prone Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+open module com.google.errorprone.annotations {
+ requires java.compiler;
+
+ exports com.google.errorprone.annotations;
+ exports com.google.errorprone.annotations.concurrent;
+}
diff --git a/check_api/pom.xml b/check_api/pom.xml
index 1a96ffd6473..a3c011f48a9 100644
--- a/check_api/pom.xml
+++ b/check_api/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
error-prone check api
diff --git a/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java b/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java
index 1707c6a4b50..79bf0896a8f 100644
--- a/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java
+++ b/check_api/src/main/java/com/google/errorprone/util/ASTHelpers.java
@@ -2813,6 +2813,9 @@ private static Method getCaseTreeGetExpressionsMethod() {
* }.
*/
public static boolean isRuleKind(CaseTree caseTree) {
+ if (GET_CASE_KIND_METHOD == null) {
+ return false;
+ }
Enum> kind;
try {
kind = (Enum>) GET_CASE_KIND_METHOD.invoke(caseTree);
diff --git a/core/pom.xml b/core/pom.xml
index db42176e114..1653f7ace57 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
error-prone library
diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitch.java b/core/src/main/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitch.java
index 82011eb8dc7..11ec6a498be 100644
--- a/core/src/main/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitch.java
+++ b/core/src/main/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitch.java
@@ -33,6 +33,7 @@
import static java.util.stream.Collectors.joining;
import com.google.auto.value.AutoValue;
+import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -45,6 +46,7 @@
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.util.ASTHelpers;
+import com.google.errorprone.util.ErrorProneComment;
import com.google.errorprone.util.Reachability;
import com.google.errorprone.util.SourceVersion;
import com.sun.source.tree.AssignmentTree;
@@ -513,6 +515,11 @@ private static SuggestedFix convertDirectlyToExpressionSwitch(
SwitchTree switchTree, VisitorState state, AnalysisResult analysisResult) {
List extends CaseTree> cases = switchTree.getCases();
+ ImmutableList allSwitchComments =
+ state.getTokensForNode(switchTree).stream()
+ .flatMap(errorProneToken -> errorProneToken.comments().stream())
+ .collect(toImmutableList());
+
StringBuilder replacementCodeBuilder = new StringBuilder();
replacementCodeBuilder
.append("switch ")
@@ -540,19 +547,49 @@ private static SuggestedFix convertDirectlyToExpressionSwitch(
replacementCodeBuilder.append(
isDefaultCase ? "default" : printCaseExpressions(caseTree, state));
+ Optional commentsAfterCaseOptional =
+ extractCommentsAfterCase(switchTree, allSwitchComments, state, caseIndex);
if (analysisResult.groupedWithNextCase().get(caseIndex)) {
firstCaseInGroup = false;
replacementCodeBuilder.append(", ");
// Capture comments from this case so they can be added to the group's transformed case
if (!transformedBlockSource.trim().isEmpty()) {
- groupedCaseCommentsAccumulator.append(removeFallThruLines(transformedBlockSource));
+ String commentsToAppend = removeFallThruLines(transformedBlockSource);
+ if (groupedCaseCommentsAccumulator.length() > 0) {
+ groupedCaseCommentsAccumulator.append("\n");
+ }
+ groupedCaseCommentsAccumulator.append(commentsToAppend);
+ }
+
+ if (commentsAfterCaseOptional.isPresent()) {
+ if (groupedCaseCommentsAccumulator.length() > 0) {
+ groupedCaseCommentsAccumulator.append("\n");
+ }
+ groupedCaseCommentsAccumulator.append(commentsAfterCaseOptional.get());
}
+
// Add additional cases to the list on the lhs of the arrow
continue;
} else {
- // This case is the last case in its group, so insert the collected comments from the lhs of
- // the colon here
- transformedBlockSource = groupedCaseCommentsAccumulator + transformedBlockSource;
+ // Extract comments (if any) preceding break that was removed as redundant
+ Optional commentsBeforeRemovedBreak =
+ filteredStatements.isEmpty()
+ ? Optional.empty()
+ : extractCommentsBeforeRemovedBreak(caseTree, state, filteredStatements);
+
+ // Join together all comments and code, separating with newlines
+ transformedBlockSource =
+ Joiner.on("\n")
+ .skipNulls()
+ .join(
+ // This case is the last case in its group, so insert any comments from prior
+ // grouped cases first
+ groupedCaseCommentsAccumulator.length() == 0
+ ? null
+ : groupedCaseCommentsAccumulator.toString(),
+ transformedBlockSource.isEmpty() ? null : transformedBlockSource,
+ commentsBeforeRemovedBreak.orElse(null),
+ commentsAfterCaseOptional.orElse(null));
}
replacementCodeBuilder.append(" -> ");
@@ -570,17 +607,10 @@ private static SuggestedFix convertDirectlyToExpressionSwitch(
}
} else {
// Transformed block has code
- // Extract comments (if any) for break that was removed as redundant
- Optional commentsBeforeRemovedBreak =
- extractCommentsBeforeRemovedBreak(caseTree, state, filteredStatements);
- if (commentsBeforeRemovedBreak.isPresent()) {
- transformedBlockSource = transformedBlockSource + "\n" + commentsBeforeRemovedBreak.get();
- }
-
// To improve readability, don't use braces on the rhs if not needed
if (shouldTransformCaseWithoutBraces(filteredStatements)) {
- // Single statement with no comments - no braces needed
- replacementCodeBuilder.append(transformedBlockSource);
+ // No braces needed
+ replacementCodeBuilder.append("\n").append(transformedBlockSource);
} else {
// Use braces on the rhs
replacementCodeBuilder.append("{\n").append(transformedBlockSource).append("\n}");
@@ -599,7 +629,7 @@ private static SuggestedFix convertDirectlyToExpressionSwitch(
/**
* Transforms the supplied statement switch into a {@code return switch ...} style of expression
* switch. In this conversion, each nontrivial statement block is mapped one-to-one to a new
- * expression on the right-hand side of the arrow. Comments are presevered where possible.
+ * expression on the right-hand side of the arrow. Comments are preserved where possible.
* Precondition: the {@code AnalysisResult} for the {@code SwitchTree} must have deduced that this
* conversion is possible.
*/
@@ -903,6 +933,43 @@ private static int extractLhsComments(
return lhsEnd;
}
+ /**
+ * Extracts any comments appearing after the specified {@code caseIndex} but before the subsequent
+ * case or end of the {@code switchTree}. Comments are merged into a single string separated by
+ * newlines.
+ */
+ private static Optional extractCommentsAfterCase(
+ SwitchTree switchTree,
+ ImmutableList allSwitchComments,
+ VisitorState state,
+ int caseIndex) {
+
+ // Indexing relative to the start position of the switch statement
+ int switchStart = getStartPosition(switchTree);
+ // Invariant: caseEndIndex >= 0
+ int caseEndIndex = state.getEndPosition(switchTree.getCases().get(caseIndex)) - switchStart;
+ // Invariant: nextCaseStartIndex >= caseEndIndex
+ int nextCaseStartIndex =
+ caseIndex == switchTree.getCases().size() - 1
+ ? state.getEndPosition(switchTree) - switchStart
+ : getStartPosition(switchTree.getCases().get(caseIndex + 1)) - switchStart;
+
+ String filteredComments =
+ allSwitchComments.stream()
+ // Comments after the end of the current case and before the start of the next case
+ .filter(
+ comment ->
+ comment.getPos() >= caseEndIndex && comment.getPos() < nextCaseStartIndex)
+ .map(ErrorProneComment::getText)
+ // Remove "fall thru" comments
+ .map(commentText -> removeFallThruLines(commentText))
+ // Remove empty comments
+ .filter(commentText -> !commentText.isEmpty())
+ .collect(joining("\n"));
+
+ return filteredComments.isEmpty() ? Optional.empty() : Optional.of(filteredComments);
+ }
+
/**
* Finds the position in source corresponding to the end of the code block of the supplied {@code
* caseIndex} within all {@code cases}.
diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitchTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitchTest.java
index d1e2d6c144e..08f4a892626 100644
--- a/core/src/test/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitchTest.java
+++ b/core/src/test/java/com/google/errorprone/bugpatterns/StatementSwitchToExpressionSwitchTest.java
@@ -110,6 +110,7 @@ public void switchByEnum_removesRedundantBreak_error() {
" // Middle comment",
" System.out.println(\"obverse\");",
" // Break comment",
+ " // End comment",
" }",
" case REVERSE -> System.out.println(\"reverse\");",
" }",
@@ -268,7 +269,8 @@ public void switchByEnumCard_combinesCaseComments_error() {
" public void foo(Side side) { ",
" switch(side) {",
" case HEART -> System.out.println(\"heart2\");",
- " case DIAMOND, SPADE, CLUB -> { /* sparkly */",
+ " case DIAMOND, SPADE, CLUB -> {",
+ " /* sparkly */",
" // Empty block comment 1",
" // Empty block comment 2",
" // Start of block comment 1",
@@ -354,7 +356,7 @@ public void switchByEnumCard2_removesRedundantBreaks_error() {
" case HEART -> ",
" System.out.println(\"heart\");",
" // Pre break comment",
- " ",
+ " // Post break comment",
" case DIAMOND -> {",
" // Diamond break comment",
" break;",
@@ -444,7 +446,8 @@ public void switchByEnumCard_onlyExpressionsAndThrowAreBraceless_error() {
" case SPADE -> {",
" return;",
" }",
- " case CLUB -> throw new AssertionError();",
+ " case CLUB ->",
+ " throw new AssertionError();",
" }",
" }",
" }",
@@ -591,10 +594,11 @@ public void switchWithDefaultInMiddle_error() {
" System.out.println(\"diamond\");",
" return;",
" }",
- " default -> /* comment: */",
+ " default -> ",
+ " /* comment: */",
" System.out.println(\"club\");",
- " ",
- " case SPADE -> System.out.println(\"spade\");",
+ " case SPADE -> ",
+ " System.out.println(\"spade\");",
" }",
" }",
"}")
@@ -675,8 +679,11 @@ public void switchWithLabelledBreak_error() {
" System.out.println(\"will return\");",
" return;",
" }",
- " case DIAMOND -> {break outer;}",
- " case SPADE, CLUB -> System.out.println(\"everything else\");",
+ " case DIAMOND -> {",
+ " break outer;",
+ " }",
+ " case SPADE, CLUB -> ",
+ " System.out.println(\"everything else\");",
" }",
" }",
" }",
@@ -886,9 +893,12 @@ public void switchByEnumCardWithReturnNested1_error() {
" ",
" public void foo(Side side) { ",
" switch(side) {",
- " case HEART-> System.out.println(\"heart\");",
- " case DIAMOND -> System.out.println(\"nested1\");",
- " case SPADE, CLUB -> System.out.println(\"everything else\");",
+ " case HEART-> ",
+ " System.out.println(\"heart\");",
+ " case DIAMOND -> ",
+ " System.out.println(\"nested1\");",
+ " case SPADE, CLUB -> ",
+ " System.out.println(\"everything else\");",
" }",
" }",
"}")
@@ -1217,7 +1227,6 @@ public void switchByEnum_caseHasOnlyComments_error() {
" // more comments.",
" // Diamond comment",
" System.out.println(\"Heart or diamond\");",
- " ",
" case SPADES, CLUBS -> {",
" bar();",
" System.out.println(\"Black suit\");",
@@ -1230,6 +1239,86 @@ public void switchByEnum_caseHasOnlyComments_error() {
.doTest();
}
+ @Test
+ public void switchByEnum_accumulatedComments_error() {
+ // Comments should be aggregated across multiple cases
+ assumeTrue(RuntimeVersion.isAtLeast14());
+ helper
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};",
+ " public Test() {}",
+ " private void foo(Suit suit) {",
+ " // BUG: Diagnostic contains: [StatementSwitchToExpressionSwitch]",
+ " switch(suit) {",
+ " case /* red */ HEARTS:",
+ " // A comment here",
+ " // more comments.",
+ " case /* red */ DIAMONDS:",
+ " // Diamonds comment",
+ " case /* black */SPADES:",
+ " // Spades comment",
+ " case /* black */CLUBS:",
+ " bar();",
+ " System.out.println(\"Any suit\");",
+ " }",
+ " }",
+ " private void bar() {}",
+ "}")
+ .setArgs("-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion")
+ .doTest();
+
+ refactoringHelper
+ .addInputLines(
+ "Test.java",
+ "class Test {",
+ " enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};",
+ " public Test() {}",
+ " private void foo(Suit suit) {",
+ " // BUG: Diagnostic contains: [StatementSwitchToExpressionSwitch]",
+ " switch(suit) {",
+ " case /* red */ HEARTS:",
+ " // A comment here",
+ " // more comments.",
+ " case /* red */ DIAMONDS:",
+ " // Diamonds comment",
+ " case /* black */SPADES:",
+ " // Spades comment",
+ " case /* black */CLUBS:",
+ " bar();",
+ " System.out.println(\"Any suit\");",
+ " }",
+ " }",
+ " private void bar() {}",
+ "}")
+ .addOutputLines(
+ "Test.java",
+ "class Test {",
+ " enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};",
+ " public Test() {}",
+ " private void foo(Suit suit) {",
+ " switch(suit) {",
+ " case HEARTS, DIAMONDS, SPADES, CLUBS -> {",
+ " /* red */",
+ " // A comment here",
+ " /* red */",
+ " // more comments.",
+ " // Diamonds comment",
+ " /* black */",
+ " // Spades comment",
+ " /* black */",
+ " bar();",
+ " System.out.println(\"Any suit\");",
+ " }",
+ " }",
+ " }",
+ " private void bar() {}",
+ "}")
+ .setArgs("-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion")
+ .doTest();
+ }
+
@Test
public void switchByEnum_surroundingBracesCannotRemove_error() {
// Can't remove braces around OBVERSE because break statements are not a member of
@@ -1380,6 +1469,80 @@ public void switchByEnum_surroundingBracesEmpty_error() {
.doTest();
}
+ @Test
+ public void switchByEnum_afterReturnComments_error() {
+ assumeTrue(RuntimeVersion.isAtLeast14());
+ helper
+ .addSourceLines(
+ "Test.java",
+ "class Test {",
+ " enum Suit {HEART, SPADE, DIAMOND, CLUB};",
+ " public Test(int foo) {",
+ " }",
+ " ",
+ " public int foo(Suit suit) { ",
+ " // BUG: Diagnostic contains: [StatementSwitchToExpressionSwitch]",
+ " switch(suit) {",
+ " case HEART:",
+ " // before return comment",
+ " return 123;",
+ " // after return comment",
+ " /* more comments */",
+ " default:",
+ " }",
+ " return 0;",
+ " }",
+ "}")
+ .setArgs(
+ ImmutableList.of("-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion"))
+ .doTest();
+
+ refactoringHelper
+ .addInputLines(
+ "Test.java",
+ "class Test {",
+ " enum Suit {HEART, SPADE, DIAMOND, CLUB};",
+ " public Test(int foo) {",
+ " }",
+ " ",
+ " public int foo(Suit suit) { ",
+ " // BUG: Diagnostic contains: [StatementSwitchToExpressionSwitch]",
+ " switch(suit) {",
+ " case HEART:",
+ " // before return comment",
+ " return 123;",
+ " // after return comment",
+ " /* more comments */",
+ " default:",
+ " //default comment",
+ " }",
+ " return 0;",
+ " }",
+ "}")
+ .addOutputLines(
+ "Test.java",
+ "class Test {",
+ " enum Suit {HEART, SPADE, DIAMOND, CLUB};",
+ " public Test(int foo) {}",
+ " public int foo(Suit suit) {",
+ " switch(suit) {",
+ " case HEART -> {",
+ " // before return comment",
+ " return 123;",
+ " // after return comment",
+ " /* more comments */",
+ " }",
+ " default -> {",
+ " //default comment",
+ " }",
+ " }",
+ " return 0;",
+ " }",
+ "}")
+ .setArgs("-XepOpt:StatementSwitchToExpressionSwitch:EnableDirectConversion")
+ .doTest();
+ }
+
/**********************************
*
* Return switch test cases
diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java
index 931626e04b4..00a3dbc18c4 100644
--- a/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java
+++ b/core/src/test/java/com/google/errorprone/bugpatterns/TraditionalSwitchExpressionTest.java
@@ -50,7 +50,6 @@ public void positive() {
@Test
public void negativeStatement() {
- assumeTrue(RuntimeVersion.isAtLeast14());
testHelper
.addSourceLines(
"Test.java",
diff --git a/docgen/pom.xml b/docgen/pom.xml
index 8ee00587502..fcfda431b00 100644
--- a/docgen/pom.xml
+++ b/docgen/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
Documentation tool for generating Error Prone bugpattern documentation
diff --git a/docgen_processor/pom.xml b/docgen_processor/pom.xml
index 7d97a4cb4ca..80b0dc557f4 100644
--- a/docgen_processor/pom.xml
+++ b/docgen_processor/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
JSR-269 annotation processor for @BugPattern annotation
diff --git a/pom.xml b/pom.xml
index 8b5587c1b00..471bb4ef4f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
Error Prone parent POM
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
pom
Error Prone is a static analysis tool for Java that catches common programming mistakes at compile-time.
diff --git a/refaster/pom.xml b/refaster/pom.xml
index 968b0c2bae4..dabb53219b1 100644
--- a/refaster/pom.xml
+++ b/refaster/pom.xml
@@ -19,7 +19,7 @@
error_prone_parent
com.google.errorprone
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
4.0.0
diff --git a/test_helpers/pom.xml b/test_helpers/pom.xml
index 76fbfaf30d9..4e7ec27fd1f 100644
--- a/test_helpers/pom.xml
+++ b/test_helpers/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
error-prone test helpers
diff --git a/type_annotations/pom.xml b/type_annotations/pom.xml
index 365123af188..75ea6d5928e 100644
--- a/type_annotations/pom.xml
+++ b/type_annotations/pom.xml
@@ -21,7 +21,7 @@
com.google.errorprone
error_prone_parent
- 1.0-HEAD-SNAPSHOT
+ 2.29.2
error-prone type annotations