mirror of
https://github.com/dlang-community/D-Scanner.git
synced 2025-04-26 21:30:14 +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
|
||||
)
|
||||
|
||||
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=
|
||||
|
|
|
@ -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",
|
||||
|
|
6
makefile
6
makefile
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue