https://github.com/ckandeler updated 
https://github.com/llvm/llvm-project/pull/71950

>From 5505b52f8d8027e90d4da6a7295cc47cac2365af Mon Sep 17 00:00:00 2001
From: Christian Kandeler <[email protected]>
Date: Fri, 10 Nov 2023 16:01:18 +0100
Subject: [PATCH] [clangd] Let DefineOutline tweak create a definition from
 scratch

---
 .../clangd/refactor/tweaks/DefineOutline.cpp  | 73 ++++++++++++++++---
 .../unittests/tweaks/DefineOutlineTests.cpp   | 32 ++++++--
 2 files changed, 88 insertions(+), 17 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
index 45e7adeeefcd9..6b2fa5ff1fbd9 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
@@ -13,6 +13,7 @@
 #include "ParsedAST.h"
 #include "Selection.h"
 #include "SourceCode.h"
+#include "XRefs.h"
 #include "refactor/Tweak.h"
 #include "support/Logger.h"
 #include "support/Path.h"
@@ -164,6 +165,14 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
     Source.insert(0, "inline ");
   if (!TemplatePrefix.empty())
     Source.insert(0, TemplatePrefix);
+  if (!FD->hasBody() && !FD->isExplicitlyDefaulted()) {
+    Source.pop_back(); // The semicolon finishing the declaration.
+    Source += " {";
+    if (!FD->getReturnType()->isVoidType())
+      Source += " return {}; ";
+    Source += "}";
+  }
+
   return Source;
 }
 
@@ -375,6 +384,30 @@ struct InsertionAnchor {
 // initializers.
 SourceRange getDeletionRange(const FunctionDecl *FD,
                              const syntax::TokenBuffer &TokBuf) {
+  if (!FD->hasBody()) {
+    if (!FD->isExplicitlyDefaulted())
+      return {};
+
+    auto tokens = TokBuf.expandedTokens(FD->getSourceRange());
+    for (auto It = std::rbegin(tokens); It != std::rend(tokens); ++It) {
+      if (It->kind() == tok::kw_default) {
+        const auto nextIt = std::next(It);
+        if (nextIt == std::rend(tokens))
+          return {};
+        const auto &ExpandedEquals = *nextIt;
+        if (ExpandedEquals.kind() != tok::equal)
+          return {};
+        auto SpelledEquals =
+            TokBuf.spelledForExpanded(llvm::ArrayRef(ExpandedEquals));
+        if (!SpelledEquals)
+          return {};
+        return {SpelledEquals->front().location(),
+                FD->getDefaultLoc().getLocWithOffset(1)};
+      }
+    }
+    return {};
+  }
+
   auto DeletionRange = FD->getBody()->getSourceRange();
   if (auto *CD = llvm::dyn_cast<CXXConstructorDecl>(FD)) {
     // AST doesn't contain the location for ":" in ctor initializers. Therefore
@@ -432,19 +465,32 @@ class DefineOutline : public Tweak {
     return CodeAction::REFACTOR_KIND;
   }
   std::string title() const override {
-    return "Move function body to out-of-line";
+    if (Source->doesThisDeclarationHaveABody())
+      return "Move function body to out-of-line";
+    return "Create function body out-of-line";
   }
 
   bool prepare(const Selection &Sel) override {
     SameFile = !isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts());
     Source = getSelectedFunction(Sel.ASTSelection.commonAncestor());
 
-    // Bail out if the selection is not a in-line function definition.
-    if (!Source || !Source->doesThisDeclarationHaveABody() ||
-        Source->isOutOfLine())
+    // Bail out if the selection is not a function declaration.
+    if (!Source || Source->isDeleted() || Source->isOutOfLine())
       return false;
 
-    // Bail out if this is a function template specialization, as their
+    // Bail out if a definition exists somewhere else.
+    if (!Source->hasBody() && !Source->isExplicitlyDefaulted() && Sel.Index) {
+      bool HasDefinition = false;
+      Sel.Index->lookup({{getSymbolID(Source)}},
+                        [&HasDefinition](const Symbol &S) {
+                          if (S.Definition)
+                            HasDefinition = true;
+                        });
+      if (HasDefinition)
+        return false;
+    }
+
+    // Bail out if this is a function template or specialization, as their
     // definitions need to be visible in all including translation units.
     if (Source->getTemplateSpecializationInfo())
       return false;
@@ -574,12 +620,17 @@ class DefineOutline : public Tweak {
     if (!Effect)
       return Effect.takeError();
 
-    tooling::Replacements HeaderUpdates(tooling::Replacement(
-        Sel.AST->getSourceManager(),
-        CharSourceRange::getTokenRange(*toHalfOpenFileRange(
-            SM, Sel.AST->getLangOpts(),
-            getDeletionRange(Source, Sel.AST->getTokens()))),
-        ";"));
+    tooling::Replacements HeaderUpdates;
+    auto DeletionRange = getDeletionRange(Source, Sel.AST->getTokens());
+    if (DeletionRange.isValid()) {
+      if (auto Error = HeaderUpdates.add(tooling::Replacement(
+              Sel.AST->getSourceManager(),
+              CharSourceRange::getTokenRange(*toHalfOpenFileRange(
+                  SM, Sel.AST->getLangOpts(), DeletionRange)),
+              ";"))) {
+        return Error;
+      }
+    }
 
     if (Source->isInlineSpecified()) {
       auto DelInline =
diff --git a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp 
b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
index 7689bf3181a5f..b8d5c6123a1a8 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
@@ -65,9 +65,6 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
   FileName = "Test.hpp";
   // Not available unless function name or fully body is selected.
   EXPECT_UNAVAILABLE(R"cpp(
-    // Not a definition
-    vo^i[[d^ ^f]]^oo();
-
     [[vo^id ]]foo[[()]] {[[
       [[(void)(5+3);
       return;]]
@@ -100,10 +97,9 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
       return;
     }]]]])cpp");
 
-  // Not available on defaulted/deleted members.
+  // Not available on deleted members.
   EXPECT_UNAVAILABLE(R"cpp(
     class Foo {
-      Fo^o() = default;
       F^oo(const Foo&) = delete;
     };)cpp");
 
@@ -164,12 +160,24 @@ TEST_F(DefineOutlineTest, ApplyTest) {
     llvm::StringRef ExpectedHeader;
     llvm::StringRef ExpectedSource;
   } Cases[] = {
-      // Simple check
+      // Simple check: Move
       {
           "void fo^o() { return; }",
           "void foo() ;",
           "void foo() { return; }",
       },
+      // Simple check: Create
+      {
+          "void fo^o(int i = 5);",
+          "void foo(int i = 5);",
+          "void foo(int i ) {}",
+      },
+      // Simple check: Create with return value
+      {
+          "int fo^o();",
+          "int foo();",
+          "int foo() { return {}; }",
+      },
       // Inline specifier.
       {
           "inline void fo^o() { return; }",
@@ -242,6 +250,18 @@ TEST_F(DefineOutlineTest, ApplyTest) {
 inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
 )cpp",
           ""},
+      // Default ctor
+      {
+          R"cpp(
+              class Foo {
+                F^oo() = default;
+              };)cpp",
+          R"cpp(
+              class Foo {
+                Foo() ;
+              };)cpp",
+          "Foo::Foo() = default;",
+      },
       // Virt specifiers.
       {
           R"cpp(

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

Reply via email to