D-Scanner/src/analysis/imports_sortedness.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.");
}