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