diff --git a/src/dscanner/analysis/base.d b/src/dscanner/analysis/base.d index df76164..65f8532 100644 --- a/src/dscanner/analysis/base.d +++ b/src/dscanner/analysis/base.d @@ -1,8 +1,8 @@ module dscanner.analysis.base; -import dscanner.analysis.nolint; import dparse.ast; import dparse.lexer : IdType, str, Token, tok; +import dscanner.analysis.nolint; import dsymbol.scope_ : Scope; import std.array; import std.container; @@ -413,8 +413,9 @@ public: */ override void visit(const(ModuleDeclaration) moduleDeclaration) { - if(stopLinting(moduleDeclaration)) - return; + auto currNoLint = NoLintFactory.fromModuleDeclaration(moduleDeclaration); + noLint.push(currNoLint); + scope(exit) noLint.pop(currNoLint); moduleDeclaration.accept(this); } @@ -426,12 +427,11 @@ public: */ override void visit(const(Declaration) decl) { - const msgDisabled = maybeDisableErrorMessage(decl); + auto currNoLint = NoLintFactory.fromDeclaration(decl); + noLint.push(currNoLint); + scope(exit) noLint.pop(currNoLint); decl.accept(this); - - if(msgDisabled) - reenableErrorMessage(); } AutoFix.CodeReplacement[] resolveAutoFix( @@ -452,7 +452,7 @@ protected: bool inAggregate; bool skipTests; - int errorMsgDisabled; + NoLint noLint; template visitTemplate(T) { @@ -467,42 +467,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(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) return; _messages.insert(Message(fileName, line, column, key, message, getName())); } void addErrorMessage(const BaseNode node, string key, string message, AutoFix[] autofixes = null) { - if(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) return; addErrorMessage(Message.Diagnostic.from(fileName, node, message), key, autofixes); } void addErrorMessage(const Token token, string key, string message, AutoFix[] autofixes = null) { - if(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) return; addErrorMessage(Message.Diagnostic.from(fileName, token, message), key, autofixes); } void addErrorMessage(const Token[] tokens, string key, string message, AutoFix[] autofixes = null) { - if(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) 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(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) 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(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) return; auto d = Message.Diagnostic.from(fileName, index, lines, columns, message); _messages.insert(Message(d, key, getName(), autofixes)); @@ -510,57 +510,18 @@ protected: void addErrorMessage(Message.Diagnostic diagnostic, string key, AutoFix[] autofixes = null) { - if(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) return; _messages.insert(Message(diagnostic, key, getName(), autofixes)); } void addErrorMessage(Message.Diagnostic diagnostic, Message.Diagnostic[] supplemental, string key, AutoFix[] autofixes = null) { - if(!errorMsgEnabled()) + if(noLint.containsCheck(this.getName())) return; _messages.insert(Message(diagnostic, supplemental, key, getName(), autofixes)); } - void reenableErrorMessage() - in(this.errorMsgDisabled > 0) - { - this.errorMsgDisabled--; - } - - bool errorMsgEnabled() const - { - return this.errorMsgDisabled == 0; - } - - // Disable error message if declaration contains UDA : - // @("nolint(..)") and @nolint(".."), .. - // that indicates to skip linting on this declaration - // Return wheter the message is actually disabled or not - bool maybeDisableErrorMessage(const Declaration decl) - { - if(stopLinting(decl)) - { - this.errorMsgDisabled++; - return true; - } - else - return false; - } - - bool stopLinting(AstNode)(const AstNode node) - { - import std.typecons: Nullable; - Nullable!NoLint noLint; - - static if(is(AstNode == ModuleDeclaration)) - noLint = NoLintFactory.fromModuleDeclaration(node); - else static if(is(AstNode == Declaration)) - noLint = NoLintFactory.fromDeclaration(node); - - return !noLint.isNull && noLint.get.containsCheck(this.getName()); - } - /** * The file name */ diff --git a/src/dscanner/analysis/nolint.d b/src/dscanner/analysis/nolint.d index 9abf754..7e06cfd 100644 --- a/src/dscanner/analysis/nolint.d +++ b/src/dscanner/analysis/nolint.d @@ -13,28 +13,47 @@ struct NoLint { bool containsCheck(in string check) const { - return disabledChecks.canFind(check); + return (check in disabledChecks) !is null && + disabledChecks[check] > 0; } package: - const(string[]) getDisabledChecks() const + const(int[string]) getDisabledChecks() const { return this.disabledChecks; } - void addCheck(in string check) + void pushCheck(in string check) { - disabledChecks ~= check; + disabledChecks[check]++; } - void merge(in Nullable!NoLint other) + void push(in Nullable!NoLint other) { - if(!other.isNull) - this.disabledChecks ~= other.get.getDisabledChecks(); + if(other.isNull) + return; + + foreach(item; other.get.getDisabledChecks.byKeyValue) + this.disabledChecks[item.key] += item.value; } + void pop(in Nullable!NoLint other) + { + if(other.isNull) + return; + + foreach(item; other.get.getDisabledChecks.byKeyValue) + { + assert((item.key in disabledChecks) !is null && + this.disabledChecks[item.key] >= item.value); + + this.disabledChecks[item.key] -= item.value; + } + } + + private: - string[] disabledChecks; + int[string] disabledChecks; } struct NoLintFactory @@ -44,7 +63,7 @@ struct NoLintFactory NoLint noLint; foreach(atAttribute; moduleDeclaration.atAttributes) - noLint.merge(NoLintFactory.fromAtAttribute(atAttribute)); + noLint.push(NoLintFactory.fromAtAttribute(atAttribute)); if(!noLint.getDisabledChecks.length) return nullNoLint; @@ -56,7 +75,7 @@ struct NoLintFactory { NoLint noLint; foreach(attribute; declaration.attributes) - noLint.merge(NoLintFactory.fromAttribute(attribute)); + noLint.push(NoLintFactory.fromAttribute(attribute)); if(!noLint.getDisabledChecks.length) return nullNoLint; @@ -115,7 +134,7 @@ private: if(primaryExpression.primary != tok!"stringLiteral") continue; - noLint.addCheck(primaryExpression.primary.text.strip("\"")); + noLint.pushCheck(primaryExpression.primary.text.strip("\"")); } } @@ -144,7 +163,7 @@ private: auto str = primaryExpression.primary.text.strip("\""); Nullable!NoLint currNoLint = NoLintFactory.fromString(str); - noLint.merge(currNoLint); + noLint.push(currNoLint); } } @@ -175,7 +194,7 @@ private: while(matches) { - noLint.addCheck(matches.hit); + noLint.pushCheck(matches.hit); matches.popFront; } @@ -195,8 +214,14 @@ unittest const s3 = " nolint ( abc , efg ) "; const s4 = "OtherUda(abc)"; - assert(NoLintFactory.fromString(s1).get == NoLint(["abc"])); - assert(NoLintFactory.fromString(s2).get == NoLint(["abc", "efg", "hij"])); - assert(NoLintFactory.fromString(s3).get == NoLint(["abc", "efg"])); + assert(NoLintFactory.fromString(s1).get.containsCheck("abc")); + + assert(NoLintFactory.fromString(s2).get.containsCheck("abc")); + assert(NoLintFactory.fromString(s2).get.containsCheck("efg")); + assert(NoLintFactory.fromString(s2).get.containsCheck("hij")); + + assert(NoLintFactory.fromString(s3).get.containsCheck("abc")); + assert(NoLintFactory.fromString(s3).get.containsCheck("efg")); + assert(NoLintFactory.fromString(s4).isNull); } diff --git a/src/dscanner/analysis/style.d b/src/dscanner/analysis/style.d index 6154b79..4499d37 100644 --- a/src/dscanner/analysis/style.d +++ b/src/dscanner/analysis/style.d @@ -14,6 +14,7 @@ import std.conv; import std.format; import dscanner.analysis.helpers; import dscanner.analysis.base; +import dscanner.analysis.nolint; import dsymbol.scope_ : Scope; final class StyleChecker : BaseAnalyzer @@ -33,8 +34,9 @@ final class StyleChecker : BaseAnalyzer override void visit(const ModuleDeclaration dec) { - if(stopLinting(dec)) - return; + auto currNoLint = NoLintFactory.fromModuleDeclaration(dec); + noLint.push(currNoLint); + scope(exit) noLint.pop(currNoLint); foreach (part; dec.moduleName.identifiers) { diff --git a/src/dscanner/analysis/useless_initializer.d b/src/dscanner/analysis/useless_initializer.d index 0a27072..ce986a1 100644 --- a/src/dscanner/analysis/useless_initializer.d +++ b/src/dscanner/analysis/useless_initializer.d @@ -5,6 +5,7 @@ module dscanner.analysis.useless_initializer; import dscanner.analysis.base; +import dscanner.analysis.nolint; import dscanner.utils : safeAccess; import containers.dynamicarray; import containers.hashmap; @@ -93,13 +94,12 @@ public: { _inStruct.insert(decl.structDeclaration !is null); - const msgDisabled = maybeDisableErrorMessage(decl); + auto currNoLint = NoLintFactory.fromDeclaration(decl); + noLint.push(currNoLint); + scope(exit) noLint.pop(currNoLint); decl.accept(this); - if(msgDisabled) - reenableErrorMessage(); - if (_inStruct.length > 1 && _inStruct[$-2] && decl.constructor && ((decl.constructor.parameters && decl.constructor.parameters.parameters.length == 0) || !decl.constructor.parameters))