diff --git a/src/dscanner/analysis/allman.d b/src/dscanner/analysis/allman.d index ace6ddd5..e8b1969e 100644 --- a/src/dscanner/analysis/allman.d +++ b/src/dscanner/analysis/allman.d @@ -4,13 +4,10 @@ module dscanner.analysis.allman; -import dparse.lexer; -import dparse.ast; import dscanner.analysis.base; -import dsymbol.scope_ : Scope; - -import std.algorithm; -import std.range; +import dmd.tokens : Token, TOK; +import std.algorithm : canFind, until; +import std.range : retro; /** Checks for the allman style (braces should be on their own line) @@ -25,50 +22,85 @@ if (param < 0) } ------------ */ -final class AllManCheck : BaseAnalyzer +extern (C++) class AllManCheck : BaseAnalyzerDmd { mixin AnalyzerInfo!"allman_braces_check"; - /// - this(BaseAnalyzerArguments args) + private enum string KEY = "dscanner.style.allman"; + private enum string MESSAGE = "Braces should be on their own line"; + + private Token[] tokens; + + extern (D) this(string fileName, bool skipTests = false) + { + super(fileName, skipTests); + lexFile(); + checkBraces(); + } + + private void lexFile() + { + import dscanner.utils : readFile; + import dmd.errorsink : ErrorSinkNull; + import dmd.globals : global; + import dmd.lexer : Lexer; + + auto bytes = readFile(fileName) ~ '\0'; + + __gshared ErrorSinkNull errorSinkNull; + if (!errorSinkNull) + errorSinkNull = new ErrorSinkNull; + + scope lexer = new Lexer(null, cast(char*) bytes, 0, bytes.length, 0, 0, errorSinkNull, &global.compileEnv); + + do + { + lexer.nextToken(); + tokens ~= lexer.token; + } + while (lexer.token.value != TOK.endOfFile); + } + + private void checkBraces() { - super(args); foreach (i; 1 .. tokens.length - 1) { - const curLine = tokens[i].line; - const prevTokenLine = tokens[i-1].line; - if (tokens[i].type == tok!"{" && curLine == prevTokenLine) + const curLine = tokens[i].loc.linnum; + const prevTokenLine = tokens[i - 1].loc.linnum; + + if (tokens[i].value == TOK.leftCurly && curLine == prevTokenLine) { // ignore struct initialization - if (tokens[i-1].type == tok!"=") + if (tokens[i - 1].value == TOK.assign) continue; + // ignore duplicate braces - if (tokens[i-1].type == tok!"{" && tokens[i - 2].line != curLine) + if (tokens[i - 1].value == TOK.leftCurly && tokens[i - 2].loc.linnum != curLine) continue; + // ignore inline { } braces - if (curLine != tokens[i + 1].line) - addErrorMessage(tokens[i], KEY, MESSAGE); + if (curLine != tokens[i + 1].loc.linnum) + addErrorMessage(cast(ulong) tokens[i].loc.linnum, cast(ulong) tokens[i].loc.charnum, KEY, MESSAGE); } - if (tokens[i].type == tok!"}" && curLine == prevTokenLine) + + if (tokens[i].value == TOK.rightCurly && curLine == prevTokenLine) { // ignore duplicate braces - if (tokens[i-1].type == tok!"}" && tokens[i - 2].line != curLine) + if (tokens[i-1].value == TOK.rightCurly && tokens[i - 2].loc.linnum != curLine) continue; + // ignore inline { } braces - if (!tokens[0 .. i].retro.until!(t => t.line != curLine).canFind!(t => t.type == tok!"{")) - addErrorMessage(tokens[i], KEY, MESSAGE); + if (!tokens[0 .. i].retro.until!(t => t.loc.linnum != curLine).canFind!(t => t.value == TOK.leftCurly)) + addErrorMessage(cast(ulong) tokens[i].loc.linnum, cast(ulong) tokens[i].loc.charnum, KEY, MESSAGE); } } } - - enum string KEY = "dscanner.style.allman"; - enum string MESSAGE = "Braces should be on their own line"; } unittest { import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; - import dscanner.analysis.helpers : assertAnalyzerWarnings; + import dscanner.analysis.helpers : assertAnalyzerWarningsDMD; import std.format : format; import std.stdio : stderr; @@ -76,11 +108,10 @@ unittest sac.allman_braces_check = Check.enabled; // check common allman style violation - assertAnalyzerWarnings(q{ + assertAnalyzerWarningsDMD(` void testAllman() { - while (true) { /+ - ^ [warn]: %s +/ + while (true) { // [warn]: %s auto f = 1; } @@ -115,7 +146,7 @@ unittest } } } - }c.format( + `c.format( AllManCheck.MESSAGE, AllManCheck.MESSAGE, AllManCheck.MESSAGE, @@ -128,7 +159,7 @@ unittest ), sac); // check struct initialization - assertAnalyzerWarnings(q{ + assertAnalyzerWarningsDMD(q{ unittest { struct Foo { int a; } @@ -139,12 +170,11 @@ unittest }, sac); // allow duplicate braces - assertAnalyzerWarnings(q{ + assertAnalyzerWarningsDMD(q{ unittest {{ }} }, sac); - stderr.writeln("Unittest for Allman passed."); } diff --git a/src/dscanner/analysis/run.d b/src/dscanner/analysis/run.d index a88b79f1..d1b327bd 100644 --- a/src/dscanner/analysis/run.d +++ b/src/dscanner/analysis/run.d @@ -69,7 +69,6 @@ import dscanner.analysis.properly_documented_public_functions; import dscanner.analysis.final_attribute; import dscanner.analysis.vcall_in_ctor; import dscanner.analysis.useless_initializer; -import dscanner.analysis.allman; import dscanner.analysis.always_curly; import dscanner.analysis.redundant_attributes; import dscanner.analysis.has_public_example; @@ -668,10 +667,6 @@ BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName, checks ~= new UndocumentedDeclarationCheck(args.setSkipTests( analysisConfig.undocumented_declaration_check == Check.skipTests && !ut)); - if (moduleName.shouldRun!AllManCheck(analysisConfig)) - checks ~= new AllManCheck(args.setSkipTests( - analysisConfig.allman_braces_check == Check.skipTests && !ut)); - if (moduleName.shouldRun!IfConstraintsIndentCheck(analysisConfig)) checks ~= new IfConstraintsIndentCheck(args.setSkipTests( analysisConfig.if_constraints_indent == Check.skipTests && !ut)); diff --git a/src/dscanner/analysis/rundmd.d b/src/dscanner/analysis/rundmd.d index 06d46b52..1857a4d4 100644 --- a/src/dscanner/analysis/rundmd.d +++ b/src/dscanner/analysis/rundmd.d @@ -54,6 +54,7 @@ import dscanner.analysis.unused_variable : UnusedVariableCheck; import dscanner.analysis.useless_assert : UselessAssertCheck; import dscanner.analysis.useless_initializer : UselessInitializerChecker; import dscanner.analysis.vcall_in_ctor : VcallCtorChecker; +import dscanner.analysis.allman : AllManCheck; version (unittest) enum ut = true; @@ -317,6 +318,12 @@ MessageSet analyzeDmd(string fileName, ASTCodegen.Module m, const char[] moduleN fileName, config.vcall_in_ctor == Check.skipTests && !ut ); + + if (moduleName.shouldRunDmd!AllManCheck(config)) + visitors ~= new AllManCheck( + fileName, + config.allman_braces_check == Check.skipTests && !ut + ); foreach (visitor; visitors) {