SureYeaah updated this revision to Diff 215098.
SureYeaah added a comment.

Removed debug info


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D65526/new/

https://reviews.llvm.org/D65526

Files:
  clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
  clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
  clang-tools-extra/clangd/unittests/TweakTests.cpp

Index: clang-tools-extra/clangd/unittests/TweakTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TweakTests.cpp
+++ clang-tools-extra/clangd/unittests/TweakTests.cpp
@@ -599,6 +599,97 @@
             R"cpp(const char * x = "test")cpp");
 }
 
+TWEAK_TEST(ExtractFunction);
+TEST_F(ExtractFunctionTest, PrepareFunctionTest) {
+  Header = R"cpp(
+    #define F(BODY) void FFF() { BODY }
+  )cpp";
+  Context = Function;
+  EXPECT_AVAILABLE(R"cpp(
+    [[int sum = 0;
+    for(;;)
+      sum++;]]
+    for(int i = 0; i < 5; i++) {
+      sum += i;
+    }
+  )cpp");
+  EXPECT_AVAILABLE(R"cpp([[int x;]])cpp");
+  // TODO: Add tests for macros after selectionTree works properly for macros.
+  // EXPECT_AVAILABLE(R"cpp( F (int x = 0; [[x = 1;]])cpp");
+  // FIXME: This should be unavailable since partially selected but
+  // selectionTree doesn't always work correctly for VarDecls.
+  //EXPECT_UNAVAILABLE(R"cpp(int [[x = 0]];)cpp");
+  EXPECT_UNAVAILABLE(R"cpp(
+    int sum = 0;
+    for(;;)
+      [[sum++;
+    sum++;]]
+  )cpp");
+  // Expressions aren't extracted.
+  EXPECT_UNAVAILABLE(R"cpp(int x = 0; [[x++;]])cpp");
+  // FIXME: ExtractFunction should be unavailable inside loop construct
+  // initalizer/condition.
+  // EXPECT_UNAVAILABLE(R"cpp( for([[int i = 0; i < 5;]] i++) )cpp");
+}
+
+TEST_F(ExtractFunctionTest, PrepareMethodTest) {
+  EXPECT_UNAVAILABLE(R"cpp(
+    class T {
+      void f() {
+        [[int x;]]
+      }
+    };
+  )cpp");
+}
+
+TEST_F(ExtractFunctionTest, ApplyTest) {
+  // We can extract from templated functions as long as no reference in the
+  // extraction depends on the template.
+  // Checks const qualifier and extraction parameters.
+  Header = R"cpp(
+      )cpp";
+  EXPECT_EQ(apply(
+                R"cpp(
+struct FOO {
+  int x;
+};
+template<int N, typename T>
+void f(int a) {
+  int b = N; T t; const int c; FOO foo;
+  int *ptr = &a;
+  [[a += foo.x; b = c; *ptr++;]]
+})cpp"),
+            R"cpp(
+struct FOO {
+  int x;
+};
+void extracted(int &a, int &b, const int &c, struct FOO &foo, int * &ptr) {
+a += foo.x; b = c; *ptr++;
+}
+template<int N, typename T>
+void f(int a) {
+  int b = N; T t; const int c; FOO foo;
+  int *ptr = &a;
+  extracted(a, b, c, foo, ptr);
+})cpp");
+  auto ShouldFail = [&](llvm::StringRef Code) {
+    EXPECT_THAT(apply(Code), StartsWith("fail:"));
+  };
+  // Don't extract because needs hoisting.
+  ShouldFail(R"cpp( void f() { [[int a = 5;]] a++;})cpp");
+  // Don't extract parameters that depend on template.
+  ShouldFail(R"cpp( template<int N> void f() { [[int a = N;]] })cpp");
+  ShouldFail(R"cpp( template<typename T> void f() { [[T t;]] })cpp");
+  // Don't extract return
+  ShouldFail(R"cpp( int f() { int a = 5; [[return a;]]})cpp");
+  // Don't extract break and continue.
+  // FIXME: We should be able to extract this.
+  ShouldFail(R"cpp( int f() { [[for(;;) break;]]})cpp");
+  ShouldFail(R"cpp( int f() { for(;;) [[continue;]]})cpp");
+  // Don't extract when we need to make a function as a parameter.
+  ShouldFail(R"cpp( void f() { [[int a; f();]] } )cpp");
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -0,0 +1,534 @@
+//===--- ExtractFunction.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 "ClangdUnit.h"
+#include "Logger.h"
+#include "Selection.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using Node = SelectionTree::Node;
+
+// Source is the part of code that is being extracted.
+// EnclosingFunction is the function/method inside which the source lies.
+// PreSource is everything before source and part of EnclosingFunction.
+// PostSource is everything after source and part of EnclosingFunction.
+// We split the file into 4 parts w.r.t. the Source.
+enum LocType { PRESOURCE, INSOURCE, POSTSOURCE, OUTSIDEFUNC };
+
+// SourceView forms a view of the code wrt to Source.
+class SourceView {
+public:
+  SourceView(const Node *N, const SourceManager &SM,
+             const LangOptions &LangOpts);
+  // Get the location type for a given location.
+  LocType getLocType(SourceLocation Loc) const;
+  // Parent of RootStatements being extracted.
+  const Node *Parent = nullptr;
+  // The range of the code being extracted.
+  SourceRange SourceRng;
+  // The function inside which our source resides.
+  const Node *EnclosingFunction;
+  SourceRange EnclosingFunctionRng;
+  const Node *LastRootStmt = nullptr;
+  bool isEligibleForExtraction() const { return IsEligibleForExtraction; }
+  SourceLocation getInsertionPoint() const {
+    return EnclosingFunctionRng.getBegin();
+  }
+
+private:
+  // Check if all child nodes of Parent are Root Stmts.
+  bool hasOnlyRootStmtChildren();
+  // We only support extraction of RootStmts. A RootStmt as a statement that is
+  // fully selected including all of it's children.
+  // Returns the (unselected) parent of all RootStmts.
+  static const Node *getParentOfRootStmts(const Node *CommonAnc);
+  // Find the union of source ranges of all child nodes of Parent. Returns an
+  // inavlid SourceRange if it fails to do so.
+  SourceRange findSourceRng();
+  // Finds the function in which the source lies.
+  static const Node *computeEnclosingFunction(const Node *CommonAnc);
+  // Compute the range spanned by the enclosing function.
+  // For non-template functions, it is the range of the FunctionDecl.
+  // For templated functions, it is the range of the FunctionTemplateDecl.
+  static SourceRange computeEnclosingFunctionRng(const Node *EnclosingFunction,
+                                                 const SourceManager &SM,
+                                                 const LangOptions &LangOpts);
+  const SourceManager &SM;
+  const LangOptions &LangOpts;
+  // Source is extractable if it lies inside a function and
+  bool IsEligibleForExtraction = false;
+};
+
+const Node *SourceView::computeEnclosingFunction(const Node *CommonAnc) {
+  // Walk up the SelectionTree until we find a function Decl
+  for (const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) {
+    if (CurNode->ASTNode.get<FunctionDecl>()) {
+      // FIXME: Support extraction from methods.
+      if (CurNode->ASTNode.get<CXXMethodDecl>())
+        return nullptr;
+      return CurNode;
+    }
+  }
+  return nullptr;
+}
+
+SourceRange
+SourceView::computeEnclosingFunctionRng(const Node *EnclosingFunction,
+                                        const SourceManager &SM,
+                                        const LangOptions &LangOpts) {
+  const Node *N = EnclosingFunction->Parent->ASTNode.get<FunctionTemplateDecl>()
+                      ? EnclosingFunction->Parent
+                      : EnclosingFunction;
+  return *toHalfOpenFileRange(SM, LangOpts, N->ASTNode.getSourceRange());
+}
+
+const Node *SourceView::getParentOfRootStmts(const Node *CommonAnc) {
+  if (!CommonAnc)
+    return nullptr;
+  switch (CommonAnc->Selected) {
+  case SelectionTree::Selection::Unselected:
+    return CommonAnc;
+  case SelectionTree::Selection::Partial:
+    // Treat Partially selected VarDecl as completely selected since
+    // SelectionTree doesn't always select VarDecls correctly.
+    if (!CommonAnc->ASTNode.get<VarDecl>())
+      return nullptr;
+    [[clang::fallthrough]];
+  case SelectionTree::Selection::Complete:
+    const Node *Parent = CommonAnc->Parent;
+    // If parent is a DeclStmt, we consider it a root statement and return its
+    // parent.
+    return Parent->ASTNode.get<DeclStmt>() ? Parent->Parent : Parent;
+  }
+}
+
+// FIXME: Check we're not extracting from the initializer/condition of a control
+// flow structure.
+// FIXME: Check that we don't extract the compound statement of the
+// enclosingFunction.
+SourceView::SourceView(const Node *CommonAnc, const SourceManager &SM,
+                       const LangOptions &LangOpts)
+    : SM(SM), LangOpts(LangOpts) {
+  Parent = getParentOfRootStmts(CommonAnc);
+  if (!Parent || Parent->Children.empty())
+    return;
+  // FIXME: check if EnclosingFunction has any attributes
+  EnclosingFunction = computeEnclosingFunction(Parent);
+  if (!EnclosingFunction)
+    return;
+  EnclosingFunctionRng =
+      computeEnclosingFunctionRng(EnclosingFunction, SM, LangOpts);
+  if (!hasOnlyRootStmtChildren())
+    return;
+  LastRootStmt = Parent->Children.back();
+  // Don't extract expressions.
+  if (Parent->Children.size() == 1 && LastRootStmt->ASTNode.get<Expr>())
+    return;
+  SourceRng = findSourceRng();
+  IsEligibleForExtraction = true;
+}
+
+SourceRange SourceView::findSourceRng() {
+  SourceRange SR;
+  for (const Node *Child : Parent->Children) {
+    auto ChildFileRange =
+        toHalfOpenFileRange(SM, LangOpts, Child->ASTNode.getSourceRange());
+    if (!ChildFileRange)
+      return SourceRange();
+    if (SR.isInvalid())
+      SR = *ChildFileRange;
+    else
+      SR.setEnd(ChildFileRange->getEnd());
+  }
+  return SR;
+}
+
+bool SourceView::hasOnlyRootStmtChildren() {
+  for (const Node *Child : Parent->Children) {
+    // Ensure every child is a statement.
+    if (!Child->ASTNode.get<Stmt>())
+      return false;
+    // We don't want to extract a partially selected subtree.
+    if (Child->Selected == SelectionTree::Partial)
+      return false;
+    // Only DeclStmt can be an unselected child since VarDecls claim the entire
+    // selection range in selectionTree.
+    if (Child->Selected == SelectionTree::Selection::Unselected &&
+        !Child->ASTNode.get<DeclStmt>())
+      return false;
+  }
+  return true;
+}
+
+// TODO: check if this works properly with macros.
+LocType SourceView::getLocType(SourceLocation Loc) const {
+  if (!SM.isPointWithin(Loc, EnclosingFunctionRng.getBegin(),
+                        EnclosingFunctionRng.getEnd()))
+    return OUTSIDEFUNC;
+  if (Loc < SourceRng.getBegin())
+    return PRESOURCE;
+  if (SourceRng.getEnd() < Loc)
+    return POSTSOURCE;
+  return INSOURCE;
+}
+
+// Stores information about the extracted function and provides methods for
+// rendering it.
+class NewFunction {
+private:
+  const SourceManager &SM;
+  struct Parameter {
+    std::string Name;
+    std::string Type;
+    bool PassByReference = true;
+    bool IsConst;
+    Parameter(std::string Name, std::string Type, bool IsConst)
+        : Name(Name), Type(Type), IsConst(IsConst) {}
+    std::string render(bool IsDefinition) const;
+    // FIXME: Better priority heuristics
+    bool operator<(const Parameter &Other) const { return Name < Other.Name; }
+    bool operator==(const Parameter &Other) const { return Name == Other.Name; }
+  };
+
+public:
+  std::string FuncName = "extracted";
+  std::string ReturnType = "void";
+  std::set<Parameter> Parameters;
+  std::vector<const Decl *> DeclsToHoist;
+  SourceRange BodyRange;
+  SourceLocation InsertionPoint;
+  tooling::ExtractionSemicolonPolicy SemicolonPolicy;
+  NewFunction(const SourceManager &SM, SourceRange BodyRange,
+              SourceLocation InsertionPoint,
+              tooling::ExtractionSemicolonPolicy SemicolonPolicy)
+      : SM(SM), BodyRange(BodyRange), InsertionPoint(InsertionPoint),
+        SemicolonPolicy(SemicolonPolicy){};
+  std::string render(bool IsDefinition) const;
+  std::string getFuncBody() const;
+  void addParam(llvm::StringRef Name, llvm::StringRef Type, bool IsConst);
+};
+
+std::string NewFunction::render(bool IsDefinition) const {
+  std::string Result;
+  if (IsDefinition) {
+    Result += ReturnType + " ";
+  }
+  Result += FuncName + "(";
+  bool Comma = false;
+  for (const Parameter &P : Parameters) {
+    if (Comma)
+      Result += ", ";
+    Result += P.render(IsDefinition);
+    Comma = true;
+  }
+  Result += ")";
+  if (IsDefinition)
+    Result += " {\n" + getFuncBody() + "\n}\n";
+  else if (SemicolonPolicy.isNeededInOriginalFunction())
+    Result += ";";
+  return Result;
+}
+
+std::string NewFunction::getFuncBody() const {
+  // TODO: Hoist Decls
+  // TODO: Add return statement.
+  return toSourceCode(SM, BodyRange).str() +
+         (SemicolonPolicy.isNeededInExtractedFunction() ? ";" : "");
+}
+
+void NewFunction::addParam(llvm::StringRef Name, llvm::StringRef Type,
+                           bool IsConst) {
+  Parameters.insert(Parameter(Name, Type, IsConst));
+}
+
+std::string NewFunction::Parameter::render(bool IsDefinition) const {
+  std::string Result;
+  if (IsDefinition) {
+    if (IsConst)
+      Result += "const ";
+    Result += Type + " ";
+    if (PassByReference)
+      Result += "&";
+  }
+  Result += Name;
+  return Result;
+}
+
+// Captures information about the source.
+class CapturedSourceInfo {
+public:
+  struct ReferencedDecl {
+    const Decl *TheDecl = nullptr;
+    LocType DeclaredIn;
+    bool IsReferencedInSource = false;
+    bool IsReferencedInPostSource = false;
+    bool IsAssigned = false;
+    bool MaybeModifiedOutside = false;
+    ReferencedDecl(){};
+    ReferencedDecl(const Decl *TheDecl, LocType DeclaredIn)
+        : TheDecl(TheDecl), DeclaredIn(DeclaredIn){};
+    void markOccurence(LocType ReferenceLoc);
+  };
+  llvm::DenseMap<const Decl *, ReferencedDecl> ReferencedDecls;
+  bool HasReturnStmt = false;
+  // For now we just care whether there exists a break/continue or not.
+  bool HasBreakOrContinue = false;
+  // FIXME: capture TypeAliasDecl and UsingDirectiveDecl
+  // FIXME: Capture type information as well.
+private:
+  // Return reference for a Decl, adding it if not already present.
+  ReferencedDecl &getReferencedDeclFor(const Decl *D);
+  const SourceView &SrcView;
+  CapturedSourceInfo(const SourceView &SrcView) : SrcView(SrcView) {}
+
+public:
+  static CapturedSourceInfo captureInfo(const SourceView &SrcView);
+  void captureBreakOrContinue(const Stmt *BreakOrContinue);
+  void captureReturn(const Stmt *Return);
+  void captureReference(const DeclRefExpr *DRE);
+};
+
+CapturedSourceInfo CapturedSourceInfo::captureInfo(const SourceView &SrcView) {
+  // We use the ASTVisitor instead of using the selection tree since we need to
+  // find references in the PostSource as well.
+  // FIXME: Check which statements we don't allow to extract.
+  class SourceViewVisitor
+      : public clang::RecursiveASTVisitor<SourceViewVisitor> {
+  public:
+    SourceViewVisitor(CapturedSourceInfo &Info) : Info(Info) {}
+    bool VisitDecl(Decl *D) { // NOLINT
+      Info.getReferencedDeclFor(D);
+      return true;
+    }
+    bool VisitDeclRefExpr(DeclRefExpr *DRE) { // NOLINT
+      Info.captureReference(DRE);
+      return true;
+    }
+    bool VisitReturnStmt(ReturnStmt *Return) { // NOLINT
+      Info.captureReturn(Return);
+      return true;
+    }
+    bool VisitBreakStmt(BreakStmt *Break) { // NOLINT
+      Info.captureBreakOrContinue(Break);
+      return true;
+    }
+    bool VisitContinueStmt(ContinueStmt *Continue) { // NOLINT
+      Info.captureBreakOrContinue(Continue);
+      return true;
+    }
+
+  private:
+    CapturedSourceInfo &Info;
+  };
+  CapturedSourceInfo Info(SrcView);
+  SourceViewVisitor Visitor(Info);
+  Visitor.TraverseDecl(const_cast<FunctionDecl *>(
+      SrcView.EnclosingFunction->ASTNode.get<FunctionDecl>()));
+  return Info;
+}
+
+CapturedSourceInfo::ReferencedDecl &
+CapturedSourceInfo::getReferencedDeclFor(const Decl *D) {
+  if (ReferencedDecls.find(D) == ReferencedDecls.end())
+    ReferencedDecls.insert(
+        {D, ReferencedDecl(D, SrcView.getLocType(D->getLocation()))});
+  return ReferencedDecls[D];
+}
+
+// FIXME: check if reference mutates the Decl being referred.
+void CapturedSourceInfo::captureReference(const DeclRefExpr *DRE) {
+  ReferencedDecl &RefDecl = getReferencedDeclFor(DRE->getDecl());
+  RefDecl.markOccurence(SrcView.getLocType(DRE->getLocation()));
+}
+
+void CapturedSourceInfo::captureReturn(const Stmt *Return) {
+  if (SrcView.getLocType(Return->getBeginLoc()) == INSOURCE)
+    HasReturnStmt = true;
+}
+
+// FIXME: check for broken break/continue only.
+void CapturedSourceInfo::captureBreakOrContinue(const Stmt *BreakOrContinue) {
+  if (SrcView.getLocType(BreakOrContinue->getBeginLoc()) == INSOURCE)
+    HasBreakOrContinue = true;
+}
+
+void CapturedSourceInfo::ReferencedDecl::markOccurence(LocType ReferenceLoc) {
+  switch (ReferenceLoc) {
+  case INSOURCE:
+    IsReferencedInSource = true;
+    break;
+  case POSTSOURCE:
+    IsReferencedInPostSource = true;
+    break;
+  default:
+    break;
+  }
+}
+
+// Adds parameters to ExtractedFunc and finds the corresponding decls that needs
+// to be hoisted. Returns true if able to find the parameters successfully.
+bool createParametersAndGetDeclsToHoist(
+    NewFunction &ExtractedFunc, const CapturedSourceInfo &CapturedInfo) {
+  // FIXME: Generate better types names. e.g. remove class from object types.
+  auto GetDeclType = [](const ValueDecl *D) {
+    return D->getType()
+        .getUnqualifiedType()
+        .getNonReferenceType()
+        .getAsString();
+  };
+  auto DependsOnTemplate = [](const ValueDecl *D) {
+    if (!D)
+      return false;
+    if (D->isTemplateParameter())
+      return true;
+    const Type *T = D->getType().getTypePtr();
+    return T && T->isDependentType();
+  };
+
+  for (const auto &KeyVal : CapturedInfo.ReferencedDecls) {
+    const auto &RefDecl = KeyVal.second;
+    const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(RefDecl.TheDecl);
+    // FIXME: Add support for extracting Template dependent Decls.
+    bool WillBeParameter =
+        (RefDecl.DeclaredIn == PRESOURCE && RefDecl.IsReferencedInSource) ||
+        (RefDecl.DeclaredIn == INSOURCE && RefDecl.IsReferencedInPostSource);
+    bool TemplateDependent = DependsOnTemplate(VD);
+    // Don't extract template dependent Decls that are in source.
+    if (RefDecl.DeclaredIn == INSOURCE && TemplateDependent)
+      return false;
+    // Check if the Decl will become a parameter.
+    if (!WillBeParameter)
+      continue;
+    // Parameter specific checks.
+    // Can't parameterise if the Decl isn't a ValueDecl, is a Template Dependent
+    // decl or a FunctionDecl(this includes the case of recursive call to
+    // EnclosingFunc in Source).
+    if (!VD || TemplateDependent ||
+        dyn_cast_or_null<FunctionDecl>(RefDecl.TheDecl))
+      return false;
+    // FIXME: Need better qualifier checks: check mutated status for
+    // ReferencedDecl
+    // FIXME: check if parameter will be a non l-value reference.
+    // FIXME: We don't want to always pass variables of types like int,
+    // pointers, etc by reference.
+    bool IsConstDecl = VD->getType().isConstQualified();
+    ExtractedFunc.addParam(VD->getName(), GetDeclType(VD), IsConstDecl);
+    // If a Decl was Declared in source and referenced in post source, it needs
+    // to be hoisted.
+    if (RefDecl.DeclaredIn == INSOURCE && RefDecl.IsReferencedInPostSource)
+      ExtractedFunc.DeclsToHoist.push_back(RefDecl.TheDecl);
+  }
+  return true;
+}
+
+// FIXME: add support for adding other function return types besides void.
+// FIXME: assign the value returned by non void extracted function.
+llvm::Optional<NewFunction> getExtractedFunction(const SourceView &SrcView,
+                                                 const SourceManager &SM,
+                                                 const LangOptions &LangOpts) {
+  CapturedSourceInfo CapturedInfo = CapturedSourceInfo::captureInfo(SrcView);
+  // If there is any reference or source has a recursive call, we don't extract
+  // for now.
+  // FIXME: Bail out for Return only if we are extracting a return as well as
+  // want to return a Hoisted decl value.
+  if (CapturedInfo.HasReturnStmt || CapturedInfo.HasBreakOrContinue)
+    return llvm::None;
+  // Get semicolon extraction policy and extend FuncBodyRange if needed.
+  SourceRange FuncBodyRange = SrcView.SourceRng;
+  auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute(
+      SrcView.LastRootStmt->ASTNode.get<Stmt>(), FuncBodyRange, SM, LangOpts);
+  NewFunction ExtractedFunc(SM, FuncBodyRange, SrcView.getInsertionPoint(),
+                            std::move(SemicolonPolicy));
+  if (!createParametersAndGetDeclsToHoist(ExtractedFunc, CapturedInfo))
+    return llvm::None;
+  // For now, we give up if any Decl needs to be hoisted.
+  // FIXME: Use the hoisted decls
+  if (!ExtractedFunc.DeclsToHoist.empty())
+    return llvm::None;
+  // FIXME: Use SemicolonExtractionPolicy
+  return ExtractedFunc;
+}
+
+/// Extracts statements to a new function and replaces the statements with a
+/// call to the new function.
+class ExtractFunction : public Tweak {
+public:
+  const char *id() const override final;
+
+  bool prepare(const Selection &Inputs) override;
+  Expected<Effect> apply(const Selection &Inputs) override;
+  std::string title() const override { return "Extract to function"; }
+  Intent intent() const override { return Refactor; }
+
+private:
+  std::unique_ptr<SourceView> SrcView;
+};
+
+REGISTER_TWEAK(ExtractFunction)
+tooling::Replacement replaceWithFuncCall(const NewFunction &ExtractedFunc,
+                                         const SourceManager &SM,
+                                         const LangOptions &LangOpts) {
+  std::string FuncCall = ExtractedFunc.render(false);
+  return tooling::Replacement(
+      SM, CharSourceRange(ExtractedFunc.BodyRange, false), FuncCall, LangOpts);
+}
+
+tooling::Replacement createFunctionDefinition(const NewFunction &ExtractedFunc,
+                                              const SourceManager &SM) {
+  std::string FunctionDef = ExtractedFunc.render(true);
+  return tooling::Replacement(SM, ExtractedFunc.InsertionPoint, 0, FunctionDef);
+}
+
+bool ExtractFunction::prepare(const Selection &Inputs) {
+  const Node *CommonAnc = Inputs.ASTSelection.commonAncestor();
+  const SourceManager &SM = Inputs.AST.getSourceManager();
+  const LangOptions &LangOpts = Inputs.AST.getASTContext().getLangOpts();
+  SrcView = llvm::make_unique<SourceView>(CommonAnc, SM, LangOpts);
+  return SrcView->isEligibleForExtraction();
+}
+
+Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) {
+  const SourceManager &SM = Inputs.AST.getSourceManager();
+  const LangOptions &LangOpts = Inputs.AST.getASTContext().getLangOpts();
+  auto ExtractedFunc = getExtractedFunction(*SrcView, SM, LangOpts);
+  // FIXME: Add more types of errors.
+  if (!ExtractedFunc)
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   +"Too complex to extract.");
+  tooling::Replacements Result;
+  if (auto Err = Result.add(createFunctionDefinition(*ExtractedFunc, SM)))
+    return std::move(Err);
+  if (auto Err = Result.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts)))
+    return std::move(Err);
+  return Effect::applyEdit(Result);
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -16,6 +16,7 @@
   DumpAST.cpp
   ExpandAutoType.cpp
   ExpandMacro.cpp
+  ExtractFunction.cpp
   ExtractVariable.cpp
   RawStringLiteral.cpp
   SwapIfBranches.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to