================
@@ -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,
+                       FindReferencesLimit, Inputs.Index);
+
+    if (ReferencesResult.References.size() != 2)
+      return false;
+  }
+
+  return traverseFunctionParameters(NumberOfTemplateParameters);
+}
+
+Expected<Tweak::Effect>
+AbbreviateFunctionTemplate::apply(const Selection &Inputs) {
+  auto &Context = Inputs.AST->getASTContext();
+  auto FunctionParameterReplacements =
+      generateFunctionParameterReplacements(Context);
+
+  if (auto Err = FunctionParameterReplacements.takeError())
+    return Err;
+
+  auto Replacements = *FunctionParameterReplacements;
+  auto TemplateDeclarationReplacement =
+      generateTemplateDeclarationReplacement(Context);
+
+  if (auto Err = TemplateDeclarationReplacement.takeError())
+    return Err;
+
+  if (auto Err = Replacements.add(*TemplateDeclarationReplacement))
+    return Err;
+
+  return Effect::mainFileEdit(Context.getSourceManager(), Replacements);
+}
+
+bool AbbreviateFunctionTemplate::traverseFunctionParameters(
+    size_t NumberOfTemplateParameters) {
+  auto CurrentTemplateParameterBeingChecked = 0u;
+  auto FunctionParameters =
+      FunctionTemplateDeclaration->getAsFunction()->parameters();
+
+  for (auto ParameterIndex = 0u; ParameterIndex < FunctionParameters.size();
+       ParameterIndex++) {
+    auto [RawType, ParameterTypeQualifiers, ParameterQualifiers] =
+        deconstructType(FunctionParameters[ParameterIndex]->getOriginalType());
+
+    if (!RawType->isTemplateTypeParmType())
+      continue;
+
+    auto TemplateParameterIndex =
+        dyn_cast<TemplateTypeParmType>(RawType)->getIndex();
+
+    if (TemplateParameterIndex != CurrentTemplateParameterBeingChecked)
+      return false;
+
+    auto *TemplateParameterInfo =
+        &TemplateParameterInfoList[TemplateParameterIndex];
+    TemplateParameterInfo->FunctionParameterIndex = ParameterIndex;
+    TemplateParameterInfo->FunctionParameterTypeQualifiers =
+        ParameterTypeQualifiers;
+    TemplateParameterInfo->FunctionParameterQualifiers = ParameterQualifiers;
+
+    CurrentTemplateParameterBeingChecked++;
+  }
+
+  // All defined template parameters need to be used as function parameters
+  return CurrentTemplateParameterBeingChecked == NumberOfTemplateParameters;
+}
+
+llvm::Expected<tooling::Replacements>
+AbbreviateFunctionTemplate::generateFunctionParameterReplacements(
+    const ASTContext &Context) {
+  tooling::Replacements Replacements;
+  for (const auto &TemplateParameterInfo : TemplateParameterInfoList) {
+    auto FunctionParameterReplacement =
+        generateFunctionParameterReplacement(TemplateParameterInfo, Context);
+
+    if (auto Err = FunctionParameterReplacement.takeError())
+      return Err;
+
+    if (auto Err = Replacements.add(*FunctionParameterReplacement))
+      return Err;
+  }
+
+  return Replacements;
+}
+
+llvm::Expected<tooling::Replacement>
+AbbreviateFunctionTemplate::generateFunctionParameterReplacement(
+    const TemplateParameterInfo &TemplateParameterInfo,
+    const ASTContext &Context) {
+  auto &SourceManager = Context.getSourceManager();
+
+  const auto *Function = FunctionTemplateDeclaration->getAsFunction();
+  auto *Parameter =
+      Function->getParamDecl(TemplateParameterInfo.FunctionParameterIndex);
+  auto ParameterName = Parameter->getDeclName().getAsString();
+
+  llvm::SmallVector<std::string> ParameterTokens{};
+
+  if (const auto *TypeConstraint = TemplateParameterInfo.Constraint) {
+    auto *ConceptReference = TypeConstraint->getConceptReference();
+    auto *NamedConcept = ConceptReference->getNamedConcept();
+
+    ParameterTokens.push_back(NamedConcept->getQualifiedNameAsString());
+
+    if (const auto *TemplateArgs = TypeConstraint->getTemplateArgsAsWritten()) 
{
+      auto TemplateArgsRange = SourceRange(TemplateArgs->getLAngleLoc(),
+                                           TemplateArgs->getRAngleLoc());
+      auto TemplateArgsSource = toSourceCode(SourceManager, TemplateArgsRange);
+      ParameterTokens.push_back(TemplateArgsSource.str() + '>');
----------------
timon-ul wrote:

Honestly I am a bit confused of this codeblock, is there a case in which we 
actually generate angular brackets as output? What case would that be? I don't 
think you have a test for this.

Also why do we have to manually add the closing angular bracket? Shouldn't that 
already be optimally be part of the `SourceRange` or is that not present in the 
code for this case? An example for this would really help me understand.

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

Reply via email to