Skip to content

Commit ccd3ca6

Browse files
java-team-github-botError Prone Team
authored and
Error Prone Team
committed
Add handling of toBuilder()
toBuilder() was identified as a getter leading to not correctly identifying the case where all the getters are prefixed. PiperOrigin-RevId: 658585727
1 parent d887307 commit ccd3ca6

File tree

2 files changed

+44
-28
lines changed

2 files changed

+44
-28
lines changed

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

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import static com.google.errorprone.matchers.Description.NO_MATCH;
2222
import static com.google.errorprone.matchers.Matchers.hasModifier;
2323
import static com.google.errorprone.util.ASTHelpers.getSymbol;
24+
import static com.google.errorprone.util.ASTHelpers.getType;
25+
import static com.google.errorprone.util.ASTHelpers.hasAnnotation;
26+
import static com.google.errorprone.util.ASTHelpers.isSameType;
2427
import static java.beans.Introspector.decapitalize;
2528

2629
import com.google.auto.value.AutoValue;
@@ -34,7 +37,6 @@
3437
import com.google.errorprone.fixes.SuggestedFix;
3538
import com.google.errorprone.matchers.Description;
3639
import com.google.errorprone.matchers.Matcher;
37-
import com.google.errorprone.util.ASTHelpers;
3840
import com.sun.source.tree.ClassTree;
3941
import com.sun.source.tree.IdentifierTree;
4042
import com.sun.source.tree.MethodTree;
@@ -60,31 +62,24 @@ public class AutoValueBoxedValues extends BugChecker implements ClassTreeMatcher
6062

6163
@Override
6264
public Description matchClass(ClassTree tree, VisitorState state) {
63-
if (!ASTHelpers.hasAnnotation(tree, AutoValue.class.getName(), state)) {
65+
if (!hasAnnotation(tree, AutoValue.class.getName(), state)) {
6466
return NO_MATCH;
6567
}
6668

69+
Optional<ClassTree> builderClass = findBuilderClass(tree, state);
70+
6771
// Identify and potentially fix the getters.
68-
ImmutableList<Getter> getters = handleGetterMethods(tree, state);
72+
ImmutableList<Getter> getters = handleGetterMethods(tree, state, builderClass);
6973

7074
// If we haven't modified any getter, it's ok to stop.
7175
if (getters.stream().allMatch(getter -> getter.fix().isEmpty())) {
7276
return NO_MATCH;
7377
}
7478

75-
// Handle the Builder class, if there is one.
76-
boolean builderFound = false;
77-
for (Tree memberTree : tree.getMembers()) {
78-
if (memberTree instanceof ClassTree
79-
&& ASTHelpers.hasAnnotation(memberTree, AutoValue.Builder.class.getName(), state)) {
80-
handleSetterMethods((ClassTree) memberTree, state, getters);
81-
builderFound = true;
82-
break;
83-
}
84-
}
85-
86-
// If a builder was not found, handle the factory methods.
87-
if (!builderFound) {
79+
// Handle the Builder class, if there is one. Otherwise handle the factory methods.
80+
if (builderClass.isPresent()) {
81+
handleSetterMethods(builderClass.get(), state, getters);
82+
} else {
8883
handleFactoryMethods(tree, state, getters);
8984
}
9085

@@ -103,13 +98,16 @@ public Description matchClass(ClassTree tree, VisitorState state) {
10398
* @param state The visitor state.
10499
* @return The list of {@link Getter} in the class.
105100
*/
106-
private ImmutableList<Getter> handleGetterMethods(ClassTree classTree, VisitorState state) {
101+
private ImmutableList<Getter> handleGetterMethods(
102+
ClassTree classTree, VisitorState state, Optional<ClassTree> builderClass) {
107103
return classTree.getMembers().stream()
108104
.filter(MethodTree.class::isInstance)
109105
.map(memberTree -> (MethodTree) memberTree)
110106
.filter(
111107
methodTree ->
112-
ABSTRACT_MATCHER.matches(methodTree, state) && methodTree.getParameters().isEmpty())
108+
ABSTRACT_MATCHER.matches(methodTree, state)
109+
&& methodTree.getParameters().isEmpty()
110+
&& !isToBuilderMethod(methodTree, state, builderClass))
113111
.map(methodTree -> maybeFixGetter(methodTree, state))
114112
.collect(toImmutableList());
115113
}
@@ -120,7 +118,7 @@ private ImmutableList<Getter> handleGetterMethods(ClassTree classTree, VisitorSt
120118
*/
121119
private Getter maybeFixGetter(MethodTree method, VisitorState state) {
122120
Getter getter = Getter.of(method);
123-
Type type = ASTHelpers.getType(method.getReturnType());
121+
Type type = getType(method.getReturnType());
124122
if (!isSuppressed(method, state)
125123
&& !hasNullableAnnotation(method)
126124
&& isBoxedPrimitive(state, type)) {
@@ -143,7 +141,8 @@ private void handleSetterMethods(ClassTree classTree, VisitorState state, List<G
143141
.filter(
144142
methodTree ->
145143
ABSTRACT_MATCHER.matches(methodTree, state)
146-
&& methodTree.getParameters().size() == 1)
144+
&& methodTree.getParameters().size() == 1
145+
&& isSameType(getType(methodTree.getReturnType()), getType(classTree), state))
147146
.forEach(methodTree -> maybeFixSetter(methodTree, state, getters));
148147
}
149148

@@ -162,7 +161,7 @@ && matchGetterAndSetter(getter.method(), methodTree, allGettersPrefixed))
162161
.findAny();
163162
if (fixedGetter.isPresent()) {
164163
var parameter = methodTree.getParameters().get(0);
165-
Type type = ASTHelpers.getType(parameter);
164+
Type type = getType(parameter);
166165
if (isBoxedPrimitive(state, type) && !hasNullableAnnotation(parameter)) {
167166
suggestRemoveUnnecessaryBoxing(parameter.getType(), state, type, fixedGetter.get().fix());
168167
}
@@ -188,10 +187,8 @@ private void handleFactoryMethods(ClassTree classTree, VisitorState state, List<
188187
.filter(
189188
methodTree ->
190189
STATIC_MATCHER.matches(methodTree, state)
191-
&& ASTHelpers.isSameType(
192-
ASTHelpers.getType(methodTree.getReturnType()),
193-
ASTHelpers.getType(classTree),
194-
state)
190+
&& isSameType(
191+
getType(methodTree.getReturnType()), getType(classTree), state)
195192
&& isTrivialFactoryMethod(methodTree, getters.size()))
196193
.findAny();
197194
if (trivialFactoryMethod.isEmpty()) {
@@ -201,7 +198,7 @@ && isTrivialFactoryMethod(methodTree, getters.size()))
201198
Getter getter = getters.get(idx);
202199
if (!getter.fix().isEmpty()) {
203200
var parameter = trivialFactoryMethod.get().getParameters().get(idx);
204-
Type type = ASTHelpers.getType(parameter);
201+
Type type = getType(parameter);
205202
if (isBoxedPrimitive(state, type) && !hasNullableAnnotation(parameter)) {
206203
suggestRemoveUnnecessaryBoxing(parameter.getType(), state, type, getter.fix());
207204
}
@@ -228,6 +225,23 @@ private static boolean isBoxedPrimitive(VisitorState state, Type type) {
228225
return unboxed != null && unboxed.getTag() != TypeTag.NONE && unboxed.getTag() != TypeTag.VOID;
229226
}
230227

228+
private static Optional<ClassTree> findBuilderClass(ClassTree tree, VisitorState state) {
229+
return tree.getMembers().stream()
230+
.filter(
231+
memberTree ->
232+
memberTree instanceof ClassTree
233+
&& hasAnnotation(memberTree, AutoValue.Builder.class.getName(), state))
234+
.map(memberTree -> (ClassTree) memberTree)
235+
.findAny();
236+
}
237+
238+
private static boolean isToBuilderMethod(
239+
MethodTree methodTree, VisitorState state, Optional<ClassTree> builderClass) {
240+
return builderClass.isPresent()
241+
&& !STATIC_MATCHER.matches(methodTree, state)
242+
&& isSameType(getType(methodTree.getReturnType()), getType(builderClass.get()), state);
243+
}
244+
231245
private static boolean allGettersPrefixed(List<Getter> getters) {
232246
return getters.stream().allMatch(getter -> !getterPrefix(getter.method()).isEmpty());
233247
}
@@ -238,7 +252,7 @@ private static String getterPrefix(MethodTree getterMethod) {
238252
return "get";
239253
} else if (name.startsWith("is")
240254
&& !name.equals("is")
241-
&& ASTHelpers.getType(getterMethod.getReturnType()).getKind() == TypeKind.BOOLEAN) {
255+
&& getType(getterMethod.getReturnType()).getKind() == TypeKind.BOOLEAN) {
242256
return "is";
243257
}
244258
return "";

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ public void settersWithoutSetPrefix() {
536536
}
537537

538538
@Test
539-
public void allGettersWithPrefix() {
539+
public void allGettersWithPrefix_ignoreToBuilder() {
540540
if (!withBuilder) {
541541
return;
542542
}
@@ -548,6 +548,7 @@ public void allGettersWithPrefix() {
548548
"abstract class Test {",
549549
" public abstract Long getLongId();",
550550
" public abstract boolean isBooleanId();",
551+
" public abstract Builder toBuilder();",
551552
" @AutoValue.Builder",
552553
" abstract static class Builder {",
553554
" abstract Builder setLongId(Long value);",
@@ -562,6 +563,7 @@ public void allGettersWithPrefix() {
562563
"abstract class Test {",
563564
" public abstract long getLongId();",
564565
" public abstract boolean isBooleanId();",
566+
" public abstract Builder toBuilder();",
565567
" @AutoValue.Builder",
566568
" abstract static class Builder {",
567569
" abstract Builder setLongId(long value);",

0 commit comments

Comments
 (0)