mirror of
https://github.com/dlang-community/D-Scanner.git
synced 2025-04-27 22:00:17 +03:00
Replace libdparse in enum array functionality
This commit is contained in:
parent
ab430835ab
commit
150643c4d3
6 changed files with 117 additions and 74 deletions
|
@ -18,8 +18,8 @@ if %githashsize% == 0 (
|
||||||
move /y bin\githash_.txt bin\githash.txt
|
move /y bin\githash_.txt bin\githash.txt
|
||||||
)
|
)
|
||||||
|
|
||||||
set DFLAGS=-O -release -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -Jbin -Jdmd %MFLAGS%
|
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 -Jbin -Jdmd
|
set TESTFLAGS=-g -w -version=StdLoggerDisableWarning -version=CallbackAPI -version=DMDLIB -version=MARS -Jbin -Jdmd
|
||||||
set CORE=
|
set CORE=
|
||||||
set LIBDPARSE=
|
set LIBDPARSE=
|
||||||
set STD=
|
set STD=
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"fileVersion": 1,
|
"fileVersion": 1,
|
||||||
"versions": {
|
"versions": {
|
||||||
"dcd": "0.16.0-beta.2",
|
"dcd": "0.16.0-beta.2",
|
||||||
|
"dmd": "~master",
|
||||||
"dsymbol": "0.13.0",
|
"dsymbol": "0.13.0",
|
||||||
"emsi_containers": "0.9.0",
|
"emsi_containers": "0.9.0",
|
||||||
"inifiled": "1.3.3",
|
"inifiled": "1.3.3",
|
||||||
|
|
6
makefile
6
makefile
|
@ -71,11 +71,11 @@ INCLUDE_PATHS = \
|
||||||
-Ilibddoc/common/source \
|
-Ilibddoc/common/source \
|
||||||
-Idmd/src
|
-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
|
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
|
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
|
GDC_DEBUG_VERSIONS = -fversion=dparse_verbose
|
||||||
|
|
||||||
DC_FLAGS += -Jbin -Jdmd
|
DC_FLAGS += -Jbin -Jdmd
|
||||||
|
|
|
@ -9,6 +9,9 @@ import std.container;
|
||||||
import std.meta : AliasSeq;
|
import std.meta : AliasSeq;
|
||||||
import std.string;
|
import std.string;
|
||||||
import std.sumtype;
|
import std.sumtype;
|
||||||
|
import dmd.transitivevisitor;
|
||||||
|
import core.stdc.string;
|
||||||
|
import std.conv : to;
|
||||||
|
|
||||||
///
|
///
|
||||||
struct AutoFix
|
struct AutoFix
|
||||||
|
@ -361,11 +364,15 @@ enum comparitor = q{ a.startLine < b.startLine || (a.startLine == b.startLine &&
|
||||||
|
|
||||||
alias MessageSet = RedBlackTree!(Message, comparitor, true);
|
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)
|
mixin template AnalyzerInfo(string checkName)
|
||||||
{
|
{
|
||||||
enum string name = checkName;
|
enum string name = checkName;
|
||||||
|
|
||||||
override protected string getName()
|
extern(D) override protected string getName()
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -897,3 +904,47 @@ unittest
|
||||||
auto isOldScope = void;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -5,79 +5,33 @@
|
||||||
|
|
||||||
module dscanner.analysis.enumarrayliteral;
|
module dscanner.analysis.enumarrayliteral;
|
||||||
|
|
||||||
import dparse.ast;
|
|
||||||
import dparse.lexer;
|
|
||||||
import dscanner.analysis.base;
|
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";
|
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;
|
string message = "This enum may lead to unnecessary allocation at run-time."
|
||||||
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."
|
|
||||||
~ " Use 'static immutable "
|
~ " Use 'static immutable "
|
||||||
~ part.identifier.text ~ " = [ ...' instead.",
|
~ vd.ident.toString().idup() ~ " = [ ...' instead.";
|
||||||
[
|
|
||||||
AutoFix.replacement(enumToken[0].token, "static immutable")
|
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);
|
||||||
autoDec.accept(this);
|
super.visit(vd);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
unittest
|
private enum KEY = "dscanner.performance.enum_array_literal";
|
||||||
{
|
}
|
||||||
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.");
|
|
||||||
}
|
|
|
@ -95,6 +95,8 @@ import dsymbol.modulecache : ModuleCache;
|
||||||
import dscanner.utils;
|
import dscanner.utils;
|
||||||
import dscanner.reports : DScannerJsonReporter, SonarQubeGenericIssueDataReporter;
|
import dscanner.reports : DScannerJsonReporter, SonarQubeGenericIssueDataReporter;
|
||||||
|
|
||||||
|
import dmd.astbase : ASTBase;
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
private alias ASTAllocator = CAllocatorImpl!(
|
private alias ASTAllocator = CAllocatorImpl!(
|
||||||
|
@ -380,10 +382,32 @@ void generateSonarQubeGenericIssueDataReport(string[] fileNames, const StaticAna
|
||||||
bool analyze(string[] fileNames, const StaticAnalysisConfig config, string errorFormat,
|
bool analyze(string[] fileNames, const StaticAnalysisConfig config, string errorFormat,
|
||||||
ref StringCache cache, ref ModuleCache moduleCache, bool staticAnalyze = true)
|
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;
|
bool hasErrors;
|
||||||
foreach (fileName; fileNames)
|
foreach (fileName; fileNames)
|
||||||
{
|
{
|
||||||
auto code = readFile(fileName);
|
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
|
// Skip files that could not be read and continue with the rest
|
||||||
if (code.length == 0)
|
if (code.length == 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -397,6 +421,11 @@ bool analyze(string[] fileNames, const StaticAnalysisConfig config, string error
|
||||||
if (errorCount > 0 || (staticAnalyze && warningCount > 0))
|
if (errorCount > 0 || (staticAnalyze && warningCount > 0))
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
MessageSet results = analyze(fileName, m, config, moduleCache, tokens, staticAnalyze);
|
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)
|
if (results is null)
|
||||||
continue;
|
continue;
|
||||||
foreach (result; results[])
|
foreach (result; results[])
|
||||||
|
@ -787,10 +816,6 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
|
||||||
checks ~= new DuplicateAttributeCheck(args.setSkipTests(
|
checks ~= new DuplicateAttributeCheck(args.setSkipTests(
|
||||||
analysisConfig.duplicate_attribute == Check.skipTests && !ut));
|
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))
|
if (moduleName.shouldRun!PokemonExceptionCheck(analysisConfig))
|
||||||
checks ~= new PokemonExceptionCheck(args.setSkipTests(
|
checks ~= new PokemonExceptionCheck(args.setSkipTests(
|
||||||
analysisConfig.exception_check == Check.skipTests && !ut));
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue