[clang-tools-extra] [clangd] Support suppressions for driver diagnostics (PR #182912)
Joe Eagar via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 4 14:50:44 PST 2026
https://github.com/joeedh updated https://github.com/llvm/llvm-project/pull/182912
>From 17490a6a4e39b0b3a2ddee48d2d2c5b0fd1257ae Mon Sep 17 00:00:00 2001
From: Joe Eagar <joeedh at gmail.com>
Date: Mon, 23 Feb 2026 10:09:50 -0800
Subject: [PATCH] [clangd] Pull suppression logic into common path, apply for
driver diagnostics Rebase of https://reviews.llvm.org/D127844# Fixes #1142
---
clang-tools-extra/clangd/Diagnostics.cpp | 26 +++++++++++++------
clang-tools-extra/clangd/Diagnostics.h | 6 ++---
clang-tools-extra/clangd/ParsedAST.cpp | 5 ----
clang-tools-extra/clangd/Preamble.cpp | 5 ----
.../clangd/unittests/CompilerTests.cpp | 15 +++++++++++
.../clangd/unittests/DiagnosticsTests.cpp | 21 +++++++++++++++
6 files changed, 57 insertions(+), 21 deletions(-)
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp
index e10960ca5850f..f04995740b129 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -9,6 +9,7 @@
#include "Diagnostics.h"
#include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
#include "Compiler.h"
+#include "Config.h"
#include "Protocol.h"
#include "SourceCode.h"
#include "support/Logger.h"
@@ -695,6 +696,19 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError(
Info.getID());
+ if (!isNote(DiagLevel)) {
+ const Config &Cfg = Config::current();
+ // Check if diagnostics is suppressed (possibly by user), before doing any
+ // adjustments.
+ if (Cfg.Diagnostics.SuppressAll ||
+ isDiagnosticSuppressed(Info, Cfg.Diagnostics.Suppress, LangOpts)) {
+ DiagLevel = DiagnosticsEngine::Ignored;
+ } else if (Adjuster) {
+ // FIXME: Merge with feature modules.
+ DiagLevel = Adjuster(DiagLevel, Info);
+ }
+ }
+
if (Info.getLocation().isInvalid()) {
// Handle diagnostics coming from command-line arguments. The source manager
// is *not* available at this point, so we cannot use it.
@@ -805,8 +819,7 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
}
if (Message.empty()) // either !SyntheticMessage, or we failed to make one.
Info.FormatDiagnostic(Message);
- LastDiag->Fixes.push_back(
- Fix{std::string(Message), std::move(Edits), {}});
+ LastDiag->Fixes.push_back(Fix{std::string(Message), std::move(Edits), {}});
return true;
};
@@ -815,9 +828,6 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
flushLastDiag();
LastDiag = Diag();
- // FIXME: Merge with feature modules.
- if (Adjuster)
- DiagLevel = Adjuster(DiagLevel, Info);
FillDiagBase(*LastDiag);
if (isExcluded(LastDiag->ID))
@@ -906,12 +916,12 @@ void StoreDiags::flushLastDiag() {
bool isDiagnosticSuppressed(const clang::Diagnostic &Diag,
const llvm::StringSet<> &Suppress,
- const LangOptions &LangOpts) {
+ const std::optional<LangOptions> &LangOpts) {
// Don't complain about header-only stuff in mainfiles if it's a header.
// FIXME: would be cleaner to suppress in clang, once we decide whether the
// behavior should be to silently-ignore or respect the pragma.
- if (Diag.getID() == diag::pp_pragma_sysheader_in_main_file &&
- LangOpts.IsHeaderFile)
+ if (LangOpts && Diag.getID() == diag::pp_pragma_sysheader_in_main_file &&
+ LangOpts->IsHeaderFile)
return true;
if (const char *CodePtr = getDiagnosticCode(Diag.getID())) {
diff --git a/clang-tools-extra/clangd/Diagnostics.h b/clang-tools-extra/clangd/Diagnostics.h
index c45d8dc3aa6ce..95f3da8bd0e47 100644
--- a/clang-tools-extra/clangd/Diagnostics.h
+++ b/clang-tools-extra/clangd/Diagnostics.h
@@ -173,8 +173,8 @@ class StoreDiags : public DiagnosticConsumer {
std::vector<Diag> Output;
std::optional<LangOptions> LangOpts;
std::optional<Diag> LastDiag;
- std::optional<FullSourceLoc> LastDiagLoc; // Valid only when LastDiag is set.
- bool LastDiagOriginallyError = false; // Valid only when LastDiag is set.
+ std::optional<FullSourceLoc> LastDiagLoc; // Valid only when LastDiag is set.
+ bool LastDiagOriginallyError = false; // Valid only when LastDiag is set.
SourceManager *OrigSrcMgr = nullptr;
llvm::DenseSet<std::pair<unsigned, unsigned>> IncludedErrorLocations;
@@ -183,7 +183,7 @@ class StoreDiags : public DiagnosticConsumer {
/// Determine whether a (non-clang-tidy) diagnostic is suppressed by config.
bool isDiagnosticSuppressed(const clang::Diagnostic &Diag,
const llvm::StringSet<> &Suppressed,
- const LangOptions &);
+ const std::optional<LangOptions> &);
/// Take a user-specified diagnostic code, and convert it to a normalized form
/// stored in the config and consumed by isDiagnosticsSuppressed.
///
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index c23b5d7a38d37..4e873f1257a17 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -585,11 +585,6 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
ASTDiags.setLevelAdjuster([&](DiagnosticsEngine::Level DiagLevel,
const clang::Diagnostic &Info) {
- if (Cfg.Diagnostics.SuppressAll ||
- isDiagnosticSuppressed(Info, Cfg.Diagnostics.Suppress,
- Clang->getLangOpts()))
- return DiagnosticsEngine::Ignored;
-
auto It = OverriddenSeverity.find(Info.getID());
if (It != OverriddenSeverity.end())
DiagLevel = It->second;
diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp
index cdda0208a96d6..f5e512793e98e 100644
--- a/clang-tools-extra/clangd/Preamble.cpp
+++ b/clang-tools-extra/clangd/Preamble.cpp
@@ -598,13 +598,8 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
CompilerInstance::createDiagnostics(*VFS, CI.getDiagnosticOpts(),
&PreambleDiagnostics,
/*ShouldOwnClient=*/false);
- const Config &Cfg = Config::current();
PreambleDiagnostics.setLevelAdjuster([&](DiagnosticsEngine::Level DiagLevel,
const clang::Diagnostic &Info) {
- if (Cfg.Diagnostics.SuppressAll ||
- isDiagnosticSuppressed(Info, Cfg.Diagnostics.Suppress,
- CI.getLangOpts()))
- return DiagnosticsEngine::Ignored;
switch (Info.getID()) {
case diag::warn_no_newline_eof:
// If the preamble doesn't span the whole file, drop the no newline at
diff --git a/clang-tools-extra/clangd/unittests/CompilerTests.cpp b/clang-tools-extra/clangd/unittests/CompilerTests.cpp
index ab9e85e983d1c..9c8ad8d70b47b 100644
--- a/clang-tools-extra/clangd/unittests/CompilerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CompilerTests.cpp
@@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//
#include "Compiler.h"
+#include "Config.h"
+#include "Diagnostics.h"
#include "TestTU.h"
#include "clang/Frontend/DependencyOutputOptions.h"
#include "clang/Frontend/FrontendOptions.h"
@@ -113,6 +115,19 @@ TEST(BuildCompilerInvocation, EmptyArgs) {
// No crash.
EXPECT_EQ(buildCompilerInvocation(Inputs, Diags), nullptr);
}
+TEST(BuildCompilerInvocation, SuppressDiags) {
+ MockFS FS;
+ StoreDiags Diags;
+ TestTU TU;
+ TU.ExtraArgs = {"-funknown-arg"};
+ auto Inputs = TU.inputs(FS);
+
+ Config Cfg;
+ Cfg.Diagnostics.Suppress = {"drv_unknown_argument"};
+ WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg));
+ EXPECT_NE(buildCompilerInvocation(Inputs, Diags), nullptr);
+ EXPECT_THAT(Diags.take(), IsEmpty());
+}
} // namespace
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
index 84ceddbd4fc4b..6fe48478e1175 100644
--- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -2170,6 +2170,27 @@ TEST(DiagnosticsTest, UnusedInHeader) {
EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
+TEST(DiagnosticsTest, DontSuppressSubcategories) {
+ Annotations Source(R"cpp(
+ /*error-ok*/
+ void bar(int x) {
+ switch(x) {
+ default:
+ break;
+ break;
+ }
+ })cpp");
+ TestTU TU;
+ TU.ExtraArgs.push_back("-Wunreachable-code-aggressive");
+ TU.Code = Source.code().str();
+ Config Cfg;
+ // This shouldn't suppress subcategory unreachable-break.
+ Cfg.Diagnostics.Suppress = {"unreachable-code"};
+ WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg));
+ EXPECT_THAT(TU.build().getDiagnostics(),
+ ElementsAre(diagName("-Wunreachable-code-break")));
+}
+
} // namespace
} // namespace clangd
} // namespace clang
More information about the cfe-commits
mailing list