diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b752fd58..0b087c49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `FullyQualifiedImportCheck` analysis rule, which flags non-fully qualified imports. - **API:** `CaseItemStatementNode::getExpressions` method. ### Changed diff --git a/delphi-checks/src/main/java/au/com/integradev/delphi/checks/CheckList.java b/delphi-checks/src/main/java/au/com/integradev/delphi/checks/CheckList.java index 4f4ec7b35..720fee091 100644 --- a/delphi-checks/src/main/java/au/com/integradev/delphi/checks/CheckList.java +++ b/delphi-checks/src/main/java/au/com/integradev/delphi/checks/CheckList.java @@ -83,6 +83,7 @@ public final class CheckList { FormatArgumentTypeCheck.class, FormatStringValidCheck.class, FreeAndNilTObjectCheck.class, + FullyQualifiedImportCheck.class, GotoStatementCheck.class, GroupedFieldDeclarationCheck.class, GroupedParameterDeclarationCheck.class, diff --git a/delphi-checks/src/main/java/au/com/integradev/delphi/checks/FullyQualifiedImportCheck.java b/delphi-checks/src/main/java/au/com/integradev/delphi/checks/FullyQualifiedImportCheck.java new file mode 100644 index 000000000..15a2b27f9 --- /dev/null +++ b/delphi-checks/src/main/java/au/com/integradev/delphi/checks/FullyQualifiedImportCheck.java @@ -0,0 +1,59 @@ +/* + * Sonar Delphi Plugin + * Copyright (C) 2024 Integrated Application Development + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package au.com.integradev.delphi.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.communitydelphi.api.ast.UnitImportNode; +import org.sonar.plugins.communitydelphi.api.check.DelphiCheck; +import org.sonar.plugins.communitydelphi.api.check.DelphiCheckContext; +import org.sonar.plugins.communitydelphi.api.reporting.QuickFix; +import org.sonar.plugins.communitydelphi.api.reporting.QuickFixEdit; +import org.sonar.plugins.communitydelphi.api.symbol.declaration.UnitImportNameDeclaration; + +@Rule(key = "FullyQualifiedImport") +public class FullyQualifiedImportCheck extends DelphiCheck { + + @Override + public DelphiCheckContext visit(UnitImportNode unitImportNode, DelphiCheckContext context) { + if (!unitImportNode.isResolvedImport()) { + return context; + } + + UnitImportNameDeclaration unitImportNameDeclaration = unitImportNode.getImportNameDeclaration(); + + String unitFullyQualifiedName = unitImportNameDeclaration.getOriginalDeclaration().getImage(); + String unitImportName = unitImportNameDeclaration.getImage(); + + if (unitImportName.length() != unitFullyQualifiedName.length()) { + context + .newIssue() + .onNode(unitImportNode) + .withMessage( + "Fully qualify this unit name (found: \"%s\" expected: \"%s\").", + unitImportName, unitFullyQualifiedName) + .withQuickFixes( + QuickFix.newFix("Fully qualify unit import") + .withEdit( + QuickFixEdit.replace(unitImportNode.getNameNode(), unitFullyQualifiedName))) + .report(); + } + + return context; + } +} diff --git a/delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/FullyQualifiedImport.html b/delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/FullyQualifiedImport.html new file mode 100644 index 000000000..966658d8f --- /dev/null +++ b/delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/FullyQualifiedImport.html @@ -0,0 +1,21 @@ +
+ Imports should be done using the full name of the unit, as this facilitates code readability, avoids potential ambiguities about which unit is being imported, and ensures consistency throughout the codebase. +
++ Using the full name of the units not only improves code clarity and maintenance but also optimizes the compilation process. The compiler does not need to spend time searching implicit scopes to resolve which units are being referenced. By specifying the full unit name, the compiler can immediately locate the necessary files, resulting in faster and more efficient compilation. +
++ Fully qualify the unit name. +
++uses + Classes, + SysUtils; ++
+uses + System.Classes, + System.SysUtils; +diff --git a/delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/FullyQualifiedImport.json b/delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/FullyQualifiedImport.json new file mode 100644 index 000000000..9d5ea20f9 --- /dev/null +++ b/delphi-checks/src/main/resources/org/sonar/l10n/delphi/rules/community-delphi/FullyQualifiedImport.json @@ -0,0 +1,18 @@ +{ + "title": "Imports should be fully qualified", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant/Issue", + "constantCost": "5min" + }, + "code": { + "attribute": "CLEAR", + "impacts": { + "MAINTAINABILITY": "LOW" + } + }, + "tags": ["convention"], + "defaultSeverity": "Minor", + "scope": "ALL" +} diff --git a/delphi-checks/src/test/java/au/com/integradev/delphi/checks/FullyQualifiedImportCheckTest.java b/delphi-checks/src/test/java/au/com/integradev/delphi/checks/FullyQualifiedImportCheckTest.java new file mode 100644 index 000000000..20884bca0 --- /dev/null +++ b/delphi-checks/src/test/java/au/com/integradev/delphi/checks/FullyQualifiedImportCheckTest.java @@ -0,0 +1,102 @@ +/* + * Sonar Delphi Plugin + * Copyright (C) 2024 Integrated Application Development + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package au.com.integradev.delphi.checks; + +import au.com.integradev.delphi.builders.DelphiTestProgramBuilder; +import au.com.integradev.delphi.builders.DelphiTestUnitBuilder; +import au.com.integradev.delphi.checks.verifier.CheckVerifier; +import org.junit.jupiter.api.Test; + +class FullyQualifiedImportCheckTest { + + @Test + void testFullyQualifiedImportShouldNotAddIssue() { + var importedUnit = new DelphiTestUnitBuilder().unitName("Scope.UnitU"); + + var testFile = new DelphiTestUnitBuilder().appendDecl("uses").appendDecl(" Scope.UnitU;"); + + CheckVerifier.newVerifier() + .withCheck(new FullyQualifiedImportCheck()) + .withSearchPathUnit(importedUnit) + .onFile(testFile) + .verifyNoIssues(); + } + + @Test + void testImportFullyQualifiedWithInKeywordShouldNotAddIssue() { + var importedUnit = new DelphiTestUnitBuilder().unitName("Scope.UnitU"); + + var testFile = + new DelphiTestProgramBuilder() + .appendDecl("uses") + .appendDecl(" Scope.UnitU in 'Test.UnitU.pas';"); + + CheckVerifier.newVerifier() + .withCheck(new FullyQualifiedImportCheck()) + .withSearchPathUnit(importedUnit) + .onFile(testFile) + .verifyNoIssues(); + } + + @Test + void testImportNotFullyQualifiedWithInKeywordShouldAddIssue() { + var importedUnit = new DelphiTestUnitBuilder().unitName("Scope.UnitU"); + + var testFile = + new DelphiTestProgramBuilder() + .appendDecl("uses") + .appendDecl(" // Fix qf1@[+1:2 to +1:7] <