From f57cb1b7ac02c6be1e72d5a04f65c7080c4f9419 Mon Sep 17 00:00:00 2001 From: ghm Date: Mon, 22 Dec 2025 16:38:30 -0800 Subject: [PATCH] Consider deconstruction of a record to be usage of its record component overrides. I considered just not flagging record component overrides at all, because I assumed they'd be used by `hashCode()`, but they're not! So this is the narrower fix. PiperOrigin-RevId: 847923360 --- .../errorprone/bugpatterns/UnusedMethod.java | 23 ++++++++++ .../bugpatterns/UnusedMethodTest.java | 43 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/UnusedMethod.java b/core/src/main/java/com/google/errorprone/bugpatterns/UnusedMethod.java index 6d506e3b924..a045375b95d 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/UnusedMethod.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/UnusedMethod.java @@ -28,10 +28,12 @@ import static com.google.errorprone.matchers.Matchers.SERIALIZATION_METHODS; import static com.google.errorprone.suppliers.Suppliers.typeFromString; import static com.google.errorprone.util.ASTHelpers.canBeRemoved; +import static com.google.errorprone.util.ASTHelpers.getEnclosedElements; import static com.google.errorprone.util.ASTHelpers.getSymbol; import static com.google.errorprone.util.ASTHelpers.getType; import static com.google.errorprone.util.ASTHelpers.hasAnnotation; import static com.google.errorprone.util.ASTHelpers.isGeneratedConstructor; +import static com.google.errorprone.util.ASTHelpers.isRecord; import static com.google.errorprone.util.ASTHelpers.isSubtype; import static com.google.errorprone.util.MoreAnnotations.asStrings; import static com.google.errorprone.util.MoreAnnotations.getAnnotationValue; @@ -49,10 +51,12 @@ import com.google.errorprone.fixes.SuggestedFix; import com.google.errorprone.matchers.Description; import com.google.errorprone.suppliers.Supplier; +import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.DeconstructionPatternTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberReferenceTree; import com.sun.source.tree.MemberSelectTree; @@ -280,6 +284,25 @@ public Void visitAssignment(AssignmentTree tree, Void unused) { return super.visitAssignment(tree, null); } + @Override + public Void visitDeconstructionPattern(DeconstructionPatternTree tree, Void unused) { + var symbol = getSymbol(tree.getDeconstructor()); + if (isRecord(symbol)) { + ImmutableSet componentNames = + getEnclosedElements(symbol).stream() + .filter(ASTHelpers::isRecord) + .map(Symbol::getSimpleName) + .collect(toImmutableSet()); + for (Symbol e : getEnclosedElements(symbol)) { + if (componentNames.contains(e.getSimpleName())) { + handle(e); + } + } + } + + return super.visitDeconstructionPattern(tree, null); + } + private void handle(Symbol symbol) { if (symbol instanceof MethodSymbol) { unusedMethods.remove(symbol); diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/UnusedMethodTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/UnusedMethodTest.java index 0608003ab8d..813e0e9e358 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/UnusedMethodTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/UnusedMethodTest.java @@ -695,4 +695,47 @@ public void run() {} """) .doTest(); } + + @Test + public void overriddenRecordGetter_isUsedByDestructuring() { + helper + .addSourceLines( + "Test.java", + """ + class Test { + private record R(int x, int y) { + @Override + public int x() { + return x; + } + } + + int f(R r) { + return switch (r) { + case R(int x, int y) -> x; + default -> 0; + }; + } + } + """) + .doTest(); + } + + @Test + public void otherOverriddenMethod_stillFlagged() { + helper + .addSourceLines( + "Test.java", + """ + class Test { + private record R(int x, int y) { + // BUG: Diagnostic contains: + public int frobnicator() { + return x; + } + } + } + """) + .doTest(); + } }