D-Scanner/src/dscanner/reports.d

298 lines
6.5 KiB
D

// Copyright Brian Schott (Hackerpilot) 2012.
// 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 dscanner.reports;
import std.json;
import std.algorithm : map;
import std.array : split, array, Appender, appender;
import dscanner.analysis.base : Message, MessageSet;
import dscanner.analysis.stats_collector;
class DScannerJsonReporter
{
struct Issue
{
Message message;
string type;
}
private Appender!(Issue[]) _issues;
this()
{
_issues = appender!(Issue[]);
}
void addMessageSet(MessageSet messageSet)
{
_issues ~= toIssues(messageSet);
}
void addMessage(Message message, bool isError = false)
{
_issues ~= toIssue(message, isError);
}
string getContent(StatsCollector stats, ulong lineOfCodeCount)
{
JSONValue result = [
"issues" : JSONValue(_issues.data.map!(e => toJson(e)).array),
"interfaceCount": JSONValue(stats.interfaceCount),
"classCount": JSONValue(stats.classCount),
"functionCount": JSONValue(stats.functionCount),
"templateCount": JSONValue(stats.templateCount),
"structCount": JSONValue(stats.structCount),
"statementCount": JSONValue(stats.statementCount),
"lineOfCodeCount": JSONValue(lineOfCodeCount),
"undocumentedPublicSymbols": JSONValue(stats.undocumentedPublicSymbols)
];
return result.toPrettyString();
}
private static JSONValue toJson(Issue issue)
{
import std.sumtype : match;
import dscanner.analysis.base : AutoFix;
// dfmt off
JSONValue js = JSONValue([
"key": JSONValue(issue.message.key),
"fileName": JSONValue(issue.message.fileName),
"line": JSONValue(issue.message.startLine),
"column": JSONValue(issue.message.startColumn),
"index": JSONValue(issue.message.startIndex),
"endLine": JSONValue(issue.message.endLine),
"endColumn": JSONValue(issue.message.endColumn),
"endIndex": JSONValue(issue.message.endIndex),
"message": JSONValue(issue.message.message),
"type": JSONValue(issue.type),
"supplemental": JSONValue(
issue.message.supplemental.map!(a =>
JSONValue([
"fileName": JSONValue(a.fileName),
"line": JSONValue(a.startLine),
"column": JSONValue(a.startColumn),
"index": JSONValue(a.startIndex),
"endLine": JSONValue(a.endLine),
"endColumn": JSONValue(a.endColumn),
"endIndex": JSONValue(a.endIndex),
"message": JSONValue(a.message),
])
).array
),
"autofixes": JSONValue(
issue.message.autofixes.map!(a =>
JSONValue([
"name": JSONValue(a.name),
"replacements": a.replacements.match!(
(const AutoFix.CodeReplacement[] replacements) => JSONValue(
replacements.map!(r => JSONValue([
"range": JSONValue([
JSONValue(r.range[0]),
JSONValue(r.range[1])
]),
"newText": JSONValue(r.newText)
])).array
),
(const AutoFix.ResolveContext _) => JSONValue(
"resolvable"
)
)
])
).array
)
]);
// dfmt on
if (issue.message.checkName !is null)
{
js["name"] = JSONValue(issue.message.checkName);
}
return js;
}
private static Issue[] toIssues(MessageSet messageSet)
{
return messageSet[].map!(e => toIssue(e)).array;
}
private static Issue toIssue(Message message, bool isError = false)
{
// dfmt off
Issue issue = {
message: message,
type : isError ? "error" : "warn"
};
// dfmt on
return issue;
}
}
class SonarQubeGenericIssueDataReporter
{
enum Type
{
bug = "BUG",
vulnerability = "VULNERABILITY",
codeSmell = "CODE_SMELL"
}
enum Severity
{
blocker = "BLOCKER",
critical = "CRITICAL",
major = "MAJOR",
minor = "MINOR",
info = "INFO"
}
struct Issue
{
string engineId;
string ruleId;
Location primaryLocation;
string type;
string severity;
int effortMinutes;
Location[] secondaryLocations;
}
struct Location
{
string message;
string filePath;
TextRange textRange;
}
struct TextRange
{
long startLine;
long endLine;
long startColumn;
long endColumn;
}
private Appender!(Issue[]) _issues;
this()
{
_issues = appender!(Issue[]);
}
void addMessageSet(MessageSet messageSet)
{
_issues ~= toIssues(messageSet);
}
void addMessage(Message message, bool isError = false)
{
_issues ~= toIssue(message, isError);
}
string getContent()
{
JSONValue result = [
"issues" : JSONValue(_issues.data.map!(e => toJson(e)).array)
];
return result.toPrettyString();
}
private static JSONValue toJson(Location location)
{
return JSONValue([
"message": JSONValue(location.message),
"filePath": JSONValue(location.filePath),
"textRange": JSONValue([
"startLine": JSONValue(location.textRange.startLine),
"endLine": JSONValue(location.textRange.endLine),
"startColumn": JSONValue(location.textRange.startColumn),
"endColumn": JSONValue(location.textRange.endColumn)
]),
]);
}
private static JSONValue toJson(Issue issue)
{
// dfmt off
return JSONValue([
"engineId": JSONValue(issue.engineId),
"ruleId": JSONValue(issue.ruleId),
"severity": JSONValue(issue.severity),
"type": JSONValue(issue.type),
"primaryLocation": toJson(issue.primaryLocation),
"secondaryLocations": JSONValue(issue.secondaryLocations.map!toJson.array),
]);
// dfmt on
}
private static Issue[] toIssues(MessageSet messageSet)
{
return messageSet[].map!(e => toIssue(e)).array;
}
private static Issue toIssue(Message message, bool isError = false)
{
// dfmt off
Issue issue = {
engineId: "dscanner",
ruleId: message.key,
severity: (isError) ? Severity.blocker : getSeverity(message.key),
type: getType(message.key),
primaryLocation: getLocation(message.diagnostic),
secondaryLocations: message.supplemental.map!getLocation.array
};
// dfmt on
return issue;
}
private static Location getLocation(Message.Diagnostic diag)
{
return Location(diag.message, diag.fileName,
TextRange(diag.startLine, diag.endLine, diag.startColumn, diag.endColumn));
}
private static string getSeverity(string key)
{
auto a = key.split(".");
if (a.length <= 1)
{
return Severity.major;
}
else
{
switch (a[1])
{
case "style":
return Severity.minor;
default:
return Severity.major;
}
}
}
private static string getType(string key)
{
auto a = key.split(".");
if (a.length <= 1)
{
return Type.bug;
}
else
{
switch (a[1])
{
case "style":
return Type.codeSmell;
default:
return Type.bug;
}
}
}
}