Skip to content

Commit 80f3e71

Browse files
authored
Upgrade errorprone to 2.39.0 (#68)
1 parent 2867975 commit 80f3e71

12 files changed

Lines changed: 257 additions & 67 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
<sonar-java-frontend.version>8.2.0.36672</sonar-java-frontend.version>
4242
<sonar-orchestrator.version>5.6.0.2578</sonar-orchestrator.version>
4343

44-
<errorprone.version>2.38.0</errorprone.version>
44+
<errorprone.version>2.39.0</errorprone.version>
4545
<nullaway.version>0.12.6</nullaway.version>
4646
<errorprone.slf4j.version>0.1.28</errorprone.slf4j.version>
4747
<picnic.errorprone.support.version>0.23.0</picnic.errorprone.support.version>

sonar-erroraway-lib/src/main/java/com/github/erroraway/rules/ErrorAwayRulesMapping.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public final class ErrorAwayRulesMapping {
3434
public static final String ERRORPRONE_SLF4J_REPOSITORY = "errorprone-slf4j";
3535
public static final String PICNIC_REPOSITORY = "picnic-errorprone";
3636

37-
public static final int ERRORPRONE_REPOSITORY_RULES_COUNT = 468;
37+
public static final int ERRORPRONE_REPOSITORY_RULES_COUNT = 471;
3838
public static final int NULLAWAY_REPOSITORY_RULES_COUNT = 1;
3939
public static final int ERRORPRONE_SLF4J_REPOSITORY_RULES_COUNT = 8;
4040
public static final int PICNIC_REPOSITORY_RULES_COUNT = 45;

sonar-erroraway-maven-plugin/src/main/resources/errorprone/bugpattern/AndroidJdkLibsChecker.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ only the latest or unreleased devices can handle
33

44
## Suppression
55

6-
WARNING: We _strongly_ recommend checking your code with Android Lint if
6+
WARNING: We *strongly* recommend checking your code with Android Lint if
77
suppressing or disabling this check.
88

99
The check can be suppressed in code that deliberately only targets newer Android
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Lenient format strings, such as those accepted by `Preconditions`, are often
2+
constructed lazily. The message is rarely needed, so it should either be cheap
3+
to construct or constructed only when needed. This check ensures that these
4+
messages are not constructed using expensive methods that are evaluated eagerly.
5+
6+
Prefer this:
7+
8+
```java
9+
checkNotNull(foo, "hello %s", name);
10+
```
11+
12+
instead of this:
13+
14+
```java
15+
checkNotNull(foo, String.format("hello %s", name));
16+
```

sonar-erroraway-maven-plugin/src/main/resources/errorprone/bugpattern/MathAbsoluteNegative.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ or map negative numbers onto the non-negative range:
2828

2929
```java
3030
long lng = r.nextLong();
31-
lng = (lng == Long.MIN_VALUE) ? 0 : Math.abs(lng);
31+
32+
long bestForHashCodes = lng & Long.MAX_VALUE;
33+
long bestForMath = LongMath.saturatedAbs(lng);
3234
```

sonar-erroraway-maven-plugin/src/main/resources/errorprone/bugpattern/MissingCasesInEnumSwitch.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,41 @@ switch statement on an enum type to either handle all values of the enum, or
2929
have a default statement group.
3030

3131
[style]: https://google.github.io/styleguide/javaguide.html#s4.8.4.3-switch-default
32+
33+
## Library skew
34+
35+
If libraries are compiled against different versions of the same enum it's
36+
possible for the switch statement to encounter an enum value despite it
37+
otherwise being thought to be exhaustive. If there is no default branch code
38+
execution will simply fall out of the switch statement.
39+
40+
Since developers may have assumed this to be impossible, it may be helpful to
41+
add a default branch when library skew is a concern, however, you may not want
42+
to give up checking to ensure that all cases are handled. Therefore if a default
43+
branch exists with a comment containing "skew", the default will not be
44+
considered for exhaustiveness. For example:
45+
46+
```java
47+
enum TrafficLightColour { RED, GREEN, YELLOW }
48+
49+
void approachIntersection(TrafficLightColour state) {
50+
switch (state) {
51+
case GREEN:
52+
proceed();
53+
break;
54+
case YELLOW:
55+
case RED:
56+
stop();
57+
break;
58+
default: // In case of skew we may get an unknown value, always stop.
59+
stop();
60+
break;
61+
}
62+
}
63+
```
64+
65+
In this case the default branch is providing runtime safety for unknown enum
66+
values while also still enforcing that all known enum values are handled.
67+
68+
Note: The [UnnecessaryDefaultInEnumSwitch](UnnecessaryDefaultInEnumSwitch.md)
69+
check will not classify the default as unnecessary if it has the "skew" comment.

sonar-erroraway-maven-plugin/src/main/resources/errorprone/bugpattern/OperatorPrecedence.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ misinterpreted.
1414
For example, consider this:
1515

1616
```java
17-
boolean d = (a && b) || c;",
18-
boolean e = (a || b) ? c : d;",
19-
int z = (x + y) << 2;",
17+
boolean d = (a && b) || c;
18+
boolean e = (a || b) ? c : d;
19+
int z = (x + y) << 2;
2020
```
2121

2222
Instead of this:
2323

2424
```java
25-
boolean r = a && b || c;",
26-
boolean e = a || b ? c : d;",
27-
int z = x + y << 2;",
25+
boolean r = a && b || c;
26+
boolean e = a || b ? c : d;
27+
int z = x + y << 2;
2828
```

sonar-erroraway-maven-plugin/src/main/resources/errorprone/bugpattern/PreconditionsExpensiveString.md

Lines changed: 0 additions & 16 deletions
This file was deleted.

sonar-erroraway-maven-plugin/src/main/resources/errorprone/bugpattern/StatementSwitchToExpressionSwitch.md

Lines changed: 140 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,34 @@
1-
We're trying to make `switch` statements simpler to understand at a glance.
2-
Misunderstanding the control flow of a `switch` block is a common source of
3-
bugs.
1+
We're trying to make `switch`es simpler to understand at a glance.
2+
Misunderstanding the control flow of a `switch` is a common source of bugs.
43

5-
### Statement `switch` statements:
4+
As part of this simplification, new-style arrow (`->`) switches are encouraged
5+
instead of old-style colon (`:`) switches. And where possible, neighboring cases
6+
are grouped together (e.g. `case A, B, C`).
67

7-
* Have a colon between the `case` and the case's code. For example, `case
8+
### Old-style colon (`:`) `switch`es:
9+
10+
* Have a colon between the `case` and the `case`'s code. For example, `case
811
HEARTS:`
912
* Because of the potential for fall-through, it takes time and cognitive load
10-
to understand the control flow for each `case`
11-
* When a `switch` block is large, just skimming each `case` can be toilsome
12-
* Fall-though can also be conditional (see example below). In this scenario,
13-
one would need to reason about all possible flows for each `case`. When
14-
conditionally falling-through multiple `case`s in a row is possible, the
15-
number of potential control flows can grow rapidly
16-
17-
### Expression `switch` statements
18-
19-
* Have an arrow between the `case` and the case's code. For example, `case
13+
to understand the control flow. When a `switch` block is large, just
14+
skimming each `case` can be toilsome. Fall-through can also be conditional
15+
(see example 5. below). In this scenario, one would potentially need to
16+
reason about all possible flows for each `case`. When conditionally
17+
falling-through multiple `case`s, the number of potential control flows can
18+
grow rapidly
19+
* Lexical scopes overlap, which can lead to surprising behaviors: definitions
20+
of local variables from earlier `case`s are propagated down to later
21+
`case`s, however the *values* that initialize those local variables do not
22+
propagate in the same way
23+
24+
### New-style arrow (`->`) `switch`es:
25+
26+
* Have an arrow between the `case` and the `case`'s code. For example, `case
2027
HEARTS ->`
21-
* With an expression `switch` statement, you know at a glance that no cases
22-
fall through. No control flow analysis needed
28+
* No `case`s fall through; no control flow analysis needed
2329
* Safely and easily reorder `case`s (within a `switch`)
24-
* It's also possible to group identical cases together (`case A, B, C`) for
25-
improved readability
30+
* Lexical scopes are isolated between different `case`s; if you define a local
31+
variable within a `case`, it can only be used within that specific `case`.
2632

2733
### Examples
2834

@@ -48,7 +54,7 @@ private void foo(Suit suit) {
4854
}
4955
```
5056

51-
Which can be simplified into the following expression `switch`:
57+
Which can be simplified by grouping and using a new-style switch:
5258

5359
``` {.good}
5460
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
@@ -65,16 +71,14 @@ private void foo(Suit suit) {
6571
}
6672
```
6773

68-
#### 2. Return switch
74+
#### 2. `return switch ...`
6975

70-
Sometimes `switch` is used with `return`. Below, even though a `case` is
71-
specified for each possible value of the `enum`, note that we nevertheless need
72-
a "should never happen" clause:
76+
Sometimes `switch` is used with a `return` for each `case`, like this:
7377

7478
``` {.bad}
7579
enum SideOfCoin {OBVERSE, REVERSE};
7680
77-
private String foo(SideOfCoin sideOfCoin) {
81+
private String renderName(SideOfCoin sideOfCoin) {
7882
switch(sideOfCoin) {
7983
case OBVERSE:
8084
return "Heads";
@@ -86,43 +90,45 @@ private String foo(SideOfCoin sideOfCoin) {
8690
}
8791
```
8892

89-
Using an expression switch simplifies the code and removes the need for an
90-
explicit "should never happen" clause.
93+
Note that even though a `case` is present for each possible value of the `enum`,
94+
a boilerplate "should never happen" clause is still needed. The transformed code
95+
is simpler and doesn't need a "should never happen" clause.
9196

9297
```
9398
enum SideOfCoin {OBVERSE, REVERSE};
9499
95-
private String foo(SideOfCoin sideOfCoin) {
100+
private String renderName(SideOfCoin sideOfCoin) {
96101
return switch(sideOfCoin) {
97102
case OBVERSE -> "Heads";
98103
case REVERSE -> "Tails";
99104
};
100105
}
101106
```
102107

103-
If you nevertheless wish to have an explicit "should never happen" clause, this
104-
can be accomplished by placing the logic under a `default` case. For example:
108+
If you nevertheless wish to define an explicit "should never happen" clause,
109+
this can be accomplished by placing the logic inside a `default` case. For
110+
example:
105111

106112
```
107-
108113
enum SideOfCoin {OBVERSE, REVERSE};
109114
110115
private String foo(SideOfCoin sideOfCoin) {
111116
return switch(sideOfCoin) {
112117
case OBVERSE -> "Heads";
113118
case REVERSE -> "Tails";
114-
default -> {
115-
// This should never happen
116-
throw new RuntimeException("Unknown side of coin");
117-
}
119+
default -> throw new RuntimeException("Unknown side of coin"); // should never happen
118120
};
119121
}
120122
```
121123

122-
#### 3. Assignment switch
124+
When the checker detects an existing `default` that appears to be redundant, it
125+
may suggest a secondary auto-fix which removes the redundant `default` and its
126+
code (if any).
127+
128+
#### 3. Assignment `switch`
123129

124-
If every branch of a `switch` is making an assignment to the same variable, it
125-
can be re-written as an assignment switch:
130+
If every branch of a `switch` is making an assignment to the same variable, the
131+
code can be simplified into a combined assignment and `switch`:
126132

127133
``` {.bad}
128134
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
@@ -157,14 +163,107 @@ private void updateScore(Suit suit) {
157163
case HEARTS, DIAMONDS -> -1;
158164
case SPADES -> 2;
159165
case CLUBS -> 3;
160-
};
166+
};
167+
}
168+
```
169+
170+
Taking this one step further: if a local variable is defined, and then
171+
immediately followed by a `switch` in which every `case` assigns to that same
172+
variable, then all three (the `switch`, the variable declaration, and the
173+
assignment) can be merged:
174+
175+
``` {.bad}
176+
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
177+
178+
private void updateStatus(Suit suit) {
179+
int score;
180+
181+
switch(suit) {
182+
case HEARTS:
183+
// Fall thru
184+
case DIAMONDS:
185+
score = 1;
186+
break;
187+
case SPADES:
188+
score = 2;
189+
break;
190+
case CLUBS:
191+
score = 3;
192+
}
193+
...
194+
195+
}
196+
```
197+
198+
Becomes:
199+
200+
```
201+
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
202+
203+
private void updateStatus(Suit suit) {
204+
int score = switch(suit) {
205+
case HEARTS, DIAMONDS -> 1;
206+
case SPADES -> 2;
207+
case CLUBS -> 3;
208+
};
209+
...
161210
}
162211
```
163212

164-
#### 4. Complex control flows
213+
#### 4. Just converting to new arrow `switch`
214+
215+
Even when the simplifications discussed above are not applicable, conversion to
216+
new arrow `switch`es can be automated by this checker:
217+
218+
``` {.bad}
219+
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
220+
221+
private void processEvent(Suit suit) {
222+
switch (suit) {
223+
case CLUBS:
224+
String message = "hello";
225+
var anotherMessage = "salut";
226+
processMessages(message, anotherMessage);
227+
break;
228+
case DIAMONDS:
229+
anotherMessage = "bonjour";
230+
processMessage(anotherMessage);
231+
}
232+
}
233+
```
234+
235+
Note that the local variables referenced in multiple cases are hoisted up out of
236+
the `switch` statement, and `var` declarations are converted to explicit types,
237+
resulting in:
238+
239+
```
240+
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
241+
242+
private void processEvent(Suit suit) {
243+
String anotherMessage;
244+
switch (suit) {
245+
case CLUBS -> {
246+
String message = "hello";
247+
anotherMessage = "salut";
248+
processMessages(message, anotherMessage);
249+
}
250+
case DIAMONDS -> {
251+
anotherMessage = "bonjour";
252+
processMessage(anotherMessage);
253+
}
254+
}
255+
}
256+
```
257+
258+
#### 5. Complex control flows
165259

166260
Here's an example of a complex statement `switch` with conditional fall-through
167-
and complex control flows. How many potential execution paths can you spot?
261+
and various control flows. Unfortunately, the checker does not currently have
262+
the ability to automatically convert such code to new-style arrow `switch`es.
263+
Manually converting the code could be a good opportunity to improve its
264+
readability.
265+
266+
How many potential execution paths can you spot?
168267

169268
``` {.bad}
170269
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};

0 commit comments

Comments
 (0)