diff --git a/src/dscanner/analysis/imports_sortedness.d b/src/dscanner/analysis/imports_sortedness.d index e32a26c..6238215 100644 --- a/src/dscanner/analysis/imports_sortedness.d +++ b/src/dscanner/analysis/imports_sortedness.d @@ -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."); -} +} \ No newline at end of file diff --git a/src/dscanner/analysis/run.d b/src/dscanner/analysis/run.d index 7f1b819..1872f53 100644 --- a/src/dscanner/analysis/run.d +++ b/src/dscanner/analysis/run.d @@ -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);