18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/StringMap.h" 20 #include "llvm/Support/ConvertUTF.h" 21 #include "llvm/Support/JSON.h" 22 #include "llvm/Support/Path.h" 25 using namespace clang;
29 class SarifDiagnostics :
public PathDiagnosticConsumer {
30 std::string OutputFile;
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(
53 C.push_back(
new SarifDiagnostics(AnalyzerOpts, Output, PP.
getLangOpts()));
69 if (llvm::isAlnum(C) ||
70 StringRef::npos != StringRef(
"-._~:@!$&'()*+,;=").find(C))
71 return std::string(&C, 1);
72 return "%" + llvm::toHex(StringRef(&C, 1));
79 StringRef Root = sys::path::root_name(Filename);
80 if (Root.startswith(
"//")) {
82 Ret += Root.drop_front(2).str();
83 }
else if (!Root.empty()) {
85 Ret += Twine(
"/" + Root).str();
88 auto Iter = sys::path::begin(Filename),
End = sys::path::end(Filename);
89 assert(Iter !=
End &&
"Expected there to be a non-root path component.");
92 std::for_each(++Iter,
End, [&Ret](StringRef Component) {
96 if (Component ==
"\\")
104 for (
char C : Component) {
109 return Ret.str().str();
118 {
"roles", json::Array{
"resultFile"}},
120 {
"mimeType",
"text/plain"}};
124 json::Array &Artifacts) {
129 auto I = llvm::find_if(Artifacts, [&](
const json::Value &File) {
130 if (
const json::Object *Obj = File.getAsObject()) {
131 if (
const json::Object *FileLoc = Obj->getObject(
"location")) {
133 return URI && URI->equals(FileURI);
141 auto Index =
static_cast<unsigned>(
std::distance(Artifacts.begin(), I));
142 if (I == Artifacts.end())
145 return json::Object{{
"uri", FileURI}, {
"index", Index}};
149 unsigned int TokenLen = 0) {
150 assert(!Loc.
isInvalid() &&
"invalid Loc when adjusting column position");
154 "position in file is before column number?");
156 bool InvalidBuffer =
false;
157 const MemoryBuffer *Buf = SM.
getBuffer(LocInfo.first, &InvalidBuffer);
158 assert(!InvalidBuffer &&
"got an invalid buffer for the location's file");
159 assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
160 "token extends past end of buffer?");
165 unsigned int Ret = 1;
166 while (Off < (LocInfo.second + TokenLen)) {
167 Off += getNumBytesForUTF8(Buf->getBuffer()[Off]);
186 Lexer::MeasureTokenLength(R.
getEnd(),
SM, LO));
194 json::Array &Artifacts) {
209 return "unimportant";
211 llvm_unreachable(
"Fully covered switch is not so fully covered");
216 return json::Object{{
"location", std::move(Location)},
221 return json::Object{{
"text", Text.str()}};
225 StringRef Message =
"") {
226 json::Object
Ret{{
"physicalLocation", std::move(PhysicalLocation)}};
227 if (!Message.empty())
228 Ret.insert({
"message", createMessage(Message)});
233 switch (Piece.getKind()) {
234 case PathDiagnosticPiece::Call:
235 case PathDiagnosticPiece::Macro:
237 case PathDiagnosticPiece::PopUp:
240 case PathDiagnosticPiece::Event:
243 case PathDiagnosticPiece::ControlFlow:
250 const PathPieces &Pieces,
251 json::Array &Artifacts) {
252 const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
253 json::Array Locations;
254 for (
const auto &Piece : Pieces) {
255 const PathDiagnosticLocation &
P = Piece->getLocation();
259 *P.asLocation().getExpansionLoc().getFileEntry(),
264 return json::Object{{
"locations", std::move(Locations)}};
268 const PathPieces &Pieces,
269 json::Array &Artifacts) {
275 const PathDiagnostic &
Diag,
276 json::Array &Artifacts,
277 const StringMap<unsigned> &RuleMapping) {
278 const PathPieces &Path = Diag.path.flatten(
false);
279 const SourceManager &SMgr = Path.front()->getLocation().getManager();
281 auto Iter = RuleMapping.find(Diag.getCheckerName());
282 assert(Iter != RuleMapping.end() &&
"Rule ID is not in the array index map?");
289 LO, Diag.getLocation().asRange(),
290 *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(),
292 {
"ruleIndex", Iter->getValue()},
293 {
"ruleId", Diag.getCheckerName()}};
297 return llvm::StringSwitch<StringRef>(CheckName)
299 #define
CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
300 .Case(FULLNAME, HELPTEXT)
301 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 308 return llvm::StringSwitch<StringRef>(CheckName)
310 #define
CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
311 .Case(FULLNAME, DOC_URI)
312 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 319 StringRef CheckName = Diag.getCheckerName();
326 if (!RuleURI.empty())
327 Ret[
"helpUri"] = RuleURI;
332 static json::Array
createRules(std::vector<const PathDiagnostic *> &Diags,
333 StringMap<unsigned> &RuleMapping) {
335 llvm::StringSet<> Seen;
337 llvm::for_each(Diags, [&](
const PathDiagnostic *D) {
338 StringRef RuleID = D->getCheckerName();
339 std::pair<llvm::StringSet<>::iterator,
bool>
P = Seen.insert(RuleID);
341 RuleMapping[RuleID] = Rules.size();
349 static json::Object
createTool(std::vector<const PathDiagnostic *> &Diags,
350 StringMap<unsigned> &RuleMapping) {
352 {
"driver", json::Object{{
"name",
"clang"},
353 {
"fullName",
"clang static analyzer"},
354 {
"language",
"en-US"},
360 std::vector<const PathDiagnostic *> &Diags) {
361 json::Array Results, Artifacts;
362 StringMap<unsigned> RuleMapping;
363 json::Object Tool =
createTool(Diags, RuleMapping);
365 llvm::for_each(Diags, [&](
const PathDiagnostic *D) {
366 Results.push_back(
createResult(LO, *D, Artifacts, RuleMapping));
369 return json::Object{{
"tool", std::move(Tool)},
370 {
"results", std::move(Results)},
371 {
"artifacts", std::move(Artifacts)},
372 {
"columnKind",
"unicodeCodePoints"}};
375 void SarifDiagnostics::FlushDiagnosticsImpl(
376 std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
383 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_Text);
385 llvm::errs() <<
"warning: could not create file: " << EC.message() <<
'\n';
390 "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"},
391 {
"version",
"2.1.0"},
392 {
"runs", json::Array{
createRun(LO, Diags)}}};
393 OS << llvm::formatv(
"{0:2}\n",
json::Value(std::move(Sarif)));
StringRef tryGetRealPathName() const
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
static StringRef getFileName(const FileEntry &FE)
static json::Object createThreadFlow(const LangOptions &LO, const PathPieces &Pieces, json::Array &Artifacts)
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number...
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
float __ovld __cnfn distance(float p0, float p1)
Returns the distance between p0 and p1.
static json::Object createThreadFlowLocation(json::Object &&Location, Importance I)
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
const LangOptions & getLangOpts() const
std::pair< FileID, unsigned > getDecomposedExpansionLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
static StringRef getRuleDescription(StringRef CheckName)
static json::Object createArtifactLocation(const FileEntry &FE)
static StringRef importanceToStr(Importance I)
static json::Object createRun(const LangOptions &LO, std::vector< const PathDiagnostic *> &Diags)
static Importance calculateImportance(const PathDiagnosticPiece &Piece)
Defines version macros and version-related utility functions for Clang.
Defines the clang::Preprocessor interface.
static std::string percentEncodeURICharacter(char C)
static json::Object createRule(const PathDiagnostic &Diag)
SourceLocation getEnd() const
static json::Object createTool(std::vector< const PathDiagnostic *> &Diags, StringMap< unsigned > &RuleMapping)
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
Encodes a location in the source.
static json::Object createLocation(json::Object &&PhysicalLocation, StringRef Message="")
StringRef getName() const
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
static bool Ret(InterpState &S, CodePtr &PC, APValue &Result)
static json::Object createResult(const LangOptions &LO, const PathDiagnostic &Diag, json::Array &Artifacts, const StringMap< unsigned > &RuleMapping)
Cached information about one file (either on disk or in the virtual file system). ...
static json::Object createPhysicalLocation(const LangOptions &LO, SourceRange R, const FileEntry &FE, const SourceManager &SMgr, json::Array &Artifacts)
static json::Object createArtifact(const FileEntry &FE)
unsigned getExpansionColumnNumber(SourceLocation Loc, bool *Invalid=nullptr) const
const llvm::MemoryBuffer * getBuffer(FileID FID, SourceLocation Loc, bool *Invalid=nullptr) const
Return the buffer for the specified FileID.
Dataflow Directional Tag Classes.
static std::string getName(const CallEvent &Call)
static json::Object createTextRegion(const LangOptions &LO, SourceRange R, const SourceManager &SM)
This class is used for tools that requires cross translation unit capability.
#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN)
static json::Array createRules(std::vector< const PathDiagnostic *> &Diags, StringMap< unsigned > &RuleMapping)
Stores options for the analyzer from the command line.
static StringRef getRuleHelpURIStr(StringRef CheckName)
static std::string fileNameToURI(StringRef Filename)
static json::Object createCodeFlow(const LangOptions &LO, const PathPieces &Pieces, json::Array &Artifacts)
static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc, unsigned int TokenLen=0)
A trivial tuple used to represent a source range.
static json::Object createMessage(StringRef Text)
SourceLocation getBegin() const
This class handles loading and caching of source files into memory.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.