Skip to content

Commit da64499

Browse files
committed
--reportFile added
1 parent 4ca8449 commit da64499

File tree

3 files changed

+123
-26
lines changed

3 files changed

+123
-26
lines changed

src/dscanner/analysis/run.d

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import std.algorithm;
1414
import std.range;
1515
import std.array;
1616
import std.functional : toDelegate;
17+
import std.file : mkdirRecurse;
18+
import std.path : dirName;
1719
import dparse.lexer;
1820
import dparse.parser;
1921
import dparse.ast;
@@ -87,7 +89,7 @@ import dsymbol.conversion.second;
8789
import dsymbol.modulecache : ModuleCache;
8890

8991
import dscanner.utils;
90-
import dscanner.reports : SonarQubeGenericIssueDataReporter;
92+
import dscanner.reports : DScannerJsonReporter, SonarQubeGenericIssueDataReporter;
9193

9294
bool first = true;
9395

@@ -146,10 +148,14 @@ bool syntaxCheck(string[] fileNames, string errorFormat, ref StringCache stringC
146148
}
147149

148150
void generateReport(string[] fileNames, const StaticAnalysisConfig config,
149-
ref StringCache cache, ref ModuleCache moduleCache)
151+
ref StringCache cache, ref ModuleCache moduleCache, string reportFile = "")
150152
{
151-
writeln("{");
152-
writeln(` "issues": [`);
153+
auto reporter = new DScannerJsonReporter();
154+
155+
auto writeMessages = delegate void(string fileName, size_t line, size_t column, string message, bool isError){
156+
reporter.addMessage(Message(fileName, line, column, "dscanner.syntax", message), isError);
157+
};
158+
153159
first = true;
154160
StatsCollector stats = new StatsCollector("");
155161
ulong lineOfCodeCount;
@@ -161,29 +167,26 @@ void generateReport(string[] fileNames, const StaticAnalysisConfig config,
161167
continue;
162168
RollbackAllocator r;
163169
const(Token)[] tokens;
164-
const Module m = parseModule(fileName, code, &r, defaultErrorFormat, cache, true, tokens, &lineOfCodeCount);
170+
const Module m = parseModule(fileName, code, &r, cache, tokens, writeMessages, &lineOfCodeCount, null, null);
165171
stats.visit(m);
166-
MessageSet results = analyze(fileName, m, config, moduleCache, tokens, true);
167-
foreach (result; results[])
168-
{
169-
writeJSON(result);
170-
}
172+
MessageSet messageSet = analyze(fileName, m, config, moduleCache, tokens, true);
173+
reporter.addMessageSet(messageSet);
174+
}
175+
176+
string reportFileContent = reporter.getContent(stats, lineOfCodeCount);
177+
if (reportFile == "")
178+
{
179+
writeln(reportFileContent);
180+
}
181+
else
182+
{
183+
mkdirRecurse(reportFile.dirName);
184+
toFile(reportFileContent, reportFile);
171185
}
172-
writeln();
173-
writeln(" ],");
174-
writefln(` "interfaceCount": %d,`, stats.interfaceCount);
175-
writefln(` "classCount": %d,`, stats.classCount);
176-
writefln(` "functionCount": %d,`, stats.functionCount);
177-
writefln(` "templateCount": %d,`, stats.templateCount);
178-
writefln(` "structCount": %d,`, stats.structCount);
179-
writefln(` "statementCount": %d,`, stats.statementCount);
180-
writefln(` "lineOfCodeCount": %d,`, lineOfCodeCount);
181-
writefln(` "undocumentedPublicSymbols": %d`, stats.undocumentedPublicSymbols);
182-
writeln("}");
183186
}
184187

185188
void generateSonarQubeGenericIssueDataReport(string[] fileNames, const StaticAnalysisConfig config,
186-
ref StringCache cache, ref ModuleCache moduleCache)
189+
ref StringCache cache, ref ModuleCache moduleCache, string reportFile = "")
187190
{
188191
auto reporter = new SonarQubeGenericIssueDataReporter();
189192

@@ -204,7 +207,16 @@ void generateSonarQubeGenericIssueDataReport(string[] fileNames, const StaticAna
204207
reporter.addMessageSet(messageSet);
205208
}
206209

207-
writeln(reporter.getContent());
210+
string reportFileContent = reporter.getContent();
211+
if (reportFile == "")
212+
{
213+
writeln(reportFileContent);
214+
}
215+
else
216+
{
217+
mkdirRecurse(reportFile.dirName);
218+
toFile(reportFileContent, reportFile);
219+
}
208220
}
209221

210222
/**

src/dscanner/main.d

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ else
6363
bool report;
6464
bool skipTests;
6565
string reportFormat;
66+
string reportFile;
6667
string symbolName;
6768
string configLocation;
6869
string[] importPaths;
@@ -93,6 +94,7 @@ else
9394
"config", &configLocation,
9495
"report", &report,
9596
"reportFormat", &reportFormat,
97+
"reportFile", &reportFile,
9698
"I", &importPaths,
9799
"version", &printVersion,
98100
"muffinButton", &muffin,
@@ -157,7 +159,7 @@ else
157159
if (absImportPaths.length)
158160
moduleCache.addImportPaths(absImportPaths);
159161

160-
if (reportFormat.length)
162+
if (reportFormat.length || reportFile.length)
161163
report = true;
162164

163165
immutable optionCount = count!"a"([sloc, highlight, ctags, tokenCount, syntaxCheck, ast, imports,
@@ -250,10 +252,10 @@ else
250252
goto case;
251253
case "":
252254
case "dscanner":
253-
generateReport(expandArgs(args), config, cache, moduleCache);
255+
generateReport(expandArgs(args), config, cache, moduleCache, reportFile);
254256
break;
255257
case "sonarQubeGenericIssueData":
256-
generateSonarQubeGenericIssueDataReport(expandArgs(args), config, cache, moduleCache);
258+
generateSonarQubeGenericIssueDataReport(expandArgs(args), config, cache, moduleCache, reportFile);
257259
break;
258260
}
259261
}
@@ -407,6 +409,9 @@ Options:
407409
however the exit code will still be zero if errors or warnings are
408410
found.
409411
412+
--reportFile <file>
413+
Write report into file instead of STDOUT.
414+
410415
--reportFormat <dscanner | sonarQubeGenericIssueData>...
411416
Specifies the format of the generated report.
412417

src/dscanner/reports.d

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,86 @@ import std.algorithm : map;
1010
import std.array : split, array, Appender, appender;
1111

1212
import dscanner.analysis.base : Message, MessageSet;
13+
import dscanner.analysis.stats_collector;
14+
15+
class DScannerJsonReporter
16+
{
17+
struct Issue
18+
{
19+
Message message;
20+
string type;
21+
}
22+
23+
private Appender!(Issue[]) _issues;
24+
25+
this()
26+
{
27+
_issues = appender!(Issue[]);
28+
}
29+
30+
void addMessageSet(MessageSet messageSet)
31+
{
32+
_issues ~= toIssues(messageSet);
33+
}
34+
35+
void addMessage(Message message, bool isError = false)
36+
{
37+
_issues ~= toIssue(message, isError);
38+
}
39+
40+
string getContent(StatsCollector stats, ulong lineOfCodeCount)
41+
{
42+
JSONValue result = [
43+
"issues" : JSONValue(_issues.data.map!(e => toJson(e)).array),
44+
"interfaceCount": JSONValue(stats.interfaceCount),
45+
"classCount": JSONValue(stats.classCount),
46+
"functionCount": JSONValue(stats.functionCount),
47+
"templateCount": JSONValue(stats.templateCount),
48+
"structCount": JSONValue(stats.structCount),
49+
"statementCount": JSONValue(stats.statementCount),
50+
"lineOfCodeCount": JSONValue(lineOfCodeCount),
51+
"undocumentedPublicSymbols": JSONValue(stats.undocumentedPublicSymbols)
52+
];
53+
return result.toPrettyString();
54+
}
55+
56+
private static JSONValue toJson(Issue issue)
57+
{
58+
// dfmt off
59+
JSONValue js = JSONValue([
60+
"key": JSONValue(issue.message.key),
61+
"fileName": JSONValue(issue.message.fileName),
62+
"line": JSONValue(issue.message.line),
63+
"column": JSONValue(issue.message.column),
64+
"message": JSONValue(issue.message.message),
65+
"type": JSONValue(issue.type)
66+
]);
67+
// dfmt on
68+
69+
if (issue.message.checkName !is null)
70+
{
71+
js["name"] = JSONValue(issue.message.checkName);
72+
}
73+
74+
return js;
75+
}
76+
77+
private static Issue[] toIssues(MessageSet messageSet)
78+
{
79+
return messageSet[].map!(e => toIssue(e)).array;
80+
}
81+
82+
private static Issue toIssue(Message message, bool isError = false)
83+
{
84+
// dfmt off
85+
Issue issue = {
86+
message: message,
87+
type : isError ? "error" : "warn"
88+
};
89+
// dfmt on
90+
return issue;
91+
}
92+
}
1393

1494
class SonarQubeGenericIssueDataReporter
1595
{

0 commit comments

Comments
 (0)