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