[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