https://github.com/dbartol updated https://github.com/llvm/llvm-project/pull/168153
>From f23857c7df24ef166aa0bb4a90e2ba13e5e49bdc Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo <[email protected]> Date: Fri, 14 Nov 2025 17:45:25 -0500 Subject: [PATCH 1/4] [clang] [diagnostics] Stable IDs for Clang diagnostics SARIF diagnostics require that each rule have a stable `id` property to identify that rule across runs, even when the compiler or analysis tool has changed. We were previously setting the `id` property to the numeric value of the enum value for that diagnostic within the Clang implementation; this value changes whenever an unrelated diagnostic is inserted or removed earlier in the list. This change sets the `id` property to the _text_ of that same enum value. This value would only change if someone renames the enum value for that diagnostic, which should happen much less frequently than renumbering. For now, we will just assume that renaming happens infrequently enough that existing consumers of SARIF will not notice. In the future, we could take advantage of SARIF's support for `deprecatedIds`, which let a rule specify the IDs by which it was previously known. This would let us rename, split, or combine diagnostics while still being able to correlate the new diagnostic IDs with older SARIF logs and/or suppressions. Nothing in this change affects how warnings are configured on the command line or in `#pragma clang diagnostic`. Those still use warning groups, not the stable IDs. --- clang/include/clang/Basic/DiagnosticIDs.h | 3 ++ clang/lib/Basic/DiagnosticIDs.cpp | 45 ++++++++++++++++++++++- clang/lib/Frontend/SARIFDiagnostic.cpp | 4 +- clang/test/Frontend/sarif-diagnostics.cpp | 36 +++++++++--------- 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 06446cf580389..9fc49325205a2 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -332,6 +332,9 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> { /// Given a diagnostic ID, return a description of the issue. StringRef getDescription(unsigned DiagID) const; + /// Given a diagnostic ID, return the stable ID of the diagnostic. + std::string getStableID(unsigned DiagID) const; + /// Return true if the unmapped diagnostic levelof the specified /// diagnostic ID is a Warning or Extension. /// diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index a1d9d0f34d20d..e3903a3edadfd 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -51,6 +51,22 @@ const StaticDiagInfoDescriptionStringTable StaticDiagInfoDescriptions = { #undef DIAG }; +struct StaticDiagInfoStableIDStringTable { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + char ENUM##_id[sizeof(#ENUM)]; +#include "clang/Basic/AllDiagnosticKinds.inc" +#undef DIAG +}; + +const StaticDiagInfoStableIDStringTable StaticDiagInfoStableIDs = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + #ENUM, +#include "clang/Basic/AllDiagnosticKinds.inc" +#undef DIAG +}; + extern const StaticDiagInfoRec StaticDiagInfo[]; // Stored separately from StaticDiagInfoRec to pack better. Otherwise, @@ -63,6 +79,14 @@ const uint32_t StaticDiagInfoDescriptionOffsets[] = { #undef DIAG }; +const uint32_t StaticDiagInfoStableIDOffsets[] = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + offsetof(StaticDiagInfoStableIDStringTable, ENUM##_id), +#include "clang/Basic/AllDiagnosticKinds.inc" +#undef DIAG +}; + enum DiagnosticClass { CLASS_NOTE = DiagnosticIDs::CLASS_NOTE, CLASS_REMARK = DiagnosticIDs::CLASS_REMARK, @@ -95,6 +119,7 @@ struct StaticDiagInfoRec { uint16_t Deferrable : 1; uint16_t DescriptionLen; + uint16_t StableIDLen; unsigned getOptionGroupIndex() const { return OptionGroupIndex; @@ -107,6 +132,14 @@ struct StaticDiagInfoRec { return StringRef(&Table[StringOffset], DescriptionLen); } + StringRef getStableID() const { + size_t MyIndex = this - &StaticDiagInfo[0]; + uint32_t StringOffset = StaticDiagInfoStableIDOffsets[MyIndex]; + const char *Table = + reinterpret_cast<const char *>(&StaticDiagInfoStableIDs); + return StringRef(&Table[StringOffset], StableIDLen); + } + diag::Flavor getFlavor() const { return Class == CLASS_REMARK ? diag::Flavor::Remark : diag::Flavor::WarningOrError; @@ -159,7 +192,8 @@ const StaticDiagInfoRec StaticDiagInfo[] = { SHOWINSYSMACRO, \ GROUP, \ DEFERRABLE, \ - STR_SIZE(DESC, uint16_t)}, + STR_SIZE(DESC, uint16_t), \ + STR_SIZE(#ENUM, uint16_t)}, #include "clang/Basic/DiagnosticCommonKinds.inc" #include "clang/Basic/DiagnosticDriverKinds.inc" #include "clang/Basic/DiagnosticFrontendKinds.inc" @@ -434,6 +468,15 @@ StringRef DiagnosticIDs::getDescription(unsigned DiagID) const { return CustomDiagInfo->getDescription(DiagID).GetDescription(); } +/// getIDString - Given a diagnostic ID, return the stable ID of the diagnostic. +std::string DiagnosticIDs::getStableID(unsigned DiagID) const { + if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) + return Info->getStableID().str(); + assert(CustomDiagInfo && "Invalid CustomDiagInfo"); + // TODO: Stable IDs for custom diagnostics? + return std::to_string(DiagID); +} + static DiagnosticIDs::Level toLevel(diag::Severity SV) { switch (SV) { case diag::Severity::Ignored: diff --git a/clang/lib/Frontend/SARIFDiagnostic.cpp b/clang/lib/Frontend/SARIFDiagnostic.cpp index ac27d7480de3e..ac72a7af05429 100644 --- a/clang/lib/Frontend/SARIFDiagnostic.cpp +++ b/clang/lib/Frontend/SARIFDiagnostic.cpp @@ -46,7 +46,9 @@ void SARIFDiagnostic::emitDiagnosticMessage( if (!Diag) return; - SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID())); + std::string StableID = + Diag->getDiags()->getDiagnosticIDs()->getStableID(Diag->getID()); + SarifRule Rule = SarifRule::create().setRuleId(StableID); Rule = addDiagnosticLevelToRule(Rule, Level); diff --git a/clang/test/Frontend/sarif-diagnostics.cpp b/clang/test/Frontend/sarif-diagnostics.cpp index 767c5802ca13d..3f7adb80c67fd 100644 --- a/clang/test/Frontend/sarif-diagnostics.cpp +++ b/clang/test/Frontend/sarif-diagnostics.cpp @@ -34,35 +34,35 @@ void f1(t1 x, t1 y) { // Omit filepath to llvm project directory // CHECK: test/Frontend/sarif-diagnostics.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results": // CHECK: [{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":1,"startColumn":1,"startLine":12}}}],"message":{"text":"'main' must return 'int'"},"ruleId":"{{[0-9]+}}","ruleIndex":0}, +// CHECK: {"endColumn":1,"startColumn":1,"startLine":12}}}],"message":{"text":"'main' must return 'int'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":0}, // CHECK: {"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// // CHECK: {"endColumn":11,"startColumn":11,"startLine":13}}}],"message":{"text":"use of undeclared identifier -// CHECK: 'hello'"},"ruleId":"{{[0-9]+}}","ruleIndex":1},{"level":"error","locations":[{"physicalLocation":{"artifactLocation": +// CHECK: 'hello'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":1},{"level":"error","locations":[{"physicalLocation":{"artifactLocation": // CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":17,"startColumn":17,"startLine":15}}}],"message":{"text":"invalid digit 'a' in decimal -// CHECK: constant"},"ruleId":"{{[0-9]+}}","ruleIndex":2},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation": +// CHECK: constant"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":2},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation": // CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":5,"startColumn":5,"startLine":19}}}],"message":{"text":"misleading indentation; statement is not part -// CHECK: of the previous 'if'"},"ruleId":"{{[0-9]+}}","ruleIndex":3},{"level":"note","locations":[{"physicalLocation":{"artifactLocation": +// CHECK: of the previous 'if'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":3},{"level":"note","locations":[{"physicalLocation":{"artifactLocation": // CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":3,"startColumn":3,"startLine":17}}}],"message":{"text":"previous statement is -// CHECK: here"},"ruleId":"{{[0-9]+}}","ruleIndex":4},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation": +// CHECK: here"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":4},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation": // CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":10,"startColumn":10,"startLine":18}}}],"message":{"text":"unused variable -// CHECK: 'Yes'"},"ruleId":"{{[0-9]+}}","ruleIndex":5},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// +// CHECK: 'Yes'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":5},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// // CHECK: {"endColumn":12,"startColumn":12,"startLine":21}}}],"message":{"text":"use of undeclared identifier -// CHECK: 'hi'"},"ruleId":"{{[0-9]+}}","ruleIndex":6},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// +// CHECK: 'hi'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":6},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// // CHECK: {"endColumn":1,"startColumn":1,"startLine":23}}}],"message":{"text":"extraneous closing brace -// CHECK: ('}')"},"ruleId":"{{[0-9]+}}","ruleIndex":7},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// +// CHECK: ('}')"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":7},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// // CHECK: {"endColumn":6,"endLine":27,"startColumn":5,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// // CHECK: {"endColumn":10,"endLine":27,"startColumn":9,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// // CHECK: {"endColumn":7,"startColumn":7,"startLine":27}}}],"message":{"text":"invalid operands to binary expression ('t1' and -// CHECK: 't1')"},"ruleId":"{{[0-9]+}}","ruleIndex":8}],"tool":{"driver":{"fullName":"","informationUri":"https://clang.llvm.org/docs/ +// CHECK: 't1')"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":8}],"tool":{"driver":{"fullName":"","informationUri":"https://clang.llvm.org/docs/ // CHECK: UsersManual.html","language":"en-US","name":"clang","rules":[{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"note","rank":-1},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[0-9]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"note","rank":-1},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": +// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": // CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription": -// CHECK: {"text":""},"id":"{{[0-9]+}}","name":""}],"version":"{{[0-9]+\.[0-9]+\.[0-9]+[^" ]*}}"}}}],"version":"2.1.0"} +// CHECK: {"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""}],"version":"{{[0-9]+\.[0-9]+\.[0-9]+[^" ]*}}"}}}],"version":"2.1.0"} // CHECK: 2 warnings and 6 errors generated. >From 402e6a80d69ee569d9399e964306598dbc9d1947 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo <[email protected]> Date: Wed, 3 Dec 2025 18:11:50 -0800 Subject: [PATCH 2/4] [clang] [diagnostics] Use `normalize_sarif` for Clang SARIF testing This change modifies the one existing Clang test for SARIF output to use the same `normalize_sarif` sed script that we use for diffing SARIF in the Clang Static Analyzer tests, rather than using a complicated set of `CHECK` statements. This did require changing the SARIF output to include line breaks. --- clang/lib/Frontend/SARIFDiagnosticPrinter.cpp | 2 +- clang/test/Analysis/lit.local.cfg | 19 +- .../sarif-diagnostics.cpp.sarif | 424 ++++++++++++++++++ clang/test/Frontend/sarif-diagnostics.cpp | 50 +-- clang/test/lit.cfg.py | 17 + 5 files changed, 448 insertions(+), 64 deletions(-) create mode 100644 clang/test/Frontend/Inputs/expected-sarif/sarif-diagnostics.cpp.sarif diff --git a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp index 988159693389b..72b796f8db798 100644 --- a/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/SARIFDiagnosticPrinter.cpp @@ -40,7 +40,7 @@ void SARIFDiagnosticPrinter::EndSourceFile() { assert(SARIFDiag && "SARIFDiagnostic has not been set."); Writer->endRun(); llvm::json::Value Value(Writer->createDocument()); - OS << "\n" << Value << "\n\n"; + OS << llvm::formatv("\n{0:2}\n\n", Value); OS.flush(); SARIFDiag.reset(); } diff --git a/clang/test/Analysis/lit.local.cfg b/clang/test/Analysis/lit.local.cfg index 03ab418a5a4f7..3bc2f94809c85 100644 --- a/clang/test/Analysis/lit.local.cfg +++ b/clang/test/Analysis/lit.local.cfg @@ -17,24 +17,7 @@ config.substitutions.append( ) ) -sed_cmd = "/opt/freeware/bin/sed" if "system-aix" in config.available_features else "sed" - -# Filtering command for testing SARIF output against reference output. -config.substitutions.append( - ( - "%normalize_sarif", - f"{sed_cmd} -r '%s;%s;%s;%s'" - % ( - # Replace version strings that are likely to change. - r's/"version": ".* version .*"/"version": "[clang version]"/', - r's/"version": "2.1.0"/"version": "[SARIF version]"/', - # Strip directories from file URIs - r's/"file:(\/+)([^"\/]+\/)*([^"]+)"/"file:\1[...]\/\3"/', - # Set "length" to -1 - r's/"length": [[:digit:]]+/"length": -1/' - ), - ) -) +# SARIF filtering is now shared with the rest of Clang in ../lit.cfg.py if not config.root.clang_staticanalyzer: config.unsupported = True diff --git a/clang/test/Frontend/Inputs/expected-sarif/sarif-diagnostics.cpp.sarif b/clang/test/Frontend/Inputs/expected-sarif/sarif-diagnostics.cpp.sarif new file mode 100644 index 0000000000000..21f06bf0a1b23 --- /dev/null +++ b/clang/test/Frontend/Inputs/expected-sarif/sarif-diagnostics.cpp.sarif @@ -0,0 +1,424 @@ +clang: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable] + +{ + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json", + "runs": [ + { + "artifacts": [ + { + "length": -1, + "location": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "mimeType": "text/plain", + "roles": [ + "resultFile" + ] + } + ], + "columnKind": "unicodeCodePoints", + "results": [ + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 1, + "startColumn": 1, + "startLine": 12 + } + } + } + ], + "message": { + "text": "'main' must return 'int'" + }, + "ruleId": "err_main_returns_nonint", + "ruleIndex": 0 + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 16, + "endLine": 13, + "startColumn": 11, + "startLine": 13 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 11, + "startColumn": 11, + "startLine": 13 + } + } + } + ], + "message": { + "text": "use of undeclared identifier 'hello'" + }, + "ruleId": "err_undeclared_var_use", + "ruleIndex": 1 + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 17, + "startColumn": 17, + "startLine": 15 + } + } + } + ], + "message": { + "text": "invalid digit 'a' in decimal constant" + }, + "ruleId": "err_invalid_digit", + "ruleIndex": 2 + }, + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 5, + "startColumn": 5, + "startLine": 19 + } + } + } + ], + "message": { + "text": "misleading indentation; statement is not part of the previous 'if'" + }, + "ruleId": "warn_misleading_indentation", + "ruleIndex": 3 + }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 3, + "startColumn": 3, + "startLine": 17 + } + } + } + ], + "message": { + "text": "previous statement is here" + }, + "ruleId": "note_previous_statement", + "ruleIndex": 4 + }, + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 13, + "endLine": 18, + "startColumn": 10, + "startLine": 18 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 10, + "startColumn": 10, + "startLine": 18 + } + } + } + ], + "message": { + "text": "unused variable 'Yes'" + }, + "ruleId": "warn_unused_variable", + "ruleIndex": 5 + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 14, + "endLine": 21, + "startColumn": 12, + "startLine": 21 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 12, + "startColumn": 12, + "startLine": 21 + } + } + } + ], + "message": { + "text": "use of undeclared identifier 'hi'" + }, + "ruleId": "err_undeclared_var_use", + "ruleIndex": 6 + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 1, + "startColumn": 1, + "startLine": 23 + } + } + } + ], + "message": { + "text": "extraneous closing brace ('}')" + }, + "ruleId": "err_extraneous_closing_brace", + "ruleIndex": 7 + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 6, + "endLine": 27, + "startColumn": 5, + "startLine": 27 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 10, + "endLine": 27, + "startColumn": 9, + "startLine": 27 + } + } + }, + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-diagnostics.cpp" + }, + "region": { + "endColumn": 7, + "startColumn": 7, + "startLine": 27 + } + } + } + ], + "message": { + "text": "invalid operands to binary expression ('t1' and 't1')" + }, + "ruleId": "err_typecheck_invalid_operands", + "ruleIndex": 8 + } + ], + "tool": { + "driver": { + "fullName": "", + "informationUri": "https://clang.llvm.org/docs/UsersManual.html", + "language": "en-US", + "name": "clang", + "rules": [ + { + "defaultConfiguration": { + "enabled": true, + "level": "error", + "rank": 50 + }, + "fullDescription": { + "text": "" + }, + "id": "err_main_returns_nonint", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "error", + "rank": 50 + }, + "fullDescription": { + "text": "" + }, + "id": "err_undeclared_var_use", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "error", + "rank": 50 + }, + "fullDescription": { + "text": "" + }, + "id": "err_invalid_digit", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "rank": -1 + }, + "fullDescription": { + "text": "" + }, + "id": "warn_misleading_indentation", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "note", + "rank": -1 + }, + "fullDescription": { + "text": "" + }, + "id": "note_previous_statement", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "rank": -1 + }, + "fullDescription": { + "text": "" + }, + "id": "warn_unused_variable", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "error", + "rank": 50 + }, + "fullDescription": { + "text": "" + }, + "id": "err_undeclared_var_use", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "error", + "rank": 50 + }, + "fullDescription": { + "text": "" + }, + "id": "err_extraneous_closing_brace", + "name": "" + }, + { + "defaultConfiguration": { + "enabled": true, + "level": "error", + "rank": 50 + }, + "fullDescription": { + "text": "" + }, + "id": "err_typecheck_invalid_operands", + "name": "" + } + ], + "version": "[clang version]" + } + } + } + ], + "version": "[SARIF version]" +} + +2 warnings and 6 errors generated. \ No newline at end of file diff --git a/clang/test/Frontend/sarif-diagnostics.cpp b/clang/test/Frontend/sarif-diagnostics.cpp index 3f7adb80c67fd..04cd19516fc0a 100644 --- a/clang/test/Frontend/sarif-diagnostics.cpp +++ b/clang/test/Frontend/sarif-diagnostics.cpp @@ -1,14 +1,14 @@ // RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif %s > %t 2>&1 || true -// RUN: FileCheck -dump-input=always %s --input-file=%t +// RUN: cat %t | %normalize_sarif | diff -U1 -b %S/Inputs/expected-sarif/sarif-diagnostics.cpp.sarif - // FIXME: this test is incredibly fragile because the `main()` function -// must be on line 12 in order for the CHECK lines to get the correct line -// number values. +// must be on line 12 in order for the line numbers in the SARIF output +// to match the expected values // // So these comment lines are being used to ensure the code below happens // to work properly for the test coverage, which as you can imagine, is not -// the best way to structure the test. We really need to introduce a better -// tool than FileCheck for diff'ing JSON output like SARIF. +// the best way to structure the test. We should consider having a way to +// tag line numbers in the test source to match in the SARIF output. void main() { int i = hello; @@ -26,43 +26,3 @@ struct t1 { }; void f1(t1 x, t1 y) { x + y; } - -// CHECK: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable] -// CHECK: {"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length": -// Omit exact length of this file -// CHECK: ,"location":{"index":0,"uri":"file:// -// Omit filepath to llvm project directory -// CHECK: test/Frontend/sarif-diagnostics.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results": -// CHECK: [{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":1,"startColumn":1,"startLine":12}}}],"message":{"text":"'main' must return 'int'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":0}, -// CHECK: {"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":11,"startColumn":11,"startLine":13}}}],"message":{"text":"use of undeclared identifier -// CHECK: 'hello'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":1},{"level":"error","locations":[{"physicalLocation":{"artifactLocation": -// CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":17,"startColumn":17,"startLine":15}}}],"message":{"text":"invalid digit 'a' in decimal -// CHECK: constant"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":2},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation": -// CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":5,"startColumn":5,"startLine":19}}}],"message":{"text":"misleading indentation; statement is not part -// CHECK: of the previous 'if'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":3},{"level":"note","locations":[{"physicalLocation":{"artifactLocation": -// CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":3,"startColumn":3,"startLine":17}}}],"message":{"text":"previous statement is -// CHECK: here"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":4},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation": -// CHECK: {"index":0,"uri":"file://{{.+}}"},"region":{"endColumn":10,"startColumn":10,"startLine":18}}}],"message":{"text":"unused variable -// CHECK: 'Yes'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":5},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":12,"startColumn":12,"startLine":21}}}],"message":{"text":"use of undeclared identifier -// CHECK: 'hi'"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":6},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":1,"startColumn":1,"startLine":23}}}],"message":{"text":"extraneous closing brace -// CHECK: ('}')"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":7},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":6,"endLine":27,"startColumn":5,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":10,"endLine":27,"startColumn":9,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:// -// CHECK: {"endColumn":7,"startColumn":7,"startLine":27}}}],"message":{"text":"invalid operands to binary expression ('t1' and -// CHECK: 't1')"},"ruleId":"{{[A-Za-z0-9_]+}}","ruleIndex":8}],"tool":{"driver":{"fullName":"","informationUri":"https://clang.llvm.org/docs/ -// CHECK: UsersManual.html","language":"en-US","name":"clang","rules":[{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"note","rank":-1},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""},{"defaultConfiguration": -// CHECK: {"enabled":true,"level":"error","rank":50},"fullDescription": -// CHECK: {"text":""},"id":"{{[A-Za-z0-9_]+}}","name":""}],"version":"{{[0-9]+\.[0-9]+\.[0-9]+[^" ]*}}"}}}],"version":"2.1.0"} -// CHECK: 2 warnings and 6 errors generated. diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 52b275c095475..58e0eb8b9597b 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -89,7 +89,24 @@ config.substitutions.append(("%PATH%", config.environment["PATH"])) +sed_cmd = "/opt/freeware/bin/sed" if "system-aix" in config.available_features else "sed" +# Filtering command for testing SARIF output against reference output. +config.substitutions.append( + ( + "%normalize_sarif", + f"{sed_cmd} -r '%s;%s;%s;%s'" + % ( + # Replace version strings that are likely to change. + r's/"version": "2.1.0"/"version": "[SARIF version]"/', + r's/"version": ".*[0-9]+\.[0-9]+\.[0-9]+.*"/"version": "[clang version]"/', + # Strip directories from file URIs + r's/"file:(\/+)([^"\/]+\/)*([^"]+)"/"file:\1[...]\/\3"/', + # Set "length" to -1 + r's/"length": [[:digit:]]+/"length": -1/' + ), + ) +) # For each occurrence of a clang tool name, replace it with the full path to # the build directory holding that tool. We explicitly specify the directories # to search to ensure that we get the tools just built and not some random >From 6ed63a1ca06d0144dc4e9c7e01ad7ef4d655b013 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo <[email protected]> Date: Thu, 4 Dec 2025 17:18:39 -0800 Subject: [PATCH 3/4] Fix formatting --- clang/test/lit.cfg.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 58e0eb8b9597b..8b969ef3b08c5 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -89,7 +89,9 @@ config.substitutions.append(("%PATH%", config.environment["PATH"])) -sed_cmd = "/opt/freeware/bin/sed" if "system-aix" in config.available_features else "sed" +sed_cmd = ( + "/opt/freeware/bin/sed" if "system-aix" in config.available_features else "sed" +) # Filtering command for testing SARIF output against reference output. config.substitutions.append( @@ -103,7 +105,7 @@ # Strip directories from file URIs r's/"file:(\/+)([^"\/]+\/)*([^"]+)"/"file:\1[...]\/\3"/', # Set "length" to -1 - r's/"length": [[:digit:]]+/"length": -1/' + r's/"length": [[:digit:]]+/"length": -1/', ), ) ) >From 987f8f5e172048bc73faccc001f9b85f1c90a81a Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo <[email protected]> Date: Mon, 8 Dec 2025 13:15:38 -0500 Subject: [PATCH 4/4] [clang] [diagnostics] `StableId` and `LegacyStableIds` properties for diagnostics This change adds tblgen support for two new properties on `Diagnostic` objects: - `StableId<id>` - Explicitly specifies the stable ID for a diagnostic. If not specified, the stable ID defaults to the name of the diagnostic's enum member as before. The stable ID is what gets emitted as the `id` property of the diagnostic's `Rule` object in SARIF. - `LegacyStableIds<[id1, id2, ...]> - Specifies a list of stable IDs by which this diagnostic was previously known. These are emitted in the `deprecatedIds` property of the diagnostic's `Rule` object in SARIF. This property is used when the diagnostic's stable ID is renamed, when a diagnostic is split into multiple diagnostics, or when multiple diagnostics are merged into a single diagnostic. Most of the code changes are in tblgen, in the code that loads the new properties from their static data tables, and in the SARIF emitter code. There were some further mechanical changes wherever the `DIAG` macro was invoked, since it now has two new parameters. To test these changes, I found a warning that was recently renamed, and added an `LegacyStableIds` list with the previous name, which is what we would have to do for any renamed warning in the future. I then added a test that ensures that when this warning is emitted, the SARIF has the right `id` and `deprecatedIds` properties. --- clang-tools-extra/clangd/Diagnostics.cpp | 3 +- clang/include/clang/Basic/CMakeLists.txt | 4 + clang/include/clang/Basic/Diagnostic.td | 22 +++ clang/include/clang/Basic/DiagnosticIDs.h | 3 + .../clang/Basic/DiagnosticSemaKinds.td | 11 +- clang/include/clang/Basic/Sarif.h | 7 + clang/lib/Basic/DiagnosticIDs.cpp | 79 +++++---- clang/lib/Basic/Sarif.cpp | 3 + clang/lib/Frontend/SARIFDiagnostic.cpp | 8 +- .../sarif-legacy-stable-ids.c.sarif | 76 +++++++++ clang/test/Frontend/sarif-legacy-stable-ids.c | 8 + clang/test/TableGen/DiagnosticBase.inc | 2 + clang/test/TableGen/deferred-diag.td | 10 +- clang/tools/diagtool/DiagnosticNames.cpp | 3 +- .../TableGen/ClangDiagnosticsEmitter.cpp | 152 +++++++++++++++++- clang/utils/TableGen/TableGen.cpp | 6 + clang/utils/TableGen/TableGenBackends.h | 2 + 17 files changed, 354 insertions(+), 45 deletions(-) create mode 100644 clang/test/Frontend/Inputs/expected-sarif/sarif-legacy-stable-ids.c.sarif create mode 100644 clang/test/Frontend/sarif-legacy-stable-ids.c diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp index bc605fda5b0ce..d5b5a00dd7a9b 100644 --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -53,7 +53,8 @@ namespace { const char *getDiagnosticCode(unsigned ID) { switch (ID) { #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROPU, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ case clang::diag::ENUM: \ return #ENUM; #include "clang/Basic/DiagnosticASTKinds.inc" diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index cfd165e6fa7e1..f6bd093250e0c 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -42,6 +42,10 @@ clang_tablegen(DiagnosticIndexName.inc -gen-clang-diags-index-name SOURCE Diagnostic.td TARGET ClangDiagnosticIndexName) +clang_tablegen(DiagnosticStableIDs.inc -gen-clang-diags-stable-ids + SOURCE Diagnostic.td + TARGET ClangDiagnosticStableIDs) + clang_tablegen(DiagnosticAllCompatIDs.inc -gen-clang-diags-compat-ids SOURCE Diagnostic.td diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 53b1db265ccd0..48d7311616370 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -65,6 +65,24 @@ class DiagGroup<string Name, list<DiagGroup> subgroups = [], code docs = [{}]> { class InGroup<DiagGroup G> { DiagGroup Group = G; } //class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; } +// Specifies an explicit Stable ID for a diagnostic, rather than the default one +// generated from the diagnostic's name. +class StableId<string Id> { string StableId = Id; } + +// Specifies a list of Stable IDs by which the diagnostic was previously known. +// If the Stable ID of a diagnostic is renamed, the previous Stable ID should be +// added to the LegacyStableIds list. +// +// If a diagnostic is split into two or more diagnostics for specific cases, the +// Stable ID of the original diagnostic should be added to the LegacyStableIds +// list of each of the new diagnostics created from it. +// +// If two or more diagnostics +// are merged into a single diagnostic, the Stable ID of each of the original +// diagnostics should be added to the LegacyStableIds list of the new (merged) +// diagnostic. +class LegacyStableIds<list<string> Ids> { list<string> LegacyStableIds = Ids; } + // This defines documentation for diagnostic groups. include "DiagnosticDocs.td" @@ -90,6 +108,10 @@ class Diagnostic<string summary, DiagClass DC, Severity defaultmapping> { Severity DefaultSeverity = defaultmapping; DiagGroup Group; string CategoryName = ""; + /// The default of "" means that the actual Stable ID should be generated from + /// the name of the diagnostic. + string StableId = ""; + list<string> LegacyStableIds = []; } class SFINAEFailure { diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 9fc49325205a2..09e2d12dd040e 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -335,6 +335,9 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> { /// Given a diagnostic ID, return the stable ID of the diagnostic. std::string getStableID(unsigned DiagID) const; + /// Given a diagnostic ID, return the previous stable IDs of the diagnostic. + llvm::SmallVector<StringRef, 4> getLegacyStableIDs(unsigned DiagID) const; + /// Return true if the unmapped diagnostic levelof the specified /// diagnostic ID is a Warning or Extension. /// diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e864475f22a1..e17ef6403d1d9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -415,11 +415,12 @@ def warn_arm_interrupt_save_fp_without_vfp_unit : Warning< InGroup<DiagGroup<"arm-interrupt-save-fp-no-vfp-unit">>; def err_arm_interrupt_called : Error< "interrupt service routine cannot be called directly">; -def warn_interrupt_signal_attribute_invalid : Warning< - "%select{MIPS|MSP430|RISC-V|AVR}0 '%select{interrupt|signal}1' " - "attribute only applies to functions that have " - "%select{no parameters|a 'void' return type}2">, - InGroup<IgnoredAttributes>; +def warn_interrupt_signal_attribute_invalid + : Warning<"%select{MIPS|MSP430|RISC-V|AVR}0 '%select{interrupt|signal}1' " + "attribute only applies to functions that have " + "%select{no parameters|a 'void' return type}2">, + InGroup<IgnoredAttributes>, + LegacyStableIds<["warn_interrupt_attribute_invalid"]>; def warn_riscv_repeated_interrupt_attribute : Warning< "repeated RISC-V 'interrupt' attribute">, InGroup<IgnoredAttributes>; def note_riscv_repeated_interrupt_attribute : Note< diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h index a88d1ee2965a9..975a1a0adfe67 100644 --- a/clang/include/clang/Basic/Sarif.h +++ b/clang/include/clang/Basic/Sarif.h @@ -261,6 +261,7 @@ class SarifRule { std::string Id; std::string Description; std::string HelpURI; + std::vector<std::string> DeprecatedIds; SarifReportingConfiguration DefaultConfiguration; SarifRule() : DefaultConfiguration(SarifReportingConfiguration::create()) {} @@ -288,6 +289,12 @@ class SarifRule { return *this; } + SarifRule + setDeprecatedIds(llvm::ArrayRef<llvm::StringRef> RuleDeprecatedIds) { + DeprecatedIds.assign(RuleDeprecatedIds.begin(), RuleDeprecatedIds.end()); + return *this; + } + SarifRule setDefaultConfiguration(const SarifReportingConfiguration &Configuration) { DefaultConfiguration = Configuration; diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index e3903a3edadfd..36add56de66c6 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -32,12 +32,17 @@ namespace { struct StaticDiagInfoRec; +#define GET_DIAG_STABLE_ID_ARRAYS +#include "clang/Basic/DiagnosticStableIDs.inc" +#undef GET_DIAG_STABLE_ID_ARRAYS + // Store the descriptions in a separate table to avoid pointers that need to // be relocated, and also decrease the amount of data needed on 64-bit // platforms. See "How To Write Shared Libraries" by Ulrich Drepper. struct StaticDiagInfoDescriptionStringTable { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ char ENUM##_desc[sizeof(DESC)]; #include "clang/Basic/AllDiagnosticKinds.inc" #undef DIAG @@ -45,35 +50,21 @@ struct StaticDiagInfoDescriptionStringTable { const StaticDiagInfoDescriptionStringTable StaticDiagInfoDescriptions = { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ DESC, #include "clang/Basic/AllDiagnosticKinds.inc" #undef DIAG }; -struct StaticDiagInfoStableIDStringTable { -#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ - char ENUM##_id[sizeof(#ENUM)]; -#include "clang/Basic/AllDiagnosticKinds.inc" -#undef DIAG -}; - -const StaticDiagInfoStableIDStringTable StaticDiagInfoStableIDs = { -#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ - #ENUM, -#include "clang/Basic/AllDiagnosticKinds.inc" -#undef DIAG -}; - extern const StaticDiagInfoRec StaticDiagInfo[]; // Stored separately from StaticDiagInfoRec to pack better. Otherwise, // StaticDiagInfoRec would have extra padding on 64-bit platforms. const uint32_t StaticDiagInfoDescriptionOffsets[] = { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ offsetof(StaticDiagInfoDescriptionStringTable, ENUM##_desc), #include "clang/Basic/AllDiagnosticKinds.inc" #undef DIAG @@ -81,8 +72,18 @@ const uint32_t StaticDiagInfoDescriptionOffsets[] = { const uint32_t StaticDiagInfoStableIDOffsets[] = { #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ - offsetof(StaticDiagInfoStableIDStringTable, ENUM##_id), + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ + STABLE_ID, +#include "clang/Basic/AllDiagnosticKinds.inc" +#undef DIAG +}; + +const uint32_t StaticDiagInfoLegacyStableIDStartOffsets[] = { +#define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ + LEGACY_STABLE_IDS, #include "clang/Basic/AllDiagnosticKinds.inc" #undef DIAG }; @@ -119,7 +120,6 @@ struct StaticDiagInfoRec { uint16_t Deferrable : 1; uint16_t DescriptionLen; - uint16_t StableIDLen; unsigned getOptionGroupIndex() const { return OptionGroupIndex; @@ -135,9 +135,19 @@ struct StaticDiagInfoRec { StringRef getStableID() const { size_t MyIndex = this - &StaticDiagInfo[0]; uint32_t StringOffset = StaticDiagInfoStableIDOffsets[MyIndex]; - const char *Table = - reinterpret_cast<const char *>(&StaticDiagInfoStableIDs); - return StringRef(&Table[StringOffset], StableIDLen); + return DiagStableIDs[StringOffset]; + } + + llvm::SmallVector<StringRef, 4> getLegacyStableIDs() const { + llvm::SmallVector<StringRef, 4> Result; + size_t MyIndex = this - &StaticDiagInfo[0]; + uint32_t StartOffset = StaticDiagInfoLegacyStableIDStartOffsets[MyIndex]; + for (uint32_t Offset = StartOffset; DiagLegacyStableIDs[Offset] != 0; + ++Offset) { + Result.push_back(DiagStableIDs[DiagLegacyStableIDs[Offset]]); + } + + return Result; } diag::Flavor getFlavor() const { @@ -180,7 +190,8 @@ VALIDATE_DIAG_SIZE(TRAP) const StaticDiagInfoRec StaticDiagInfo[] = { // clang-format off #define DIAG(ENUM, CLASS, DEFAULT_SEVERITY, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ { \ diag::ENUM, \ DEFAULT_SEVERITY, \ @@ -192,8 +203,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = { SHOWINSYSMACRO, \ GROUP, \ DEFERRABLE, \ - STR_SIZE(DESC, uint16_t), \ - STR_SIZE(#ENUM, uint16_t)}, + STR_SIZE(DESC, uint16_t)}, #include "clang/Basic/DiagnosticCommonKinds.inc" #include "clang/Basic/DiagnosticDriverKinds.inc" #include "clang/Basic/DiagnosticFrontendKinds.inc" @@ -468,7 +478,7 @@ StringRef DiagnosticIDs::getDescription(unsigned DiagID) const { return CustomDiagInfo->getDescription(DiagID).GetDescription(); } -/// getIDString - Given a diagnostic ID, return the stable ID of the diagnostic. +/// getStableID - Given a diagnostic ID, return the stable ID of the diagnostic. std::string DiagnosticIDs::getStableID(unsigned DiagID) const { if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) return Info->getStableID().str(); @@ -477,6 +487,17 @@ std::string DiagnosticIDs::getStableID(unsigned DiagID) const { return std::to_string(DiagID); } +/// getLegacyStableIDs - Given a diagnostic ID, return the previous stable IDs +/// of the diagnostic. +SmallVector<StringRef, 4> +DiagnosticIDs::getLegacyStableIDs(unsigned DiagID) const { + if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) + return Info->getLegacyStableIDs(); + assert(CustomDiagInfo && "Invalid CustomDiagInfo"); + // TODO: Stable IDs for custom diagnostics? + return {}; +} + static DiagnosticIDs::Level toLevel(diag::Severity SV) { switch (SV) { case diag::Severity::Ignored: diff --git a/clang/lib/Basic/Sarif.cpp b/clang/lib/Basic/Sarif.cpp index b3fb9a21249e9..e4eae95afce5f 100644 --- a/clang/lib/Basic/Sarif.cpp +++ b/clang/lib/Basic/Sarif.cpp @@ -283,6 +283,9 @@ void SarifDocumentWriter::endRun() { {"defaultConfiguration", std::move(Config)}}; if (!R.HelpURI.empty()) Rule["helpUri"] = R.HelpURI; + if (!R.DeprecatedIds.empty()) + Rule["deprecatedIds"] = json::Array(R.DeprecatedIds); + Rules.emplace_back(std::move(Rule)); } json::Object &Driver = *Tool.getObject("driver"); diff --git a/clang/lib/Frontend/SARIFDiagnostic.cpp b/clang/lib/Frontend/SARIFDiagnostic.cpp index ac72a7af05429..cb3a3fe714410 100644 --- a/clang/lib/Frontend/SARIFDiagnostic.cpp +++ b/clang/lib/Frontend/SARIFDiagnostic.cpp @@ -46,9 +46,11 @@ void SARIFDiagnostic::emitDiagnosticMessage( if (!Diag) return; - std::string StableID = - Diag->getDiags()->getDiagnosticIDs()->getStableID(Diag->getID()); - SarifRule Rule = SarifRule::create().setRuleId(StableID); + const auto &DiagnosticIDs = *(Diag->getDiags()->getDiagnosticIDs()); + std::string StableID = DiagnosticIDs.getStableID(Diag->getID()); + auto LegacyStableIDs = DiagnosticIDs.getLegacyStableIDs(Diag->getID()); + SarifRule Rule = + SarifRule::create().setRuleId(StableID).setDeprecatedIds(LegacyStableIDs); Rule = addDiagnosticLevelToRule(Rule, Level); diff --git a/clang/test/Frontend/Inputs/expected-sarif/sarif-legacy-stable-ids.c.sarif b/clang/test/Frontend/Inputs/expected-sarif/sarif-legacy-stable-ids.c.sarif new file mode 100644 index 0000000000000..903d141ffea24 --- /dev/null +++ b/clang/test/Frontend/Inputs/expected-sarif/sarif-legacy-stable-ids.c.sarif @@ -0,0 +1,76 @@ + +{ + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json", + "runs": [ + { + "artifacts": [ + { + "length": -1, + "location": { + "index": 0, + "uri": "file:///[...]/sarif-legacy-stable-ids.c" + }, + "mimeType": "text/plain", + "roles": [ + "resultFile" + ] + } + ], + "columnKind": "unicodeCodePoints", + "results": [ + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "index": 0, + "uri": "file:///[...]/sarif-legacy-stable-ids.c" + }, + "region": { + "endColumn": 32, + "startColumn": 32, + "startLine": 8 + } + } + } + ], + "message": { + "text": "AVR 'interrupt' attribute only applies to functions that have a 'void' return type" + }, + "ruleId": "warn_interrupt_signal_attribute_invalid", + "ruleIndex": 0 + } + ], + "tool": { + "driver": { + "fullName": "", + "informationUri": "https://clang.llvm.org/docs/UsersManual.html", + "language": "en-US", + "name": "clang", + "rules": [ + { + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "rank": -1 + }, + "deprecatedIds": [ + "warn_interrupt_attribute_invalid" + ], + "fullDescription": { + "text": "" + }, + "id": "warn_interrupt_signal_attribute_invalid", + "name": "" + } + ], + "version": "[clang version]" + } + } + } + ], + "version": "[SARIF version]" +} + +1 warning generated. \ No newline at end of file diff --git a/clang/test/Frontend/sarif-legacy-stable-ids.c b/clang/test/Frontend/sarif-legacy-stable-ids.c new file mode 100644 index 0000000000000..414e37ad694a3 --- /dev/null +++ b/clang/test/Frontend/sarif-legacy-stable-ids.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple avr-unknown-unknown -fsyntax-only -fdiagnostics-format sarif %s > %t 2>&1 || true +// RUN: cat %t | %normalize_sarif | diff -U1 -b %S/Inputs/expected-sarif/sarif-legacy-stable-ids.c.sarif - + +struct a { int b; }; + +// Warning 'warn_interrupt_signal_attribute_invalid' was previously known as 'warn_interrupt_attribute_invalid'. +// In SARIF, it will be referred to by its new ID, with a "deprecatedIds" entry specifying the old ID. +__attribute__((interrupt)) int fooa(void) { return 0; } diff --git a/clang/test/TableGen/DiagnosticBase.inc b/clang/test/TableGen/DiagnosticBase.inc index 2fc7bb4266edb..4f9e44e081076 100644 --- a/clang/test/TableGen/DiagnosticBase.inc +++ b/clang/test/TableGen/DiagnosticBase.inc @@ -81,6 +81,8 @@ class Diagnostic<string summary, DiagClass DC, Severity defaultmapping> { Severity DefaultSeverity = defaultmapping; DiagGroup Group; string CategoryName = ""; + string StableId = ""; + list<string> LegacyStableIds = []; } class SFINAEFailure { diff --git a/clang/test/TableGen/deferred-diag.td b/clang/test/TableGen/deferred-diag.td index d7e8e694c7b3e..53adc22fe94d3 100644 --- a/clang/test/TableGen/deferred-diag.td +++ b/clang/test/TableGen/deferred-diag.td @@ -5,23 +5,23 @@ include "DiagnosticBase.inc" // Test usage of Deferrable and NonDeferrable in diagnostics. def test_default : Error<"this error is non-deferrable by default">; -// CHECK-DAG: DIAG(test_default, {{.*}}SFINAE_SubstitutionFailure, false, true, true, false, 0) +// CHECK-DAG: DIAG(test_default, {{.*}}SFINAE_SubstitutionFailure, false, true, true, false, 0, {{[0-9]+}}, 0) def test_deferrable : Error<"this error is deferrable">, Deferrable; -// CHECK-DAG: DIAG(test_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0) +// CHECK-DAG: DIAG(test_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0, {{[0-9]+}}, 0) def test_non_deferrable : Error<"this error is non-deferrable">, NonDeferrable; -// CHECK-DAG: DIAG(test_non_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, false, 0) +// CHECK-DAG: DIAG(test_non_deferrable, {{.*}} SFINAE_SubstitutionFailure, false, true, true, false, 0, {{[0-9]+}}, 0) let Deferrable = 1 in { def test_let : Error<"this error is deferrable by let">; -// CHECK-DAG: DIAG(test_let, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0) +// CHECK-DAG: DIAG(test_let, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0, {{[0-9]+}}, 0) // Make sure TextSubstitution is allowed in the let Deferrable block. def textsub : TextSubstitution<"%select{text1|text2}0">; def test_let2 : Error<"this error is deferrable by let %sub{textsub}0">; -// CHECK-DAG: DIAG(test_let2, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0) +// CHECK-DAG: DIAG(test_let2, {{.*}} SFINAE_SubstitutionFailure, false, true, true, true, 0, {{[0-9]+}}, 0) } diff --git a/clang/tools/diagtool/DiagnosticNames.cpp b/clang/tools/diagtool/DiagnosticNames.cpp index 4ac9825848ef3..1538167022aca 100644 --- a/clang/tools/diagtool/DiagnosticNames.cpp +++ b/clang/tools/diagtool/DiagnosticNames.cpp @@ -28,7 +28,8 @@ llvm::ArrayRef<DiagnosticRecord> diagtool::getBuiltinDiagnosticsByName() { // out of sync easily? static const DiagnosticRecord BuiltinDiagnosticsByID[] = { #define DIAG(ENUM, CLASS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFER, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFER, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ {#ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t)}, #include "clang/Basic/AllDiagnosticKinds.inc" #undef DIAG diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp index 17078e2bc1505..e68bcc4e1087c 100644 --- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -1584,7 +1584,8 @@ namespace clang { namespace diag { enum { #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ - SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY, STABLE_ID, \ + LEGACY_STABLE_IDS) \ ENUM, #define %sSTART #include "clang/Basic/Diagnostic%sKinds.inc" @@ -1672,6 +1673,144 @@ void clang::EmitClangDiagsEnums(const RecordKeeper &Records, raw_ostream &OS, } } +//===----------------------------------------------------------------------===// +// Stable ID Tables generation +//===----------------------------------------------------------------------===// + +namespace { + +/// Holds the string table for all Stable IDs, plus the arrays of legacy Stable +/// IDs for renamed diagnostics. +class DiagStableIDsMap { + StringToOffsetTable StableIDs; + std::vector<uint32_t> LegacyStableIDs; + std::map<StringRef, uint32_t> LegacyStableIDsStartOffsets; + +public: + DiagStableIDsMap(const RecordKeeper &Records) { + LegacyStableIDs.push_back(0); // Empty array at offset 0 + + for (const Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) { + StringRef StableID = getStableID(*Diag); + // Memoize the Stable ID + StableIDs.GetOrAddStringOffset(StableID); + + auto LegacyIDList = Diag->getValueAsListOfStrings("LegacyStableIds"); + if (!LegacyIDList.empty()) { + // Memoize any Legacy Stable IDs, and list their offsets in an array. + size_t StartOffset = LegacyStableIDs.size(); + LegacyStableIDsStartOffsets.insert( + std::make_pair(Diag->getName(), StartOffset)); + for (const auto LegacyID : LegacyIDList) { + unsigned Offset = StableIDs.GetOrAddStringOffset(LegacyID); + LegacyStableIDs.push_back(Offset); + } + LegacyStableIDs.push_back(0); // Terminate the array. + } + } + } + + /// Gets the string table offset of the Stable ID for the specified Diagnostic + /// record. + uint32_t getStableIDOffset(const Record &R) const { + return StableIDs.GetStringOffset(getStableID(R)).value(); + } + + /// Gets the offset in the DiagLegacyStableIDs array of the first element of + /// the diagnostic's list of legacy Stable IDs. + uint32_t getLegacyStableIDsStartOffset(StringRef Name) const { + auto found = LegacyStableIDsStartOffsets.find(Name); + if (found != LegacyStableIDsStartOffsets.cend()) { + return found->second; + } else { + return 0; + } + } + + /// Emit diagnostic stable ID arrays and related data structures. + /// + /// This creates the table of stable IDs, plus the array of arrays of old + /// stable IDs. + /// + /// \code + /// #ifdef GET_DIAG_STABLE_ID_ARRAYS + /// static const int32_t DiagOldStableIds[]; + /// static constexpr llvm::StringTable DiagStableIds; + /// #endif + /// \endcode + void emit(raw_ostream &OS) const { + OS << "\n#ifdef GET_DIAG_STABLE_ID_ARRAYS\n"; + emitStableIDs(OS); + emitLegacyStableIDs(OS); + OS << "#endif // GET_DIAG_STABLE_ID_ARRAYS\n\n"; + } + +private: + /// Gets the Stable ID for the specified Diagnostic record. + /// The Stable ID can be explicitly specified via the "StableId" + /// property. If not specified explicitly, the Stable ID defaults + /// to the name of the diagnostic. + static StringRef getStableID(const Record &R) { + StringRef StableID = R.getValueAsString("StableId"); + if (!StableID.empty()) { + return StableID; + } else { + return R.getName(); + } + } + + /// Emit a list of stable IDs, used by both the "StableId" and + /// "LegacyStableIds" properties. + /// + /// This creates an `llvm::StringTable` of all the stable ids in use. + void emitStableIDs(raw_ostream &OS) const { + StableIDs.EmitStringTableDef(OS, "DiagStableIDs"); + OS << "\n"; + } + + /// Emit the array of legacy stable IDs for diagnostics. + /// + /// The array of stable IDs contains for each diagnostic a list of its legacy + /// stable IDs. The individual lists are separated by '0'. Diagnostics with + /// no legacy stable IDs are skipped. + /// + /// \code + /// static const uint16_t DiagOldStableIds[] = { + /// /* Empty */ 0, + /// /* Diag0 */ 142, 0, + /// /* Diag13 */ 265, 322, 399, 0 + /// } + /// \endcode + /// + void emitLegacyStableIDs(raw_ostream &OS) const { + OS << "static const uint32_t DiagLegacyStableIDs[] = {\n"; + + bool StartOfLine = true; + for (auto Offset : LegacyStableIDs) { + if (StartOfLine) { + OS << " "; + StartOfLine = false; + } + OS << Offset << ","; + if (Offset > 0) { + OS << " "; + } else { + OS << "\n"; + StartOfLine = true; + } + } + OS << "};\n\n"; + } +}; +} // namespace + +/// Emit the definitions of the Stable ID and old Stable ID tables. +void clang::EmitClangDiagsStableIDs(const RecordKeeper &Records, + raw_ostream &OS) { + DiagStableIDsMap StableIDs(Records); + StableIDs.emit(OS); +} + /// ClangDiagsDefsEmitter - The top-level class emits .def files containing /// declarations of Clang diagnostics. void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS, @@ -1699,6 +1838,7 @@ void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS, DiagCategoryIDMap CategoryIDs(Records); DiagGroupParentMap DGParentMap(Records); + DiagStableIDsMap StableIDs(Records); // Compute the set of diagnostics that are in -Wpedantic. RecordSet DiagsInPedantic; @@ -1781,6 +1921,16 @@ void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS, // Category number. OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap)); + + // Stable ID. + uint32_t StableIDOffset = StableIDs.getStableIDOffset(R); + OS << ", " << StableIDOffset; + + // Old Stable IDs. + uint32_t LegacyStableIDsStartOffset = + StableIDs.getLegacyStableIDsStartOffset(R.getName()); + OS << ", " << LegacyStableIDsStartOffset; + OS << ")\n"; } } diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index 866040d503646..2abd5dee0f384 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -54,6 +54,7 @@ enum ActionType { GenClangDiagsEnums, GenClangDiagGroups, GenClangDiagsIndexName, + GenClangDiagsStableIDs, GenClangDiagsInterface, GenClangCommentNodes, GenClangDeclNodes, @@ -197,6 +198,8 @@ cl::opt<ActionType> Action( "Generate Clang diagnostic groups"), clEnumValN(GenClangDiagsIndexName, "gen-clang-diags-index-name", "Generate Clang diagnostic name index"), + clEnumValN(GenClangDiagsStableIDs, "gen-clang-diags-stable-ids", + "Generate Clang diagnostic stable IDs"), clEnumValN(GenClangDiagsInterface, "gen-clang-diags-iface", "Generate Clang diagnostic interface headers"), clEnumValN(GenClangBasicReader, "gen-clang-basic-reader", @@ -450,6 +453,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) { case GenClangDiagsIndexName: EmitClangDiagsIndexName(Records, OS); break; + case GenClangDiagsStableIDs: + EmitClangDiagsStableIDs(Records, OS); + break; case GenClangDiagsInterface: EmitClangDiagsInterface(OS, ClangComponent); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h index fa49dcd289bc2..95c479ba2a5be 100644 --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -105,6 +105,8 @@ void EmitClangDiagGroups(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangDiagsIndexName(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangDiagsStableIDs(const llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); void EmitClangDiagsInterface(llvm::raw_ostream &OS, const std::string &Component); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
