Skip to content

Commit ed0ff42

Browse files
klueverError Prone Team
authored and
Error Prone Team
committed
Forbid @InlineMe on methods with super(...) calls.
PiperOrigin-RevId: 711505452
1 parent c0e032c commit ed0ff42

File tree

2 files changed

+56
-60
lines changed

2 files changed

+56
-60
lines changed

core/src/main/java/com/google/errorprone/bugpatterns/inlineme/InlinabilityResult.java

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
import static com.google.common.collect.ImmutableSet.toImmutableSet;
2121
import static com.google.errorprone.util.ASTHelpers.getSymbol;
2222
import static com.google.errorprone.util.ASTHelpers.hasAnnotation;
23+
import static com.google.errorprone.util.ASTHelpers.isLocal;
24+
import static com.google.errorprone.util.ASTHelpers.isSuper;
25+
import static com.google.errorprone.util.ASTHelpers.methodCanBeOverridden;
2326

2427
import com.google.auto.value.AutoValue;
2528
import com.google.common.collect.ImmutableSet;
@@ -125,7 +128,8 @@ enum InlineValidationErrorReason {
125128
+ " call to another varargs method"),
126129
EMPTY_VOID("InlineMe cannot yet be applied to no-op void methods"),
127130
REUSE_OF_ARGUMENTS("Implementations cannot use an argument more than once:"),
128-
PARAM_NAMES_ARE_NAMED_ARGN("Method parameter names cannot match `arg[0-9]+`.");
131+
PARAM_NAMES_ARE_NAMED_ARGN("Method parameter names cannot match `arg[0-9]+`."),
132+
NO_SUPER_CALLS("super(...) calls cannot be inlined.");
129133

130134
private final @Nullable String errorMessage;
131135

@@ -142,21 +146,21 @@ static boolean matchesArgN(String paramName) {
142146
return paramName.matches("arg[0-9]+");
143147
}
144148

145-
static InlinabilityResult forMethod(MethodTree tree, VisitorState state) {
146-
if (tree.getBody() == null) {
149+
static InlinabilityResult forMethod(MethodTree methodTree, VisitorState state) {
150+
if (methodTree.getBody() == null) {
147151
return fromError(InlineValidationErrorReason.NO_BODY);
148152
}
149153

150-
if (tree.getBody().getStatements().size() != 1) {
154+
if (methodTree.getBody().getStatements().size() != 1) {
151155
return fromError(InlineValidationErrorReason.NOT_EXACTLY_ONE_STATEMENT);
152156
}
153157

154-
MethodSymbol methSymbol = getSymbol(tree);
155-
if (methSymbol.getModifiers().contains(Modifier.PRIVATE)) {
158+
MethodSymbol methodSym = getSymbol(methodTree);
159+
if (methodSym.getModifiers().contains(Modifier.PRIVATE)) {
156160
return fromError(InlineValidationErrorReason.API_IS_PRIVATE);
157161
}
158162

159-
StatementTree statement = tree.getBody().getStatements().get(0);
163+
StatementTree statement = methodTree.getBody().getStatements().get(0);
160164

161165
if (state.getSourceForNode(statement) == null) {
162166
return fromError(InlineValidationErrorReason.NO_BODY);
@@ -179,11 +183,16 @@ static InlinabilityResult forMethod(MethodTree tree, VisitorState state) {
179183
}
180184
}
181185

182-
if (methSymbol.isVarArgs() && usesVarargsParamPoorly(body, methSymbol.params().last(), state)) {
186+
if (body instanceof MethodInvocationTree methodInvocation
187+
&& isSuper(methodInvocation.getMethodSelect())) {
188+
return fromError(InlineValidationErrorReason.NO_SUPER_CALLS);
189+
}
190+
191+
if (methodSym.isVarArgs() && usesVarargsParamPoorly(body, methodSym.params().last(), state)) {
183192
return fromError(InlineValidationErrorReason.VARARGS_USED_UNSAFELY, body);
184193
}
185194

186-
for (VarSymbol param : methSymbol.params()) {
195+
for (VarSymbol param : methodSym.params()) {
187196
if (matchesArgN(param.name.toString())) {
188197
return fromError(InlineValidationErrorReason.PARAM_NAMES_ARE_NAMED_ARGN);
189198
}
@@ -195,30 +204,30 @@ static InlinabilityResult forMethod(MethodTree tree, VisitorState state) {
195204
return fromError(InlineValidationErrorReason.COMPLEX_STATEMENT, body);
196205
}
197206

198-
Symbol usedMultipleTimes = usesVariablesMultipleTimes(body, methSymbol.params(), state);
207+
Symbol usedMultipleTimes = usesVariablesMultipleTimes(body, methodSym.params(), state);
199208
if (usedMultipleTimes != null) {
200209
return fromError(
201210
InlineValidationErrorReason.REUSE_OF_ARGUMENTS, body, usedMultipleTimes.toString());
202211
}
203212

204213
Tree privateOrDeprecatedApi =
205-
usesPrivateOrDeprecatedApis(body, state, getVisibility(methSymbol));
214+
usesPrivateOrDeprecatedApis(body, state, getVisibility(methodSym));
206215
if (privateOrDeprecatedApi != null) {
207216
return fromError(
208217
InlineValidationErrorReason.CALLS_DEPRECATED_OR_PRIVATE_APIS,
209218
body,
210219
state.getSourceForNode(privateOrDeprecatedApi));
211220
}
212221

213-
if (hasArgumentInPossiblyNonExecutedPosition(tree, body)) {
222+
if (hasArgumentInPossiblyNonExecutedPosition(methodTree, body)) {
214223
return fromError(InlineValidationErrorReason.BODY_WOULD_EVALUATE_DIFFERENTLY, body);
215224
}
216225

217-
if (ASTHelpers.methodCanBeOverridden(methSymbol)) {
226+
if (methodCanBeOverridden(methodSym)) {
218227
// TODO(glorioso): One additional edge case we can check is if the owning class can't be
219228
// overridden due to having no publicly-accessible constructors.
220229
return fromError(
221-
methSymbol.isDefault()
230+
methodSym.isDefault()
222231
? InlineValidationErrorReason.METHOD_CAN_BE_OVERRIDDEN_AND_CANT_BE_FIXED
223232
: InlineValidationErrorReason.METHOD_CAN_BE_OVERRIDDEN_BUT_CAN_BE_FIXED,
224233
body);
@@ -234,7 +243,7 @@ private static Symbol usesVariablesMultipleTimes(
234243
final Set<Symbol> usedVariables = new HashSet<>();
235244

236245
@Override
237-
public Void visitIdentifier(IdentifierTree identifierTree, Void aVoid) {
246+
public Void visitIdentifier(IdentifierTree identifierTree, Void unused) {
238247
Symbol usedSymbol = getSymbol(identifierTree);
239248
if (parameterVariables.contains(usedSymbol) && !usedVariables.add(usedSymbol)) {
240249
usesVarsTwice.set(usedSymbol);
@@ -252,7 +261,7 @@ private static boolean usesVarargsParamPoorly(
252261
AtomicBoolean usesVarargsPoorly = new AtomicBoolean(false);
253262
new TreePathScanner<Void, Void>() {
254263
@Override
255-
public Void visitIdentifier(IdentifierTree identifierTree, Void aVoid) {
264+
public Void visitIdentifier(IdentifierTree identifierTree, Void unused) {
256265
if (!getSymbol(identifierTree).equals(varargsParam)) {
257266
return super.visitIdentifier(identifierTree, null);
258267
}
@@ -293,48 +302,47 @@ private static Tree usesPrivateOrDeprecatedApis(
293302
AtomicReference<Tree> usesDeprecatedOrLessVisibleApis = new AtomicReference<>();
294303
new TreeScanner<Void, Void>() {
295304
@Override
296-
public Void visitLambdaExpression(LambdaExpressionTree node, Void unused) {
297-
// we override so we can ignore the node.getParameters()
298-
return super.scan(node.getBody(), null);
305+
public Void visitLambdaExpression(LambdaExpressionTree lambda, Void unused) {
306+
// we override so we can ignore the lambda.getParameters()
307+
return super.scan(lambda.getBody(), null);
299308
}
300309

301310
@Override
302-
public Void visitMemberSelect(MemberSelectTree memberSelectTree, Void aVoid) {
311+
public Void visitMemberSelect(MemberSelectTree memberSelect, Void unused) {
303312
// This check is necessary as the TreeScanner doesn't visit the "name" part of the
304313
// left-hand of an assignment.
305-
if (isDeprecatedOrLessVisible(memberSelectTree, minVisibility)) {
306-
// short circuit
307-
return null;
314+
if (isDeprecatedOrLessVisible(memberSelect, minVisibility)) {
315+
return null; // short-circuit
308316
}
309-
return super.visitMemberSelect(memberSelectTree, null);
317+
return super.visitMemberSelect(memberSelect, null);
310318
}
311319

312320
@Override
313-
public Void visitIdentifier(IdentifierTree node, Void unused) {
314-
if (!ASTHelpers.isLocal(getSymbol(node))) {
315-
if (!node.getName().contentEquals("this")) {
316-
if (isDeprecatedOrLessVisible(node, minVisibility)) {
321+
public Void visitIdentifier(IdentifierTree identifierTree, Void unused) {
322+
if (!isLocal(getSymbol(identifierTree))) {
323+
if (!identifierTree.getName().contentEquals("this")) {
324+
if (isDeprecatedOrLessVisible(identifierTree, minVisibility)) {
317325
return null; // short-circuit
318326
}
319327
}
320328
}
321-
return super.visitIdentifier(node, null);
329+
return super.visitIdentifier(identifierTree, null);
322330
}
323331

324332
@Override
325-
public Void visitNewClass(NewClassTree newClassTree, Void aVoid) {
333+
public Void visitNewClass(NewClassTree newClassTree, Void unused) {
326334
if (isDeprecatedOrLessVisible(newClassTree, minVisibility)) {
327-
return null;
335+
return null; // short-circuit
328336
}
329337
return super.visitNewClass(newClassTree, null);
330338
}
331339

332340
@Override
333-
public Void visitMethodInvocation(MethodInvocationTree node, Void unused) {
334-
if (isDeprecatedOrLessVisible(node, minVisibility)) {
341+
public Void visitMethodInvocation(MethodInvocationTree methodInvocation, Void unused) {
342+
if (isDeprecatedOrLessVisible(methodInvocation, minVisibility)) {
335343
return null; // short-circuit
336344
}
337-
return super.visitMethodInvocation(node, null);
345+
return super.visitMethodInvocation(methodInvocation, null);
338346
}
339347

340348
private boolean isDeprecatedOrLessVisible(Tree tree, Visibility minVisibility) {
@@ -376,43 +384,43 @@ private static Visibility getVisibility(Symbol symbol) {
376384
}
377385

378386
private static boolean hasArgumentInPossiblyNonExecutedPosition(
379-
MethodTree meth, ExpressionTree statement) {
387+
MethodTree methodTree, ExpressionTree statement) {
380388
AtomicBoolean paramReferred = new AtomicBoolean(false);
381389
ImmutableSet<VarSymbol> params =
382-
meth.getParameters().stream().map(ASTHelpers::getSymbol).collect(toImmutableSet());
390+
methodTree.getParameters().stream().map(ASTHelpers::getSymbol).collect(toImmutableSet());
383391
new TreeScanner<Void, Void>() {
384392
Tree currentContextTree = null;
385393

386394
@Override
387-
public Void visitLambdaExpression(LambdaExpressionTree lambdaExpressionTree, Void o) {
395+
public Void visitLambdaExpression(LambdaExpressionTree lambda, Void unused) {
388396
Tree lastContext = currentContextTree;
389-
currentContextTree = lambdaExpressionTree;
390-
scan(lambdaExpressionTree.getBody(), null);
397+
currentContextTree = lambda;
398+
scan(lambda.getBody(), null);
391399
currentContextTree = lastContext;
392400
return null;
393401
}
394402

395403
@Override
396-
public Void visitConditionalExpression(ConditionalExpressionTree ceTree, Void o) {
397-
scan(ceTree.getCondition(), null);
404+
public Void visitConditionalExpression(ConditionalExpressionTree conditional, Void unused) {
405+
scan(conditional.getCondition(), null);
398406
// If the variables show up in the left or right side, they may conditionally not be
399407
// executed.
400408
Tree lastContext = currentContextTree;
401-
currentContextTree = ceTree;
402-
scan(ceTree.getTrueExpression(), null);
403-
scan(ceTree.getFalseExpression(), null);
409+
currentContextTree = conditional;
410+
scan(conditional.getTrueExpression(), null);
411+
scan(conditional.getFalseExpression(), null);
404412
currentContextTree = lastContext;
405413
return null;
406414
}
407415

408416
@Override
409-
public Void visitIdentifier(IdentifierTree identifierTree, Void aVoid) {
417+
public Void visitIdentifier(IdentifierTree identifier, Void unused) {
410418
// If the lambda captures method parameters, inlining the method body can change the
411419
// timing of the evaluation of the arguments.
412-
if (currentContextTree != null && params.contains(getSymbol(identifierTree))) {
420+
if (currentContextTree != null && params.contains(getSymbol(identifier))) {
413421
paramReferred.set(true);
414422
}
415-
return super.visitIdentifier(identifierTree, null);
423+
return super.visitIdentifier(identifier, null);
416424
}
417425
}.scan(statement, null);
418426
return paramReferred.get();

core/src/test/java/com/google/errorprone/bugpatterns/inlineme/SuggesterTest.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,19 +1403,7 @@ public ExecutionError(String message) {
14031403
}
14041404
}
14051405
""")
1406-
// TODO(b/387265535): we shouldn't suggest @InlineMe when there's a super() call...
1407-
.addOutputLines(
1408-
"ExecutionError.java",
1409-
"""
1410-
import com.google.errorprone.annotations.InlineMe;
1411-
public final class ExecutionError extends Error {
1412-
@InlineMe(replacement = "this.super(message)")
1413-
@Deprecated
1414-
public ExecutionError(String message) {
1415-
super(message);
1416-
}
1417-
}
1418-
""")
1406+
.expectUnchanged()
14191407
.doTest();
14201408
}
14211409
}

0 commit comments

Comments
 (0)