Added various metrics to --report output

This commit is contained in:
Hackerpilot 2014-08-28 19:06:54 -07:00
parent 2ff3b02aa7
commit e2e324cd7b
6 changed files with 137 additions and 42 deletions

View File

@ -2,4 +2,4 @@ sonar.projectKey=dscanner
sonar.projectName=D Scanner
sonar.projectVersion=1.0
sonar.sourceEncoding=UTF-8
sonar.sources=.
sonar.sources=src

View File

@ -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__)
{
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
MessageSet rawWarnings = analyze("test", cast(ubyte[]) code, config);
MessageSet rawWarnings = analyze("test", m, config);
string[] codeLines = code.split("\n");
// Get the warnings ordered by line

View File

@ -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;
import std.stdio;
@ -29,6 +34,7 @@ import analysis.length_subtraction;
import analysis.builtin_property_names;
import analysis.asm_style;
import analysis.logic_precedence;
import analysis.stats_collector;
bool first = true;
@ -65,63 +71,74 @@ void syntaxCheck(string[] fileNames)
analyze(fileNames, config, false);
}
// For multiple files
void analyze(string[] fileNames, StaticAnalysisConfig config,
bool staticAnalyze = true, bool report = false)
void generateReport(string[] fileNames, const StaticAnalysisConfig config)
{
if (report)
{
writeln("{");
writeln(` "issues": [`);
}
writeln("{");
writeln(` "issues": [`);
first = true;
StatsCollector stats = new StatsCollector("");
ulong lineOfCodeCount;
foreach (fileName; fileNames)
{
File f = File(fileName);
if (f.size == 0) continue;
auto code = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(code);
MessageSet results = analyze(fileName, code, config, staticAnalyze, report);
if (report)
ParseAllocator p = new ParseAllocator;
StringCache cache = StringCache(StringCache.defaultBucketCount);
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);
}
}
else if (results !is null)
{
foreach (result; results[])
writefln("%s(%d:%d)[warn]: %s", result.fileName, result.line,
result.column, result.message);
writeJSON(result.key, 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();
writeln(" ]");
writeln("}");
File f = File(fileName);
if (f.size == 0) continue;
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
MessageSet analyze(string fileName, ubyte[] code, const StaticAnalysisConfig analysisConfig,
bool staticAnalyze = true, bool report = false)
const(Module) parseModule(string fileName, ubyte[] code, ParseAllocator p,
ref StringCache cache, bool report, ulong* linesOfCode = null)
{
import std.parallelism;
import stats : isLineOfCode;
auto lexer = byToken(code);
LexerConfig config;
config.fileName = fileName;
config.stringBehavior = StringBehavior.source;
StringCache cache = StringCache(StringCache.defaultBucketCount);
const(Token)[] tokens = getTokensForParser(code, config, &cache);
if (linesOfCode !is null)
(*linesOfCode) += count!(a => isLineOfCode(a.type))(tokens);
foreach (message; lexer.messages)
{
if (report)
@ -131,9 +148,14 @@ MessageSet analyze(string fileName, ubyte[] code, const StaticAnalysisConfig ana
messageFunction(fileName, message.line, message.column, message.message,
message.isError);
}
return std.d.parser.parseModule(tokens, fileName, p,
report ? &messageFunctionJSON : &messageFunction);
}
ParseAllocator p = new ParseAllocator;
Module m = parseModule(tokens, fileName, p, report ? &messageFunctionJSON : &messageFunction);
MessageSet analyze(string fileName, const Module m,
const StaticAnalysisConfig analysisConfig, bool staticAnalyze = true)
{
import std.parallelism;
if (!staticAnalyze)
return null;
@ -167,7 +189,6 @@ MessageSet analyze(string fileName, ubyte[] code, const StaticAnalysisConfig ana
foreach (check; checks)
foreach (message; check.messages)
set.insert(message);
p.deallocateAll();
return set;
}

View File

@ -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;
}

View File

@ -52,7 +52,7 @@ class StyleChecker : BaseAnalyzer
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 '"
~ name.text ~ "' does not match style guidelines.");
}

View File

@ -160,7 +160,10 @@ int run(string[] args)
string s = configLocation is null ? getConfigurationLocation() : configLocation;
if (s.exists())
readINIFile(config, s);
analyze(expandArgs(args), config, true, report);
if (report)
generateReport(expandArgs(args), config);
else
analyze(expandArgs(args), config, true);
}
else if (syntaxCheck)
{