From c681e77486d9e338db3b29e2a7d6fddfaeb757e0 Mon Sep 17 00:00:00 2001 From: Jonah Jeleniewski Date: Fri, 11 Apr 2025 13:49:44 +1000 Subject: [PATCH] Fix FPs around enums that are never referenced by name in `UnusedType` Enums are considered used as long as any of their elements are used. --- CHANGELOG.md | 1 + .../delphi/checks/UnusedTypeCheck.java | 16 ++++++++++ .../delphi/checks/UnusedTypeCheckTest.java | 30 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2acccca9..11c34290f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - False positives on enumerable method `GetEnumerator` in `UnusedRoutine`. - False positives on enumerator method `MoveNext` in `UnusedRoutine`. - False positives on enumerator property `Current` in `UnusedProperty`. +- False positives on enums that are never referenced by name (but have used values) in `UnusedType`. - Name resolution failures in legacy initialization sections referencing the implementation section. - Incorrect file position calculation for multiline string tokens. diff --git a/delphi-checks/src/main/java/au/com/integradev/delphi/checks/UnusedTypeCheck.java b/delphi-checks/src/main/java/au/com/integradev/delphi/checks/UnusedTypeCheck.java index 67129db69..a558b97a9 100644 --- a/delphi-checks/src/main/java/au/com/integradev/delphi/checks/UnusedTypeCheck.java +++ b/delphi-checks/src/main/java/au/com/integradev/delphi/checks/UnusedTypeCheck.java @@ -18,9 +18,14 @@ */ package au.com.integradev.delphi.checks; +import java.util.List; +import java.util.function.Predicate; import org.sonar.check.Rule; import org.sonar.check.RuleProperty; +import org.sonar.plugins.communitydelphi.api.ast.EnumElementNode; +import org.sonar.plugins.communitydelphi.api.ast.EnumTypeNode; import org.sonar.plugins.communitydelphi.api.ast.InterfaceSectionNode; +import org.sonar.plugins.communitydelphi.api.ast.NameDeclarationNode; import org.sonar.plugins.communitydelphi.api.ast.TypeDeclarationNode; import org.sonar.plugins.communitydelphi.api.check.DelphiCheck; import org.sonar.plugins.communitydelphi.api.check.DelphiCheckContext; @@ -63,6 +68,10 @@ private boolean isViolation(TypeDeclarationNode node) { return false; } + if (node.isEnum() && hasElementsWithUsages((EnumTypeNode) node.getTypeNode())) { + return false; + } + if (excludeApi && (node.isPublic() || node.isPublished()) && node.getFirstParentOfType(InterfaceSectionNode.class) != null) { @@ -77,6 +86,13 @@ private boolean isViolation(TypeDeclarationNode node) { .allMatch(occurrence -> isWithinType(occurrence, type)); } + private static boolean hasElementsWithUsages(EnumTypeNode node) { + return node.getElements().stream() + .map(EnumElementNode::getNameDeclarationNode) + .map(NameDeclarationNode::getUsages) + .anyMatch(Predicate.not(List::isEmpty)); + } + private static boolean isWithinType(NameOccurrence occurrence, Type type) { DelphiScope scope = occurrence.getLocation().getScope(); while (scope != null) { diff --git a/delphi-checks/src/test/java/au/com/integradev/delphi/checks/UnusedTypeCheckTest.java b/delphi-checks/src/test/java/au/com/integradev/delphi/checks/UnusedTypeCheckTest.java index ac965bc81..7bc8d97a0 100644 --- a/delphi-checks/src/test/java/au/com/integradev/delphi/checks/UnusedTypeCheckTest.java +++ b/delphi-checks/src/test/java/au/com/integradev/delphi/checks/UnusedTypeCheckTest.java @@ -212,4 +212,34 @@ void testUnusedTypeWithAttributeShouldNotAddIssue() { .appendDecl(" end;")) .verifyNoIssues(); } + + @Test + void testUnusedEnumWithUsedElementsShouldNotAddIssue() { + CheckVerifier.newVerifier() + .withCheck(new UnusedTypeCheck()) + .onFile( + new DelphiTestUnitBuilder() + .appendImpl("function Foo: Integer;") + .appendImpl("type") + .appendImpl(" TBar = (Baz, Flarp);") + .appendImpl("begin") + .appendImpl(" Result := Ord(Baz) + 123;") + .appendImpl("end;")) + .verifyNoIssues(); + } + + @Test + void testUnusedEnumWithUnusedElementsShouldAddIssue() { + CheckVerifier.newVerifier() + .withCheck(new UnusedTypeCheck()) + .onFile( + new DelphiTestUnitBuilder() + .appendImpl("function Foo: Integer;") + .appendImpl("type") + .appendImpl(" TBar = (Baz, Flarp); // Noncompliant") + .appendImpl("begin") + .appendImpl(" Result := 123;") + .appendImpl("end;")) + .verifyIssues(); + } }