[clang-tools-extra] 161d8a7 - [clangd][nfc] Avoid type erasure for local recursive callbacks (#203042)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 16 00:09:07 PDT 2026
Author: David Zbarsky
Date: 2026-06-16T10:09:03+03:00
New Revision: 161d8a7806b7b7388343beb56e1de62905c7b229
URL: https://github.com/llvm/llvm-project/commit/161d8a7806b7b7388343beb56e1de62905c7b229
DIFF: https://github.com/llvm/llvm-project/commit/161d8a7806b7b7388343beb56e1de62905c7b229.diff
LOG: [clangd][nfc] Avoid type erasure for local recursive callbacks (#203042)
Four local clangd callbacks use std::function only to call themselves.
Switch to local structs and static functions to avoid std::function
type-erasure and copy-support machinery.
In matched Release AArch64 builds, the four object files shrink by 8,152
bytes and 131 relocations; linked clangd shrinks by 3,872 bytes
unstripped and 16 bytes stripped, with __text down 360 bytes,
__DATA_CONST,__const down 208 bytes, unwind data down 32 bytes, and 21
fewer dyld fixups.
Work towards #202616
AI tool disclosure: Co-authored with OpenAI Codex.
Added:
Modified:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/HeaderSourceSwitch.cpp
clang-tools-extra/clangd/Protocol.cpp
clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index d42e8eff554e5..43e8b35e45c89 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -41,7 +41,6 @@
#include <chrono>
#include <cstddef>
#include <cstdint>
-#include <functional>
#include <map>
#include <memory>
#include <mutex>
@@ -1003,23 +1002,29 @@ static std::vector<SymbolInformation>
flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols,
const URIForFile &FileURI) {
std::vector<SymbolInformation> Results;
- std::function<void(const DocumentSymbol &, llvm::StringRef)> Process =
- [&](const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) {
- SymbolInformation SI;
- SI.containerName = std::string(ParentName ? "" : *ParentName);
- SI.name = S.name;
- SI.kind = S.kind;
- SI.location.range = S.range;
- SI.location.uri = FileURI;
-
- Results.push_back(std::move(SI));
- std::string FullName =
- !ParentName ? S.name : (ParentName->str() + "::" + S.name);
- for (auto &C : S.children)
- Process(C, /*ParentName=*/FullName);
- };
- for (auto &S : Symbols)
- Process(S, /*ParentName=*/"");
+ struct SymbolHierarchyFlattener {
+ const URIForFile &FileURI;
+ std::vector<SymbolInformation> &Results;
+
+ void append(const DocumentSymbol &S,
+ std::optional<llvm::StringRef> ParentName) {
+ SymbolInformation SI;
+ SI.containerName = std::string(ParentName ? "" : *ParentName);
+ SI.name = S.name;
+ SI.kind = S.kind;
+ SI.location.range = S.range;
+ SI.location.uri = FileURI;
+
+ Results.push_back(std::move(SI));
+ std::string FullName =
+ !ParentName ? S.name : (ParentName->str() + "::" + S.name);
+ for (const DocumentSymbol &C : S.children)
+ append(C, /*ParentName=*/FullName);
+ }
+ };
+ SymbolHierarchyFlattener Flattener{FileURI, Results};
+ for (const DocumentSymbol &S : Symbols)
+ Flattener.append(S, /*ParentName=*/"");
return Results;
}
diff --git a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
index 6c1e2ca99427e..0bce98b0dacfd 100644
--- a/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
+++ b/clang-tools-extra/clangd/HeaderSourceSwitch.cpp
@@ -125,29 +125,34 @@ std::optional<Path> getCorrespondingHeaderOrSource(PathRef OriginalFile,
std::vector<const Decl *> getIndexableLocalDecls(ParsedAST &AST) {
std::vector<const Decl *> Results;
- std::function<void(Decl *)> TraverseDecl = [&](Decl *D) {
- auto *ND = llvm::dyn_cast<NamedDecl>(D);
- if (!ND || ND->isImplicit())
- return;
- if (!SymbolCollector::shouldCollectSymbol(*ND, D->getASTContext(), {},
- /*IsMainFileSymbol=*/false))
- return;
- if (!llvm::isa<FunctionDecl>(ND)) {
- // Visit the children, but we skip function decls as we are not interested
- // in the function body.
- if (auto *Scope = llvm::dyn_cast<DeclContext>(ND)) {
- for (auto *D : Scope->decls())
- TraverseDecl(D);
+ struct IndexableLocalDeclCollector {
+ std::vector<const Decl *> &Results;
+
+ void traverse(Decl *D) {
+ auto *ND = llvm::dyn_cast<NamedDecl>(D);
+ if (!ND || ND->isImplicit())
+ return;
+ if (!SymbolCollector::shouldCollectSymbol(*ND, D->getASTContext(), {},
+ /*IsMainFileSymbol=*/false))
+ return;
+ if (!llvm::isa<FunctionDecl>(ND)) {
+ // Visit the children, but we skip function decls as we are not
+ // interested in the function body.
+ if (auto *Scope = llvm::dyn_cast<DeclContext>(ND)) {
+ for (Decl *Child : Scope->decls())
+ traverse(Child);
+ }
}
+ if (llvm::isa<NamespaceDecl>(D))
+ return; // namespace is indexable, but we're not interested.
+ Results.push_back(D);
}
- if (llvm::isa<NamespaceDecl>(D))
- return; // namespace is indexable, but we're not interested.
- Results.push_back(D);
};
+ IndexableLocalDeclCollector Collector{Results};
// Traverses the ParsedAST directly to collect all decls present in the main
// file.
for (auto *TopLevel : AST.getLocalTopLevelDecls())
- TraverseDecl(TopLevel);
+ Collector.traverse(TopLevel);
return Results;
}
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index f77b0773d445a..d5c50cc1cfa35 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -1754,17 +1754,18 @@ llvm::json::Value toJSON(const ASTNode &N) {
return Result;
}
+static void printASTNode(llvm::raw_ostream &OS, const ASTNode &N,
+ unsigned Level) {
+ OS.indent(2 * Level) << N.role << ": " << N.kind;
+ if (!N.detail.empty())
+ OS << " - " << N.detail;
+ OS << "\n";
+ for (const ASTNode &C : N.children)
+ printASTNode(OS, C, Level + 1);
+}
+
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ASTNode &Root) {
- std::function<void(const ASTNode &, unsigned)> Print = [&](const ASTNode &N,
- unsigned Level) {
- OS.indent(2 * Level) << N.role << ": " << N.kind;
- if (!N.detail.empty())
- OS << " - " << N.detail;
- OS << "\n";
- for (const ASTNode &C : N.children)
- Print(C, Level + 1);
- };
- Print(Root, 0);
+ printASTNode(OS, Root, 0);
return OS;
}
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
index c74250ccbe9ea..e02f892da1bf3 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
@@ -483,17 +483,19 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
OuterImplicit.ASTNode.get<Expr>()))
return false;
- std::function<bool(const SelectionTree::Node *)> IsFullySelected =
- [&](const SelectionTree::Node *N) {
- if (N->ASTNode.getSourceRange().isValid() &&
- N->Selected != SelectionTree::Complete)
+ struct FullySelectedChecker {
+ bool isFullySelected(const SelectionTree::Node *N) const {
+ if (N->ASTNode.getSourceRange().isValid() &&
+ N->Selected != SelectionTree::Complete)
+ return false;
+ for (const auto *Child : N->Children) {
+ if (!isFullySelected(Child))
return false;
- for (const auto *Child : N->Children) {
- if (!IsFullySelected(Child))
- return false;
- }
- return true;
- };
+ }
+ return true;
+ }
+ };
+ const FullySelectedChecker FullySelected;
auto ExprIsFullySelectedTargetNode = [&](const Expr *E) {
if (E != OuterImplicit.ASTNode.get<Expr>())
return false;
@@ -504,7 +506,7 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
// See the documentation of ParsedBinaryOperator for further details.
if (!IsBinOp)
return true;
- return IsFullySelected(N);
+ return FullySelected.isFullySelected(N);
};
// Disable extraction of full RHS on assignment operations, e.g:
More information about the cfe-commits
mailing list