cmSarif: Classify results by diagnostic category

Add additional rules to CMake's SARIF reporting for diagnostic categories.

Fixes: #27883
This commit is contained in:
Daniel Tierney
2026-06-29 14:12:31 -04:00
parent 4cc948ad08
commit 1c3d648fc5
7 changed files with 166 additions and 49 deletions
+82 -47
View File
@@ -11,7 +11,9 @@
#include <cm/string_view>
#include "cmsys/FStream.hxx"
#include "cmsys/String.h"
#include "cmDiagnostics.h"
#include "cmListFileCache.h"
#include "cmMessageType.h"
#include "cmMessenger.h"
@@ -72,6 +74,69 @@ cmSarif::Tool CreateCMakeTool()
return cmSarif::Tool{ cmDriver };
}
std::string RuleIdForMessageType(MessageType type,
cmDiagnosticCategory category)
{
cm::string_view name = cmDiagnostics::GetCategoryString(category);
if (!name.empty()) {
// Strip the "CMD_" prefix from the category name and convert to PascalCase
std::string sarifIdName;
bool nextWord = true;
for (char c : name.substr(4)) {
if (c == '_') {
nextWord = true;
continue;
}
if (nextWord) {
sarifIdName += c;
nextWord = false;
} else {
sarifIdName += cmsysString_tolower(c);
}
}
return cmStrCat("CMake.", sarifIdName);
}
// Fall back to message type if not a diagnostic
switch (type) {
case MessageType::FATAL_ERROR:
return "CMake.FatalError";
case MessageType::INTERNAL_ERROR:
return "CMake.InternalError";
case MessageType::WARNING:
return "CMake.Warning";
default:
return "";
}
}
cm::string_view NameForMessageType(MessageType type,
cmDiagnosticCategory category)
{
if (category != cmDiagnostics::CMD_NONE) {
return cmDiagnostics::GetCategoryString(category);
}
switch (type) {
case MessageType::FATAL_ERROR:
return "CMake Error";
case MessageType::INTERNAL_ERROR:
return "CMake Internal Error";
case MessageType::WARNING:
return "CMake Warning";
default:
return "";
}
}
cmSarif::ReportingDescriptor ReportingDescriptorForMessageType(
MessageType type, cmDiagnosticCategory category)
{
cmSarif::ReportingDescriptor rd;
rd.Id = RuleIdForMessageType(type, category);
rd.Name = NameForMessageType(type, category);
return rd;
};
cmSarif::ResultSeverityLevel SarifLevelFromMessageType(MessageType type)
{
switch (type) {
@@ -85,48 +150,6 @@ cmSarif::ResultSeverityLevel SarifLevelFromMessageType(MessageType type)
}
}
cm::string_view MessageRuleId(MessageType type)
{
switch (type) {
case MessageType::FATAL_ERROR:
return "CMake.FatalError";
case MessageType::INTERNAL_ERROR:
return "CMake.InternalError";
case MessageType::WARNING:
return "CMake.Warning";
case MessageType::MESSAGE:
return "CMake.Message";
case MessageType::LOG:
default:
return "CMake.Log";
}
}
cm::string_view MessageDisplayName(MessageType type)
{
switch (type) {
case MessageType::FATAL_ERROR:
return "CMake Error";
case MessageType::INTERNAL_ERROR:
return "CMake Internal Error";
case MessageType::WARNING:
return "CMake Warning";
case MessageType::MESSAGE:
return "CMake Message";
case MessageType::LOG:
default:
return "CMake Log";
}
}
cmSarif::ReportingDescriptor RuleForMessageType(MessageType type)
{
cmSarif::ReportingDescriptor rd;
rd.Id = MessageRuleId(type);
rd.Name = MessageDisplayName(type);
return rd;
}
} // namespace
cmCMakeSarifLogger::cmCMakeSarifLogger(cmake& cm)
@@ -178,20 +201,32 @@ bool cmCMakeSarifLogger::WriteFile(std::string const& path,
// Helper to add rules to the run as encountered in results and get their
// index for reporting
std::unordered_map<cm::string_view, std::size_t> ruleIndices;
auto use_rule = [&](MessageType t) {
cm::string_view category_name = MessageRuleId(t);
std::unordered_map<std::string, std::size_t> ruleIndices;
auto use_rule = [&](MessageType type, cmDiagnosticCategory category) {
std::string category_name = RuleIdForMessageType(type, category);
auto result = ruleIndices.emplace(category_name, 0);
if (result.second) {
result.first->second = run.Tool.Driver.Rules.size();
run.Tool.Driver.Rules.emplace_back(RuleForMessageType(t));
run.Tool.Driver.Rules.emplace_back(
ReportingDescriptorForMessageType(type, category));
}
return *result.first;
};
cmMessenger const& messenger = *this->CM.GetMessenger();
for (auto const& message : messenger.GetDisplayedMessages()) {
std::pair<cm::string_view, std::size_t> ruleInfo = use_rule(message.Type);
// SARIF should only emit diagnostic messages, not general messages/logs
switch (message.Type) {
case MessageType::MESSAGE:
case MessageType::LOG:
case MessageType::UNDEFINED:
continue;
default:
break;
}
std::pair<std::string, std::size_t> ruleInfo =
use_rule(message.Type, message.Category);
cmSarif::Result result;
result.RuleId = ruleInfo.first;
@@ -44,6 +44,48 @@
},
"ruleId": "CMake.Warning",
"ruleIndex": 0
},
{
"level": "warning",
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "GenerateSarifResults.cmake",
"uriBaseId": "PATH:<SOURCE_DIR>"
},
"region": {
"startLine": 11
}
}
}
],
"message": {
"text": "Example author warning message"
},
"ruleId": "CMake.Author",
"ruleIndex": 1
},
{
"level": "error",
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "GenerateSarifResults.cmake",
"uriBaseId": "PATH:<SOURCE_DIR>"
},
"region": {
"startLine": 16
}
}
}
],
"message": {
"text": "Another example author warning message"
},
"ruleId": "CMake.Author",
"ruleIndex": 1
}
],
"tool": {
@@ -53,6 +95,10 @@
{
"id": "CMake.Warning",
"name": "CMake Warning"
},
{
"id": "CMake.Author",
"name": "CMD_AUTHOR"
}
],
"version": "<IGNORE>"
@@ -0,0 +1 @@
1
@@ -6,4 +6,16 @@ Call Stack \(most recent call first\):
CMake Warning at GenerateSarifResults\.cmake:5 \(message\):
A second example warning message
Call Stack \(most recent call first\):
CMakeLists\.txt:[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\):
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\):
CMakeLists\.txt:[0-9]+ \(include\)
This error is for project developers\. Use -Wno-error=author to suppress it\.$
@@ -6,3 +6,11 @@ message(WARNING "A second example warning message")
# Status message should not be logged
message(STATUS "Example status message")
# Test diagnostic reporting
message(AUTHOR_WARNING "Example author warning message")
# Diagnostic results should include a level specific to each reported item
# Change it and issue another one
cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR)
message(AUTHOR_WARNING "Another example author warning message")
@@ -0,0 +1 @@
1
@@ -8,4 +8,18 @@ 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\)$
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\.$