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