diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java b/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java index 018b85228ac..dca3245af04 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/UnusedVariable.java @@ -35,6 +35,8 @@ import static com.google.errorprone.util.ASTHelpers.isAbstract; import static com.google.errorprone.util.ASTHelpers.isStatic; import static com.google.errorprone.util.ASTHelpers.isSubtype; +import static com.google.errorprone.util.AnnotationNames.AFTER_TEMPLATE_ANNOTATION; +import static com.google.errorprone.util.AnnotationNames.BEFORE_TEMPLATE_ANNOTATION; import static com.google.errorprone.util.SideEffectAnalysis.hasSideEffect; import static com.sun.source.tree.Tree.Kind.POSTFIX_DECREMENT; import static com.sun.source.tree.Tree.Kind.POSTFIX_INCREMENT; @@ -139,8 +141,8 @@ public final class UnusedVariable extends BugChecker implements CompilationUnitT .addAll(InjectMatchers.PROVIDES_ANNOTATIONS) .addAll(InjectMatchers.MULTIBINDINGS_ANNOTATIONS) .add( - "com.google.errorprone.refaster.annotation.AfterTemplate", - "com.google.errorprone.refaster.annotation.BeforeTemplate", + AFTER_TEMPLATE_ANNOTATION, + BEFORE_TEMPLATE_ANNOTATION, // Parameters on test methods imply the test is parameterised, and those parameters // should be used or removed. "org.junit.Test") @@ -711,10 +713,16 @@ && exemptedFieldBySuperType(getType(variableTree), state)) { } } case LOCAL_VARIABLE -> { + if (isInsideRefasterTemplate(getCurrentPath())) { + return; + } unusedElements.put(symbol, getCurrentPath()); usageSites.put(symbol, getCurrentPath()); } case BINDING_VARIABLE -> { + if (isInsideRefasterTemplate(getCurrentPath())) { + return; + } if (parent instanceof BindingPatternTree && getCurrentPath().getParentPath().getParentPath().getLeaf() instanceof InstanceOfTree) { @@ -743,6 +751,18 @@ && getCurrentPath().getParentPath().getParentPath().getLeaf() } } + private boolean isInsideRefasterTemplate(TreePath path) { + for (Tree node : path) { + if (node instanceof MethodTree methodTree) { + if (hasAnnotation(methodTree, BEFORE_TEMPLATE_ANNOTATION, state) + || hasAnnotation(methodTree, AFTER_TEMPLATE_ANNOTATION, state)) { + return true; + } + } + } + return false; + } + private boolean exemptedFieldBySuperType(Type type, VisitorState state) { return EXEMPTING_FIELD_SUPER_TYPES.stream() .anyMatch(t -> isSubtype(type, state.getTypeFromString(t), state)); diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java index be6358020ea..628491ab51b 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/UnusedVariableTest.java @@ -2266,4 +2266,110 @@ public static void main(String[] args) { .expectUnchanged() .doTest(); } + + @Test + public void refasterBeforeTemplate_unusedLocalVariable_noFinding() { + helper + .addSourceLines( + "BeforeTemplate.java", + """ + package com.google.errorprone.refaster.annotation; + + public @interface BeforeTemplate {} + """) + .addSourceLines( + "Test.java", + """ + import com.google.errorprone.refaster.annotation.BeforeTemplate; + + class Test { + @BeforeTemplate + void before(String s) { + String local = s.trim(); + } + } + """) + .doTest(); + } + + @Test + public void refasterAfterTemplate_unusedLocalVariable_noFinding() { + helper + .addSourceLines( + "AfterTemplate.java", + """ + package com.google.errorprone.refaster.annotation; + + public @interface AfterTemplate {} + """) + .addSourceLines( + "Test.java", + """ + import com.google.errorprone.refaster.annotation.AfterTemplate; + + class Test { + @AfterTemplate + void after(String s) { + String local = s.trim(); + } + } + """) + .doTest(); + } + + @Test + public void refasterTemplate_unusedParameter_flagged() { + helper + .addSourceLines( + "BeforeTemplate.java", + """ + package com.google.errorprone.refaster.annotation; + + public @interface BeforeTemplate {} + """) + .addSourceLines( + "Test.java", + """ + import com.google.errorprone.refaster.annotation.BeforeTemplate; + + class Test { + @BeforeTemplate + // BUG: Diagnostic contains: The parameter 'bar' is never read + void before(String foo, String bar) { + System.out.println(foo); + } + } + """) + .doTest(); + } + + @Test + public void refasterTemplate_regularMethod_unusedLocalVariable_flagged() { + helper + .addSourceLines( + "BeforeTemplate.java", + """ + package com.google.errorprone.refaster.annotation; + + public @interface BeforeTemplate {} + """) + .addSourceLines( + "Test.java", + """ + import com.google.errorprone.refaster.annotation.BeforeTemplate; + + class Test { + @BeforeTemplate + void before(String s) { + String local = s.trim(); + } + + void regularMethod(String s) { + // BUG: Diagnostic contains: The local variable 'local' is never read + String local = s.trim(); + } + } + """) + .doTest(); + } }