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:


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to