UDA to disable linting: handle check status in NoLint struct

This commit is contained in:
Axel Ricard 2023-10-12 21:20:04 +02:00
parent 43c93e2537
commit 7fc1081630
4 changed files with 65 additions and 77 deletions

View file

@ -1,8 +1,8 @@
module dscanner.analysis.base; module dscanner.analysis.base;
import dscanner.analysis.nolint;
import dparse.ast; import dparse.ast;
import dparse.lexer : IdType, str, Token, tok; import dparse.lexer : IdType, str, Token, tok;
import dscanner.analysis.nolint;
import dsymbol.scope_ : Scope; import dsymbol.scope_ : Scope;
import std.array; import std.array;
import std.container; import std.container;
@ -413,8 +413,9 @@ public:
*/ */
override void visit(const(ModuleDeclaration) moduleDeclaration) override void visit(const(ModuleDeclaration) moduleDeclaration)
{ {
if(stopLinting(moduleDeclaration)) auto currNoLint = NoLintFactory.fromModuleDeclaration(moduleDeclaration);
return; noLint.push(currNoLint);
scope(exit) noLint.pop(currNoLint);
moduleDeclaration.accept(this); moduleDeclaration.accept(this);
} }
@ -426,12 +427,11 @@ public:
*/ */
override void visit(const(Declaration) decl) 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); decl.accept(this);
if(msgDisabled)
reenableErrorMessage();
} }
AutoFix.CodeReplacement[] resolveAutoFix( AutoFix.CodeReplacement[] resolveAutoFix(
@ -452,7 +452,7 @@ protected:
bool inAggregate; bool inAggregate;
bool skipTests; bool skipTests;
int errorMsgDisabled; NoLint noLint;
template visitTemplate(T) template visitTemplate(T)
{ {
@ -467,42 +467,42 @@ protected:
deprecated("Use the overload taking start and end locations or a Node instead") 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) void addErrorMessage(size_t line, size_t column, string key, string message)
{ {
if(!errorMsgEnabled()) if(noLint.containsCheck(this.getName()))
return; return;
_messages.insert(Message(fileName, line, column, key, message, getName())); _messages.insert(Message(fileName, line, column, key, message, getName()));
} }
void addErrorMessage(const BaseNode node, string key, string message, AutoFix[] autofixes = null) void addErrorMessage(const BaseNode node, string key, string message, AutoFix[] autofixes = null)
{ {
if(!errorMsgEnabled()) if(noLint.containsCheck(this.getName()))
return; return;
addErrorMessage(Message.Diagnostic.from(fileName, node, message), key, autofixes); addErrorMessage(Message.Diagnostic.from(fileName, node, message), key, autofixes);
} }
void addErrorMessage(const Token token, string key, string message, AutoFix[] autofixes = null) void addErrorMessage(const Token token, string key, string message, AutoFix[] autofixes = null)
{ {
if(!errorMsgEnabled()) if(noLint.containsCheck(this.getName()))
return; return;
addErrorMessage(Message.Diagnostic.from(fileName, token, message), key, autofixes); addErrorMessage(Message.Diagnostic.from(fileName, token, message), key, autofixes);
} }
void addErrorMessage(const Token[] tokens, string key, string message, AutoFix[] autofixes = null) void addErrorMessage(const Token[] tokens, string key, string message, AutoFix[] autofixes = null)
{ {
if(!errorMsgEnabled()) if(noLint.containsCheck(this.getName()))
return; return;
addErrorMessage(Message.Diagnostic.from(fileName, tokens, message), key, autofixes); 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) 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; return;
addErrorMessage(index, [line, line], columns, key, message, autofixes); 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) 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; return;
auto d = Message.Diagnostic.from(fileName, index, lines, columns, message); auto d = Message.Diagnostic.from(fileName, index, lines, columns, message);
_messages.insert(Message(d, key, getName(), autofixes)); _messages.insert(Message(d, key, getName(), autofixes));
@ -510,57 +510,18 @@ protected:
void addErrorMessage(Message.Diagnostic diagnostic, string key, AutoFix[] autofixes = null) void addErrorMessage(Message.Diagnostic diagnostic, string key, AutoFix[] autofixes = null)
{ {
if(!errorMsgEnabled()) if(noLint.containsCheck(this.getName()))
return; return;
_messages.insert(Message(diagnostic, key, getName(), autofixes)); _messages.insert(Message(diagnostic, key, getName(), autofixes));
} }
void addErrorMessage(Message.Diagnostic diagnostic, Message.Diagnostic[] supplemental, string key, AutoFix[] autofixes = null) void addErrorMessage(Message.Diagnostic diagnostic, Message.Diagnostic[] supplemental, string key, AutoFix[] autofixes = null)
{ {
if(!errorMsgEnabled()) if(noLint.containsCheck(this.getName()))
return; return;
_messages.insert(Message(diagnostic, supplemental, key, getName(), autofixes)); _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 * The file name
*/ */

View file

@ -13,28 +13,47 @@ struct NoLint
{ {
bool containsCheck(in string check) const bool containsCheck(in string check) const
{ {
return disabledChecks.canFind(check); return (check in disabledChecks) !is null &&
disabledChecks[check] > 0;
} }
package: package:
const(string[]) getDisabledChecks() const const(int[string]) getDisabledChecks() const
{ {
return this.disabledChecks; 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) if(other.isNull)
this.disabledChecks ~= other.get.getDisabledChecks(); 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: private:
string[] disabledChecks; int[string] disabledChecks;
} }
struct NoLintFactory struct NoLintFactory
@ -44,7 +63,7 @@ struct NoLintFactory
NoLint noLint; NoLint noLint;
foreach(atAttribute; moduleDeclaration.atAttributes) foreach(atAttribute; moduleDeclaration.atAttributes)
noLint.merge(NoLintFactory.fromAtAttribute(atAttribute)); noLint.push(NoLintFactory.fromAtAttribute(atAttribute));
if(!noLint.getDisabledChecks.length) if(!noLint.getDisabledChecks.length)
return nullNoLint; return nullNoLint;
@ -56,7 +75,7 @@ struct NoLintFactory
{ {
NoLint noLint; NoLint noLint;
foreach(attribute; declaration.attributes) foreach(attribute; declaration.attributes)
noLint.merge(NoLintFactory.fromAttribute(attribute)); noLint.push(NoLintFactory.fromAttribute(attribute));
if(!noLint.getDisabledChecks.length) if(!noLint.getDisabledChecks.length)
return nullNoLint; return nullNoLint;
@ -115,7 +134,7 @@ private:
if(primaryExpression.primary != tok!"stringLiteral") if(primaryExpression.primary != tok!"stringLiteral")
continue; continue;
noLint.addCheck(primaryExpression.primary.text.strip("\"")); noLint.pushCheck(primaryExpression.primary.text.strip("\""));
} }
} }
@ -144,7 +163,7 @@ private:
auto str = primaryExpression.primary.text.strip("\""); auto str = primaryExpression.primary.text.strip("\"");
Nullable!NoLint currNoLint = NoLintFactory.fromString(str); Nullable!NoLint currNoLint = NoLintFactory.fromString(str);
noLint.merge(currNoLint); noLint.push(currNoLint);
} }
} }
@ -175,7 +194,7 @@ private:
while(matches) while(matches)
{ {
noLint.addCheck(matches.hit); noLint.pushCheck(matches.hit);
matches.popFront; matches.popFront;
} }
@ -195,8 +214,14 @@ unittest
const s3 = " nolint ( abc , efg ) "; const s3 = " nolint ( abc , efg ) ";
const s4 = "OtherUda(abc)"; const s4 = "OtherUda(abc)";
assert(NoLintFactory.fromString(s1).get == NoLint(["abc"])); assert(NoLintFactory.fromString(s1).get.containsCheck("abc"));
assert(NoLintFactory.fromString(s2).get == NoLint(["abc", "efg", "hij"]));
assert(NoLintFactory.fromString(s3).get == NoLint(["abc", "efg"])); 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); assert(NoLintFactory.fromString(s4).isNull);
} }

View file

@ -14,6 +14,7 @@ import std.conv;
import std.format; import std.format;
import dscanner.analysis.helpers; import dscanner.analysis.helpers;
import dscanner.analysis.base; import dscanner.analysis.base;
import dscanner.analysis.nolint;
import dsymbol.scope_ : Scope; import dsymbol.scope_ : Scope;
final class StyleChecker : BaseAnalyzer final class StyleChecker : BaseAnalyzer
@ -33,8 +34,9 @@ final class StyleChecker : BaseAnalyzer
override void visit(const ModuleDeclaration dec) override void visit(const ModuleDeclaration dec)
{ {
if(stopLinting(dec)) auto currNoLint = NoLintFactory.fromModuleDeclaration(dec);
return; noLint.push(currNoLint);
scope(exit) noLint.pop(currNoLint);
foreach (part; dec.moduleName.identifiers) foreach (part; dec.moduleName.identifiers)
{ {

View file

@ -5,6 +5,7 @@
module dscanner.analysis.useless_initializer; module dscanner.analysis.useless_initializer;
import dscanner.analysis.base; import dscanner.analysis.base;
import dscanner.analysis.nolint;
import dscanner.utils : safeAccess; import dscanner.utils : safeAccess;
import containers.dynamicarray; import containers.dynamicarray;
import containers.hashmap; import containers.hashmap;
@ -93,13 +94,12 @@ public:
{ {
_inStruct.insert(decl.structDeclaration !is null); _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); decl.accept(this);
if(msgDisabled)
reenableErrorMessage();
if (_inStruct.length > 1 && _inStruct[$-2] && decl.constructor && if (_inStruct.length > 1 && _inStruct[$-2] && decl.constructor &&
((decl.constructor.parameters && decl.constructor.parameters.parameters.length == 0) || ((decl.constructor.parameters && decl.constructor.parameters.parameters.length == 0) ||
!decl.constructor.parameters)) !decl.constructor.parameters))