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
