diff --git a/src/dscanner/analysis/base.d b/src/dscanner/analysis/base.d index a9baca0..cc0eedc 100644 --- a/src/dscanner/analysis/base.d +++ b/src/dscanner/analysis/base.d @@ -378,6 +378,29 @@ struct BaseAnalyzerArguments const Scope* sc; bool skipTests = false; + // auto-generated members: + size_t[typeof(Token.line)] lineEndTokenIndices; + size_t[typeof(Token.line)] lineStartTokenIndices; + + void preprocess() + { + if (!tokens.length) + return; + + typeof(Token.line) line = -1; + foreach (i, t; tokens) + { + if (t.line != line) + { + lineStartTokenIndices[t.line] = i; + if (i != 0) + lineEndTokenIndices[tokens[i - 1].line] = i - 1; + } + } + + lineEndTokenIndices[tokens[$ - 1].line] = tokens.length - 1; + } + BaseAnalyzerArguments setSkipTests(bool v) { auto ret = this; @@ -406,6 +429,8 @@ public: this.tokens = args.tokens; this.fileName = args.fileName; this.skipTests = args.skipTests; + this.lineEndTokenIndices = args.lineEndTokenIndices; + this.lineStartTokenIndices = args.lineStartTokenIndices; _messages = new MessageSet; } @@ -481,6 +506,8 @@ protected: bool inAggregate; bool skipTests; const(Token)[] tokens; + size_t[typeof(Token.line)] lineEndTokenIndices; + size_t[typeof(Token.line)] lineStartTokenIndices; NoLint noLint; template visitTemplate(T) @@ -496,42 +523,42 @@ protected: deprecated("Use the overload taking start and end locations or a Node instead") void addErrorMessage(size_t line, size_t column, string key, string message) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, [findToken(line, column)])) return; _messages.insert(Message(fileName, line, column, key, message, getName())); } void addErrorMessage(const BaseNode node, string key, string message, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, node.tokens)) return; addErrorMessage(Message.Diagnostic.from(fileName, node, message), key, autofixes); } void addErrorMessage(const Token token, string key, string message, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, [token])) return; addErrorMessage(Message.Diagnostic.from(fileName, token, message), key, autofixes); } void addErrorMessage(const Token[] tokens, string key, string message, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, tokens)) return; addErrorMessage(Message.Diagnostic.from(fileName, tokens, message), key, autofixes); } void addErrorMessage(size_t[2] index, size_t line, size_t[2] columns, string key, string message, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, [findToken(index[0]), findToken(index[1])])) return; addErrorMessage(index, [line, line], columns, key, message, autofixes); } void addErrorMessage(size_t[2] index, size_t[2] lines, size_t[2] columns, string key, string message, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, [findToken(index[0]), findToken(index[1])])) return; auto d = Message.Diagnostic.from(fileName, index, lines, columns, message); _messages.insert(Message(d, key, getName(), autofixes)); @@ -539,14 +566,14 @@ protected: void addErrorMessage(Message.Diagnostic diagnostic, string key, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, null)) // TODO return; _messages.insert(Message(diagnostic, key, getName(), autofixes)); } void addErrorMessage(Message.Diagnostic diagnostic, Message.Diagnostic[] supplemental, string key, AutoFix[] autofixes = null) { - if (noLint.containsCheck(key)) + if (checkNoLint(key, null)) // TODO return; _messages.insert(Message(diagnostic, supplemental, key, getName(), autofixes)); } @@ -559,6 +586,59 @@ protected: const(Scope)* sc; MessageSet _messages; + + private const Token findToken(typeof(Token.index) index) + { + return Token.init; // todo + } + + private const Token findToken(typeof(Token.line) line, typeof(Token.column) column) + { + return Token.init; // todo + } + + private const Token getLastLineToken(typeof(Token.line) line) + { + if (auto pi = line in lineEndTokenIndices) + return tokens[*pi]; + return Token.init; + } + + private bool checkNoLint(string key, const(Token)[] tokens) + { + if (noLint.containsCheck(key)) + return true; + if (!tokens.length) + return false; + auto first = tokens[0].leadingTrivia; + auto last = tokens[$ - 1].trailingTrivia; + auto line = getLastLineToken(tokens[$ - 1].line).trailingTrivia; + + bool checkComments(typeof(Token.leadingTrivia) tokens) + { + foreach (token; tokens) + { + if (token.type == tok!"comment" + && token.text.startsWith( + "// @nolint", + "// @suppress")) // in use in some projects since this was introduced in code-d + { + auto nolintUDA = token.text[3 .. $]; + auto n = NoLintFactory.fromString(nolintUDA); + if (!n.isNull && n.get.containsCheck(key)) + return true; + } + } + return false; + } + + if (checkComments(getLastLineToken(tokens[$ - 1].line).trailingTrivia) + || checkComments(tokens[$ - 1].trailingTrivia) + || checkComments(tokens[0].leadingTrivia)) + return true; + + return false; + } } /// Find the token with the given type, otherwise returns the whole range or a user-specified fallback, if set. diff --git a/src/dscanner/analysis/nolint.d b/src/dscanner/analysis/nolint.d index 4d2ab41..3960630 100644 --- a/src/dscanner/analysis/nolint.d +++ b/src/dscanner/analysis/nolint.d @@ -116,6 +116,38 @@ struct NoLintFactory return noLint.nullable; } + // Transform a string with form "nolint(abc, efg)" + // into a NoLint struct + static Nullable!NoLint fromString(in string str) + { + static immutable re = regex(`[\w-_.]+`, "g"); + auto matches = matchAll(str, re); + + if (!matches) + return nullNoLint; + + const udaName = matches.hit; + if (udaName != "nolint") + return nullNoLint; + + matches.popFront; + + NoLint noLint; + + while (matches) + { + noLint.pushCheck(matches.hit); + matches.popFront; + } + + if (!noLint.getDisabledChecks.length) + return nullNoLint; + + return noLint.nullable; + } + + static nullNoLint = Nullable!NoLint.init; + private: static Nullable!NoLint fromAttribute(const(Attribute) attribute) { @@ -205,38 +237,6 @@ private: return noLint.nullable; } - - // Transform a string with form "nolint(abc, efg)" - // into a NoLint struct - static Nullable!NoLint fromString(in string str) - { - static immutable re = regex(`[\w-_.]+`, "g"); - auto matches = matchAll(str, re); - - if (!matches) - return nullNoLint; - - const udaName = matches.hit; - if (udaName != "nolint") - return nullNoLint; - - matches.popFront; - - NoLint noLint; - - while (matches) - { - noLint.pushCheck(matches.hit); - matches.popFront; - } - - if (!noLint.getDisabledChecks.length) - return nullNoLint; - - return noLint.nullable; - } - - static nullNoLint = Nullable!NoLint.init; } unittest diff --git a/src/dscanner/analysis/run.d b/src/dscanner/analysis/run.d index 7965135..154a8f5 100644 --- a/src/dscanner/analysis/run.d +++ b/src/dscanner/analysis/run.d @@ -754,6 +754,7 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName, tokens, moduleScope ); + args.preprocess(); if (moduleName.shouldRun!AsmStyleCheck(analysisConfig)) checks ~= new AsmStyleCheck(args.setSkipTests( diff --git a/src/dscanner/analysis/useless_initializer.d b/src/dscanner/analysis/useless_initializer.d index f2867d1..3382cd9 100644 --- a/src/dscanner/analysis/useless_initializer.d +++ b/src/dscanner/analysis/useless_initializer.d @@ -369,7 +369,8 @@ public: assertAnalyzerWarnings(q{ @("nolint(dscanner.useless-initializer)") int a = 0; - int a = 0; /+ + int a = 0; // @nolint(dscanner.useless-initializer) + int a = 0; /+ ^ [warn]: X +/ @("nolint(dscanner.useless-initializer)")