18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/StringMap.h" 20 #include "llvm/Support/JSON.h" 21 #include "llvm/Support/Path.h" 24 using namespace clang;
28 class SarifDiagnostics :
public PathDiagnosticConsumer {
29 std::string OutputFile;
33 : OutputFile(Output) {}
34 ~SarifDiagnostics()
override =
default;
36 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
37 FilesMade *FM)
override;
39 StringRef
getName()
const override {
return "SarifDiagnostics"; }
40 PathGenerationScheme getGenerationScheme()
const override {
return Minimal; }
41 bool supportsLogicalOpControlFlow()
const override {
return true; }
42 bool supportsCrossFileDiagnostics()
const override {
return true; }
48 const std::string &Output,
50 C.push_back(
new SarifDiagnostics(AnalyzerOpts, Output));
66 if (llvm::isAlnum(C) ||
67 StringRef::npos != StringRef(
"-._~:@!$&'()*+,;=").find(C))
68 return std::string(&C, 1);
69 return "%" + llvm::toHex(StringRef(&C, 1));
76 StringRef Root = sys::path::root_name(Filename);
77 if (Root.startswith(
"//")) {
79 Ret += Root.drop_front(2).str();
80 }
else if (!Root.empty()) {
82 Ret += Twine(
"/" + Root).str();
85 auto Iter = sys::path::begin(Filename),
End = sys::path::end(Filename);
86 assert(Iter !=
End &&
"Expected there to be a non-root path component.");
89 std::for_each(++Iter,
End, [&Ret](StringRef Component) {
93 if (Component ==
"\\")
101 for (
char C : Component) {
106 return Ret.str().str();
115 {
"roles", json::Array{
"resultFile"}},
117 {
"mimeType",
"text/plain"}};
121 json::Array &Files) {
126 auto I = llvm::find_if(Files, [&](
const json::Value &File) {
127 if (
const json::Object *Obj = File.getAsObject()) {
128 if (
const json::Object *FileLoc = Obj->getObject(
"fileLocation")) {
130 return URI && URI->equals(FileURI);
138 auto Index =
static_cast<unsigned>(
std::distance(Files.begin(), I));
139 if (I == Files.end())
142 return json::Object{{
"uri", FileURI}, {
"fileIndex", Index}};
155 json::Array &Files) {
169 return "unimportant";
171 llvm_unreachable(
"Fully covered switch is not so fully covered");
176 return json::Object{{
"location", std::move(Location)},
181 return json::Object{{
"text", Text.str()}};
185 StringRef Message =
"") {
186 json::Object Ret{{
"physicalLocation", std::move(PhysicalLocation)}};
187 if (!Message.empty())
188 Ret.insert({
"message", createMessage(Message)});
193 switch (Piece.getKind()) {
194 case PathDiagnosticPiece::Call:
195 case PathDiagnosticPiece::Macro:
197 case PathDiagnosticPiece::PopUp:
200 case PathDiagnosticPiece::Event:
203 case PathDiagnosticPiece::ControlFlow:
210 json::Array &Files) {
211 const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
212 json::Array Locations;
213 for (
const auto &Piece : Pieces) {
214 const PathDiagnosticLocation &
P = Piece->getLocation();
217 *P.asLocation().getFileEntry(),
222 return json::Object{{
"locations", std::move(Locations)}};
226 json::Array &Files) {
232 return json::Object{{
"name",
"clang"},
233 {
"fullName",
"clang static analyzer"},
234 {
"language",
"en-US"},
239 const StringMap<unsigned> &RuleMapping) {
240 const PathPieces &Path = Diag.path.flatten(
false);
241 const SourceManager &SMgr = Path.front()->getLocation().getManager();
243 auto Iter = RuleMapping.find(Diag.getCheckName());
244 assert(Iter != RuleMapping.end() &&
"Rule ID is not in the array index map?");
251 Diag.getLocation().asRange(),
252 *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}},
253 {
"ruleIndex", Iter->getValue()},
254 {
"ruleId", Diag.getCheckName()}};
258 return llvm::StringSwitch<StringRef>(CheckName)
260 #define
CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
261 .Case(FULLNAME, HELPTEXT)
262 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 269 return llvm::StringSwitch<StringRef>(CheckName)
271 #define
CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
272 .Case(FULLNAME, DOC_URI)
273 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 280 StringRef CheckName = Diag.getCheckName();
287 if (!RuleURI.empty())
288 Ret[
"helpUri"] = RuleURI;
293 static json::Array
createRules(std::vector<const PathDiagnostic *> &Diags,
294 StringMap<unsigned> &RuleMapping) {
296 llvm::StringSet<> Seen;
298 llvm::for_each(Diags, [&](
const PathDiagnostic *D) {
299 StringRef RuleID = D->getCheckName();
300 std::pair<llvm::StringSet<>::iterator,
bool>
P = Seen.insert(RuleID);
302 RuleMapping[RuleID] = Rules.size();
311 StringMap<unsigned> &RuleMapping) {
312 return json::Object{{
"rules",
createRules(Diags, RuleMapping)}};
315 static json::Object
createRun(std::vector<const PathDiagnostic *> &Diags) {
316 json::Array Results, Files;
317 StringMap<unsigned> RuleMapping;
320 llvm::for_each(Diags, [&](
const PathDiagnostic *D) {
321 Results.push_back(
createResult(*D, Files, RuleMapping));
325 {
"resources", std::move(Resources)},
326 {
"results", std::move(Results)},
327 {
"files", std::move(Files)}};
330 void SarifDiagnostics::FlushDiagnosticsImpl(
331 std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
338 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text);
340 llvm::errs() <<
"warning: could not create file: " << EC.message() <<
'\n';
345 "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"},
346 {
"version",
"2.0.0-csd.2.beta.2018-11-28"},
347 {
"runs", json::Array{
createRun(Diags)}}};
348 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)
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...
static json::Object createTextRegion(SourceRange R, const SourceManager &SM)
static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, const StringMap< unsigned > &RuleMapping)
float __ovld __cnfn distance(float p0, float p1)
Returns the distance between p0 and p1.
static json::Object createThreadFlow(const PathPieces &Pieces, json::Array &Files)
static json::Object createThreadFlowLocation(json::Object &&Location, Importance I)
static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE, const SourceManager &SMgr, json::Array &Files)
static StringRef getRuleDescription(StringRef CheckName)
static StringRef importanceToStr(Importance I)
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
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
static json::Object createFile(const FileEntry &FE)
static json::Object createLocation(json::Object &&PhysicalLocation, StringRef Message="")
StringRef getName() const
std::vector< PathDiagnosticConsumer * > PathDiagnosticConsumers
static json::Object createCodeFlow(const PathPieces &Pieces, json::Array &Files)
Cached information about one file (either on disk or in the virtual file system). ...
static json::Object createTool()
unsigned getExpansionColumnNumber(SourceLocation Loc, bool *Invalid=nullptr) const
Dataflow Directional Tag Classes.
static json::Object createFileLocation(const FileEntry &FE)
static std::string getName(const CallEvent &Call)
static json::Object createResources(std::vector< const PathDiagnostic *> &Diags, StringMap< unsigned > &RuleMapping)
#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN)
static json::Object createRun(std::vector< const PathDiagnostic *> &Diags)
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)
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.