Skip to content

Commit 83e6582

Browse files
graememorganError Prone Team
authored andcommitted
RedundantSetterCall: note that setFooValue and setFooBytes can be aliases for the field foo.
Flume: unknown commit PiperOrigin-RevId: 800535370
1 parent db62c8c commit 83e6582

File tree

3 files changed

+67
-3
lines changed

3 files changed

+67
-3
lines changed

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

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package com.google.errorprone.bugpatterns;
1818

19+
import static com.google.common.base.Ascii.toUpperCase;
1920
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
2021
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
22+
import static com.google.common.collect.ImmutableSet.toImmutableSet;
2123
import static com.google.common.collect.Streams.stream;
2224
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
2325
import static com.google.errorprone.VisitorState.memoize;
@@ -39,10 +41,12 @@
3941
import com.google.auto.value.AutoValue;
4042
import com.google.common.collect.ArrayListMultimap;
4143
import com.google.common.collect.ImmutableMap;
44+
import com.google.common.collect.ImmutableSet;
4245
import com.google.common.collect.Iterables;
4346
import com.google.common.collect.ListMultimap;
4447
import com.google.errorprone.BugPattern;
4548
import com.google.errorprone.BugPattern.StandardTags;
49+
import com.google.errorprone.ErrorProneFlags;
4650
import com.google.errorprone.VisitorState;
4751
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
4852
import com.google.errorprone.fixes.SuggestedFix;
@@ -105,16 +109,27 @@ && isWithinAutoValueBuilder(symbol, state)
105109
(ExpressionTree) state.getPath().getParentPath().getParentPath().getLeaf(),
106110
state)));
107111

112+
private final boolean improvements;
113+
108114
@Inject
109-
RedundantSetterCall() {}
115+
RedundantSetterCall(ErrorProneFlags flags) {
116+
this.improvements = flags.getBoolean("RedundantSetterCall:Improvements").orElse(true);
117+
}
110118

111119
@Override
112120
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
113121
if (!TERMINAL_FLUENT_SETTER.matches(tree, state)) {
114122
return Description.NO_MATCH;
115123
}
124+
125+
var owner = getUpperBound(getType(tree), state.getTypes()).tsym.owner;
126+
boolean isProto = owner != null && isSubtype(owner.type, MESSAGE_LITE.get(state), state);
127+
116128
ListMultimap<Field, FieldWithValue> setters = ArrayListMultimap.create();
117-
ImmutableMap<String, OneOfField> oneOfSetters = scanForOneOfSetters(getType(tree), state);
129+
ImmutableMap<String, OneOfField> oneOfSetters =
130+
isProto ? scanForOneOfSetters(getType(tree), state) : ImmutableMap.of();
131+
ImmutableSet<String> fieldNames = isProto ? getFields(owner) : ImmutableSet.of();
132+
118133
Type type = ASTHelpers.getReturnType(tree);
119134
for (ExpressionTree current = tree;
120135
FLUENT_SETTER.matches(current, state);
@@ -133,6 +148,17 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
133148
if (methodName.endsWith("Builder")) {
134149
break;
135150
}
151+
if (improvements && isProto && methodName.startsWith("set")) {
152+
String withoutSet = methodName.replaceFirst("^set", "");
153+
if (!fieldNames.contains(toUpperCase(withoutSet))) {
154+
if (methodName.endsWith("Value")) {
155+
methodName = methodName.replaceFirst("Value$", "");
156+
}
157+
if (methodName.endsWith("Bytes")) {
158+
methodName = methodName.replaceFirst("Bytes$", "");
159+
}
160+
}
161+
}
136162
for (FieldType fieldType : FieldType.values()) {
137163
FieldWithValue match = fieldType.match(methodName, method, state);
138164
if (match != null) {
@@ -158,7 +184,8 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
158184
return Description.NO_MATCH;
159185
}
160186

161-
private ImmutableMap<String, OneOfField> scanForOneOfSetters(Type type, VisitorState state) {
187+
private static ImmutableMap<String, OneOfField> scanForOneOfSetters(
188+
Type type, VisitorState state) {
162189
var owner = getUpperBound(type, state.getTypes()).tsym.owner;
163190
if (owner == null || isSubtype(owner.type, GENERATED_MESSAGE_LITE.get(state), state)) {
164191
return ImmutableMap.of();
@@ -179,9 +206,27 @@ private ImmutableMap<String, OneOfField> scanForOneOfSetters(Type type, VisitorS
179206
return builder.buildOrThrow();
180207
}
181208

209+
/**
210+
* Returns all the field names in the proto in uppercase with all components concatenated.
211+
*
212+
* <p>This is an odd format, but it works to compare the existence of a field based on a getter,
213+
* given both {@code foo_bar} and {@code fooBar} as field names generate a getter named {@code
214+
* getFooBar} (so we normalise to {@code FOOBAR}).
215+
*/
216+
private static ImmutableSet<String> getFields(Symbol proto) {
217+
return getEnclosedElements(proto).stream()
218+
.map(element -> element.getSimpleName().toString())
219+
.filter(name -> name.endsWith("_FIELD_NUMBER"))
220+
.map(name -> toUpperCase(name.replaceFirst("_FIELD_NUMBER$", "").replace("_", "")))
221+
.collect(toImmutableSet());
222+
}
223+
182224
private static final Supplier<Type> GENERATED_MESSAGE_LITE =
183225
memoize(state -> state.getTypeFromString("com.google.protobuf.GeneratedMessageLite"));
184226

227+
private static final Supplier<Type> MESSAGE_LITE =
228+
memoize(state -> state.getTypeFromString("com.google.protobuf.MessageLite"));
229+
185230
private Description describe(
186231
Field field, Collection<FieldWithValue> locations, VisitorState state) {
187232
// We flag up all duplicate sets, but only suggest a fix if the setter is given the same

core/src/test/proto/proto3_test.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,10 @@ enum TestProto3Enum {
3131
VALUE_1 = 1;
3232
VALUE_2 = 2;
3333
}
34+
35+
message TestProto3WithOneof {
36+
oneof foo {
37+
TestProto3Enum enum_field = 1;
38+
string string_field = 2;
39+
}
40+
}

core/src/test/proto/proto_test.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ message TestProtoMessage {
3333
/* Named ending in "builder" to check the Java builder API */
3434
optional TestFieldProtoMessage foo_builder = 6;
3535

36+
optional string string_field = 7;
37+
3638
extensions 100 to 199;
3739
}
3840

@@ -41,13 +43,23 @@ message TestOneOfMessage {
4143
string foo = 1;
4244
string bar = 2;
4345
string baz = 3;
46+
TestEnum enum_field = 6;
4447
}
4548
oneof AnotherOneOf {
4649
string quux = 4;
4750
string frobber = 5;
4851
}
4952
}
5053

54+
message TestProtoWithConfusingNames {
55+
optional TestEnum foo = 1;
56+
optional int32 foo_value = 2;
57+
58+
// Fields that both collide, and are named in camelcase.
59+
optional TestEnum barField = 3;
60+
optional int32 barFieldValue = 4;
61+
}
62+
5163
message TestMessageContainingOneOfMessage {
5264
optional TestOneOfMessage message = 1;
5365
}

0 commit comments

Comments
 (0)