================ @@ -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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
