19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/ADT/StringMap.h"
21 #include "llvm/Support/ConvertUTF.h"
22 #include "llvm/Support/JSON.h"
23 #include "llvm/Support/Path.h"
26 using namespace clang;
30 class SarifDiagnostics :
public PathDiagnosticConsumer {
36 : OutputFile(Output), LO(LO) {}
37 ~SarifDiagnostics()
override =
default;
39 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
40 FilesMade *FM)
override;
42 StringRef
getName()
const override {
return "SarifDiagnostics"; }
43 PathGenerationScheme getGenerationScheme()
const override {
return Minimal; }
44 bool supportsLogicalOpControlFlow()
const override {
return true; }
45 bool supportsCrossFileDiagnostics()
const override {
return true; }
49 void ento::createSarifDiagnosticConsumer(
59 C.push_back(
new SarifDiagnostics(Output, PP.
getLangOpts()));
60 createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
61 CTU, MacroExpansions);
77 if (llvm::isAlnum(C) ||
78 StringRef::npos != StringRef(
"-._~:@!$&'()*+,;=").find(C))
80 return "%" + llvm::toHex(StringRef(&C, 1));
87 StringRef Root = sys::path::root_name(
Filename);
88 if (Root.startswith(
"//")) {
90 Ret += Root.drop_front(2).str();
91 }
else if (!Root.empty()) {
93 Ret += Twine(
"/" + Root).str();
97 assert(Iter !=
End &&
"Expected there to be a non-root path component.");
100 std::for_each(++Iter,
End, [&
Ret](StringRef Component) {
104 if (Component ==
"\\")
112 for (
char C : Component) {
126 {
"roles", json::Array{
"resultFile"}},
128 {
"mimeType",
"text/plain"}};
132 json::Array &Artifacts) {
137 auto I = llvm::find_if(Artifacts, [&](
const json::Value &
File) {
138 if (
const json::Object *Obj =
File.getAsObject()) {
139 if (const json::Object *FileLoc = Obj->getObject(
"location")) {
140 Optional<StringRef> URI = FileLoc->getString(
"uri");
141 return URI && URI->equals(FileURI);
149 auto Index =
static_cast<unsigned>(
std::distance(Artifacts.begin(), I));
150 if (I == Artifacts.end())
153 return json::Object{{
"uri", FileURI}, {
"index", Index}};
157 unsigned int TokenLen = 0) {
158 assert(!Loc.
isInvalid() &&
"invalid Loc when adjusting column position");
160 std::pair<FileID, unsigned> LocInfo =
SM.getDecomposedExpansionLoc(Loc);
161 assert(LocInfo.second >
SM.getExpansionColumnNumber(Loc) &&
162 "position in file is before column number?");
165 assert(Buf &&
"got an invalid buffer for the location's file");
166 assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
167 "token extends past end of buffer?");
171 unsigned int Off = LocInfo.second - (
SM.getExpansionColumnNumber(Loc) - 1);
172 unsigned int Ret = 1;
173 while (Off < (LocInfo.second + TokenLen)) {
174 Off += getNumBytesForUTF8(Buf->getBuffer()[Off]);
184 {
"startLine",
SM.getExpansionLineNumber(R.
getBegin())},
190 Region[
"endLine"] =
SM.getExpansionLineNumber(R.
getEnd());
193 Lexer::MeasureTokenLength(R.
getEnd(),
SM, LO));
201 json::Array &Artifacts) {
216 return "unimportant";
218 llvm_unreachable(
"Fully covered switch is not so fully covered");
223 return json::Object{{
"location", std::move(Location)},
228 return json::Object{{
"text",
Text.str()}};
232 StringRef Message =
"") {
233 json::Object
Ret{{
"physicalLocation", std::move(PhysicalLocation)}};
234 if (!Message.empty())
240 switch (Piece.getKind()) {
241 case PathDiagnosticPiece::Call:
242 case PathDiagnosticPiece::Macro:
244 case PathDiagnosticPiece::PopUp:
247 case PathDiagnosticPiece::Event:
250 case PathDiagnosticPiece::ControlFlow:
257 const PathPieces &Pieces,
258 json::Array &Artifacts) {
259 const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
260 json::Array Locations;
261 for (
const auto &Piece : Pieces) {
262 const PathDiagnosticLocation &
P = Piece->getLocation();
266 *
P.asLocation().getExpansionLoc().getFileEntry(),
271 return json::Object{{
"locations", std::move(Locations)}};
275 const PathPieces &Pieces,
276 json::Array &Artifacts) {
282 const PathDiagnostic &
Diag,
283 json::Array &Artifacts,
284 const StringMap<unsigned> &RuleMapping) {
285 const PathPieces &Path =
Diag.path.flatten(
false);
286 const SourceManager &SMgr = Path.front()->getLocation().getManager();
288 auto Iter = RuleMapping.find(
Diag.getCheckerName());
289 assert(Iter != RuleMapping.end() &&
"Rule ID is not in the array index map?");
296 LO,
Diag.getLocation().asRange(),
297 *
Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(),
299 {
"ruleIndex", Iter->getValue()},
300 {
"ruleId",
Diag.getCheckerName()}};
304 return llvm::StringSwitch<StringRef>(CheckName)
306 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
307 .Case(FULLNAME, HELPTEXT)
308 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
315 return llvm::StringSwitch<StringRef>(CheckName)
317 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
318 .Case(FULLNAME, DOC_URI)
319 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
326 StringRef CheckName =
Diag.getCheckerName();
333 if (!RuleURI.empty())
334 Ret[
"helpUri"] = RuleURI;
339 static json::Array
createRules(std::vector<const PathDiagnostic *> &Diags,
340 StringMap<unsigned> &RuleMapping) {
342 llvm::StringSet<> Seen;
344 llvm::for_each(Diags, [&](
const PathDiagnostic *D) {
345 StringRef RuleID = D->getCheckerName();
346 std::pair<llvm::StringSet<>::iterator,
bool>
P = Seen.insert(RuleID);
348 RuleMapping[RuleID] = Rules.size();
349 Rules.push_back(createRule(*D));
356 static json::Object
createTool(std::vector<const PathDiagnostic *> &Diags,
357 StringMap<unsigned> &RuleMapping) {
359 {
"driver", json::Object{{
"name",
"clang"},
360 {
"fullName",
"clang static analyzer"},
361 {
"language",
"en-US"},
367 std::vector<const PathDiagnostic *> &Diags) {
368 json::Array Results, Artifacts;
369 StringMap<unsigned> RuleMapping;
370 json::Object Tool =
createTool(Diags, RuleMapping);
372 llvm::for_each(Diags, [&](
const PathDiagnostic *D) {
373 Results.push_back(
createResult(LO, *D, Artifacts, RuleMapping));
376 return json::Object{{
"tool", std::move(Tool)},
377 {
"results", std::move(Results)},
378 {
"artifacts", std::move(Artifacts)},
379 {
"columnKind",
"unicodeCodePoints"}};
382 void SarifDiagnostics::FlushDiagnosticsImpl(
383 std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
390 llvm::raw_fd_ostream
OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
392 llvm::errs() <<
"warning: could not create file: " << EC.message() <<
'\n';
397 "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"},
398 {
"version",
"2.1.0"},
399 {
"runs", json::Array{
createRun(LO, Diags)}}};
400 OS << llvm::formatv(
"{0:2}\n", json::Value(std::move(Sarif)));