replace libdparse in imports sortedness visitor (#35)

* replace libdparse in imports sortedness visitor

* minor refactor
This commit is contained in:
lucica28 2022-11-04 18:09:08 +02:00 committed by Vladiwostok
parent fd2011ecf5
commit bb16676c98
2 changed files with 102 additions and 203 deletions

View file

@ -5,106 +5,100 @@
module dscanner.analysis.imports_sortedness;
import dscanner.analysis.base;
import dparse.lexer;
import dparse.ast;
import std.stdio;
/**
* Checks the sortedness of module imports
*/
final class ImportSortednessCheck : BaseAnalyzer
extern(C++) class ImportSortednessCheck(AST) : BaseAnalyzerDmd
{
enum string KEY = "dscanner.style.imports_sortedness";
enum string MESSAGE = "The imports are not sorted in alphabetical order";
mixin AnalyzerInfo!"imports_sortedness";
alias visit = BaseAnalyzerDmd.visit;
// alias visit = BaseAnalyzerDmd!AST.visit;
///
this(BaseAnalyzerArguments args)
extern(D) this(string fileName)
{
super(args);
super(fileName);
}
mixin ScopedVisit!Module;
mixin ScopedVisit!Statement;
mixin ScopedVisit!BlockStatement;
mixin ScopedVisit!StructBody;
mixin ScopedVisit!IfStatement;
mixin ScopedVisit!TemplateDeclaration;
mixin ScopedVisit!ConditionalDeclaration;
mixin ScopedVisit!(AST.StructDeclaration);
mixin ScopedVisit!(AST.FuncDeclaration);
mixin ScopedVisit!(AST.InterfaceDeclaration);
mixin ScopedVisit!(AST.UnionDeclaration);
mixin ScopedVisit!(AST.TemplateDeclaration);
mixin ScopedVisit!(AST.IfStatement);
mixin ScopedVisit!(AST.WhileStatement);
mixin ScopedVisit!(AST.ForStatement);
mixin ScopedVisit!(AST.ForeachStatement);
mixin ScopedVisit!(AST.ScopeStatement);
mixin ScopedVisit!(AST.ConditionalDeclaration);
override void visit(const VariableDeclaration id)
override void visit(AST.VarDeclaration vd)
{
imports[level] = [];
}
override void visit(const ImportDeclaration id)
override void visit(AST.Import i)
{
import std.algorithm.iteration : map;
import std.algorithm : map;
import std.array : join;
import std.string : strip;
import std.conv : to;
if (id.importBindings is null || id.importBindings.importBinds.length == 0)
{
bool suppress;
foreach (singleImport; id.singleImports)
{
string importModuleName = singleImport.identifierChain.identifiers.map!`a.text`.join(".");
addImport(importModuleName, singleImport, null, suppress);
}
}
string importModuleName = i.packages.map!(a => a.toString().dup).join(".");
if (importModuleName != "")
importModuleName ~= "." ~ i.id.toString();
else
{
string importModuleName = id.importBindings.singleImport.identifierChain.identifiers.map!`a.text`.join(".");
importModuleName ~= i.id.toString();
bool suppress;
foreach (importBind; id.importBindings.importBinds)
if (i.names.length)
{
foreach (name; i.names)
{
addImport(importModuleName ~ "-" ~ importBind.left.text, importBind, id.importBindings.singleImport, suppress);
string aux = to!string(importModuleName ~ "-" ~ name.toString());
addImport(aux, i);
}
}
else addImport(importModuleName, i);
}
alias visit = BaseAnalyzer.visit;
private:
enum maxDepth = 20;
int level;
string[][int] imports;
bool[maxDepth] levelAvailable;
template ScopedVisit(NodeType)
{
override void visit(const NodeType n)
override void visit(NodeType n)
{
if (level >= maxDepth)
return;
imports[level] = [];
imports[++level] = [];
n.accept(this);
levelAvailable[level] = true;
super.visit(n);
level--;
}
}
void addImport(string importModuleName, const BaseNode range, const BaseNode parent, ref bool suppress)
extern(D) void addImport(string importModuleName, AST.Import i)
{
import std.algorithm : findSplit;
import std.string : indexOf;
import std.uni : sicmp;
if (imports[level].length > 0 && imports[level][$ -1].sicmp(importModuleName) > 0)
if (!levelAvailable[level])
{
if (parent !is null)
{
auto parentEnd = importModuleName.indexOf("-");
if (parentEnd != -1 && imports[level][$ -1].findSplit("-")[0].sicmp(importModuleName) > 0)
{
// mark module name as broken, not selected symbols, since it's the module name is not belonging here
if (!suppress)
addErrorMessage(parent, KEY, MESSAGE);
suppress = true;
return;
}
}
if (!suppress)
addErrorMessage(range, KEY, MESSAGE);
suppress = true;
imports[level] = [];
levelAvailable[level] = true;
}
if (imports[level].length > 0 && imports[level][$ - 1].sicmp(importModuleName) > 0)
{
addErrorMessage(cast(ulong) i.loc.linnum, cast(ulong) i.loc.charnum, KEY, MESSAGE);
}
else
{
@ -116,9 +110,8 @@ private:
unittest
{
import std.stdio : stderr;
import std.format : format;
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
import dscanner.analysis.helpers : assertAnalyzerWarnings;
import dscanner.analysis.helpers : assertAnalyzerWarnings = assertAnalyzerWarningsDMD;
StaticAnalysisConfig sac = disabledConfig();
sac.imports_sortedness = Check.enabled;
@ -130,62 +123,30 @@ unittest
assertAnalyzerWarnings(q{
import foo.bar;
import bar.foo; /+
^^^^^^^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
), sac);
import bar.foo; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
assertAnalyzerWarnings(q{
import c;
import c.b;
import c.a; /+
^^^ [warn]: %s +/
import c.a; // [warn]: The imports are not sorted in alphabetical order
import d.a;
import d; /+
^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import d; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
assertAnalyzerWarnings(q{
unittest
{
import a.b, a.c, a.d;
}
unittest
{
import a.b, a.d, a.c; /+
^^^ [warn]: %s +/
}
unittest
{
import a.c, a.b, a.c; /+
^^^ [warn]: %s +/
}
unittest
{
import foo.bar, bar.foo; /+
^^^^^^^ [warn]: %s +/
}
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import a.b, a.c, a.d;
import a.b, a.d, a.c; // [warn]: The imports are not sorted in alphabetical order
import a.c, a.b, a.c; // [warn]: The imports are not sorted in alphabetical order
import foo.bar, bar.foo; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
// multiple items out of order
assertAnalyzerWarnings(q{
import foo.bar;
import bar.foo; /+
^^^^^^^ [warn]: %s +/
import bar.bar.foo; /+
^^^^^^^^^^^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import bar.foo; // [warn]: The imports are not sorted in alphabetical order
import bar.bar.foo; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
assertAnalyzerWarnings(q{
import test : bar;
@ -195,47 +156,28 @@ unittest
// selective imports
assertAnalyzerWarnings(q{
import test : foo;
import test : bar; /+
^^^ [warn]: %s +/
import before : zzz; /+
^^^^^^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import test : bar; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
// selective imports
assertAnalyzerWarnings(q{
import test : foo, bar; /+
^^^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
), sac);
import test : foo, bar; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
assertAnalyzerWarnings(q{
import b;
import c : foo;
import c : bar; /+
^^^ [warn]: %s +/
import a; /+
^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import c : bar; // [warn]: The imports are not sorted in alphabetical order
import a; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
assertAnalyzerWarnings(q{
import c;
import c : bar;
import d : bar;
import d; /+
^ [warn]: %s +/
import a : bar; /+
^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import d; // [warn]: The imports are not sorted in alphabetical order
import a : bar; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
assertAnalyzerWarnings(q{
import t0;
@ -245,25 +187,18 @@ unittest
assertAnalyzerWarnings(q{
import t1 : a, b = foo;
import t1 : b, a = foo; /+
^^^^^^^ [warn]: %s +/
import t0 : a, b = foo; /+
^^ [warn]: %s +/
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
import t1 : b, a = foo; // [warn]: The imports are not sorted in alphabetical order
import t0 : a, b = foo; // [warn]: The imports are not sorted in alphabetical order
}c, sac);
// local imports in functions
assertAnalyzerWarnings(q{
import t2;
import t1; /+
^^ [warn]: %s +/
import t1; // [warn]: The imports are not sorted in alphabetical order
void foo()
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
}
void bar()
@ -271,26 +206,20 @@ unittest
import f1;
import f2;
}
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
}c, sac);
// local imports in scopes
assertAnalyzerWarnings(q{
import t2;
import t1; /+
^^ [warn]: %s +/
import t1; // [warn]: The imports are not sorted in alphabetical order
void foo()
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
}
{
@ -299,27 +228,20 @@ unittest
import f3;
}
}
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
}c, sac);
// local imports in functions
assertAnalyzerWarnings(q{
import t2;
import t1; /+
^^ [warn]: %s +/
import t1; // [warn]: The imports are not sorted in alphabetical order
void foo()
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
while (true) {
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
}
for (;;) {
@ -329,66 +251,47 @@ unittest
}
foreach (el; arr) {
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
}
}
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
}c, sac);
// nested scopes
assertAnalyzerWarnings(q{
import t2;
import t1; /+
^^ [warn]: %s +/
import t1; // [warn]: The imports are not sorted in alphabetical order
void foo()
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
}
}
}
}
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
}c, sac);
// local imports in functions
assertAnalyzerWarnings(q{
import t2;
import t1; /+
^^ [warn]: %s +/
import t1; // [warn]: The imports are not sorted in alphabetical order
struct foo()
{
import f2;
import f1; /+
^^ [warn]: %s +/
import f1; // [warn]: The imports are not sorted in alphabetical order
import f3;
}
class bar()
@ -396,10 +299,7 @@ unittest
import f1;
import f2;
}
}c.format(
ImportSortednessCheck.MESSAGE,
ImportSortednessCheck.MESSAGE,
), sac);
}c, sac);
// issue 422 - sorted imports with :
assertAnalyzerWarnings(q{
@ -448,4 +348,4 @@ unittest
}, sac);
stderr.writeln("Unittest for ImportSortednessCheck passed.");
}
}

View file

@ -949,10 +949,6 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
checks ~= new AutoFunctionChecker(args.setSkipTests(
analysisConfig.auto_function_check == Check.skipTests && !ut));
if (moduleName.shouldRun!ImportSortednessCheck(analysisConfig))
checks ~= new ImportSortednessCheck(args.setSkipTests(
analysisConfig.imports_sortedness == Check.skipTests && !ut));
if (moduleName.shouldRun!ExplicitlyAnnotatedUnittestCheck(analysisConfig))
checks ~= new ExplicitlyAnnotatedUnittestCheck(args.setSkipTests(
analysisConfig.explicitly_annotated_unittests == Check.skipTests && !ut));
@ -1325,6 +1321,9 @@ MessageSet analyzeDmd(string fileName, ASTBase.Module m, const char[] moduleName
if (moduleName.shouldRunDmd!(FinalAttributeChecker!ASTBase)(config))
visitors ~= new FinalAttributeChecker!ASTBase(fileName);
if (moduleName.shouldRunDmd!(ImportSortednessCheck!ASTBase)(config))
visitors ~= new ImportSortednessCheck!ASTBase(fileName);
if (moduleName.shouldRunDmd!(IncorrectInfiniteRangeCheck!ASTBase)(config))
visitors ~= new IncorrectInfiniteRangeCheck!ASTBase(fileName);