https://github.com/brandonxin created https://github.com/llvm/llvm-project/pull/190420
…iagnostic push/pop across PCH boundary `DiagStateOnPushStack` was not serialized in PCH files, causing `#pragma clang diagnostic pop` to emit a spurious "no matching push" warning when the corresponding push was in the preamble. This is because clangd splits files into a preamble (compiled to PCH) and the main file body, and the push/pop stack was lost during the PCH round-trip. Serialize and deserialize DiagStateOnPushStack in `WritePragmaDiagnosticMappings`/`ReadPragmaDiagnosticMappings` so that unmatched pushes from a preamble are correctly restored. Fixes https://github.com/clangd/clangd/issues/1167 >From 6c6f10fd9d2fe24ae52ada3c80deac4c29e48764 Mon Sep 17 00:00:00 2001 From: Brandon Xin <[email protected]> Date: Fri, 3 Apr 2026 15:26:50 -0700 Subject: [PATCH] [clang][Serialization] Serialize DiagStateOnPushStack to fix pragma diagnostic push/pop across PCH boundary DiagStateOnPushStack was not serialized in PCH files, causing `#pragma clang diagnostic pop` to emit a spurious "no matching push" warning when the corresponding push was in the preamble. This is because clangd splits files into a preamble (compiled to PCH) and the main file body, and the push/pop stack was lost during the PCH round-trip. Serialize and deserialize DiagStateOnPushStack in WritePragmaDiagnosticMappings/ReadPragmaDiagnosticMappings so that unmatched pushes from a preamble are correctly restored. Fixes https://github.com/clangd/clangd/issues/1167 --- .../clangd/unittests/DiagnosticsTests.cpp | 12 ++++++++++++ clang/lib/Serialization/ASTReader.cpp | 12 ++++++++++++ clang/lib/Serialization/ASTWriter.cpp | 7 +++++++ clang/test/PCH/Inputs/pragma-diag-push.h | 1 + clang/test/PCH/pragma-diag-push-pop-across-pch.c | 11 +++++++++++ 5 files changed, 43 insertions(+) create mode 100644 clang/test/PCH/Inputs/pragma-diag-push.h create mode 100644 clang/test/PCH/pragma-diag-push-pop-across-pch.c diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index 6fe48478e1175..81df26f979ac3 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -1091,6 +1091,18 @@ void foo(int *x); NullabilityKind::NonNull); } +TEST(DiagnosticsTest, PreamblePragmaDiagnosticPushPop) { + auto TU = TestTU::withCode(R"cpp( +#pragma clang diagnostic push +int main() { + return 0; +} +#pragma clang diagnostic pop +)cpp"); + auto AST = TU.build(); + EXPECT_THAT(AST.getDiagnostics(), IsEmpty()); +} + TEST(DiagnosticsTest, PreambleHeaderWithBadPragmaAssumeNonnull) { Annotations Header(R"cpp( #pragma clang assume_nonnull begin // error-ok diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index b211b0d32e1de..36d3b1123fd47 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7285,6 +7285,18 @@ void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) { T[0].State = CurState; } + // Restore the push stack so that unmatched pushes from a preamble are + // visible when the main file is parsed, allowing the corresponding + // `#pragma diagnostic pop` to succeed. + assert(Idx < Record.size() && + "Invalid data, missing diagnostic push stack"); + unsigned NumPushes = Record[Idx++]; + for (unsigned I = 0; I != NumPushes; ++I) { + auto *State = ReadDiagState(*FirstState, false); + if (!F.isModule()) + Diag.DiagStateOnPushStack.push_back(State); + } + // Don't try to read these mappings again. Record.clear(); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 4b3adce07f10c..f1e6ee8de1b70 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -3374,6 +3374,13 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag, AddSourceLocation(Diag.DiagStatesByLoc.CurDiagStateLoc, Record); AddDiagState(Diag.DiagStatesByLoc.CurDiagState, false); + // Emit the push stack so that unmatched pushes from a preamble can be + // restored when the main file is parsed. Each entry is a DiagState that + // was active at the time of a `#pragma diagnostic push`. + Record.push_back(Diag.DiagStateOnPushStack.size()); + for (const auto *State : Diag.DiagStateOnPushStack) + AddDiagState(State, false); + Stream.EmitRecord(DIAG_PRAGMA_MAPPINGS, Record); } diff --git a/clang/test/PCH/Inputs/pragma-diag-push.h b/clang/test/PCH/Inputs/pragma-diag-push.h new file mode 100644 index 0000000000000..4c1079ebd8ab9 --- /dev/null +++ b/clang/test/PCH/Inputs/pragma-diag-push.h @@ -0,0 +1 @@ +#pragma clang diagnostic push diff --git a/clang/test/PCH/pragma-diag-push-pop-across-pch.c b/clang/test/PCH/pragma-diag-push-pop-across-pch.c new file mode 100644 index 0000000000000..4fa1620700bbc --- /dev/null +++ b/clang/test/PCH/pragma-diag-push-pop-across-pch.c @@ -0,0 +1,11 @@ +// Test that #pragma diagnostic push in a PCH is matched by pop in the main file. + +// RUN: %clang_cc1 %S/Inputs/pragma-diag-push.h -emit-pch -o %t.pch +// RUN: %clang_cc1 %s -include-pch %t.pch -verify -fsyntax-only +// expected-no-diagnostics + +#pragma clang diagnostic pop + +int main(void) { + return 0; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
