14b03ad65SVaibhav Yenamandra //===-- clang/Basic/Sarif.cpp - SarifDocumentWriter class definition ------===// 24b03ad65SVaibhav Yenamandra // 34b03ad65SVaibhav Yenamandra // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 44b03ad65SVaibhav Yenamandra // See https://llvm.org/LICENSE.txt for license information. 54b03ad65SVaibhav Yenamandra // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 64b03ad65SVaibhav Yenamandra // 74b03ad65SVaibhav Yenamandra //===----------------------------------------------------------------------===// 84b03ad65SVaibhav Yenamandra /// 94b03ad65SVaibhav Yenamandra /// \file 104b03ad65SVaibhav Yenamandra /// This file contains the declaration of the SARIFDocumentWriter class, and 114b03ad65SVaibhav Yenamandra /// associated builders such as: 124b03ad65SVaibhav Yenamandra /// - \ref SarifArtifact 134b03ad65SVaibhav Yenamandra /// - \ref SarifArtifactLocation 144b03ad65SVaibhav Yenamandra /// - \ref SarifRule 154b03ad65SVaibhav Yenamandra /// - \ref SarifResult 164b03ad65SVaibhav Yenamandra //===----------------------------------------------------------------------===// 174b03ad65SVaibhav Yenamandra #include "clang/Basic/Sarif.h" 184b03ad65SVaibhav Yenamandra #include "clang/Basic/SourceLocation.h" 194b03ad65SVaibhav Yenamandra #include "clang/Basic/SourceManager.h" 204b03ad65SVaibhav Yenamandra #include "llvm/ADT/ArrayRef.h" 214b03ad65SVaibhav Yenamandra #include "llvm/ADT/STLExtras.h" 22b0abd489SElliot Goodrich #include "llvm/ADT/StringExtras.h" 234b03ad65SVaibhav Yenamandra #include "llvm/ADT/StringRef.h" 244b03ad65SVaibhav Yenamandra #include "llvm/Support/ConvertUTF.h" 254b03ad65SVaibhav Yenamandra #include "llvm/Support/JSON.h" 264b03ad65SVaibhav Yenamandra #include "llvm/Support/Path.h" 274b03ad65SVaibhav Yenamandra 28a1580d7bSKazu Hirata #include <optional> 294b03ad65SVaibhav Yenamandra #include <string> 304b03ad65SVaibhav Yenamandra #include <utility> 314b03ad65SVaibhav Yenamandra 324b03ad65SVaibhav Yenamandra using namespace clang; 334b03ad65SVaibhav Yenamandra using namespace llvm; 344b03ad65SVaibhav Yenamandra 354b03ad65SVaibhav Yenamandra using clang::detail::SarifArtifact; 364b03ad65SVaibhav Yenamandra using clang::detail::SarifArtifactLocation; 374b03ad65SVaibhav Yenamandra 38715257eaSJan Svoboda static StringRef getFileName(FileEntryRef FE) { 39715257eaSJan Svoboda StringRef Filename = FE.getFileEntry().tryGetRealPathName(); 404b03ad65SVaibhav Yenamandra if (Filename.empty()) 414b03ad65SVaibhav Yenamandra Filename = FE.getName(); 424b03ad65SVaibhav Yenamandra return Filename; 434b03ad65SVaibhav Yenamandra } 444b03ad65SVaibhav Yenamandra /// \name URI 454b03ad65SVaibhav Yenamandra /// @{ 464b03ad65SVaibhav Yenamandra 474b03ad65SVaibhav Yenamandra /// \internal 484b03ad65SVaibhav Yenamandra /// \brief 494b03ad65SVaibhav Yenamandra /// Return the RFC3986 encoding of the input character. 504b03ad65SVaibhav Yenamandra /// 514b03ad65SVaibhav Yenamandra /// \param C Character to encode to RFC3986. 524b03ad65SVaibhav Yenamandra /// 534b03ad65SVaibhav Yenamandra /// \return The RFC3986 representation of \c C. 544b03ad65SVaibhav Yenamandra static std::string percentEncodeURICharacter(char C) { 554b03ad65SVaibhav Yenamandra // RFC 3986 claims alpha, numeric, and this handful of 564b03ad65SVaibhav Yenamandra // characters are not reserved for the path component and 574b03ad65SVaibhav Yenamandra // should be written out directly. Otherwise, percent 584b03ad65SVaibhav Yenamandra // encode the character and write that out instead of the 594b03ad65SVaibhav Yenamandra // reserved character. 6034fba4fbSKazu Hirata if (llvm::isAlnum(C) || StringRef("-._~:@!$&'()*+,;=").contains(C)) 614b03ad65SVaibhav Yenamandra return std::string(&C, 1); 624b03ad65SVaibhav Yenamandra return "%" + llvm::toHex(StringRef(&C, 1)); 634b03ad65SVaibhav Yenamandra } 644b03ad65SVaibhav Yenamandra 654b03ad65SVaibhav Yenamandra /// \internal 664b03ad65SVaibhav Yenamandra /// \brief Return a URI representing the given file name. 674b03ad65SVaibhav Yenamandra /// 684b03ad65SVaibhav Yenamandra /// \param Filename The filename to be represented as URI. 694b03ad65SVaibhav Yenamandra /// 704b03ad65SVaibhav Yenamandra /// \return RFC3986 URI representing the input file name. 714b03ad65SVaibhav Yenamandra static std::string fileNameToURI(StringRef Filename) { 724b03ad65SVaibhav Yenamandra SmallString<32> Ret = StringRef("file://"); 734b03ad65SVaibhav Yenamandra 744b03ad65SVaibhav Yenamandra // Get the root name to see if it has a URI authority. 754b03ad65SVaibhav Yenamandra StringRef Root = sys::path::root_name(Filename); 76f3dcc235SKazu Hirata if (Root.starts_with("//")) { 774b03ad65SVaibhav Yenamandra // There is an authority, so add it to the URI. 784b03ad65SVaibhav Yenamandra Ret += Root.drop_front(2).str(); 794b03ad65SVaibhav Yenamandra } else if (!Root.empty()) { 804b03ad65SVaibhav Yenamandra // There is no authority, so end the component and add the root to the URI. 814b03ad65SVaibhav Yenamandra Ret += Twine("/" + Root).str(); 824b03ad65SVaibhav Yenamandra } 834b03ad65SVaibhav Yenamandra 844b03ad65SVaibhav Yenamandra auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename); 854b03ad65SVaibhav Yenamandra assert(Iter != End && "Expected there to be a non-root path component."); 864b03ad65SVaibhav Yenamandra // Add the rest of the path components, encoding any reserved characters; 874b03ad65SVaibhav Yenamandra // we skip past the first path component, as it was handled it above. 881d0b7b6cSKazu Hirata for (StringRef Component : llvm::make_range(++Iter, End)) { 894b03ad65SVaibhav Yenamandra // For reasons unknown to me, we may get a backslash with Windows native 904b03ad65SVaibhav Yenamandra // paths for the initial backslash following the drive component, which 914b03ad65SVaibhav Yenamandra // we need to ignore as a URI path part. 924b03ad65SVaibhav Yenamandra if (Component == "\\") 931d0b7b6cSKazu Hirata continue; 944b03ad65SVaibhav Yenamandra 954b03ad65SVaibhav Yenamandra // Add the separator between the previous path part and the one being 964b03ad65SVaibhav Yenamandra // currently processed. 974b03ad65SVaibhav Yenamandra Ret += "/"; 984b03ad65SVaibhav Yenamandra 994b03ad65SVaibhav Yenamandra // URI encode the part. 1004b03ad65SVaibhav Yenamandra for (char C : Component) { 1014b03ad65SVaibhav Yenamandra Ret += percentEncodeURICharacter(C); 1024b03ad65SVaibhav Yenamandra } 1031d0b7b6cSKazu Hirata } 1044b03ad65SVaibhav Yenamandra 1054b03ad65SVaibhav Yenamandra return std::string(Ret); 1064b03ad65SVaibhav Yenamandra } 1074b03ad65SVaibhav Yenamandra /// @} 1084b03ad65SVaibhav Yenamandra 1094b03ad65SVaibhav Yenamandra /// \brief Calculate the column position expressed in the number of UTF-8 code 1104b03ad65SVaibhav Yenamandra /// points from column start to the source location 1114b03ad65SVaibhav Yenamandra /// 1124b03ad65SVaibhav Yenamandra /// \param Loc The source location whose column needs to be calculated. 1134b03ad65SVaibhav Yenamandra /// \param TokenLen Optional hint for when the token is multiple bytes long. 1144b03ad65SVaibhav Yenamandra /// 1154b03ad65SVaibhav Yenamandra /// \return The column number as a UTF-8 aware byte offset from column start to 1164b03ad65SVaibhav Yenamandra /// the effective source location. 1174b03ad65SVaibhav Yenamandra static unsigned int adjustColumnPos(FullSourceLoc Loc, 1184b03ad65SVaibhav Yenamandra unsigned int TokenLen = 0) { 1194b03ad65SVaibhav Yenamandra assert(!Loc.isInvalid() && "invalid Loc when adjusting column position"); 1204b03ad65SVaibhav Yenamandra 1217b6fe711SVaibhav Yenamandra std::pair<FileID, unsigned> LocInfo = Loc.getDecomposedExpansionLoc(); 1226ad0788cSKazu Hirata std::optional<MemoryBufferRef> Buf = 1234b03ad65SVaibhav Yenamandra Loc.getManager().getBufferOrNone(LocInfo.first); 1244b03ad65SVaibhav Yenamandra assert(Buf && "got an invalid buffer for the location's file"); 1254b03ad65SVaibhav Yenamandra assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) && 1264b03ad65SVaibhav Yenamandra "token extends past end of buffer?"); 1274b03ad65SVaibhav Yenamandra 1284b03ad65SVaibhav Yenamandra // Adjust the offset to be the start of the line, since we'll be counting 1294b03ad65SVaibhav Yenamandra // Unicode characters from there until our column offset. 1304b03ad65SVaibhav Yenamandra unsigned int Off = LocInfo.second - (Loc.getExpansionColumnNumber() - 1); 1314b03ad65SVaibhav Yenamandra unsigned int Ret = 1; 1324b03ad65SVaibhav Yenamandra while (Off < (LocInfo.second + TokenLen)) { 1334b03ad65SVaibhav Yenamandra Off += getNumBytesForUTF8(Buf->getBuffer()[Off]); 1344b03ad65SVaibhav Yenamandra Ret++; 1354b03ad65SVaibhav Yenamandra } 1364b03ad65SVaibhav Yenamandra 1374b03ad65SVaibhav Yenamandra return Ret; 1384b03ad65SVaibhav Yenamandra } 1394b03ad65SVaibhav Yenamandra 1404b03ad65SVaibhav Yenamandra /// \name SARIF Utilities 1414b03ad65SVaibhav Yenamandra /// @{ 1424b03ad65SVaibhav Yenamandra 1434b03ad65SVaibhav Yenamandra /// \internal 144*693d757bSFangrui Song static json::Object createMessage(StringRef Text) { 1454b03ad65SVaibhav Yenamandra return json::Object{{"text", Text.str()}}; 1464b03ad65SVaibhav Yenamandra } 1474b03ad65SVaibhav Yenamandra 1484b03ad65SVaibhav Yenamandra /// \internal 1494b03ad65SVaibhav Yenamandra /// \pre CharSourceRange must be a token range 1504b03ad65SVaibhav Yenamandra static json::Object createTextRegion(const SourceManager &SM, 1514b03ad65SVaibhav Yenamandra const CharSourceRange &R) { 1527b6fe711SVaibhav Yenamandra FullSourceLoc BeginCharLoc{R.getBegin(), SM}; 1537b6fe711SVaibhav Yenamandra FullSourceLoc EndCharLoc{R.getEnd(), SM}; 1547b6fe711SVaibhav Yenamandra json::Object Region{{"startLine", BeginCharLoc.getExpansionLineNumber()}, 1557b6fe711SVaibhav Yenamandra {"startColumn", adjustColumnPos(BeginCharLoc)}}; 1567b6fe711SVaibhav Yenamandra 1577b6fe711SVaibhav Yenamandra if (BeginCharLoc == EndCharLoc) { 1587b6fe711SVaibhav Yenamandra Region["endColumn"] = adjustColumnPos(BeginCharLoc); 1597b6fe711SVaibhav Yenamandra } else { 1607b6fe711SVaibhav Yenamandra Region["endLine"] = EndCharLoc.getExpansionLineNumber(); 1617b6fe711SVaibhav Yenamandra Region["endColumn"] = adjustColumnPos(EndCharLoc); 1624b03ad65SVaibhav Yenamandra } 1634b03ad65SVaibhav Yenamandra return Region; 1644b03ad65SVaibhav Yenamandra } 1654b03ad65SVaibhav Yenamandra 1664b03ad65SVaibhav Yenamandra static json::Object createLocation(json::Object &&PhysicalLocation, 1674b03ad65SVaibhav Yenamandra StringRef Message = "") { 1684b03ad65SVaibhav Yenamandra json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}}; 1694b03ad65SVaibhav Yenamandra if (!Message.empty()) 1704b03ad65SVaibhav Yenamandra Ret.insert({"message", createMessage(Message)}); 1714b03ad65SVaibhav Yenamandra return Ret; 1724b03ad65SVaibhav Yenamandra } 1734b03ad65SVaibhav Yenamandra 1744b03ad65SVaibhav Yenamandra static StringRef importanceToStr(ThreadFlowImportance I) { 1754b03ad65SVaibhav Yenamandra switch (I) { 1764b03ad65SVaibhav Yenamandra case ThreadFlowImportance::Important: 1774b03ad65SVaibhav Yenamandra return "important"; 1784b03ad65SVaibhav Yenamandra case ThreadFlowImportance::Essential: 1794b03ad65SVaibhav Yenamandra return "essential"; 1804b03ad65SVaibhav Yenamandra case ThreadFlowImportance::Unimportant: 1814b03ad65SVaibhav Yenamandra return "unimportant"; 1824b03ad65SVaibhav Yenamandra } 1834b03ad65SVaibhav Yenamandra llvm_unreachable("Fully covered switch is not so fully covered"); 1844b03ad65SVaibhav Yenamandra } 1854b03ad65SVaibhav Yenamandra 1861bd2b2dcSVaibhav Yenamandra static StringRef resultLevelToStr(SarifResultLevel R) { 1871bd2b2dcSVaibhav Yenamandra switch (R) { 1881bd2b2dcSVaibhav Yenamandra case SarifResultLevel::None: 1891bd2b2dcSVaibhav Yenamandra return "none"; 1901bd2b2dcSVaibhav Yenamandra case SarifResultLevel::Note: 1911bd2b2dcSVaibhav Yenamandra return "note"; 1921bd2b2dcSVaibhav Yenamandra case SarifResultLevel::Warning: 1931bd2b2dcSVaibhav Yenamandra return "warning"; 1941bd2b2dcSVaibhav Yenamandra case SarifResultLevel::Error: 1951bd2b2dcSVaibhav Yenamandra return "error"; 1961bd2b2dcSVaibhav Yenamandra } 1971bd2b2dcSVaibhav Yenamandra llvm_unreachable("Potentially un-handled SarifResultLevel. " 1981bd2b2dcSVaibhav Yenamandra "Is the switch not fully covered?"); 1991bd2b2dcSVaibhav Yenamandra } 2001bd2b2dcSVaibhav Yenamandra 2014b03ad65SVaibhav Yenamandra static json::Object 2024b03ad65SVaibhav Yenamandra createThreadFlowLocation(json::Object &&Location, 2034b03ad65SVaibhav Yenamandra const ThreadFlowImportance &Importance) { 2044b03ad65SVaibhav Yenamandra return json::Object{{"location", std::move(Location)}, 2054b03ad65SVaibhav Yenamandra {"importance", importanceToStr(Importance)}}; 2064b03ad65SVaibhav Yenamandra } 2074b03ad65SVaibhav Yenamandra /// @} 2084b03ad65SVaibhav Yenamandra 2094b03ad65SVaibhav Yenamandra json::Object 2104b03ad65SVaibhav Yenamandra SarifDocumentWriter::createPhysicalLocation(const CharSourceRange &R) { 2114b03ad65SVaibhav Yenamandra assert(R.isValid() && 2124b03ad65SVaibhav Yenamandra "Cannot create a physicalLocation from invalid SourceRange!"); 2134b03ad65SVaibhav Yenamandra assert(R.isCharRange() && 2144b03ad65SVaibhav Yenamandra "Cannot create a physicalLocation from a token range!"); 2154b03ad65SVaibhav Yenamandra FullSourceLoc Start{R.getBegin(), SourceMgr}; 216715257eaSJan Svoboda OptionalFileEntryRef FE = Start.getExpansionLoc().getFileEntryRef(); 217715257eaSJan Svoboda assert(FE && "Diagnostic does not exist within a valid file!"); 2184b03ad65SVaibhav Yenamandra 2194b03ad65SVaibhav Yenamandra const std::string &FileURI = fileNameToURI(getFileName(*FE)); 2204b03ad65SVaibhav Yenamandra auto I = CurrentArtifacts.find(FileURI); 2214b03ad65SVaibhav Yenamandra 2224b03ad65SVaibhav Yenamandra if (I == CurrentArtifacts.end()) { 2234b03ad65SVaibhav Yenamandra uint32_t Idx = static_cast<uint32_t>(CurrentArtifacts.size()); 2244b03ad65SVaibhav Yenamandra const SarifArtifactLocation &Location = 2254b03ad65SVaibhav Yenamandra SarifArtifactLocation::create(FileURI).setIndex(Idx); 2264b03ad65SVaibhav Yenamandra const SarifArtifact &Artifact = SarifArtifact::create(Location) 2274b03ad65SVaibhav Yenamandra .setRoles({"resultFile"}) 2284b03ad65SVaibhav Yenamandra .setLength(FE->getSize()) 2294b03ad65SVaibhav Yenamandra .setMimeType("text/plain"); 2304b03ad65SVaibhav Yenamandra auto StatusIter = CurrentArtifacts.insert({FileURI, Artifact}); 2314b03ad65SVaibhav Yenamandra // If inserted, ensure the original iterator points to the newly inserted 2324b03ad65SVaibhav Yenamandra // element, so it can be used downstream. 2334b03ad65SVaibhav Yenamandra if (StatusIter.second) 2344b03ad65SVaibhav Yenamandra I = StatusIter.first; 2354b03ad65SVaibhav Yenamandra } 2364b03ad65SVaibhav Yenamandra assert(I != CurrentArtifacts.end() && "Failed to insert new artifact"); 2374b03ad65SVaibhav Yenamandra const SarifArtifactLocation &Location = I->second.Location; 2387b6fe711SVaibhav Yenamandra json::Object ArtifactLocationObject{{"uri", Location.URI}}; 2397b6fe711SVaibhav Yenamandra if (Location.Index.has_value()) 24053e5cd4dSFangrui Song ArtifactLocationObject["index"] = *Location.Index; 2417b6fe711SVaibhav Yenamandra return json::Object{{{"artifactLocation", std::move(ArtifactLocationObject)}, 2424b03ad65SVaibhav Yenamandra {"region", createTextRegion(SourceMgr, R)}}}; 2434b03ad65SVaibhav Yenamandra } 2444b03ad65SVaibhav Yenamandra 2454b03ad65SVaibhav Yenamandra json::Object &SarifDocumentWriter::getCurrentTool() { 2464b03ad65SVaibhav Yenamandra assert(!Closed && "SARIF Document is closed. " 2474b03ad65SVaibhav Yenamandra "Need to call createRun() before using getcurrentTool!"); 2484b03ad65SVaibhav Yenamandra 2494b03ad65SVaibhav Yenamandra // Since Closed = false here, expect there to be at least 1 Run, anything 2504b03ad65SVaibhav Yenamandra // else is an invalid state. 2514b03ad65SVaibhav Yenamandra assert(!Runs.empty() && "There are no runs associated with the document!"); 2524b03ad65SVaibhav Yenamandra 2534b03ad65SVaibhav Yenamandra return *Runs.back().getAsObject()->get("tool")->getAsObject(); 2544b03ad65SVaibhav Yenamandra } 2554b03ad65SVaibhav Yenamandra 2564b03ad65SVaibhav Yenamandra void SarifDocumentWriter::reset() { 2574b03ad65SVaibhav Yenamandra CurrentRules.clear(); 2584b03ad65SVaibhav Yenamandra CurrentArtifacts.clear(); 2594b03ad65SVaibhav Yenamandra } 2604b03ad65SVaibhav Yenamandra 2614b03ad65SVaibhav Yenamandra void SarifDocumentWriter::endRun() { 2624b03ad65SVaibhav Yenamandra // Exit early if trying to close a closed Document. 2634b03ad65SVaibhav Yenamandra if (Closed) { 2644b03ad65SVaibhav Yenamandra reset(); 2654b03ad65SVaibhav Yenamandra return; 2664b03ad65SVaibhav Yenamandra } 2674b03ad65SVaibhav Yenamandra 2684b03ad65SVaibhav Yenamandra // Since Closed = false here, expect there to be at least 1 Run, anything 2694b03ad65SVaibhav Yenamandra // else is an invalid state. 2704b03ad65SVaibhav Yenamandra assert(!Runs.empty() && "There are no runs associated with the document!"); 2714b03ad65SVaibhav Yenamandra 2724b03ad65SVaibhav Yenamandra // Flush all the rules. 2734b03ad65SVaibhav Yenamandra json::Object &Tool = getCurrentTool(); 2744b03ad65SVaibhav Yenamandra json::Array Rules; 2754b03ad65SVaibhav Yenamandra for (const SarifRule &R : CurrentRules) { 2761bd2b2dcSVaibhav Yenamandra json::Object Config{ 2771bd2b2dcSVaibhav Yenamandra {"enabled", R.DefaultConfiguration.Enabled}, 2781bd2b2dcSVaibhav Yenamandra {"level", resultLevelToStr(R.DefaultConfiguration.Level)}, 2791bd2b2dcSVaibhav Yenamandra {"rank", R.DefaultConfiguration.Rank}}; 2804b03ad65SVaibhav Yenamandra json::Object Rule{ 2814b03ad65SVaibhav Yenamandra {"name", R.Name}, 2824b03ad65SVaibhav Yenamandra {"id", R.Id}, 2831bd2b2dcSVaibhav Yenamandra {"fullDescription", json::Object{{"text", R.Description}}}, 2841bd2b2dcSVaibhav Yenamandra {"defaultConfiguration", std::move(Config)}}; 2854b03ad65SVaibhav Yenamandra if (!R.HelpURI.empty()) 2864b03ad65SVaibhav Yenamandra Rule["helpUri"] = R.HelpURI; 2874b03ad65SVaibhav Yenamandra Rules.emplace_back(std::move(Rule)); 2884b03ad65SVaibhav Yenamandra } 2894b03ad65SVaibhav Yenamandra json::Object &Driver = *Tool.getObject("driver"); 2904b03ad65SVaibhav Yenamandra Driver["rules"] = std::move(Rules); 2914b03ad65SVaibhav Yenamandra 2924b03ad65SVaibhav Yenamandra // Flush all the artifacts. 2934b03ad65SVaibhav Yenamandra json::Object &Run = getCurrentRun(); 2944b03ad65SVaibhav Yenamandra json::Array *Artifacts = Run.getArray("artifacts"); 295dda8ac8dSFangrui Song SmallVector<std::pair<StringRef, SarifArtifact>, 0> Vec; 296dda8ac8dSFangrui Song for (const auto &[K, V] : CurrentArtifacts) 297dda8ac8dSFangrui Song Vec.emplace_back(K, V); 298dda8ac8dSFangrui Song llvm::sort(Vec, llvm::less_first()); 299dda8ac8dSFangrui Song for (const auto &[_, A] : Vec) { 3004b03ad65SVaibhav Yenamandra json::Object Loc{{"uri", A.Location.URI}}; 30141ae78eaSKazu Hirata if (A.Location.Index.has_value()) { 30253e5cd4dSFangrui Song Loc["index"] = static_cast<int64_t>(*A.Location.Index); 3034b03ad65SVaibhav Yenamandra } 3044b03ad65SVaibhav Yenamandra json::Object Artifact; 3054b03ad65SVaibhav Yenamandra Artifact["location"] = std::move(Loc); 30641ae78eaSKazu Hirata if (A.Length.has_value()) 30753e5cd4dSFangrui Song Artifact["length"] = static_cast<int64_t>(*A.Length); 3084b03ad65SVaibhav Yenamandra if (!A.Roles.empty()) 3094b03ad65SVaibhav Yenamandra Artifact["roles"] = json::Array(A.Roles); 3104b03ad65SVaibhav Yenamandra if (!A.MimeType.empty()) 3114b03ad65SVaibhav Yenamandra Artifact["mimeType"] = A.MimeType; 31241ae78eaSKazu Hirata if (A.Offset.has_value()) 3131da3a795SFangrui Song Artifact["offset"] = *A.Offset; 3144b03ad65SVaibhav Yenamandra Artifacts->push_back(json::Value(std::move(Artifact))); 3154b03ad65SVaibhav Yenamandra } 3164b03ad65SVaibhav Yenamandra 3174b03ad65SVaibhav Yenamandra // Clear, reset temporaries before next run. 3184b03ad65SVaibhav Yenamandra reset(); 3194b03ad65SVaibhav Yenamandra 3204b03ad65SVaibhav Yenamandra // Mark the document as closed. 3214b03ad65SVaibhav Yenamandra Closed = true; 3224b03ad65SVaibhav Yenamandra } 3234b03ad65SVaibhav Yenamandra 3244b03ad65SVaibhav Yenamandra json::Array 3254b03ad65SVaibhav Yenamandra SarifDocumentWriter::createThreadFlows(ArrayRef<ThreadFlow> ThreadFlows) { 3264b03ad65SVaibhav Yenamandra json::Object Ret{{"locations", json::Array{}}}; 3274b03ad65SVaibhav Yenamandra json::Array Locs; 3284b03ad65SVaibhav Yenamandra for (const auto &ThreadFlow : ThreadFlows) { 3294b03ad65SVaibhav Yenamandra json::Object PLoc = createPhysicalLocation(ThreadFlow.Range); 3304b03ad65SVaibhav Yenamandra json::Object Loc = createLocation(std::move(PLoc), ThreadFlow.Message); 3314b03ad65SVaibhav Yenamandra Locs.emplace_back( 3324b03ad65SVaibhav Yenamandra createThreadFlowLocation(std::move(Loc), ThreadFlow.Importance)); 3334b03ad65SVaibhav Yenamandra } 3344b03ad65SVaibhav Yenamandra Ret["locations"] = std::move(Locs); 3354b03ad65SVaibhav Yenamandra return json::Array{std::move(Ret)}; 3364b03ad65SVaibhav Yenamandra } 3374b03ad65SVaibhav Yenamandra 3384b03ad65SVaibhav Yenamandra json::Object 3394b03ad65SVaibhav Yenamandra SarifDocumentWriter::createCodeFlow(ArrayRef<ThreadFlow> ThreadFlows) { 3404b03ad65SVaibhav Yenamandra return json::Object{{"threadFlows", createThreadFlows(ThreadFlows)}}; 3414b03ad65SVaibhav Yenamandra } 3424b03ad65SVaibhav Yenamandra 3434b03ad65SVaibhav Yenamandra void SarifDocumentWriter::createRun(StringRef ShortToolName, 3444b03ad65SVaibhav Yenamandra StringRef LongToolName, 3454b03ad65SVaibhav Yenamandra StringRef ToolVersion) { 3464b03ad65SVaibhav Yenamandra // Clear resources associated with a previous run. 3474b03ad65SVaibhav Yenamandra endRun(); 3484b03ad65SVaibhav Yenamandra 3494b03ad65SVaibhav Yenamandra // Signify a new run has begun. 3504b03ad65SVaibhav Yenamandra Closed = false; 3514b03ad65SVaibhav Yenamandra 3524b03ad65SVaibhav Yenamandra json::Object Tool{ 3534b03ad65SVaibhav Yenamandra {"driver", 3544b03ad65SVaibhav Yenamandra json::Object{{"name", ShortToolName}, 3554b03ad65SVaibhav Yenamandra {"fullName", LongToolName}, 3564b03ad65SVaibhav Yenamandra {"language", "en-US"}, 3574b03ad65SVaibhav Yenamandra {"version", ToolVersion}, 3584b03ad65SVaibhav Yenamandra {"informationUri", 3594b03ad65SVaibhav Yenamandra "https://clang.llvm.org/docs/UsersManual.html"}}}}; 3604b03ad65SVaibhav Yenamandra json::Object TheRun{{"tool", std::move(Tool)}, 3614b03ad65SVaibhav Yenamandra {"results", {}}, 3624b03ad65SVaibhav Yenamandra {"artifacts", {}}, 3634b03ad65SVaibhav Yenamandra {"columnKind", "unicodeCodePoints"}}; 3644b03ad65SVaibhav Yenamandra Runs.emplace_back(std::move(TheRun)); 3654b03ad65SVaibhav Yenamandra } 3664b03ad65SVaibhav Yenamandra 3674b03ad65SVaibhav Yenamandra json::Object &SarifDocumentWriter::getCurrentRun() { 3684b03ad65SVaibhav Yenamandra assert(!Closed && 3694b03ad65SVaibhav Yenamandra "SARIF Document is closed. " 3704b03ad65SVaibhav Yenamandra "Can only getCurrentRun() if document is opened via createRun(), " 3714b03ad65SVaibhav Yenamandra "create a run first"); 3724b03ad65SVaibhav Yenamandra 3734b03ad65SVaibhav Yenamandra // Since Closed = false here, expect there to be at least 1 Run, anything 3744b03ad65SVaibhav Yenamandra // else is an invalid state. 3754b03ad65SVaibhav Yenamandra assert(!Runs.empty() && "There are no runs associated with the document!"); 3764b03ad65SVaibhav Yenamandra return *Runs.back().getAsObject(); 3774b03ad65SVaibhav Yenamandra } 3784b03ad65SVaibhav Yenamandra 3794b03ad65SVaibhav Yenamandra size_t SarifDocumentWriter::createRule(const SarifRule &Rule) { 3804b03ad65SVaibhav Yenamandra size_t Ret = CurrentRules.size(); 3814b03ad65SVaibhav Yenamandra CurrentRules.emplace_back(Rule); 3824b03ad65SVaibhav Yenamandra return Ret; 3834b03ad65SVaibhav Yenamandra } 3844b03ad65SVaibhav Yenamandra 3854b03ad65SVaibhav Yenamandra void SarifDocumentWriter::appendResult(const SarifResult &Result) { 3864b03ad65SVaibhav Yenamandra size_t RuleIdx = Result.RuleIdx; 3874b03ad65SVaibhav Yenamandra assert(RuleIdx < CurrentRules.size() && 3884b03ad65SVaibhav Yenamandra "Trying to reference a rule that doesn't exist"); 3891bd2b2dcSVaibhav Yenamandra const SarifRule &Rule = CurrentRules[RuleIdx]; 3901bd2b2dcSVaibhav Yenamandra assert(Rule.DefaultConfiguration.Enabled && 3911bd2b2dcSVaibhav Yenamandra "Cannot add a result referencing a disabled Rule"); 3924b03ad65SVaibhav Yenamandra json::Object Ret{{"message", createMessage(Result.DiagnosticMessage)}, 3934b03ad65SVaibhav Yenamandra {"ruleIndex", static_cast<int64_t>(RuleIdx)}, 3941bd2b2dcSVaibhav Yenamandra {"ruleId", Rule.Id}}; 3954b03ad65SVaibhav Yenamandra if (!Result.Locations.empty()) { 3964b03ad65SVaibhav Yenamandra json::Array Locs; 3974b03ad65SVaibhav Yenamandra for (auto &Range : Result.Locations) { 3984b03ad65SVaibhav Yenamandra Locs.emplace_back(createLocation(createPhysicalLocation(Range))); 3994b03ad65SVaibhav Yenamandra } 4004b03ad65SVaibhav Yenamandra Ret["locations"] = std::move(Locs); 4014b03ad65SVaibhav Yenamandra } 4024b03ad65SVaibhav Yenamandra if (!Result.ThreadFlows.empty()) 4034b03ad65SVaibhav Yenamandra Ret["codeFlows"] = json::Array{createCodeFlow(Result.ThreadFlows)}; 4041bd2b2dcSVaibhav Yenamandra 4051bd2b2dcSVaibhav Yenamandra Ret["level"] = resultLevelToStr( 4061bd2b2dcSVaibhav Yenamandra Result.LevelOverride.value_or(Rule.DefaultConfiguration.Level)); 4071bd2b2dcSVaibhav Yenamandra 4084b03ad65SVaibhav Yenamandra json::Object &Run = getCurrentRun(); 4094b03ad65SVaibhav Yenamandra json::Array *Results = Run.getArray("results"); 4104b03ad65SVaibhav Yenamandra Results->emplace_back(std::move(Ret)); 4114b03ad65SVaibhav Yenamandra } 4124b03ad65SVaibhav Yenamandra 4134b03ad65SVaibhav Yenamandra json::Object SarifDocumentWriter::createDocument() { 4144b03ad65SVaibhav Yenamandra // Flush all temporaries to their destinations if needed. 4154b03ad65SVaibhav Yenamandra endRun(); 4164b03ad65SVaibhav Yenamandra 4174b03ad65SVaibhav Yenamandra json::Object Doc{ 4184b03ad65SVaibhav Yenamandra {"$schema", SchemaURI}, 4194b03ad65SVaibhav Yenamandra {"version", SchemaVersion}, 4204b03ad65SVaibhav Yenamandra }; 4214b03ad65SVaibhav Yenamandra if (!Runs.empty()) 4224b03ad65SVaibhav Yenamandra Doc["runs"] = json::Array(Runs); 4234b03ad65SVaibhav Yenamandra return Doc; 4244b03ad65SVaibhav Yenamandra } 425