Skip to content

Commit

Permalink
Create rule FullyQualifiedImportCheck
Browse files Browse the repository at this point in the history
  • Loading branch information
EduardoVaz06 authored and Cirras committed Sep 25, 2024
1 parent ba1fbc7 commit 3143498
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public final class CheckList {
FormatArgumentTypeCheck.class,
FormatStringValidCheck.class,
FreeAndNilTObjectCheck.class,
FullyQualifiedImportCheck.class,
GotoStatementCheck.class,
GroupedFieldDeclarationCheck.class,
GroupedParameterDeclarationCheck.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<h2>Why is this an issue?</h2>
<p>
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.
</p>
<p>
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.
</p>
<h2>How to fix it</h2>
<p>
Fully qualify the unit name.
</p>
<pre data-diff-id="1" data-diff-type="noncompliant">
uses
Classes,
SysUtils;
</pre>
<pre data-diff-id="1" data-diff-type="compliant">
uses
System.Classes,
System.SysUtils;
</pre>
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
@@ -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] <<Scope.UnitU>>")
.appendDecl(" UnitU in 'Test.UnitU.pas'; // Noncompliant");

CheckVerifier.newVerifier()
.withCheck(new FullyQualifiedImportCheck())
.withSearchPathUnit(importedUnit)
.onFile(testFile)
.withUnitScopeName("Scope")
.verifyIssues();
}

@Test
void testUnresolvedImportShouldNotAddIssue() {
var testFile = new DelphiTestUnitBuilder().appendDecl("uses").appendDecl(" RandomUnit;");

CheckVerifier.newVerifier()
.withCheck(new FullyQualifiedImportCheck())
.onFile(testFile)
.verifyNoIssues();
}

@Test
void testNotFullyQualifiedImportShouldAddIssue() {
var importedUnit = new DelphiTestUnitBuilder().unitName("Scope.UnitU");

var testFile =
new DelphiTestUnitBuilder()
.appendDecl("uses")
.appendDecl(" // Fix qf1@[+1:2 to +1:7] <<Scope.UnitU>>")
.appendDecl(" UnitU; // Noncompliant");

CheckVerifier.newVerifier()
.withCheck(new FullyQualifiedImportCheck())
.withSearchPathUnit(importedUnit)
.onFile(testFile)
.withUnitScopeName("Scope")
.verifyIssues();
}
}

0 comments on commit 3143498

Please sign in to comment.