mirror of
https://github.com/dlang-community/D-Scanner.git
synced 2025-04-26 21:30:14 +03:00
replace libdparse in imports sortedness visitor (#35)
* replace libdparse in imports sortedness visitor * minor refactor
This commit is contained in:
parent
fd2011ecf5
commit
bb16676c98
2 changed files with 102 additions and 203 deletions
|
@ -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.");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue