Skip to content

Commit a9ffbd8

Browse files
SONARJAVA-4837 Fix FP on records with no components (#4721)
1 parent 7aa9c21 commit a9ffbd8

2 files changed

Lines changed: 16 additions & 3 deletions

File tree

java-checks-test-sources/default/src/main/java/checks/RecordPatternInsteadOfFieldAccessCheckSample.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
public class RecordPatternInsteadOfFieldAccessCheckSample {
66

7+
record Box() { }
8+
9+
static void switchOnSealedClass(Object shape) {
10+
switch (shape) {
11+
case Box unused -> { } // Compliant, record has no components
12+
default -> {}
13+
}
14+
}
15+
716
int sameComponentAccessTwice(Object obj){
817
if (obj instanceof Point p) { // Compliant; not all record components are used
918
return p.x() + p.x();

java-checks/src/main/java/org/sonar/java/checks/RecordPatternInsteadOfFieldAccessCheck.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ && isRecordPattern(typePattern)) {
8080

8181
private void checkTypePatternVariableUsage(VariableTree patternVariable) {
8282
var secondaryLocationsTrees = new HashSet<MemberSelectExpressionTree>();
83-
var type = patternVariable.symbol().type().symbol();
84-
var comps = recordComponentNames(type);
83+
var recordSymbol = patternVariable.symbol().type().symbol();
8584
for (Tree usage : patternVariable.symbol().usages()) {
8685
if (usage.parent() instanceof MemberSelectExpressionTree mse && isNotRecordGetter(mse)) {
8786
secondaryLocationsTrees.add(mse);
@@ -90,12 +89,17 @@ private void checkTypePatternVariableUsage(VariableTree patternVariable) {
9089
}
9190
}
9291
// only if all the records components are used we report an issue
93-
if (secondaryLocationsTrees.stream().map(mse -> mse.identifier().name()).toList().containsAll(comps)) {
92+
if (isEveryRecordComponentUsed(secondaryLocationsTrees, recordSymbol)) {
9493
reportIssue(patternVariable, "Use the record pattern instead of this pattern match variable.",
9594
getSecondaryLocations(secondaryLocationsTrees), null);
9695
}
9796
}
9897

98+
private static boolean isEveryRecordComponentUsed(Set<MemberSelectExpressionTree> secondaryLocationsTrees, Symbol.TypeSymbol recordSymbol) {
99+
var recordComponentNames = recordComponentNames(recordSymbol);
100+
return !recordComponentNames.isEmpty() && secondaryLocationsTrees.stream().map(mse -> mse.identifier().name()).toList().containsAll(recordComponentNames);
101+
}
102+
99103
private static boolean isNotRecordGetter(MemberSelectExpressionTree mse) {
100104
return !ALLOWED_METHODS.contains(mse.identifier().name());
101105
}

0 commit comments

Comments
 (0)