Added various metrics to --report output
This commit is contained in:
parent
2ff3b02aa7
commit
e2e324cd7b
|
@ -2,4 +2,4 @@ sonar.projectKey=dscanner
|
||||||
sonar.projectName=D Scanner
|
sonar.projectName=D Scanner
|
||||||
sonar.projectVersion=1.0
|
sonar.projectVersion=1.0
|
||||||
sonar.sourceEncoding=UTF-8
|
sonar.sourceEncoding=UTF-8
|
||||||
sonar.sources=.
|
sonar.sources=src
|
||||||
|
|
|
@ -53,9 +53,15 @@ S after(S)(S value, S separator)
|
||||||
void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, string file=__FILE__, size_t line=__LINE__)
|
void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, string file=__FILE__, size_t line=__LINE__)
|
||||||
{
|
{
|
||||||
import analysis.run;
|
import analysis.run;
|
||||||
|
import std.d.lexer : StringCache;
|
||||||
|
import std.d.parser;
|
||||||
|
|
||||||
|
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
||||||
|
ParseAllocator p = new ParseAllocator;
|
||||||
|
const(Module) m = parseModule(file, cast(ubyte[]) code, p, cache, false);
|
||||||
|
|
||||||
// Run the code and get any warnings
|
// Run the code and get any warnings
|
||||||
MessageSet rawWarnings = analyze("test", cast(ubyte[]) code, config);
|
MessageSet rawWarnings = analyze("test", m, config);
|
||||||
string[] codeLines = code.split("\n");
|
string[] codeLines = code.split("\n");
|
||||||
|
|
||||||
// Get the warnings ordered by line
|
// Get the warnings ordered by line
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// Copyright Brian Schott (Hackerpilot) 2014.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
module analysis.run;
|
module analysis.run;
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
@ -29,6 +34,7 @@ import analysis.length_subtraction;
|
||||||
import analysis.builtin_property_names;
|
import analysis.builtin_property_names;
|
||||||
import analysis.asm_style;
|
import analysis.asm_style;
|
||||||
import analysis.logic_precedence;
|
import analysis.logic_precedence;
|
||||||
|
import analysis.stats_collector;
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
|
@ -65,63 +71,74 @@ void syntaxCheck(string[] fileNames)
|
||||||
analyze(fileNames, config, false);
|
analyze(fileNames, config, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For multiple files
|
void generateReport(string[] fileNames, const StaticAnalysisConfig config)
|
||||||
void analyze(string[] fileNames, StaticAnalysisConfig config,
|
|
||||||
bool staticAnalyze = true, bool report = false)
|
|
||||||
{
|
{
|
||||||
if (report)
|
writeln("{");
|
||||||
{
|
writeln(` "issues": [`);
|
||||||
writeln("{");
|
|
||||||
writeln(` "issues": [`);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
first = true;
|
first = true;
|
||||||
|
StatsCollector stats = new StatsCollector("");
|
||||||
|
ulong lineOfCodeCount;
|
||||||
foreach (fileName; fileNames)
|
foreach (fileName; fileNames)
|
||||||
{
|
{
|
||||||
File f = File(fileName);
|
File f = File(fileName);
|
||||||
if (f.size == 0) continue;
|
if (f.size == 0) continue;
|
||||||
auto code = uninitializedArray!(ubyte[])(to!size_t(f.size));
|
auto code = uninitializedArray!(ubyte[])(to!size_t(f.size));
|
||||||
f.rawRead(code);
|
f.rawRead(code);
|
||||||
|
ParseAllocator p = new ParseAllocator;
|
||||||
MessageSet results = analyze(fileName, code, config, staticAnalyze, report);
|
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
||||||
if (report)
|
const Module m = parseModule(fileName, code, p, cache, true, &lineOfCodeCount);
|
||||||
|
stats.visit(m);
|
||||||
|
MessageSet results = analyze(fileName, m, config, true);
|
||||||
|
foreach (result; results[])
|
||||||
{
|
{
|
||||||
foreach (result; results[])
|
writeJSON(result.key, result.fileName, result.line, result.column, result.message);
|
||||||
{
|
|
||||||
writeJSON(result.key, result.fileName, result.line, result.column, result.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (results !is null)
|
|
||||||
{
|
|
||||||
foreach (result; results[])
|
|
||||||
writefln("%s(%d:%d)[warn]: %s", result.fileName, result.line,
|
|
||||||
result.column, result.message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
writeln();
|
||||||
|
writeln(" ],");
|
||||||
|
writefln(` "interfaceCount": %d,`, stats.interfaceCount);
|
||||||
|
writefln(` "classCount": %d,`, stats.classCount);
|
||||||
|
writefln(` "functionCount": %d,`, stats.functionCount);
|
||||||
|
writefln(` "templateCount": %d,`, stats.templateCount);
|
||||||
|
writefln(` "structCount": %d,`, stats.structCount);
|
||||||
|
writefln(` "statementCount": %d,`, stats.statementCount);
|
||||||
|
writefln(` "lineOfCodeCount": %d,`, lineOfCodeCount);
|
||||||
|
writefln(` "undocumentedPublicSymbols": %d`, stats.undocumentedPublicSymbols);
|
||||||
|
writeln("}");
|
||||||
|
}
|
||||||
|
|
||||||
if (report)
|
// For multiple files
|
||||||
|
void analyze(string[] fileNames, const StaticAnalysisConfig config, bool staticAnalyze = true)
|
||||||
|
{
|
||||||
|
foreach (fileName; fileNames)
|
||||||
{
|
{
|
||||||
writeln();
|
File f = File(fileName);
|
||||||
writeln(" ]");
|
if (f.size == 0) continue;
|
||||||
writeln("}");
|
auto code = uninitializedArray!(ubyte[])(to!size_t(f.size));
|
||||||
|
f.rawRead(code);
|
||||||
|
ParseAllocator p = new ParseAllocator;
|
||||||
|
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
||||||
|
const Module m = parseModule(fileName, code, p, cache, false);
|
||||||
|
MessageSet results = analyze(fileName, m, config, staticAnalyze);
|
||||||
|
if (results is null)
|
||||||
|
continue;
|
||||||
|
foreach (result; results[])
|
||||||
|
writefln("%s(%d:%d)[warn]: %s", result.fileName, result.line,
|
||||||
|
result.column, result.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For a string
|
const(Module) parseModule(string fileName, ubyte[] code, ParseAllocator p,
|
||||||
MessageSet analyze(string fileName, ubyte[] code, const StaticAnalysisConfig analysisConfig,
|
ref StringCache cache, bool report, ulong* linesOfCode = null)
|
||||||
bool staticAnalyze = true, bool report = false)
|
|
||||||
{
|
{
|
||||||
import std.parallelism;
|
import stats : isLineOfCode;
|
||||||
|
|
||||||
auto lexer = byToken(code);
|
auto lexer = byToken(code);
|
||||||
LexerConfig config;
|
LexerConfig config;
|
||||||
config.fileName = fileName;
|
config.fileName = fileName;
|
||||||
config.stringBehavior = StringBehavior.source;
|
config.stringBehavior = StringBehavior.source;
|
||||||
StringCache cache = StringCache(StringCache.defaultBucketCount);
|
|
||||||
const(Token)[] tokens = getTokensForParser(code, config, &cache);
|
const(Token)[] tokens = getTokensForParser(code, config, &cache);
|
||||||
|
if (linesOfCode !is null)
|
||||||
|
(*linesOfCode) += count!(a => isLineOfCode(a.type))(tokens);
|
||||||
foreach (message; lexer.messages)
|
foreach (message; lexer.messages)
|
||||||
{
|
{
|
||||||
if (report)
|
if (report)
|
||||||
|
@ -131,9 +148,14 @@ MessageSet analyze(string fileName, ubyte[] code, const StaticAnalysisConfig ana
|
||||||
messageFunction(fileName, message.line, message.column, message.message,
|
messageFunction(fileName, message.line, message.column, message.message,
|
||||||
message.isError);
|
message.isError);
|
||||||
}
|
}
|
||||||
|
return std.d.parser.parseModule(tokens, fileName, p,
|
||||||
|
report ? &messageFunctionJSON : &messageFunction);
|
||||||
|
}
|
||||||
|
|
||||||
ParseAllocator p = new ParseAllocator;
|
MessageSet analyze(string fileName, const Module m,
|
||||||
Module m = parseModule(tokens, fileName, p, report ? &messageFunctionJSON : &messageFunction);
|
const StaticAnalysisConfig analysisConfig, bool staticAnalyze = true)
|
||||||
|
{
|
||||||
|
import std.parallelism;
|
||||||
|
|
||||||
if (!staticAnalyze)
|
if (!staticAnalyze)
|
||||||
return null;
|
return null;
|
||||||
|
@ -167,7 +189,6 @@ MessageSet analyze(string fileName, ubyte[] code, const StaticAnalysisConfig ana
|
||||||
foreach (check; checks)
|
foreach (check; checks)
|
||||||
foreach (message; check.messages)
|
foreach (message; check.messages)
|
||||||
set.insert(message);
|
set.insert(message);
|
||||||
p.deallocateAll();
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright Brian Schott (Hackerpilot) 2014.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
module analysis.stats_collector;
|
||||||
|
|
||||||
|
import std.d.ast;
|
||||||
|
import std.d.lexer;
|
||||||
|
import analysis.base;
|
||||||
|
|
||||||
|
class StatsCollector : BaseAnalyzer
|
||||||
|
{
|
||||||
|
alias visit = ASTVisitor.visit;
|
||||||
|
|
||||||
|
this(string fileName)
|
||||||
|
{
|
||||||
|
super(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const Statement statement)
|
||||||
|
{
|
||||||
|
statementCount++;
|
||||||
|
statement.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const ClassDeclaration classDeclaration)
|
||||||
|
{
|
||||||
|
classCount++;
|
||||||
|
classDeclaration.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const InterfaceDeclaration interfaceDeclaration)
|
||||||
|
{
|
||||||
|
interfaceCount++;
|
||||||
|
interfaceDeclaration.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const FunctionDeclaration functionDeclaration)
|
||||||
|
{
|
||||||
|
functionCount++;
|
||||||
|
functionDeclaration.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const StructDeclaration structDeclaration)
|
||||||
|
{
|
||||||
|
structCount++;
|
||||||
|
structDeclaration.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const TemplateDeclaration templateDeclaration)
|
||||||
|
{
|
||||||
|
templateCount++;
|
||||||
|
templateDeclaration.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint interfaceCount;
|
||||||
|
uint classCount;
|
||||||
|
uint functionCount;
|
||||||
|
uint templateCount;
|
||||||
|
uint structCount;
|
||||||
|
uint statementCount;
|
||||||
|
uint lineOfCodeCount;
|
||||||
|
uint undocumentedPublicSymbols;
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ class StyleChecker : BaseAnalyzer
|
||||||
|
|
||||||
void checkLowercaseName(string type, ref const Token name)
|
void checkLowercaseName(string type, ref const Token name)
|
||||||
{
|
{
|
||||||
if (name.text.matchFirst(varFunNameRegex).length == 0)
|
if (name.text.length > 0 && name.text.matchFirst(varFunNameRegex).length == 0)
|
||||||
addErrorMessage(name.line, name.column, KEY, type ~ " name '"
|
addErrorMessage(name.line, name.column, KEY, type ~ " name '"
|
||||||
~ name.text ~ "' does not match style guidelines.");
|
~ name.text ~ "' does not match style guidelines.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,10 @@ int run(string[] args)
|
||||||
string s = configLocation is null ? getConfigurationLocation() : configLocation;
|
string s = configLocation is null ? getConfigurationLocation() : configLocation;
|
||||||
if (s.exists())
|
if (s.exists())
|
||||||
readINIFile(config, s);
|
readINIFile(config, s);
|
||||||
analyze(expandArgs(args), config, true, report);
|
if (report)
|
||||||
|
generateReport(expandArgs(args), config);
|
||||||
|
else
|
||||||
|
analyze(expandArgs(args), config, true);
|
||||||
}
|
}
|
||||||
else if (syntaxCheck)
|
else if (syntaxCheck)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue