[clang] [clang-tools-extra] [clang-tidy] Add SARIF output to clang-tidy (PR #219182)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 12 03:12:35 PDT 2026
================
@@ -195,6 +211,98 @@ class ErrorReporter {
}
for (const auto &Note : Error.Notes)
reportNote(Note);
+
+ if (SarifWriter)
+ exportSarifResult(Error, Loc);
+ }
+
+ CharSourceRange getNoteRange(const tooling::DiagnosticMessage &Note) {
+ if (!Note.Ranges.empty())
+ return getRange(Note.Ranges.front());
+ const SourceLocation Loc = getLocation(Note.FilePath, Note.FileOffset);
+ if (Loc.isInvalid())
+ return {};
+ return getOffsetRange(SourceMgr, Loc);
+ }
+
+ SmallVector<ThreadFlow, 8> createThreadFlows(const ClangTidyError &Error) {
+ SmallVector<ThreadFlow, 8> Flows;
+ if (!StringRef(Error.DiagnosticName).starts_with("clang-analyzer-"))
+ return Flows;
+
+ Flows.reserve(Error.Notes.size());
+ for (const tooling::DiagnosticMessage &Note : Error.Notes) {
+ Flows.push_back(ThreadFlow::create()
+ .setRange(getNoteRange(Note))
+ .setMessage(Note.Message));
+ }
+ return Flows;
+ }
+
+ void exportSarifResult(const ClangTidyError &Error,
+ const SourceLocation Loc) {
+ assert(SarifWriter &&
+ "SarifWriter must be initialized to export SARIF results");
+
+ const auto [RuleIndexEntryIt, Inserted] =
+ SarifRuleIdx.try_emplace(Error.DiagnosticName, 0);
+ size_t &RuleIndex = RuleIndexEntryIt->second;
+
+ const DiagnosticsEngine::Level EffectiveLevel =
+ Error.IsWarningAsError
+ ? DiagnosticsEngine::Error
+ : static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
+
+ if (Inserted) {
+ const StringRef Name = Error.DiagnosticName;
+ SarifRule Rule = SarifRule::create().setRuleId(Name).setName(Name);
+ Rule = addDiagnosticLevelToRule(Rule, EffectiveLevel);
+ if (SarifWriter)
+ RuleIndex = SarifWriter->createRule(Rule);
+ }
+
+ SarifResult Result =
+ SarifResult::create(RuleIndex)
+ .setDiagnosticMessage(Error.Message.Message)
+ .setDiagnosticLevel(getSarifResultLevel(EffectiveLevel))
+ .addLocations(getResultRanges(Error, Loc));
+
+ if (StringRef(Error.DiagnosticName).starts_with("clang-analyzer-")) {
+ Result = Result.setThreadFlows(createThreadFlows(Error));
+ } else {
+ for (const tooling::DiagnosticMessage &Note : Error.Notes) {
+ const CharSourceRange Range = getNoteRange(Note);
+ if (Range.isValid())
+ Result = Result.addRelatedLocations(Range, Note.Message);
+ }
+ }
+ if (SarifWriter)
+ SarifWriter->appendResult(Result);
+ }
+
+ CharSourceRange getOffsetRange(SourceManager &SM, SourceLocation Loc) {
+ const std::pair<FileID, unsigned> &DecomposedLoc = SM.getDecomposedLoc(Loc);
+ const FileID FileId = DecomposedLoc.first;
+ const unsigned Offset = DecomposedLoc.second;
+ return CharSourceRange::getCharRange(
+ Loc, Loc.getLocWithOffset(Offset < SM.getFileIDSize(FileId) ? 1 : 0));
+ }
+
+ SmallVector<CharSourceRange, 4> getResultRanges(const ClangTidyError &Error,
+ SourceLocation Loc) {
+ SmallVector<CharSourceRange, 4> Ranges;
+ Ranges.reserve(Error.Message.Ranges.size());
+ for (const FileByteRange &FBR : Error.Message.Ranges)
----------------
matthew-j-code wrote:
Thanks, I've changed this so the results location is always the diagnostic Loc, and notes use their own location. This leaves highlight ranges unrepresented when Loc is valid, I'd propose keeping them separately as an [annotations](https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790926:~:text=to%20the%20location.-,3.28.6%20annotations%20property,-A%20location%20object) property. Ranges in another file would fall back to relatedLocations, since an annotation can't point to a different file.
This requires extensions to Sarif.cpp/h as SarifDocumentWriter does not support annotations at the moment.
https://github.com/llvm/llvm-project/pull/219182
More information about the cfe-commits
mailing list