diff --git a/Source/cmCMakeSarifLogger.cxx b/Source/cmCMakeSarifLogger.cxx index 8639defb73..2d30f3e7f6 100644 --- a/Source/cmCMakeSarifLogger.cxx +++ b/Source/cmCMakeSarifLogger.cxx @@ -31,38 +31,91 @@ namespace { constexpr char const* CMakeSarifOutputFlag = "CMAKE_EXPORT_SARIF"; constexpr char const* DefaultSarifFile = ".cmake/sarif/cmake.sarif"; -cm::optional GetLocationFromBacktrace( - cmListFileBacktrace const& backtrace, cmake const& cm) +/// @brief Express the location of a `cmListFileContext` in SARIF +/// @param[in] uriBaseIds A list of logical base directory names and their path +/// +/// Build a SARIF location object detailing the location data available from a +/// context. More specific information like the region (line number) and +/// function call name will be included if available. +/// +/// SARIF requests that paths are given relative to a logical base for +/// relocatability. Context locations will be made relative to a logical base +/// iff they fall under one of the directories listed in the `uriBaseIds` +/// map. Bases are tried in order. +cmSarif::Location LocationFromContext( + cmListFileContext const& lfc, + std::vector> const& + uriBaseIds = {}) +{ + cmSarif::Location location; + location.Physical.Artifact.Uri = lfc.FilePath; + + // SARIF requests that paths are given relative to a logical base for + // relocatability. Check if these files are under any of the bases, if + // provided. + for (auto const& baseUri : uriBaseIds) { + std::string relative = cmSystemTools::RelativeIfUnder( + std::string(baseUri.second), location.Physical.Artifact.Uri); + if (relative != location.Physical.Artifact.Uri) { + location.Physical.Artifact.Uri = relative; + location.Physical.Artifact.UriBaseId = std::string(baseUri.first); + } + } + + if (!lfc.Name.empty()) { + location.Logical.emplace_back( + cmSarif::LogicalLocation{ lfc.Name, cmSarif::LocationKind::Function }); + } + + // Add info about the region within the file depending on how specific the + // context is. Watch for deferred call and variable watch placeholders or + // a zero, which indicates the start of processing a list file. + if (lfc.Line == cmListFileContext::DeferPlaceholderLine) { + location.Message = cmSarif::Message{ "DEFERRED" }; + } else if (lfc.Line > 0 && lfc.Line != std::numeric_limits::max()) { + cmSarif::Region region; + region.StartLine = lfc.Line; + location.Physical.ArtifactRegion = region; + } + + return location; +} + +cm::optional LastLocation( + cmListFileBacktrace backtrace, + std::vector> const& + uriBaseIds = {}) { if (backtrace.Empty()) { return {}; } - cmListFileContext const& lfc = backtrace.Top(); - // Exclude frames with no real location: negative lines are deferred-call - // placeholders, and LONG_MAX is the synthetic line used by variable_watch - // callback dispatch. Neither is a meaningful source location. - if (lfc.Line < 0 || lfc.Line == std::numeric_limits::max()) { + return LocationFromContext(backtrace.Top(), uriBaseIds); +} + +cm::optional StackFromBacktrace( + cmListFileBacktrace bt, + std::vector> const& + uriBaseIds = {}) +{ + if (bt.Empty()) { return {}; } - cmSarif::PhysicalLocation location; - location.Artifact.Uri = lfc.FilePath; + cmSarif::Stack stack; + for (; !bt.Empty(); bt = bt.Pop()) { + cmSarif::Location topLocation = LocationFromContext(bt.Top(), uriBaseIds); - // SARIF requests that paths are given relative to a logical base. Report - // paths relative to the source dir / script working directory if possible. - location.Artifact.UriBaseId = cm.GetHomeDirectory(); - std::string relative = cmSystemTools::RelativePath( - location.Artifact.UriBaseId, location.Artifact.Uri); - if (!relative.empty()) { - location.Artifact.Uri = relative; - } + // If the location doesn't have a specific region, this entry is a + // placeholder and should not appear in the call stack. + if (!topLocation.Message && !topLocation.Physical.ArtifactRegion) { + continue; + } - if (lfc.Line != 0) { - cmSarif::Region region; - region.StartLine = lfc.Line; - location.ArtifactRegion = region; + cmSarif::StackFrame frame; + frame.Location = std::move(topLocation); + stack.Frames.emplace_back(std::move(frame)); } - return cmSarif::Location{ location }; + return stack; } cmSarif::Tool CreateCMakeTool() @@ -213,6 +266,27 @@ bool cmCMakeSarifLogger::WriteFile(std::string const& path, return *result.first; }; + // Make a prioritized list of base directories applicable in this context. + // This is used for normalizing the paths of related locations. + std::vector> uriBaseIds; + + std::string const& binDir = this->CM.GetHomeOutputDirectory(); + if (!binDir.empty()) { + uriBaseIds.emplace_back("CMAKE_BINARY_DIR", binDir); + } + + std::string const& homeDir = this->CM.GetHomeDirectory(); + if (!homeDir.empty()) { + uriBaseIds.emplace_back("CMAKE_SOURCE_DIR", homeDir); + } + + // Log the base directories for this run. + for (auto const& base : uriBaseIds) { + run.OriginalUriBaseIds.emplace( + std::string(base.first), + cmSarif::ArtifactLocation{ cmStrCat("file://", base.second, "/"), "" }); + } + cmMessenger const& messenger = *this->CM.GetMessenger(); for (auto const& message : messenger.GetDisplayedMessages()) { // SARIF should only emit diagnostic messages, not general messages/logs @@ -231,8 +305,12 @@ bool cmCMakeSarifLogger::WriteFile(std::string const& path, cmSarif::Result result; result.RuleId = ruleInfo.first; result.RuleIndex = ruleInfo.second; - result.Message = message.Text; - result.Location = GetLocationFromBacktrace(message.Backtrace, this->CM); + result.Message = cmSarif::Message{ message.Text }; + result.Location = LastLocation(message.Backtrace, uriBaseIds); + if (cm::optional stack = + StackFromBacktrace(message.Backtrace, uriBaseIds)) { + result.Stacks.emplace_back(std::move(*stack)); + } result.Level = SarifLevelFromMessageType(message.Type); run.Results.emplace_back(std::move(result)); diff --git a/Source/cmSarif.cxx b/Source/cmSarif.cxx index 7b711d8821..00affb6efc 100644 --- a/Source/cmSarif.cxx +++ b/Source/cmSarif.cxx @@ -3,6 +3,7 @@ #include "cmSarif.h" #include +#include #include #include @@ -31,6 +32,13 @@ Json::Value GetJson(ResultSeverityLevel level) } } +Json::Value GetJson(Message const& message) +{ + Json::Value obj(Json::objectValue); + obj["text"] = message.Text; + return obj; +} + Json::Value GetJson(ArtifactLocation const& artifactLocation) { Json::Value obj(Json::objectValue); @@ -58,13 +66,54 @@ Json::Value GetJson(PhysicalLocation const& physicalLocation) return obj; } +Json::Value GetJson(LogicalLocation const& logicalLocation) +{ + Json::Value obj(Json::objectValue); + obj["name"] = logicalLocation.Name; + if (!logicalLocation.Kind.empty()) { + obj["kind"] = std::string(logicalLocation.Kind); + } + return obj; +} + Json::Value GetJson(Location const& location) { Json::Value obj(Json::objectValue); obj["physicalLocation"] = cmSarif::GetJson(location.Physical); + if (!location.Logical.empty()) { + Json::Value logical(Json::arrayValue); + for (auto const& loc : location.Logical) { + logical.append(cmSarif::GetJson(loc)); + } + obj["logicalLocations"] = logical; + } + if (location.Message) { + obj["message"] = cmSarif::GetJson(*location.Message); + } + return obj; } +Json::Value GetJson(StackFrame const& frame) +{ + Json::Value frameJson(Json::objectValue); + if (frame.Location) { + frameJson["location"] = cmSarif::GetJson(*frame.Location); + } + return frameJson; +} + +Json::Value GetJson(Stack const& stack) +{ + Json::Value stackJson(Json::objectValue); + Json::Value frames(Json::arrayValue); + for (auto const& frame : stack.Frames) { + frames.append(cmSarif::GetJson(frame)); + } + stackJson["frames"] = frames; + return stackJson; +} + Json::Value GetJson(ReportingDescriptor const& reportingDescriptor) { Json::Value rd(Json::objectValue); @@ -80,7 +129,7 @@ Json::Value GetJson(Result const& result) Json::Value resultJson(Json::objectValue); if (result.Message) { - resultJson["message"]["text"] = *result.Message; + resultJson["message"] = cmSarif::GetJson(*result.Message); } if (result.Level) { @@ -98,6 +147,14 @@ Json::Value GetJson(Result const& result) resultJson["locations"][0] = cmSarif::GetJson(*result.Location); } + if (!result.Stacks.empty()) { + Json::Value stacks(Json::arrayValue); + for (auto const& stack : result.Stacks) { + stacks.append(cmSarif::GetJson(stack)); + } + resultJson["stacks"] = stacks; + } + return resultJson; } @@ -125,11 +182,21 @@ Json::Value GetJson(Run const& run) { Json::Value runJson(Json::objectValue); runJson["tool"] = cmSarif::GetJson(run.Tool); + + if (!run.OriginalUriBaseIds.empty()) { + Json::Value uriBaseIds(Json::objectValue); + for (auto const& base : run.OriginalUriBaseIds) { + uriBaseIds[base.first] = cmSarif::GetJson(base.second); + } + runJson["originalUriBaseIds"] = uriBaseIds; + } + Json::Value results(Json::arrayValue); for (auto const& result : run.Results) { results.append(cmSarif::GetJson(result)); } runJson["results"] = results; + return runJson; } diff --git a/Source/cmSarif.h b/Source/cmSarif.h index b0883d633f..c18bad07c3 100644 --- a/Source/cmSarif.h +++ b/Source/cmSarif.h @@ -2,9 +2,12 @@ #include #include +#include #include #include +#include +#include #include @@ -25,6 +28,14 @@ enum class ResultSeverityLevel Json::Value GetJson(ResultSeverityLevel level); +/// @brief SARIF message object (§3.11) +struct Message +{ + std::string Text; +}; + +Json::Value GetJson(Message const& message); + /// @brief SARIF artifactLocation object (§3.4) struct ArtifactLocation { @@ -51,14 +62,55 @@ struct PhysicalLocation Json::Value GetJson(PhysicalLocation const& physicalLocation); +/// @brief Suggested values for the logical location `kind` property (§3.33.7) +/// +/// The SARIF-recommended terminology for identifying the type of construct at +/// the associated location. This namespace is for defining the SARIF-specified +/// vocabulary only (although the actual `kind` property can be any string). +namespace LocationKind { +cm::string_view const Function = "function"_s; +} + +/// @brief SARIF logicalLocation object (§3.33) +struct LogicalLocation +{ + std::string Name; + + /// @brief The type of construct identified by the logical location + /// + /// The value should be from the suggestions in §3.33.7 but can be any string + /// if none of the specified suggestions apply. See `cmSarif::LocationKind` + /// for the suggested values. + cm::string_view Kind; +}; + +Json::Value GetJson(LogicalLocation const& logicalLocation); + /// @brief SARIF location object (§3.28) struct Location { PhysicalLocation Physical; + std::vector Logical; + cm::optional Message; }; Json::Value GetJson(Location const& location); +struct StackFrame +{ + cm::optional Location; + std::vector Parameters; +}; + +Json::Value GetJson(StackFrame const& stackFrame); + +struct Stack +{ + std::vector Frames; +}; + +Json::Value GetJson(Stack const& stack); + /// @brief A result reported by a run of a static analysis tool /// /// This is the data model for results in a SARIF log. Typically, a result only @@ -66,11 +118,14 @@ Json::Value GetJson(Location const& location); struct Result { /// @brief The message text of the result (required if no rule index) - cm::optional Message; + cm::optional Message; /// @brief The location of the result (optional) cm::optional Location; + /// @brief Call stacks related to the result (optional) + std::vector Stacks; + /// @brief The severity level of the result (optional) cm::optional Level; @@ -86,7 +141,7 @@ Json::Value GetJson(Result const& result); /// @brief A reporting descriptor provides information about an analysis result /// /// Reporting descriptors (SARIF specification section 3.49) provide -/// information about categories of reporting items and is used to define +/// information about categories of reporting items and are used to define /// rules and taxa. struct ReportingDescriptor { @@ -116,6 +171,7 @@ struct Run { cmSarif::Tool Tool; std::vector Results; + std::unordered_map OriginalUriBaseIds; }; Json::Value GetJson(Run const& run); diff --git a/Tests/RunCMake/SarifOutput/GenerateSarifResults-Included.cmake b/Tests/RunCMake/SarifOutput/GenerateSarifResults-Included.cmake new file mode 100644 index 0000000000..a999defb47 --- /dev/null +++ b/Tests/RunCMake/SarifOutput/GenerateSarifResults-Included.cmake @@ -0,0 +1 @@ +message(WARNING "Warning from an included file") diff --git a/Tests/RunCMake/SarifOutput/GenerateSarifResults-expected.sarif b/Tests/RunCMake/SarifOutput/GenerateSarifResults-expected.sarif index c1f1fb5645..eecdd696f8 100644 --- a/Tests/RunCMake/SarifOutput/GenerateSarifResults-expected.sarif +++ b/Tests/RunCMake/SarifOutput/GenerateSarifResults-expected.sarif @@ -7,10 +7,16 @@ "level": "warning", "locations": [ { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], "physicalLocation": { "artifactLocation": { "uri": "GenerateSarifResults.cmake", - "uriBaseId": "PATH:" + "uriBaseId": "CMAKE_SOURCE_DIR" }, "region": { "startLine": 2 @@ -22,16 +28,66 @@ "text": "Example warning message" }, "ruleId": "CMake.Warning", - "ruleIndex": 0 + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 2 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] }, { "level": "warning", "locations": [ { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], "physicalLocation": { "artifactLocation": { "uri": "GenerateSarifResults.cmake", - "uriBaseId": "PATH:" + "uriBaseId": "CMAKE_SOURCE_DIR" }, "region": { "startLine": 5 @@ -43,16 +99,66 @@ "text": "A second example warning message" }, "ruleId": "CMake.Warning", - "ruleIndex": 0 + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 5 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] }, { "level": "warning", "locations": [ { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], "physicalLocation": { "artifactLocation": { "uri": "GenerateSarifResults.cmake", - "uriBaseId": "PATH:" + "uriBaseId": "CMAKE_SOURCE_DIR" }, "region": { "startLine": 11 @@ -64,16 +170,66 @@ "text": "Example author warning message" }, "ruleId": "CMake.Author", - "ruleIndex": 1 + "ruleIndex": 1, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] }, { "level": "error", "locations": [ { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], "physicalLocation": { "artifactLocation": { "uri": "GenerateSarifResults.cmake", - "uriBaseId": "PATH:" + "uriBaseId": "CMAKE_SOURCE_DIR" }, "region": { "startLine": 16 @@ -85,9 +241,415 @@ "text": "Another example author warning message" }, "ruleId": "CMake.Author", - "ruleIndex": 1 + "ruleIndex": 1, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 16 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] + }, + { + "level": "warning", + "locations": [ + { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 20 + } + } + } + ], + "message": { + "text": "Warning from a nested function call" + }, + "ruleId": "CMake.Warning", + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 20 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "a" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "b" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 27 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] + }, + { + "level": "warning", + "locations": [ + { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults-Included.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 1 + } + } + } + ], + "message": { + "text": "Warning from an included file" + }, + "ruleId": "CMake.Warning", + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults-Included.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 1 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 30 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] + }, + { + "level": "warning", + "locations": [ + { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 38 + } + } + } + ], + "message": { + "text": "Warning from a variable_watch callback" + }, + "ruleId": "CMake.Warning", + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 38 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "set" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 42 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] + }, + { + "level": "error", + "locations": [ + { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 46 + } + } + } + ], + "message": { + "text": "Encabulator: a deferred error" + }, + "ruleId": "CMake.FatalError", + "ruleIndex": 2, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "GenerateSarifResults.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 46 + } + } + } + }, + { + "location": { + "message": { + "text": "DEFERRED" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + } + } + } + } + ] + } + ] } ], + "originalUriBaseIds": { + "CMAKE_BINARY_DIR": { + "uri": "file://PATH:/" + }, + "CMAKE_SOURCE_DIR": { + "uri": "file://PATH:/" + } + }, "tool": { "driver": { "name": "CMake", @@ -99,6 +661,10 @@ { "id": "CMake.Author", "name": "CMD_AUTHOR" + }, + { + "id": "CMake.FatalError", + "name": "CMake Error" } ], "version": "" diff --git a/Tests/RunCMake/SarifOutput/GenerateSarifResults-stderr.txt b/Tests/RunCMake/SarifOutput/GenerateSarifResults-stderr.txt index 8a2d98a1c2..e586a82e3f 100644 --- a/Tests/RunCMake/SarifOutput/GenerateSarifResults-stderr.txt +++ b/Tests/RunCMake/SarifOutput/GenerateSarifResults-stderr.txt @@ -18,4 +18,29 @@ CMake Error \(author\) at GenerateSarifResults\.cmake:16 \(message\): Another example author warning message Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) -This error is for project developers\. Use -Wno-error=author to suppress it\.$ +This error is for project developers\. Use -Wno-error=author to suppress it\. ++ +CMake Warning at GenerateSarifResults\.cmake:20 \(message\): + Warning from a nested function call +Call Stack \(most recent call first\): + GenerateSarifResults\.cmake:24 \(a\) + GenerateSarifResults\.cmake:27 \(b\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Warning at GenerateSarifResults-Included\.cmake:1 \(message\): + Warning from an included file +Call Stack \(most recent call first\): + GenerateSarifResults\.cmake:30 \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Warning at GenerateSarifResults\.cmake:38 \(message\): + Warning from a variable_watch callback +Call Stack \(most recent call first\): + GenerateSarifResults\.cmake:[0-9]+ \(warn_callback\) + GenerateSarifResults\.cmake:42 \(set\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at GenerateSarifResults\.cmake:46 \(message\): + Encabulator: a deferred error +Call Stack \(most recent call first\): + CMakeLists\.txt:DEFERRED$ diff --git a/Tests/RunCMake/SarifOutput/GenerateSarifResults.cmake b/Tests/RunCMake/SarifOutput/GenerateSarifResults.cmake index 0b414fcda0..14ba5be64d 100644 --- a/Tests/RunCMake/SarifOutput/GenerateSarifResults.cmake +++ b/Tests/RunCMake/SarifOutput/GenerateSarifResults.cmake @@ -14,3 +14,33 @@ message(AUTHOR_WARNING "Example author warning message") # Change it and issue another one cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) message(AUTHOR_WARNING "Another example author warning message") + +# Define and call some functions to test stack reporting +function(a) + message(WARNING "Warning from a nested function call") +endfunction() + +function(b) + a() +endfunction() + +b() + +# Include another file that generates a warning +include("${CMAKE_CURRENT_LIST_DIR}/GenerateSarifResults-Included.cmake") + +# variable_watch and deferred calls add placeholders to the CMake backtrace. +# Placeholders on the stack are prone to accidental reporting as a stack frame +# or line number. Ensure location info is reported but the stack is not +# polluted with placeholders. + +function(warn_callback variable access value file stack) + message(WARNING "Warning from a variable_watch callback") +endfunction() + +variable_watch(x warn_callback) +set(x "x_value") + +# A result from a deferred call should report the location it was deferred from +# and the list file that finished processing and executed the deferred call. +cmake_language(DEFER CALL message SEND_ERROR "Encabulator: a deferred error") diff --git a/Tests/RunCMake/SarifOutput/ProjectFatalError-expected.sarif b/Tests/RunCMake/SarifOutput/ProjectFatalError-expected.sarif index 0c7df3025f..2f9463b828 100644 --- a/Tests/RunCMake/SarifOutput/ProjectFatalError-expected.sarif +++ b/Tests/RunCMake/SarifOutput/ProjectFatalError-expected.sarif @@ -7,10 +7,16 @@ "level": "error", "locations": [ { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], "physicalLocation": { "artifactLocation": { "uri": "ProjectFatalError.cmake", - "uriBaseId": "PATH:" + "uriBaseId": "CMAKE_SOURCE_DIR" }, "region": { "startLine": 1 @@ -22,9 +28,61 @@ "text": "Example error" }, "ruleId": "CMake.FatalError", - "ruleIndex": 0 + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "ProjectFatalError.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 1 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] } ], + "originalUriBaseIds": { + "CMAKE_BINARY_DIR": { + "uri": "file://PATH:/" + }, + "CMAKE_SOURCE_DIR": { + "uri": "file://PATH:/" + } + }, "tool": { "driver": { "name": "CMake", diff --git a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-check.cmake b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-check.cmake index 6b26efec5c..4a23ea2f8d 100644 --- a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-check.cmake +++ b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-check.cmake @@ -1,5 +1,6 @@ include("${CMAKE_CURRENT_LIST_DIR}/check-sarif.cmake") -# This test should produce the same output as GenerateSarifResults +# The result issued before CMAKE_EXPORT_SARIF was enabled should still be +# captured in the SARIF output check_sarif_output("${RunCMake_TEST_BINARY_DIR}/.cmake/sarif/cmake.sarif" - "${CMAKE_CURRENT_LIST_DIR}/GenerateSarifResults-expected.sarif") + "${CMAKE_CURRENT_LIST_DIR}/ToggleExportSarifVariable-expected.sarif") diff --git a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-expected.sarif b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-expected.sarif new file mode 100644 index 0000000000..436c6d4071 --- /dev/null +++ b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-expected.sarif @@ -0,0 +1,101 @@ +{ + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "runs": [ + { + "results": [ + { + "level": "warning", + "locations": [ + { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "ToggleExportSarifVariable.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 2 + } + } + } + ], + "message": { + "text": "Example warning message" + }, + "ruleId": "CMake.Warning", + "ruleIndex": 0, + "stacks": [ + { + "frames": [ + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "message" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "ToggleExportSarifVariable.cmake", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 2 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "kind": "function", + "name": "include" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "CMakeLists.txt", + "uriBaseId": "CMAKE_SOURCE_DIR" + }, + "region": { + "startLine": 3 + } + } + } + } + ] + } + ] + } + ], + "originalUriBaseIds": { + "CMAKE_BINARY_DIR": { + "uri": "file://PATH:/" + }, + "CMAKE_SOURCE_DIR": { + "uri": "file://PATH:/" + } + }, + "tool": { + "driver": { + "name": "CMake", + "rules": [ + { + "id": "CMake.Warning", + "name": "CMake Warning" + } + ], + "version": "" + } + } + } + ], + "version": "2.1.0" +} diff --git a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-result.txt b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-result.txt deleted file mode 100644 index d00491fd7e..0000000000 --- a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-stderr.txt b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-stderr.txt index 405923bf8f..8823493b90 100644 --- a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-stderr.txt +++ b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable-stderr.txt @@ -1,25 +1,4 @@ -^CMake Warning at GenerateSarifResults\.cmake:2 \(message\): +^CMake Warning at ToggleExportSarifVariable\.cmake:2 \(message\): Example warning message Call Stack \(most recent call first\): - ToggleExportSarifVariable\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Warning at GenerateSarifResults\.cmake:5 \(message\): - A second example warning message -Call Stack \(most recent call first\): - ToggleExportSarifVariable\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Warning \(author\) at GenerateSarifResults\.cmake:11 \(message\): - Example author warning message -Call Stack \(most recent call first\): - ToggleExportSarifVariable\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -This warning is for project developers\. Use -Wno-author to suppress it\. -+ -CMake Error \(author\) at GenerateSarifResults\.cmake:16 \(message\): - Another example author warning message -Call Stack \(most recent call first\): - ToggleExportSarifVariable\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -This error is for project developers\. Use -Wno-error=author to suppress it\.$ + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable.cmake b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable.cmake index 19c0a6e2b2..df44c7dc7c 100644 --- a/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable.cmake +++ b/Tests/RunCMake/SarifOutput/ToggleExportSarifVariable.cmake @@ -1,6 +1,3 @@ -# Generate potential SARIF results -include("${CMAKE_CURRENT_LIST_DIR}/GenerateSarifResults.cmake") - -# Enable SARIF logging at the end for the most behavior coverage -# All results should be captured regardless of when enabled +# Enable logging after generating a result to ensure it is still captured. +message(WARNING "Example warning message") set(CMAKE_EXPORT_SARIF ON CACHE BOOL "Export SARIF results" FORCE)