387 lines
7.4 KiB
D
387 lines
7.4 KiB
D
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
module analysis.imports_sortedness;
|
|
|
|
import analysis.base : BaseAnalyzer;
|
|
import dparse.lexer;
|
|
import dparse.ast;
|
|
|
|
import std.stdio;
|
|
|
|
/**
|
|
* Checks the sortedness of module imports
|
|
*/
|
|
class ImportSortednessCheck : BaseAnalyzer
|
|
{
|
|
enum string KEY = "dscanner.style.imports_sortedness";
|
|
enum string MESSAGE = "The imports are not sorted in alphabetical order";
|
|
|
|
///
|
|
this(string fileName, bool skipTests = false)
|
|
{
|
|
super(fileName, null, skipTests);
|
|
}
|
|
|
|
mixin ScopedVisit!Module;
|
|
mixin ScopedVisit!Statement;
|
|
mixin ScopedVisit!BlockStatement;
|
|
mixin ScopedVisit!StructBody;
|
|
mixin ScopedVisit!IfStatement;
|
|
mixin ScopedVisit!TemplateDeclaration;
|
|
mixin ScopedVisit!ConditionalDeclaration;
|
|
|
|
override void visit(const VariableDeclaration id)
|
|
{
|
|
imports[level] = [];
|
|
}
|
|
|
|
override void visit(const ImportDeclaration id)
|
|
{
|
|
import std.algorithm.iteration : map;
|
|
import std.array : join;
|
|
import std.string : strip;
|
|
|
|
if (id.importBindings is null || id.importBindings.importBinds.length == 0)
|
|
{
|
|
foreach (singleImport; id.singleImports)
|
|
{
|
|
string importModuleName = singleImport.identifierChain.identifiers.map!`a.text`.join(".");
|
|
addImport(importModuleName, singleImport);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string importModuleName = id.importBindings.singleImport.identifierChain.identifiers.map!`a.text`.join(".");
|
|
|
|
foreach (importBind; id.importBindings.importBinds)
|
|
{
|
|
addImport(importModuleName ~ "-" ~ importBind.left.text, id.importBindings.singleImport);
|
|
}
|
|
}
|
|
}
|
|
|
|
alias visit = BaseAnalyzer.visit;
|
|
|
|
private:
|
|
|
|
int level = 0;
|
|
string[][int] imports;
|
|
|
|
template ScopedVisit(NodeType)
|
|
{
|
|
override void visit(const NodeType n)
|
|
{
|
|
imports[++level] = [];
|
|
n.accept(this);
|
|
level--;
|
|
}
|
|
}
|
|
|
|
void addImport(string importModuleName, const SingleImport singleImport)
|
|
{
|
|
import std.uni : sicmp;
|
|
|
|
if (imports[level].length > 0 && imports[level][$ -1].sicmp(importModuleName) > 0)
|
|
{
|
|
addErrorMessage(singleImport.identifierChain.identifiers[0].line,
|
|
singleImport.identifierChain.identifiers[0].column, KEY, MESSAGE);
|
|
}
|
|
else
|
|
{
|
|
imports[level] ~= importModuleName;
|
|
}
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.stdio : stderr;
|
|
import std.format : format;
|
|
import analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
|
import analysis.helpers : assertAnalyzerWarnings;
|
|
|
|
StaticAnalysisConfig sac = disabledConfig();
|
|
sac.imports_sortedness = Check.enabled;
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import bar.foo;
|
|
import foo.bar;
|
|
}c, sac);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import foo.bar;
|
|
import bar.foo; // [warn]: %s
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import c;
|
|
import c.b;
|
|
import c.a; // [warn]: %s
|
|
import d.a;
|
|
import d; // [warn]: %s
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import a.b, a.c, a.d;
|
|
import a.b, a.d, a.c; // [warn]: %s
|
|
import a.c, a.b, a.c; // [warn]: %s
|
|
import foo.bar, bar.foo; // [warn]: %s
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), 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);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import test : bar;
|
|
import test : foo;
|
|
}c, sac);
|
|
|
|
// selective imports
|
|
assertAnalyzerWarnings(q{
|
|
import test : foo;
|
|
import test : bar; // [warn]: %s
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// selective imports
|
|
assertAnalyzerWarnings(q{
|
|
import test : foo, bar; // [warn]: %s
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import b;
|
|
import c : foo;
|
|
import c : bar; // [warn]: %s
|
|
import a; // [warn]: %s
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), 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);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
import t0;
|
|
import t1 : a, b = foo;
|
|
import t2;
|
|
}c, sac);
|
|
|
|
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);
|
|
|
|
// local imports in functions
|
|
assertAnalyzerWarnings(q{
|
|
import t2;
|
|
import t1; // [warn]: %s
|
|
void foo()
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
}
|
|
void bar()
|
|
{
|
|
import f1;
|
|
import f2;
|
|
}
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// local imports in scopes
|
|
assertAnalyzerWarnings(q{
|
|
import t2;
|
|
import t1; // [warn]: %s
|
|
void foo()
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
}
|
|
{
|
|
import f1;
|
|
import f2;
|
|
import f3;
|
|
}
|
|
}
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// local imports in functions
|
|
assertAnalyzerWarnings(q{
|
|
import t2;
|
|
import t1; // [warn]: %s
|
|
void foo()
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
while (true) {
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
}
|
|
for (;;) {
|
|
import f1;
|
|
import f2;
|
|
import f3;
|
|
}
|
|
foreach (el; arr) {
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
}
|
|
}
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// nested scopes
|
|
assertAnalyzerWarnings(q{
|
|
import t2;
|
|
import t1; // [warn]: %s
|
|
void foo()
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// local imports in functions
|
|
assertAnalyzerWarnings(q{
|
|
import t2;
|
|
import t1; // [warn]: %s
|
|
struct foo()
|
|
{
|
|
import f2;
|
|
import f1; // [warn]: %s
|
|
import f3;
|
|
}
|
|
class bar()
|
|
{
|
|
import f1;
|
|
import f2;
|
|
}
|
|
}c.format(
|
|
ImportSortednessCheck.MESSAGE,
|
|
ImportSortednessCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// issue 422 - sorted imports with :
|
|
assertAnalyzerWarnings(q{
|
|
import foo.bar : bar;
|
|
import foo.barbar;
|
|
}, sac);
|
|
|
|
// issue 422 - sorted imports with :
|
|
assertAnalyzerWarnings(q{
|
|
import foo;
|
|
import foo.bar;
|
|
import fooa;
|
|
import std.range : Take;
|
|
import std.range.primitives : isInputRange, walkLength;
|
|
}, sac);
|
|
|
|
// condition declaration
|
|
assertAnalyzerWarnings(q{
|
|
import t2;
|
|
version(unittest)
|
|
{
|
|
import t1;
|
|
}
|
|
}, sac);
|
|
|
|
// if statements
|
|
assertAnalyzerWarnings(q{
|
|
unittest
|
|
{
|
|
import t2;
|
|
if (true)
|
|
{
|
|
import t1;
|
|
}
|
|
}
|
|
}, sac);
|
|
|
|
// intermediate imports
|
|
assertAnalyzerWarnings(q{
|
|
unittest
|
|
{
|
|
import t2;
|
|
int a = 1;
|
|
import t1;
|
|
}
|
|
}, sac);
|
|
|
|
stderr.writeln("Unittest for ImportSortednessCheck passed.");
|
|
}
|