[clang-tools-extra] [clangd] Add tweak to abbreviate function templates (PR #74710)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 13 04:44:57 PDT 2026
================
@@ -0,0 +1,352 @@
+//===-- AbbreviateFunctionTemplate.cpp ---------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "XRefs.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <numeric>
+
+namespace clang {
+namespace clangd {
+namespace {
+/// Converts a function template to its abbreviated form using auto parameters.
+/// Before:
+/// template <std::integral T>
+/// auto foo(T param) { }
+/// ^^^^^^^^^^^
+/// After:
+/// auto foo(std::integral auto param) { }
+class AbbreviateFunctionTemplate : public Tweak {
+public:
+ const char *id() const final;
+
+ bool prepare(const Selection &Inputs) override;
+ Expected<Effect> apply(const Selection &Inputs) override;
+
+ std::string title() const override {
+ return llvm::formatv("Abbreviate function template");
+ }
+
+ llvm::StringLiteral kind() const override {
+ return CodeAction::REFACTOR_KIND;
+ }
+
+private:
+ static const char *AutoKeywordSpelling;
+ const FunctionTemplateDecl *FunctionTemplateDeclaration;
+
+ struct TemplateParameterInfo {
+ const TypeConstraint *Constraint;
+ unsigned int FunctionParameterIndex;
+ llvm::SmallVector<tok::TokenKind> FunctionParameterQualifiers;
+ llvm::SmallVector<tok::TokenKind> FunctionParameterTypeQualifiers;
+ };
+
+ llvm::SmallVector<TemplateParameterInfo> TemplateParameterInfoList;
+
+ bool traverseFunctionParameters(size_t NumberOfTemplateParameters);
+
+ llvm::Expected<tooling::Replacements>
+ generateFunctionParameterReplacements(const ASTContext &Context);
+
+ llvm::Expected<tooling::Replacement> generateFunctionParameterReplacement(
+ const TemplateParameterInfo &TemplateParameterInfo,
+ const ASTContext &Context);
+
+ llvm::Expected<tooling::Replacement>
+ generateTemplateDeclarationReplacement(const ASTContext &Context);
+
+ static std::tuple<QualType, llvm::SmallVector<tok::TokenKind>,
+ llvm::SmallVector<tok::TokenKind>>
+ deconstructType(QualType Type);
+};
+
+REGISTER_TWEAK(AbbreviateFunctionTemplate)
+
+const char *AbbreviateFunctionTemplate::AutoKeywordSpelling =
+ getKeywordSpelling(tok::kw_auto);
+
+template <typename T>
+const T *findDeclaration(const SelectionTree::Node &Root) {
+ for (const auto *Node = &Root; Node; Node = Node->Parent) {
+ if (const T *Result = dyn_cast_or_null<T>(Node->ASTNode.get<Decl>()))
+ return Result;
+ }
+
+ return nullptr;
+}
+
+const char *getSpellingForQualifier(tok::TokenKind const &Qualifier) {
+ if (const auto *Spelling = getKeywordSpelling(Qualifier))
+ return Spelling;
+
+ if (const auto *Spelling = getPunctuatorSpelling(Qualifier))
+ return Spelling;
+
+ return nullptr;
+}
+
+bool AbbreviateFunctionTemplate::prepare(const Selection &Inputs) {
+ const auto *CommonAncestor = Inputs.ASTSelection.commonAncestor();
+ if (!CommonAncestor)
+ return false;
+
+ FunctionTemplateDeclaration =
+ findDeclaration<FunctionTemplateDecl>(*CommonAncestor);
+
+ if (!FunctionTemplateDeclaration)
+ return false;
+
+ auto *TemplateParameters =
+ FunctionTemplateDeclaration->getTemplateParameters();
+
+ auto NumberOfTemplateParameters = TemplateParameters->size();
+ TemplateParameterInfoList =
+ llvm::SmallVector<TemplateParameterInfo>(NumberOfTemplateParameters);
+
+ // Check how many times each template parameter is referenced.
+ // Depending on the number of references it can be checked
+ // if the refactoring is possible:
+ // - exactly one: The template parameter was declared but never used, which
+ // means we know for sure it doesn't appear as a parameter.
+ // - exactly two: The template parameter was used exactly once, either as a
+ // parameter or somewhere else. This is the case we are
+ // interested in.
+ // - more than two: The template parameter was either used for multiple
+ // parameters or somewhere else in the function.
+ for (unsigned TemplateParameterIndex = 0;
+ TemplateParameterIndex < NumberOfTemplateParameters;
+ TemplateParameterIndex++) {
+ auto *TemplateParameter =
+ TemplateParameters->getParam(TemplateParameterIndex);
+ auto *TemplateParameterInfo =
+ &TemplateParameterInfoList[TemplateParameterIndex];
+
+ auto *TemplateParameterDeclaration =
+ dyn_cast_or_null<TemplateTypeParmDecl>(TemplateParameter);
+ if (!TemplateParameterDeclaration)
+ return false;
+
+ TemplateParameterInfo->Constraint =
+ TemplateParameterDeclaration->getTypeConstraint();
+
+ auto TemplateParameterPosition = sourceLocToPosition(
+ Inputs.AST->getSourceManager(), TemplateParameter->getEndLoc());
+
+ auto FindReferencesLimit = 3;
+ auto ReferencesResult =
+ findReferences(*Inputs.AST, TemplateParameterPosition,
----------------
timon-ul wrote:
This will do an index lookup, which is considered slow and thus not desired for the `prepare` stage, do you really need that? If you do not pass the Index to `findReferences` it will only check the AST (which is cheap), but as far as I understand how this code works this should suffice no? Because as a disclaimer I can already tell you that while technically allowed, if you want to do an Index lookup you would need to build caching infrastructure too and that will be quite a lot of work I assume.
https://github.com/llvm/llvm-project/pull/74710
More information about the cfe-commits
mailing list