1*fcaf7f86SDimitry Andric //===-- clang/Basic/Sarif.cpp - SarifDocumentWriter class definition ------===// 2*fcaf7f86SDimitry Andric // 3*fcaf7f86SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fcaf7f86SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fcaf7f86SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fcaf7f86SDimitry Andric // 7*fcaf7f86SDimitry Andric //===----------------------------------------------------------------------===// 8*fcaf7f86SDimitry Andric /// 9*fcaf7f86SDimitry Andric /// \file 10*fcaf7f86SDimitry Andric /// This file contains the declaration of the SARIFDocumentWriter class, and 11*fcaf7f86SDimitry Andric /// associated builders such as: 12*fcaf7f86SDimitry Andric /// - \ref SarifArtifact 13*fcaf7f86SDimitry Andric /// - \ref SarifArtifactLocation 14*fcaf7f86SDimitry Andric /// - \ref SarifRule 15*fcaf7f86SDimitry Andric /// - \ref SarifResult 16*fcaf7f86SDimitry Andric //===----------------------------------------------------------------------===// 17*fcaf7f86SDimitry Andric #include "clang/Basic/Sarif.h" 18*fcaf7f86SDimitry Andric #include "clang/Basic/SourceLocation.h" 19*fcaf7f86SDimitry Andric #include "clang/Basic/SourceManager.h" 20*fcaf7f86SDimitry Andric #include "llvm/ADT/ArrayRef.h" 21*fcaf7f86SDimitry Andric #include "llvm/ADT/STLExtras.h" 22*fcaf7f86SDimitry Andric #include "llvm/ADT/StringMap.h" 23*fcaf7f86SDimitry Andric #include "llvm/ADT/StringRef.h" 24*fcaf7f86SDimitry Andric #include "llvm/Support/ConvertUTF.h" 25*fcaf7f86SDimitry Andric #include "llvm/Support/JSON.h" 26*fcaf7f86SDimitry Andric #include "llvm/Support/Path.h" 27*fcaf7f86SDimitry Andric 28*fcaf7f86SDimitry Andric #include <string> 29*fcaf7f86SDimitry Andric #include <utility> 30*fcaf7f86SDimitry Andric 31*fcaf7f86SDimitry Andric using namespace clang; 32*fcaf7f86SDimitry Andric using namespace llvm; 33*fcaf7f86SDimitry Andric 34*fcaf7f86SDimitry Andric using clang::detail::SarifArtifact; 35*fcaf7f86SDimitry Andric using clang::detail::SarifArtifactLocation; 36*fcaf7f86SDimitry Andric 37*fcaf7f86SDimitry Andric static StringRef getFileName(const FileEntry &FE) { 38*fcaf7f86SDimitry Andric StringRef Filename = FE.tryGetRealPathName(); 39*fcaf7f86SDimitry Andric if (Filename.empty()) 40*fcaf7f86SDimitry Andric Filename = FE.getName(); 41*fcaf7f86SDimitry Andric return Filename; 42*fcaf7f86SDimitry Andric } 43*fcaf7f86SDimitry Andric /// \name URI 44*fcaf7f86SDimitry Andric /// @{ 45*fcaf7f86SDimitry Andric 46*fcaf7f86SDimitry Andric /// \internal 47*fcaf7f86SDimitry Andric /// \brief 48*fcaf7f86SDimitry Andric /// Return the RFC3986 encoding of the input character. 49*fcaf7f86SDimitry Andric /// 50*fcaf7f86SDimitry Andric /// \param C Character to encode to RFC3986. 51*fcaf7f86SDimitry Andric /// 52*fcaf7f86SDimitry Andric /// \return The RFC3986 representation of \c C. 53*fcaf7f86SDimitry Andric static std::string percentEncodeURICharacter(char C) { 54*fcaf7f86SDimitry Andric // RFC 3986 claims alpha, numeric, and this handful of 55*fcaf7f86SDimitry Andric // characters are not reserved for the path component and 56*fcaf7f86SDimitry Andric // should be written out directly. Otherwise, percent 57*fcaf7f86SDimitry Andric // encode the character and write that out instead of the 58*fcaf7f86SDimitry Andric // reserved character. 59*fcaf7f86SDimitry Andric if (llvm::isAlnum(C) || 60*fcaf7f86SDimitry Andric StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C)) 61*fcaf7f86SDimitry Andric return std::string(&C, 1); 62*fcaf7f86SDimitry Andric return "%" + llvm::toHex(StringRef(&C, 1)); 63*fcaf7f86SDimitry Andric } 64*fcaf7f86SDimitry Andric 65*fcaf7f86SDimitry Andric /// \internal 66*fcaf7f86SDimitry Andric /// \brief Return a URI representing the given file name. 67*fcaf7f86SDimitry Andric /// 68*fcaf7f86SDimitry Andric /// \param Filename The filename to be represented as URI. 69*fcaf7f86SDimitry Andric /// 70*fcaf7f86SDimitry Andric /// \return RFC3986 URI representing the input file name. 71*fcaf7f86SDimitry Andric static std::string fileNameToURI(StringRef Filename) { 72*fcaf7f86SDimitry Andric SmallString<32> Ret = StringRef("file://"); 73*fcaf7f86SDimitry Andric 74*fcaf7f86SDimitry Andric // Get the root name to see if it has a URI authority. 75*fcaf7f86SDimitry Andric StringRef Root = sys::path::root_name(Filename); 76*fcaf7f86SDimitry Andric if (Root.startswith("//")) { 77*fcaf7f86SDimitry Andric // There is an authority, so add it to the URI. 78*fcaf7f86SDimitry Andric Ret += Root.drop_front(2).str(); 79*fcaf7f86SDimitry Andric } else if (!Root.empty()) { 80*fcaf7f86SDimitry Andric // There is no authority, so end the component and add the root to the URI. 81*fcaf7f86SDimitry Andric Ret += Twine("/" + Root).str(); 82*fcaf7f86SDimitry Andric } 83*fcaf7f86SDimitry Andric 84*fcaf7f86SDimitry Andric auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename); 85*fcaf7f86SDimitry Andric assert(Iter != End && "Expected there to be a non-root path component."); 86*fcaf7f86SDimitry Andric // Add the rest of the path components, encoding any reserved characters; 87*fcaf7f86SDimitry Andric // we skip past the first path component, as it was handled it above. 88*fcaf7f86SDimitry Andric std::for_each(++Iter, End, [&Ret](StringRef Component) { 89*fcaf7f86SDimitry Andric // For reasons unknown to me, we may get a backslash with Windows native 90*fcaf7f86SDimitry Andric // paths for the initial backslash following the drive component, which 91*fcaf7f86SDimitry Andric // we need to ignore as a URI path part. 92*fcaf7f86SDimitry Andric if (Component == "\\") 93*fcaf7f86SDimitry Andric return; 94*fcaf7f86SDimitry Andric 95*fcaf7f86SDimitry Andric // Add the separator between the previous path part and the one being 96*fcaf7f86SDimitry Andric // currently processed. 97*fcaf7f86SDimitry Andric Ret += "/"; 98*fcaf7f86SDimitry Andric 99*fcaf7f86SDimitry Andric // URI encode the part. 100*fcaf7f86SDimitry Andric for (char C : Component) { 101*fcaf7f86SDimitry Andric Ret += percentEncodeURICharacter(C); 102*fcaf7f86SDimitry Andric } 103*fcaf7f86SDimitry Andric }); 104*fcaf7f86SDimitry Andric 105*fcaf7f86SDimitry Andric return std::string(Ret); 106*fcaf7f86SDimitry Andric } 107*fcaf7f86SDimitry Andric /// @} 108*fcaf7f86SDimitry Andric 109*fcaf7f86SDimitry Andric /// \brief Calculate the column position expressed in the number of UTF-8 code 110*fcaf7f86SDimitry Andric /// points from column start to the source location 111*fcaf7f86SDimitry Andric /// 112*fcaf7f86SDimitry Andric /// \param Loc The source location whose column needs to be calculated. 113*fcaf7f86SDimitry Andric /// \param TokenLen Optional hint for when the token is multiple bytes long. 114*fcaf7f86SDimitry Andric /// 115*fcaf7f86SDimitry Andric /// \return The column number as a UTF-8 aware byte offset from column start to 116*fcaf7f86SDimitry Andric /// the effective source location. 117*fcaf7f86SDimitry Andric static unsigned int adjustColumnPos(FullSourceLoc Loc, 118*fcaf7f86SDimitry Andric unsigned int TokenLen = 0) { 119*fcaf7f86SDimitry Andric assert(!Loc.isInvalid() && "invalid Loc when adjusting column position"); 120*fcaf7f86SDimitry Andric 121*fcaf7f86SDimitry Andric std::pair<FileID, unsigned> LocInfo = Loc.getDecomposedLoc(); 122*fcaf7f86SDimitry Andric Optional<MemoryBufferRef> Buf = 123*fcaf7f86SDimitry Andric Loc.getManager().getBufferOrNone(LocInfo.first); 124*fcaf7f86SDimitry Andric assert(Buf && "got an invalid buffer for the location's file"); 125*fcaf7f86SDimitry Andric assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) && 126*fcaf7f86SDimitry Andric "token extends past end of buffer?"); 127*fcaf7f86SDimitry Andric 128*fcaf7f86SDimitry Andric // Adjust the offset to be the start of the line, since we'll be counting 129*fcaf7f86SDimitry Andric // Unicode characters from there until our column offset. 130*fcaf7f86SDimitry Andric unsigned int Off = LocInfo.second - (Loc.getExpansionColumnNumber() - 1); 131*fcaf7f86SDimitry Andric unsigned int Ret = 1; 132*fcaf7f86SDimitry Andric while (Off < (LocInfo.second + TokenLen)) { 133*fcaf7f86SDimitry Andric Off += getNumBytesForUTF8(Buf->getBuffer()[Off]); 134*fcaf7f86SDimitry Andric Ret++; 135*fcaf7f86SDimitry Andric } 136*fcaf7f86SDimitry Andric 137*fcaf7f86SDimitry Andric return Ret; 138*fcaf7f86SDimitry Andric } 139*fcaf7f86SDimitry Andric 140*fcaf7f86SDimitry Andric /// \name SARIF Utilities 141*fcaf7f86SDimitry Andric /// @{ 142*fcaf7f86SDimitry Andric 143*fcaf7f86SDimitry Andric /// \internal 144*fcaf7f86SDimitry Andric json::Object createMessage(StringRef Text) { 145*fcaf7f86SDimitry Andric return json::Object{{"text", Text.str()}}; 146*fcaf7f86SDimitry Andric } 147*fcaf7f86SDimitry Andric 148*fcaf7f86SDimitry Andric /// \internal 149*fcaf7f86SDimitry Andric /// \pre CharSourceRange must be a token range 150*fcaf7f86SDimitry Andric static json::Object createTextRegion(const SourceManager &SM, 151*fcaf7f86SDimitry Andric const CharSourceRange &R) { 152*fcaf7f86SDimitry Andric FullSourceLoc FirstTokenLoc{R.getBegin(), SM}; 153*fcaf7f86SDimitry Andric FullSourceLoc LastTokenLoc{R.getEnd(), SM}; 154*fcaf7f86SDimitry Andric json::Object Region{{"startLine", FirstTokenLoc.getExpansionLineNumber()}, 155*fcaf7f86SDimitry Andric {"startColumn", adjustColumnPos(FirstTokenLoc)}, 156*fcaf7f86SDimitry Andric {"endColumn", adjustColumnPos(LastTokenLoc)}}; 157*fcaf7f86SDimitry Andric if (FirstTokenLoc != LastTokenLoc) { 158*fcaf7f86SDimitry Andric Region["endLine"] = LastTokenLoc.getExpansionLineNumber(); 159*fcaf7f86SDimitry Andric } 160*fcaf7f86SDimitry Andric return Region; 161*fcaf7f86SDimitry Andric } 162*fcaf7f86SDimitry Andric 163*fcaf7f86SDimitry Andric static json::Object createLocation(json::Object &&PhysicalLocation, 164*fcaf7f86SDimitry Andric StringRef Message = "") { 165*fcaf7f86SDimitry Andric json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}}; 166*fcaf7f86SDimitry Andric if (!Message.empty()) 167*fcaf7f86SDimitry Andric Ret.insert({"message", createMessage(Message)}); 168*fcaf7f86SDimitry Andric return Ret; 169*fcaf7f86SDimitry Andric } 170*fcaf7f86SDimitry Andric 171*fcaf7f86SDimitry Andric static StringRef importanceToStr(ThreadFlowImportance I) { 172*fcaf7f86SDimitry Andric switch (I) { 173*fcaf7f86SDimitry Andric case ThreadFlowImportance::Important: 174*fcaf7f86SDimitry Andric return "important"; 175*fcaf7f86SDimitry Andric case ThreadFlowImportance::Essential: 176*fcaf7f86SDimitry Andric return "essential"; 177*fcaf7f86SDimitry Andric case ThreadFlowImportance::Unimportant: 178*fcaf7f86SDimitry Andric return "unimportant"; 179*fcaf7f86SDimitry Andric } 180*fcaf7f86SDimitry Andric llvm_unreachable("Fully covered switch is not so fully covered"); 181*fcaf7f86SDimitry Andric } 182*fcaf7f86SDimitry Andric 183*fcaf7f86SDimitry Andric static json::Object 184*fcaf7f86SDimitry Andric createThreadFlowLocation(json::Object &&Location, 185*fcaf7f86SDimitry Andric const ThreadFlowImportance &Importance) { 186*fcaf7f86SDimitry Andric return json::Object{{"location", std::move(Location)}, 187*fcaf7f86SDimitry Andric {"importance", importanceToStr(Importance)}}; 188*fcaf7f86SDimitry Andric } 189*fcaf7f86SDimitry Andric /// @} 190*fcaf7f86SDimitry Andric 191*fcaf7f86SDimitry Andric json::Object 192*fcaf7f86SDimitry Andric SarifDocumentWriter::createPhysicalLocation(const CharSourceRange &R) { 193*fcaf7f86SDimitry Andric assert(R.isValid() && 194*fcaf7f86SDimitry Andric "Cannot create a physicalLocation from invalid SourceRange!"); 195*fcaf7f86SDimitry Andric assert(R.isCharRange() && 196*fcaf7f86SDimitry Andric "Cannot create a physicalLocation from a token range!"); 197*fcaf7f86SDimitry Andric FullSourceLoc Start{R.getBegin(), SourceMgr}; 198*fcaf7f86SDimitry Andric const FileEntry *FE = Start.getExpansionLoc().getFileEntry(); 199*fcaf7f86SDimitry Andric assert(FE != nullptr && "Diagnostic does not exist within a valid file!"); 200*fcaf7f86SDimitry Andric 201*fcaf7f86SDimitry Andric const std::string &FileURI = fileNameToURI(getFileName(*FE)); 202*fcaf7f86SDimitry Andric auto I = CurrentArtifacts.find(FileURI); 203*fcaf7f86SDimitry Andric 204*fcaf7f86SDimitry Andric if (I == CurrentArtifacts.end()) { 205*fcaf7f86SDimitry Andric uint32_t Idx = static_cast<uint32_t>(CurrentArtifacts.size()); 206*fcaf7f86SDimitry Andric const SarifArtifactLocation &Location = 207*fcaf7f86SDimitry Andric SarifArtifactLocation::create(FileURI).setIndex(Idx); 208*fcaf7f86SDimitry Andric const SarifArtifact &Artifact = SarifArtifact::create(Location) 209*fcaf7f86SDimitry Andric .setRoles({"resultFile"}) 210*fcaf7f86SDimitry Andric .setLength(FE->getSize()) 211*fcaf7f86SDimitry Andric .setMimeType("text/plain"); 212*fcaf7f86SDimitry Andric auto StatusIter = CurrentArtifacts.insert({FileURI, Artifact}); 213*fcaf7f86SDimitry Andric // If inserted, ensure the original iterator points to the newly inserted 214*fcaf7f86SDimitry Andric // element, so it can be used downstream. 215*fcaf7f86SDimitry Andric if (StatusIter.second) 216*fcaf7f86SDimitry Andric I = StatusIter.first; 217*fcaf7f86SDimitry Andric } 218*fcaf7f86SDimitry Andric assert(I != CurrentArtifacts.end() && "Failed to insert new artifact"); 219*fcaf7f86SDimitry Andric const SarifArtifactLocation &Location = I->second.Location; 220*fcaf7f86SDimitry Andric uint32_t Idx = Location.Index.value(); 221*fcaf7f86SDimitry Andric return json::Object{{{"artifactLocation", json::Object{{{"index", Idx}}}}, 222*fcaf7f86SDimitry Andric {"region", createTextRegion(SourceMgr, R)}}}; 223*fcaf7f86SDimitry Andric } 224*fcaf7f86SDimitry Andric 225*fcaf7f86SDimitry Andric json::Object &SarifDocumentWriter::getCurrentTool() { 226*fcaf7f86SDimitry Andric assert(!Closed && "SARIF Document is closed. " 227*fcaf7f86SDimitry Andric "Need to call createRun() before using getcurrentTool!"); 228*fcaf7f86SDimitry Andric 229*fcaf7f86SDimitry Andric // Since Closed = false here, expect there to be at least 1 Run, anything 230*fcaf7f86SDimitry Andric // else is an invalid state. 231*fcaf7f86SDimitry Andric assert(!Runs.empty() && "There are no runs associated with the document!"); 232*fcaf7f86SDimitry Andric 233*fcaf7f86SDimitry Andric return *Runs.back().getAsObject()->get("tool")->getAsObject(); 234*fcaf7f86SDimitry Andric } 235*fcaf7f86SDimitry Andric 236*fcaf7f86SDimitry Andric void SarifDocumentWriter::reset() { 237*fcaf7f86SDimitry Andric CurrentRules.clear(); 238*fcaf7f86SDimitry Andric CurrentArtifacts.clear(); 239*fcaf7f86SDimitry Andric } 240*fcaf7f86SDimitry Andric 241*fcaf7f86SDimitry Andric void SarifDocumentWriter::endRun() { 242*fcaf7f86SDimitry Andric // Exit early if trying to close a closed Document. 243*fcaf7f86SDimitry Andric if (Closed) { 244*fcaf7f86SDimitry Andric reset(); 245*fcaf7f86SDimitry Andric return; 246*fcaf7f86SDimitry Andric } 247*fcaf7f86SDimitry Andric 248*fcaf7f86SDimitry Andric // Since Closed = false here, expect there to be at least 1 Run, anything 249*fcaf7f86SDimitry Andric // else is an invalid state. 250*fcaf7f86SDimitry Andric assert(!Runs.empty() && "There are no runs associated with the document!"); 251*fcaf7f86SDimitry Andric 252*fcaf7f86SDimitry Andric // Flush all the rules. 253*fcaf7f86SDimitry Andric json::Object &Tool = getCurrentTool(); 254*fcaf7f86SDimitry Andric json::Array Rules; 255*fcaf7f86SDimitry Andric for (const SarifRule &R : CurrentRules) { 256*fcaf7f86SDimitry Andric json::Object Rule{ 257*fcaf7f86SDimitry Andric {"name", R.Name}, 258*fcaf7f86SDimitry Andric {"id", R.Id}, 259*fcaf7f86SDimitry Andric {"fullDescription", json::Object{{"text", R.Description}}}}; 260*fcaf7f86SDimitry Andric if (!R.HelpURI.empty()) 261*fcaf7f86SDimitry Andric Rule["helpUri"] = R.HelpURI; 262*fcaf7f86SDimitry Andric Rules.emplace_back(std::move(Rule)); 263*fcaf7f86SDimitry Andric } 264*fcaf7f86SDimitry Andric json::Object &Driver = *Tool.getObject("driver"); 265*fcaf7f86SDimitry Andric Driver["rules"] = std::move(Rules); 266*fcaf7f86SDimitry Andric 267*fcaf7f86SDimitry Andric // Flush all the artifacts. 268*fcaf7f86SDimitry Andric json::Object &Run = getCurrentRun(); 269*fcaf7f86SDimitry Andric json::Array *Artifacts = Run.getArray("artifacts"); 270*fcaf7f86SDimitry Andric for (const auto &Pair : CurrentArtifacts) { 271*fcaf7f86SDimitry Andric const SarifArtifact &A = Pair.getValue(); 272*fcaf7f86SDimitry Andric json::Object Loc{{"uri", A.Location.URI}}; 273*fcaf7f86SDimitry Andric if (A.Location.Index.has_value()) { 274*fcaf7f86SDimitry Andric Loc["index"] = static_cast<int64_t>(A.Location.Index.value()); 275*fcaf7f86SDimitry Andric } 276*fcaf7f86SDimitry Andric json::Object Artifact; 277*fcaf7f86SDimitry Andric Artifact["location"] = std::move(Loc); 278*fcaf7f86SDimitry Andric if (A.Length.has_value()) 279*fcaf7f86SDimitry Andric Artifact["length"] = static_cast<int64_t>(A.Length.value()); 280*fcaf7f86SDimitry Andric if (!A.Roles.empty()) 281*fcaf7f86SDimitry Andric Artifact["roles"] = json::Array(A.Roles); 282*fcaf7f86SDimitry Andric if (!A.MimeType.empty()) 283*fcaf7f86SDimitry Andric Artifact["mimeType"] = A.MimeType; 284*fcaf7f86SDimitry Andric if (A.Offset.has_value()) 285*fcaf7f86SDimitry Andric Artifact["offset"] = A.Offset; 286*fcaf7f86SDimitry Andric Artifacts->push_back(json::Value(std::move(Artifact))); 287*fcaf7f86SDimitry Andric } 288*fcaf7f86SDimitry Andric 289*fcaf7f86SDimitry Andric // Clear, reset temporaries before next run. 290*fcaf7f86SDimitry Andric reset(); 291*fcaf7f86SDimitry Andric 292*fcaf7f86SDimitry Andric // Mark the document as closed. 293*fcaf7f86SDimitry Andric Closed = true; 294*fcaf7f86SDimitry Andric } 295*fcaf7f86SDimitry Andric 296*fcaf7f86SDimitry Andric json::Array 297*fcaf7f86SDimitry Andric SarifDocumentWriter::createThreadFlows(ArrayRef<ThreadFlow> ThreadFlows) { 298*fcaf7f86SDimitry Andric json::Object Ret{{"locations", json::Array{}}}; 299*fcaf7f86SDimitry Andric json::Array Locs; 300*fcaf7f86SDimitry Andric for (const auto &ThreadFlow : ThreadFlows) { 301*fcaf7f86SDimitry Andric json::Object PLoc = createPhysicalLocation(ThreadFlow.Range); 302*fcaf7f86SDimitry Andric json::Object Loc = createLocation(std::move(PLoc), ThreadFlow.Message); 303*fcaf7f86SDimitry Andric Locs.emplace_back( 304*fcaf7f86SDimitry Andric createThreadFlowLocation(std::move(Loc), ThreadFlow.Importance)); 305*fcaf7f86SDimitry Andric } 306*fcaf7f86SDimitry Andric Ret["locations"] = std::move(Locs); 307*fcaf7f86SDimitry Andric return json::Array{std::move(Ret)}; 308*fcaf7f86SDimitry Andric } 309*fcaf7f86SDimitry Andric 310*fcaf7f86SDimitry Andric json::Object 311*fcaf7f86SDimitry Andric SarifDocumentWriter::createCodeFlow(ArrayRef<ThreadFlow> ThreadFlows) { 312*fcaf7f86SDimitry Andric return json::Object{{"threadFlows", createThreadFlows(ThreadFlows)}}; 313*fcaf7f86SDimitry Andric } 314*fcaf7f86SDimitry Andric 315*fcaf7f86SDimitry Andric void SarifDocumentWriter::createRun(StringRef ShortToolName, 316*fcaf7f86SDimitry Andric StringRef LongToolName, 317*fcaf7f86SDimitry Andric StringRef ToolVersion) { 318*fcaf7f86SDimitry Andric // Clear resources associated with a previous run. 319*fcaf7f86SDimitry Andric endRun(); 320*fcaf7f86SDimitry Andric 321*fcaf7f86SDimitry Andric // Signify a new run has begun. 322*fcaf7f86SDimitry Andric Closed = false; 323*fcaf7f86SDimitry Andric 324*fcaf7f86SDimitry Andric json::Object Tool{ 325*fcaf7f86SDimitry Andric {"driver", 326*fcaf7f86SDimitry Andric json::Object{{"name", ShortToolName}, 327*fcaf7f86SDimitry Andric {"fullName", LongToolName}, 328*fcaf7f86SDimitry Andric {"language", "en-US"}, 329*fcaf7f86SDimitry Andric {"version", ToolVersion}, 330*fcaf7f86SDimitry Andric {"informationUri", 331*fcaf7f86SDimitry Andric "https://clang.llvm.org/docs/UsersManual.html"}}}}; 332*fcaf7f86SDimitry Andric json::Object TheRun{{"tool", std::move(Tool)}, 333*fcaf7f86SDimitry Andric {"results", {}}, 334*fcaf7f86SDimitry Andric {"artifacts", {}}, 335*fcaf7f86SDimitry Andric {"columnKind", "unicodeCodePoints"}}; 336*fcaf7f86SDimitry Andric Runs.emplace_back(std::move(TheRun)); 337*fcaf7f86SDimitry Andric } 338*fcaf7f86SDimitry Andric 339*fcaf7f86SDimitry Andric json::Object &SarifDocumentWriter::getCurrentRun() { 340*fcaf7f86SDimitry Andric assert(!Closed && 341*fcaf7f86SDimitry Andric "SARIF Document is closed. " 342*fcaf7f86SDimitry Andric "Can only getCurrentRun() if document is opened via createRun(), " 343*fcaf7f86SDimitry Andric "create a run first"); 344*fcaf7f86SDimitry Andric 345*fcaf7f86SDimitry Andric // Since Closed = false here, expect there to be at least 1 Run, anything 346*fcaf7f86SDimitry Andric // else is an invalid state. 347*fcaf7f86SDimitry Andric assert(!Runs.empty() && "There are no runs associated with the document!"); 348*fcaf7f86SDimitry Andric return *Runs.back().getAsObject(); 349*fcaf7f86SDimitry Andric } 350*fcaf7f86SDimitry Andric 351*fcaf7f86SDimitry Andric size_t SarifDocumentWriter::createRule(const SarifRule &Rule) { 352*fcaf7f86SDimitry Andric size_t Ret = CurrentRules.size(); 353*fcaf7f86SDimitry Andric CurrentRules.emplace_back(Rule); 354*fcaf7f86SDimitry Andric return Ret; 355*fcaf7f86SDimitry Andric } 356*fcaf7f86SDimitry Andric 357*fcaf7f86SDimitry Andric void SarifDocumentWriter::appendResult(const SarifResult &Result) { 358*fcaf7f86SDimitry Andric size_t RuleIdx = Result.RuleIdx; 359*fcaf7f86SDimitry Andric assert(RuleIdx < CurrentRules.size() && 360*fcaf7f86SDimitry Andric "Trying to reference a rule that doesn't exist"); 361*fcaf7f86SDimitry Andric json::Object Ret{{"message", createMessage(Result.DiagnosticMessage)}, 362*fcaf7f86SDimitry Andric {"ruleIndex", static_cast<int64_t>(RuleIdx)}, 363*fcaf7f86SDimitry Andric {"ruleId", CurrentRules[RuleIdx].Id}}; 364*fcaf7f86SDimitry Andric if (!Result.Locations.empty()) { 365*fcaf7f86SDimitry Andric json::Array Locs; 366*fcaf7f86SDimitry Andric for (auto &Range : Result.Locations) { 367*fcaf7f86SDimitry Andric Locs.emplace_back(createLocation(createPhysicalLocation(Range))); 368*fcaf7f86SDimitry Andric } 369*fcaf7f86SDimitry Andric Ret["locations"] = std::move(Locs); 370*fcaf7f86SDimitry Andric } 371*fcaf7f86SDimitry Andric if (!Result.ThreadFlows.empty()) 372*fcaf7f86SDimitry Andric Ret["codeFlows"] = json::Array{createCodeFlow(Result.ThreadFlows)}; 373*fcaf7f86SDimitry Andric json::Object &Run = getCurrentRun(); 374*fcaf7f86SDimitry Andric json::Array *Results = Run.getArray("results"); 375*fcaf7f86SDimitry Andric Results->emplace_back(std::move(Ret)); 376*fcaf7f86SDimitry Andric } 377*fcaf7f86SDimitry Andric 378*fcaf7f86SDimitry Andric json::Object SarifDocumentWriter::createDocument() { 379*fcaf7f86SDimitry Andric // Flush all temporaries to their destinations if needed. 380*fcaf7f86SDimitry Andric endRun(); 381*fcaf7f86SDimitry Andric 382*fcaf7f86SDimitry Andric json::Object Doc{ 383*fcaf7f86SDimitry Andric {"$schema", SchemaURI}, 384*fcaf7f86SDimitry Andric {"version", SchemaVersion}, 385*fcaf7f86SDimitry Andric }; 386*fcaf7f86SDimitry Andric if (!Runs.empty()) 387*fcaf7f86SDimitry Andric Doc["runs"] = json::Array(Runs); 388*fcaf7f86SDimitry Andric return Doc; 389*fcaf7f86SDimitry Andric } 390