[clang-tools-extra] Handle deprecated macros similarly to deprecated symbols (PR #229197)

via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 5 12:48:46 PDT 2026


https://github.com/higher-performance created https://github.com/llvm/llvm-project/pull/229197

None

>From 936d82b0a9c0fd0672bbd48cc11ab71dccbb6048 Mon Sep 17 00:00:00 2001
From: higher-performance <higher.performance.github at gmail.com>
Date: Mon, 5 Oct 2026 15:47:02 -0400
Subject: [PATCH] Handle deprecated macros similarly to deprecated symbols

---
 clang-tools-extra/clangd/CodeComplete.cpp       |  8 +++++++-
 clang-tools-extra/clangd/Diagnostics.cpp        |  3 ++-
 clang-tools-extra/clangd/ParsedAST.cpp          | 10 ++++++++++
 clang-tools-extra/clangd/Preamble.cpp           | 17 +++++++++++++++++
 clang-tools-extra/clangd/Preamble.h             |  4 ++++
 .../clangd/SemanticHighlighting.cpp             | 12 ++++++++++--
 .../clangd/index/SymbolCollector.cpp            | 16 ++++++++++++++--
 .../clangd/unittests/CodeCompleteTests.cpp      |  9 +++++++++
 .../clangd/unittests/DiagnosticsTests.cpp       | 12 ++++++++++++
 .../unittests/SemanticHighlightingTests.cpp     |  5 +++++
 .../clangd/unittests/SymbolCollectorTests.cpp   |  8 +++++++-
 11 files changed, 97 insertions(+), 7 deletions(-)

diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 2c7a317d045a94..e7d70c3f43fad9 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -524,7 +524,9 @@ struct CodeCompletionBuilder {
     if (Completion.Deprecated) {
       if (C.SemaResult)
         Completion.Deprecated &=
-            C.SemaResult->Availability == CXAvailability_Deprecated;
+            C.SemaResult->Availability == CXAvailability_Deprecated ||
+            (C.SemaResult->Kind == CodeCompletionResult::RK_Macro &&
+             C.SemaResult->Macro && C.SemaResult->Macro->isDeprecatedMacro());
       if (C.IndexResult)
         Completion.Deprecated &=
             bool(C.IndexResult->Flags & Symbol::Deprecated);
@@ -1361,6 +1363,10 @@ void loadMainFilePreambleMacros(const Preprocessor &PP,
   IdentifierInfoLookup *PreambleIdentifiers =
       ITable.getExternalIdentifierLookup();
 
+  for (const auto &[Name, Msg] : Preamble.DeprecatedMacros) {
+    auto &II = const_cast<Preprocessor &>(PP).getIdentifierTable().get(Name);
+    II.setIsDeprecatedMacro(true);
+  }
   if (!PreambleIdentifiers || !PreambleMacros)
     return;
   for (const auto &MacroName : Preamble.Macros.Names) {
diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp
index 28351c7cc22467..fa0e3c90d64614 100644
--- a/clang-tools-extra/clangd/Diagnostics.cpp
+++ b/clang-tools-extra/clangd/Diagnostics.cpp
@@ -362,6 +362,7 @@ void setTags(clangd::Diag &D) {
       diag::warn_deprecated_volatile_return,
       diag::warn_deprecated_volatile_structured_binding,
       diag::warn_opencl_attr_deprecated_ignored,
+      diag::warn_pragma_deprecated_macro_use,
       diag::warn_property_method_deprecated,
       diag::warn_vector_mode_deprecated,
   };
@@ -738,7 +739,7 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
   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.
-    if (!OriginallyError) {
+    if (isNote(DiagLevel) || !OriginallyError) {
       IgnoreDiagnostics::log(DiagLevel, Info);
       return; // non-errors add too much noise, do not show them.
     }
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index a35d99e2a5ee20..8904e0c44a49f0 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -738,6 +738,16 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
     Marks = Patch->marks();
   }
   auto &PP = Clang->getPreprocessor();
+  if (Preamble) {
+    for (const auto &[Name, Msg] : Preamble->DeprecatedMacros) {
+      auto &II = PP.getIdentifierTable().get(Name);
+      II.setIsDeprecatedMacro(true);
+      SourceLocation Loc;
+      if (const auto *MI = PP.getMacroDefinition(&II).getMacroInfo())
+        Loc = MI->getDefinitionLoc();
+      PP.addMacroDeprecationMsg(&II, Msg, Loc);
+    }
+  }
   auto MacroCollector = std::make_unique<CollectMainFileMacros>(PP, Macros);
   auto *MacroCollectorPtr = MacroCollector.get(); // so we can call doneParse()
   PP.addPPCallbacks(std::move(MacroCollector));
diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp
index 7e68a721a9fca5..28b4c58182dc09 100644
--- a/clang-tools-extra/clangd/Preamble.cpp
+++ b/clang-tools-extra/clangd/Preamble.cpp
@@ -107,11 +107,26 @@ class CppFilePreambleCallbacks : public PreambleCallbacks {
     return std::move(Pragmas);
   }
 
+  std::vector<std::pair<std::string, std::string>> takeDeprecatedMacros() {
+    return std::move(DeprecatedMacros);
+  }
+
   std::optional<CapturedASTCtx> takeLife() { return std::move(CapturedCtx); }
 
   bool isMainFileIncludeGuarded() const { return IsMainFileIncludeGuarded; }
 
   void AfterExecute(CompilerInstance &CI) override {
+    for (const auto &Entry : CI.getPreprocessor().getIdentifierTable()) {
+      const IdentifierInfo *II = Entry.getValue();
+      if (II && II->isDeprecatedMacro()) {
+        std::string Msg;
+        const auto &Annotations = CI.getPreprocessor().getMacroAnnotations(II);
+        if (Annotations.DeprecationInfo)
+          Msg = Annotations.DeprecationInfo->Message;
+        DeprecatedMacros.emplace_back(Entry.getKey().str(), std::move(Msg));
+      }
+    }
+
     // As part of the Preamble compilation, ASTConsumer
     // PrecompilePreambleConsumer/PCHGenerator is setup. This would be called
     // when Preamble consists of modules. Therefore while capturing AST context,
@@ -202,6 +217,7 @@ class CppFilePreambleCallbacks : public PreambleCallbacks {
   include_cleaner::PragmaIncludes Pragmas;
   MainFileMacros Macros;
   std::vector<PragmaMark> Marks;
+  std::vector<std::pair<std::string, std::string>> DeprecatedMacros;
   bool IsMainFileIncludeGuarded = false;
   const clang::LangOptions *LangOpts = nullptr;
   const SourceManager *SourceMgr = nullptr;
@@ -700,6 +716,7 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
 
     Result->Macros = CapturedInfo.takeMacros();
     Result->Marks = CapturedInfo.takeMarks();
+    Result->DeprecatedMacros = CapturedInfo.takeDeprecatedMacros();
     Result->StatCache = std::move(StatCache);
     Result->MainIsIncludeGuarded = CapturedInfo.isMainFileIncludeGuarded();
     // Move the options instead of copying them. The invocation doesn't need
diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h
index 5513b683748aec..41b100edb7d6ed 100644
--- a/clang-tools-extra/clangd/Preamble.h
+++ b/clang-tools-extra/clangd/Preamble.h
@@ -125,6 +125,10 @@ struct PreambleData {
   // Cache of FS operations performed when building the preamble.
   // When reusing a preamble, this cache can be consumed to save IO.
   std::shared_ptr<PreambleFileStatusCache> StatCache;
+  // Deprecated macros defined in the preamble (including included headers).
+  // Clang's PCH serialization does not preserve isDeprecatedMacro or
+  // deprecation messages, so we propagate them by hand to subsequent parses.
+  std::vector<std::pair<std::string, std::string>> DeprecatedMacros;
   // Whether there was a (possibly-incomplete) include-guard on the main file.
   // We need to propagate this information "by hand" to subsequent parses.
   bool MainIsIncludeGuarded = false;
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 8948f1476e1082..387420a0db961d 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -1104,12 +1104,20 @@ getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens) {
       },
       AST.getHeuristicResolver());
   // Add highlightings for macro references.
+  const auto &SM = C.getSourceManager();
+  StringRef Buffer = SM.getBufferData(SM.getMainFileID());
   auto AddMacro = [&](const MacroOccurrence &M) {
-    auto &T = Builder.addToken(M.toRange(C.getSourceManager()),
-                               HighlightingKind::Macro);
+    auto &T = Builder.addToken(M.toRange(SM), HighlightingKind::Macro);
     T.addModifier(HighlightingModifier::GlobalScope);
     if (M.IsDefinition)
       T.addModifier(HighlightingModifier::Declaration);
+    if (M.EndOffset <= Buffer.size()) {
+      const auto &Identifiers = AST.getPreprocessor().getIdentifierTable();
+      auto It = Identifiers.find(
+          Buffer.substr(M.StartOffset, M.EndOffset - M.StartOffset));
+      if (It != Identifiers.end() && It->second->isDeprecatedMacro())
+        T.addModifier(HighlightingModifier::Deprecated);
+    }
   };
   for (const auto &SIDToRefs : AST.getMacros().MacroRefs)
     for (const auto &M : SIDToRefs.second)
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index e53bd3265d96c4..bd38d71bec5e82 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -837,6 +837,10 @@ bool SymbolCollector::handleMacroOccurrence(const IdentifierInfo *Name,
     S.Flags |= Symbol::IndexedForCodeCompletion;
     S.Flags |= Symbol::VisibleOutsideFile;
   }
+  if (Name->isDeprecatedMacro()) {
+    S.Flags |= Symbol::Deprecated;
+    S.Tags |= toSymbolTagBitmask(SymbolTag::Deprecated);
+  }
   S.SymInfo = index::getSymbolInfoForMacro(*MI);
   S.Origin = Opts.Origin;
   // FIXME: use the result to filter out symbols.
@@ -945,9 +949,17 @@ void SymbolCollector::finish() {
     for (const IdentifierInfo *II : IndexedMacros) {
       if (const auto *MI = PP->getMacroDefinition(II).getMacroInfo())
         if (auto ID =
-                getSymbolIDCached(II->getName(), MI, PP->getSourceManager()))
-          if (MI->isUsedForHeaderGuard())
+                getSymbolIDCached(II->getName(), MI, PP->getSourceManager())) {
+          if (MI->isUsedForHeaderGuard()) {
             Symbols.erase(ID);
+          } else if (II->isDeprecatedMacro()) {
+            if (const auto *S = Symbols.find(ID)) {
+              auto *ModS = const_cast<Symbol *>(S);
+              ModS->Flags |= Symbol::Deprecated;
+              ModS->Tags |= toSymbolTagBitmask(SymbolTag::Deprecated);
+            }
+          }
+        }
     }
   }
   llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index edfd29136881fd..bccf3a187e609a 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -3309,12 +3309,21 @@ TEST(CompletionTest, DeprecatedResults) {
   std::string Body = R"cpp(
     void TestClangd();
     void TestClangc() __attribute__((deprecated("", "")));
+    #define DEPRECATED_MACRO 1
+    #pragma clang deprecated(DEPRECATED_MACRO)
+    #define NORMAL_MACRO 1
   )cpp";
 
   EXPECT_THAT(
       completions(Body + "int main() { TestClang^ }").Completions,
       UnorderedElementsAre(AllOf(named("TestClangd"), Not(deprecated())),
                            AllOf(named("TestClangc"), deprecated())));
+  EXPECT_THAT(
+      completions(Body + "int main() { DEPRECATED_MAC^ }").Completions,
+      UnorderedElementsAre(AllOf(named("DEPRECATED_MACRO"), deprecated())));
+  EXPECT_THAT(
+      completions(Body + "int main() { NORMAL_MAC^ }").Completions,
+      UnorderedElementsAre(AllOf(named("NORMAL_MACRO"), Not(deprecated()))));
 }
 
 TEST(SignatureHelpTest, PartialSpec) {
diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
index e5ef1a006c8a43..bbbbecbc373839 100644
--- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -2125,6 +2125,18 @@ TEST(Diagnostics, Tags) {
                   AllOf(Diag(Test.range("deprecated"), "'bar' is deprecated"),
                         withTag(DiagnosticTag::Deprecated))));
 
+  Test = Annotations(R"cpp(
+    #define OLD 1
+    #pragma clang deprecated(OLD, "use NEW")
+    int y = $deprecated[[OLD]];
+  )cpp");
+  TU.Code = Test.code().str();
+  EXPECT_THAT(TU.build().getDiagnostics(),
+              UnorderedElementsAre(AllOf(
+                  Diag(Test.range("deprecated"),
+                       "macro 'OLD' has been marked as deprecated: use NEW"),
+                  withTag(DiagnosticTag::Deprecated))));
+
   Test = Annotations(R"cpp(
     $typedef[[typedef int INT]];
   )cpp");
diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
index a7ebd146c297bb..787a95cbcab6db 100644
--- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -879,6 +879,11 @@ sizeof...($TemplateParameter[[Elements]]);
             const char *$LocalVariable_def_readonly[[s]] = $LocalVariable_readonly_static[[__func__]];
         }
       )cpp",
+      R"cpp(
+        #define $Macro_decl_deprecated[[FOO]] 1
+        #pragma clang deprecated(FOO)
+        int $Variable_def[[x]] = $Macro_deprecated[[FOO]];
+      )cpp",
       // override and final
       R"cpp(
         class $Class_def_abstract[[Base]] { virtual void $Method_decl_abstract_virtual[[m]]() = 0; };
diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
index a04756518ef23b..ec8f0cc6f632df 100644
--- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp
@@ -2055,11 +2055,17 @@ TEST_F(SymbolCollectorTest, DeprecatedSymbols) {
   const std::string Header = R"(
     void TestClangc() __attribute__((deprecated("", "")));
     void TestClangd();
+    #define DEPRECATED_MACRO 1
+    #pragma clang deprecated(DEPRECATED_MACRO)
+    #define NORMAL_MACRO 1
   )";
+  CollectorOpts.CollectMacro = true;
   runSymbolCollector(Header, /**/ "");
   EXPECT_THAT(Symbols, UnorderedElementsAre(
                            AllOf(qName("TestClangc"), deprecated()),
-                           AllOf(qName("TestClangd"), Not(deprecated()))));
+                           AllOf(qName("TestClangd"), Not(deprecated())),
+                           AllOf(qName("DEPRECATED_MACRO"), deprecated()),
+                           AllOf(qName("NORMAL_MACRO"), Not(deprecated()))));
 }
 
 TEST_F(SymbolCollectorTest, implementationDetail) {



More information about the cfe-commits mailing list