Replace libdparse in enum array functionality

This commit is contained in:
Lucian Danescu 2022-04-13 09:12:25 +03:00 committed by Eduard Staniloiu
parent ab430835ab
commit 150643c4d3
6 changed files with 117 additions and 74 deletions

View file

@ -18,8 +18,8 @@ if %githashsize% == 0 (
move /y bin\githash_.txt bin\githash.txt
)
set DFLAGS=-O -release -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -Jbin -Jdmd %MFLAGS%
set TESTFLAGS=-g -w -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -Jbin -Jdmd
set DFLAGS=-O -release -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -version=MARS -Jbin -Jdmd %MFLAGS%
set TESTFLAGS=-g -w -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -version=MARS -Jbin -Jdmd
set CORE=
set LIBDPARSE=
set STD=

View file

@ -2,6 +2,7 @@
"fileVersion": 1,
"versions": {
"dcd": "0.16.0-beta.2",
"dmd": "~master",
"dsymbol": "0.13.0",
"emsi_containers": "0.9.0",
"inifiled": "1.3.3",

View file

@ -71,11 +71,11 @@ INCLUDE_PATHS = \
-Ilibddoc/common/source \
-Idmd/src
DMD_VERSIONS = -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB
DMD_VERSIONS = -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -version=MARS
DMD_DEBUG_VERSIONS = -version=dparse_verbose
LDC_VERSIONS = -d-version=StdLoggerDisableWarning -d-version=CallbackAPI -d-version=DMDLIB
LDC_VERSIONS = -d-version=StdLoggerDisableWarning -d-version=CallbackAPI -d-version=DMDLIB -version=MARS
LDC_DEBUG_VERSIONS = -d-version=dparse_verbose
GDC_VERSIONS = -fversion=StdLoggerDisableWarning -fversion=CallbackAPI -fversion=DMDLIB
GDC_VERSIONS = -fversion=StdLoggerDisableWarning -fversion=CallbackAPI -fversion=DMDLIB -version=MARS
GDC_DEBUG_VERSIONS = -fversion=dparse_verbose
DC_FLAGS += -Jbin -Jdmd

View file

@ -9,6 +9,9 @@ import std.container;
import std.meta : AliasSeq;
import std.string;
import std.sumtype;
import dmd.transitivevisitor;
import core.stdc.string;
import std.conv : to;
///
struct AutoFix
@ -361,11 +364,15 @@ enum comparitor = q{ a.startLine < b.startLine || (a.startLine == b.startLine &&
alias MessageSet = RedBlackTree!(Message, comparitor, true);
/**
* Should be present in all visitors to specify the name of the check
* done by a patricular visitor
*/
mixin template AnalyzerInfo(string checkName)
{
enum string name = checkName;
override protected string getName()
extern(D) override protected string getName()
{
return name;
}
@ -897,3 +904,47 @@ unittest
auto isOldScope = void;
});
}
/**
* Visitor that implements the AST traversal logic.
* Supports collecting error messages
*/
extern(C++) class BaseAnalyzerDmd(AST) : ParseTimeTransitiveVisitor!AST
{
alias visit = ParseTimeTransitiveVisitor!AST.visit;
extern(D) this(string fileName)
{
this.fileName = fileName;
_messages = new MessageSet;
}
/**
* Ensures that template AnalyzerInfo is instantiated in all classes
* deriving from this class
*/
extern(D) protected string getName()
{
assert(0);
}
extern(D) Message[] messages()
{
return _messages[].array;
}
protected:
extern(D) void addErrorMessage(size_t line, size_t column, string key, string message)
{
_messages.insert(Message(fileName, line, column, key, message, getName()));
}
/**
* The file name
*/
extern(D) string fileName;
extern(D) MessageSet _messages;
}

View file

@ -5,79 +5,33 @@
module dscanner.analysis.enumarrayliteral;
import dparse.ast;
import dparse.lexer;
import dscanner.analysis.base;
import std.algorithm : find, map;
import dsymbol.scope_ : Scope;
void doNothing(string, size_t, size_t, string, bool)
extern(C++) class EnumArrayVisitor(AST) : BaseAnalyzerDmd
{
}
final class EnumArrayLiteralCheck : BaseAnalyzer
{
alias visit = BaseAnalyzer.visit;
mixin AnalyzerInfo!"enum_array_literal_check";
alias visit = BaseAnalyzerDmd.visit;
this(BaseAnalyzerArguments args)
extern(D) this(string fileName)
{
super(args);
super(fileName);
}
bool looking;
override void visit(AST.VarDeclaration vd)
{
import dmd.astenums : STC, InitKind;
import std.string : toStringz;
mixin visitTemplate!ClassDeclaration;
mixin visitTemplate!InterfaceDeclaration;
mixin visitTemplate!UnionDeclaration;
mixin visitTemplate!StructDeclaration;
override void visit(const AutoDeclaration autoDec)
{
auto enumToken = autoDec.storageClasses.find!(a => a.token == tok!"enum");
if (enumToken.length)
{
foreach (part; autoDec.parts)
{
if (part.initializer is null)
continue;
if (part.initializer.nonVoidInitializer is null)
continue;
if (part.initializer.nonVoidInitializer.arrayInitializer is null)
continue;
addErrorMessage(part.initializer.nonVoidInitializer,
"dscanner.performance.enum_array_literal",
"This enum may lead to unnecessary allocation at run-time."
string message = "This enum may lead to unnecessary allocation at run-time."
~ " Use 'static immutable "
~ part.identifier.text ~ " = [ ...' instead.",
[
AutoFix.replacement(enumToken[0].token, "static immutable")
]);
}
}
autoDec.accept(this);
~ vd.ident.toString().idup() ~ " = [ ...' instead.";
if (!vd.type && vd._init.kind == InitKind.array && vd.storage_class & STC.manifest)
addErrorMessage(cast(ulong) vd.loc.linnum,
cast(ulong) vd.loc.charnum, KEY,
message);
super.visit(vd);
}
}
unittest
{
import dscanner.analysis.config : Check, disabledConfig, StaticAnalysisConfig;
import dscanner.analysis.helpers : assertAnalyzerWarnings, assertAutoFix;
import std.stdio : stderr;
StaticAnalysisConfig sac = disabledConfig();
sac.enum_array_literal_check = Check.enabled;
assertAnalyzerWarnings(q{
enum x = [1, 2, 3]; /+
^^^^^^^^^ [warn]: This enum may lead to unnecessary allocation at run-time. Use 'static immutable x = [ ...' instead. +/
}c, sac);
assertAutoFix(q{
enum x = [1, 2, 3]; // fix
}c, q{
static immutable x = [1, 2, 3]; // fix
}c, sac);
stderr.writeln("Unittest for EnumArrayLiteralCheck passed.");
}
private enum KEY = "dscanner.performance.enum_array_literal";
}

View file

@ -95,6 +95,8 @@ import dsymbol.modulecache : ModuleCache;
import dscanner.utils;
import dscanner.reports : DScannerJsonReporter, SonarQubeGenericIssueDataReporter;
import dmd.astbase : ASTBase;
bool first = true;
private alias ASTAllocator = CAllocatorImpl!(
@ -380,10 +382,32 @@ void generateSonarQubeGenericIssueDataReport(string[] fileNames, const StaticAna
bool analyze(string[] fileNames, const StaticAnalysisConfig config, string errorFormat,
ref StringCache cache, ref ModuleCache moduleCache, bool staticAnalyze = true)
{
import dmd.parse : Parser;
import dmd.astbase : ASTBase;
import dmd.id : Id;
import dmd.globals : global;
import dmd.identifier : Identifier;
import std.string : toStringz;
Id.initialize();
global._init();
global.params.useUnitTests = true;
ASTBase.Type._init();
bool hasErrors;
foreach (fileName; fileNames)
{
auto code = readFile(fileName);
auto id = Identifier.idPool(fileName);
auto ast_m = new ASTBase.Module(fileName.toStringz, id, false, false);
auto input = cast(char[]) code;
input ~= '\0';
scope p = new Parser!ASTBase(ast_m, input, false);
p.nextToken();
ast_m.members = p.parseModule();
// Skip files that could not be read and continue with the rest
if (code.length == 0)
continue;
@ -397,6 +421,11 @@ bool analyze(string[] fileNames, const StaticAnalysisConfig config, string error
if (errorCount > 0 || (staticAnalyze && warningCount > 0))
hasErrors = true;
MessageSet results = analyze(fileName, m, config, moduleCache, tokens, staticAnalyze);
MessageSet resultsDmd = analyzeDmd(fileName, ast_m);
foreach(result; resultsDmd[])
{
results.insert(result);
}
if (results is null)
continue;
foreach (result; results[])
@ -787,10 +816,6 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
checks ~= new DuplicateAttributeCheck(args.setSkipTests(
analysisConfig.duplicate_attribute == Check.skipTests && !ut));
if (moduleName.shouldRun!EnumArrayLiteralCheck(analysisConfig))
checks ~= new EnumArrayLiteralCheck(args.setSkipTests(
analysisConfig.enum_array_literal_check == Check.skipTests && !ut));
if (moduleName.shouldRun!PokemonExceptionCheck(analysisConfig))
checks ~= new PokemonExceptionCheck(args.setSkipTests(
analysisConfig.exception_check == Check.skipTests && !ut));
@ -1259,3 +1284,15 @@ version (unittest)
}
}
}
MessageSet analyzeDmd(string fileName, ASTBase.Module m)
{
scope vis = new EnumArrayVisitor!ASTBase(fileName);
m.accept(vis);
MessageSet set = new MessageSet;
foreach(message; vis.messages)
set.insert(message);
return set;
}