diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h --- a/clang/include/clang/Basic/Sarif.h +++ b/clang/include/clang/Basic/Sarif.h @@ -31,6 +31,7 @@ #ifndef LLVM_CLANG_BASIC_SARIF_H #define LLVM_CLANG_BASIC_SARIF_H +#include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Version.h" #include "llvm/ADT/ArrayRef.h" @@ -145,6 +146,17 @@ enum class ThreadFlowImportance { Important, Essential, Unimportant }; +/// The level of severity associated with a \ref SarifResult. +/// +/// This enum excludes the level \c None since that is typically used for +/// proof-oriented tools. For clang's case that assumption is that if a +/// dianostic is emitted it usually implies that some change in code is being +/// requested. +/// +/// Reference: +/// 1. level property +enum class SarifResultLevel { Note, Warning, Error }; + /// A thread flow is a sequence of code locations that specify a possible path /// through a single thread of execution. /// A thread flow in SARIF is related to a code flow which describes @@ -257,6 +269,7 @@ std::string DiagnosticMessage; llvm::SmallVector Locations; llvm::SmallVector ThreadFlows; + llvm::Optional Level; SarifResult() = delete; explicit SarifResult(uint32_t RuleIdx) : RuleIdx(RuleIdx) {} @@ -293,6 +306,11 @@ ThreadFlows.assign(ThreadFlowResults.begin(), ThreadFlowResults.end()); return *this; } + + SarifResult setDiagnosticLevel(const SarifResultLevel &TheLevel) { + Level = TheLevel; + return *this; + } }; /// This class handles creating a valid SARIF document given various input diff --git a/clang/lib/Basic/Sarif.cpp b/clang/lib/Basic/Sarif.cpp --- a/clang/lib/Basic/Sarif.cpp +++ b/clang/lib/Basic/Sarif.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" @@ -180,6 +181,19 @@ llvm_unreachable("Fully covered switch is not so fully covered"); } +static StringRef resultLevelToStr(SarifResultLevel R) { + switch (R) { + case SarifResultLevel::Note: + return "note"; + case SarifResultLevel::Warning: + return "warning"; + case SarifResultLevel::Error: + return "error"; + } + llvm_unreachable("Potentially un-handled SarifResultLevel. " + "Is the switch not fully covered?"); +} + static json::Object createThreadFlowLocation(json::Object &&Location, const ThreadFlowImportance &Importance) { @@ -370,6 +384,8 @@ } if (!Result.ThreadFlows.empty()) Ret["codeFlows"] = json::Array{createCodeFlow(Result.ThreadFlows)}; + if (Result.Level.hasValue()) + Ret["level"] = resultLevelToStr(Result.Level.getValue()); json::Object &Run = getCurrentRun(); json::Array *Results = Run.getArray("results"); Results->emplace_back(std::move(Ret)); diff --git a/clang/unittests/Basic/SarifTest.cpp b/clang/unittests/Basic/SarifTest.cpp --- a/clang/unittests/Basic/SarifTest.cpp +++ b/clang/unittests/Basic/SarifTest.cpp @@ -226,7 +226,7 @@ TEST_F(SarifDocumentWriterTest, checkSerializingArtifacts) { // GIVEN: const std::string ExpectedOutput = - R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":40,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":14,"startColumn":14,"startLine":3}}}],"message":{"text":"expected ';' after top level declarator"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; + R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":40,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":14,"startColumn":14,"startLine":3}}}],"message":{"text":"expected ';' after top level declarator"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; SarifDocumentWriter Writer{SourceMgr}; const SarifRule &Rule = @@ -252,8 +252,10 @@ DiagLocs.push_back(SourceCSR); const SarifResult &Result = - SarifResult::create(RuleIdx).setLocations(DiagLocs).setDiagnosticMessage( - "expected ';' after top level declarator"); + SarifResult::create(RuleIdx) + .setLocations(DiagLocs) + .setDiagnosticMessage("expected ';' after top level declarator") + .setDiagnosticLevel(SarifResultLevel::Error); Writer.appendResult(Result); std::string Output = serializeSarifDocument(Writer.createDocument()); @@ -264,7 +266,7 @@ TEST_F(SarifDocumentWriterTest, checkSerializingCodeflows) { // GIVEN: const std::string ExpectedOutput = - R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":27,"location":{"index":1,"uri":"file:///test-header-1.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":30,"location":{"index":2,"uri":"file:///test-header-2.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":28,"location":{"index":3,"uri":"file:///test-header-3.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":41,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"codeFlows":[{"threadFlows":[{"locations":[{"importance":"essential","location":{"message":{"text":"Message #1"},"physicalLocation":{"artifactLocation":{"index":1},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}},{"importance":"important","location":{"message":{"text":"Message #2"},"physicalLocation":{"artifactLocation":{"index":2},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}},{"importance":"unimportant","location":{"message":{"text":"Message #3"},"physicalLocation":{"artifactLocation":{"index":3},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}}]}]}],"locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":8,"endLine":2,"startColumn":5,"startLine":2}}}],"message":{"text":"Redefinition of 'foo'"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; + R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":27,"location":{"index":1,"uri":"file:///test-header-1.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":30,"location":{"index":2,"uri":"file:///test-header-2.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":28,"location":{"index":3,"uri":"file:///test-header-3.h"},"mimeType":"text/plain","roles":["resultFile"]},{"length":41,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"codeFlows":[{"threadFlows":[{"locations":[{"importance":"essential","location":{"message":{"text":"Message #1"},"physicalLocation":{"artifactLocation":{"index":1},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}},{"importance":"important","location":{"message":{"text":"Message #2"},"physicalLocation":{"artifactLocation":{"index":2},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}},{"importance":"unimportant","location":{"message":{"text":"Message #3"},"physicalLocation":{"artifactLocation":{"index":3},"region":{"endColumn":8,"endLine":2,"startColumn":1,"startLine":1}}}}]}]}],"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":8,"endLine":2,"startColumn":5,"startLine":2}}}],"message":{"text":"Redefinition of 'foo'"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})"; const char *SourceText = "int foo = 0;\n" "int foo = 1;\n" @@ -309,10 +311,12 @@ // WHEN: A result containing code flows and diagnostic locations is added Writer.createRun("sarif test", "sarif test runner", "1.0.0"); unsigned RuleIdx = Writer.createRule(Rule); - const SarifResult &Result = SarifResult::create(RuleIdx) - .setLocations({DiagLoc}) - .setDiagnosticMessage("Redefinition of 'foo'") - .setThreadFlows(Threadflows); + const SarifResult &Result = + SarifResult::create(RuleIdx) + .setLocations({DiagLoc}) + .setDiagnosticMessage("Redefinition of 'foo'") + .setThreadFlows(Threadflows) + .setDiagnosticLevel(SarifResultLevel::Warning); Writer.appendResult(Result); std::string Output = serializeSarifDocument(Writer.createDocument());