robot updated this revision to Diff 532575.
robot added a comment.

Remove left-over test file of other ptch from CMakeLists.txt


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D153152

Files:
  clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
  clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp

Index: clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp
@@ -0,0 +1,665 @@
+//===-- DeclarePureVirtualsTests.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 "TestTU.h"
+#include "TweakTesting.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TWEAK_TEST(DeclarePureVirtuals);
+
+TEST_F(DeclarePureVirtualsTest, AvailabilityTriggerOnBaseSpecifier) {
+  EXPECT_AVAILABLE(R"cpp(
+    class MyBase {
+      virtual void myFunction() = 0;
+    };
+
+    class MyDerived : ^p^u^b^l^i^c^ ^M^y^B^a^s^e {
+    };
+
+    class MyDerived2 : ^M^y^B^a^s^e {
+    };
+  )cpp");
+}
+
+TEST_F(DeclarePureVirtualsTest, AvailabilityTriggerOnClass) {
+  EXPECT_AVAILABLE(R"cpp(
+    class MyBase {
+      virtual void myFunction() = 0;
+    };
+
+    class ^M^y^D^e^r^i^v^e^d: public MyBase {^
+    // but not here, see AvailabilityTriggerInsideClass
+    ^};
+  )cpp");
+}
+
+TEST_F(DeclarePureVirtualsTest, AvailabilityTriggerInsideClass) {
+  // TODO: this should actually be available but I don't know how to implement
+  // it: the common node of the selection returns the TU, so I get no
+  // information about which class we're in.
+  EXPECT_UNAVAILABLE(R"cpp(
+    class MyBase {
+      virtual void myFunction() = 0;
+    };
+
+    class MyDerived : public MyBase {
+    ^
+    };
+  )cpp");
+}
+
+TEST_F(DeclarePureVirtualsTest, UnavailabilityNoBases) {
+  EXPECT_UNAVAILABLE(R"cpp(
+    class ^N^o^D^e^r^i^v^e^d^ ^{^
+    ^};
+  )cpp");
+}
+
+// TODO: should the tweak available if there are no pure virtual functions and
+// do nothing? or should it be unavailable?
+
+TEST_F(DeclarePureVirtualsTest, SinglePureVirtualFunction) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction() = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction() = 0;
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, MultipleInheritanceFirstClass) {
+  const char *Test = R"cpp(
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyBase2 {
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : pub^lic MyBase1, public MyBase2 {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyBase2 {
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : public MyBase1, public MyBase2 {
+virtual void myFunction1() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, MultipleInheritanceSecondClass) {
+  const char *Test = R"cpp(
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyBase2 {
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : public MyBase1, pub^lic MyBase2 {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyBase2 {
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : public MyBase1, public MyBase2 {
+virtual void myFunction2() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, SingleInheritanceMultiplePureVirtualFunctions) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction1() override;
+virtual void myFunction2() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, SingleInheritanceMixedVirtualFunctions) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+  virtual void myFunction2();
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+  virtual void myFunction2();
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction1() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest,
+       TwoLevelsInheritanceOnePureVirtualFunctionInTopBase) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+class MyIntermediate : public MyBase {
+};
+
+class MyDerived : pub^lic MyIntermediate {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+class MyIntermediate : public MyBase {
+};
+
+class MyDerived : public MyIntermediate {
+virtual void myFunction1() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest,
+       TwoLevelsInheritancePureVirtualFunctionsInBothBases) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+class MyIntermediate : public MyBase {
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : pub^lic MyIntermediate {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+class MyIntermediate : public MyBase {
+  virtual void myFunction2() = 0;
+};
+
+class MyDerived : public MyIntermediate {
+virtual void myFunction1() override;
+virtual void myFunction2() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest,
+       TwoLevelInheritanceFunctionNoLongerPureVirtual) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+class MyIntermediate : public MyBase {
+  virtual void myFunction1() override {}
+};
+
+class MyDerived : pub^lic MyIntermediate {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+class MyIntermediate : public MyBase {
+  virtual void myFunction1() override {}
+};
+
+class MyDerived : public MyIntermediate {
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, VirtualFunctionWithDefaultParameters) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction(int x, int y = 42) = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction(int x, int y = 42) = 0;
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction(int x, int y = 42) override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, FunctionOverloading) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction(int x) = 0;
+  virtual void myFunction(float x) = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction(int x) = 0;
+  virtual void myFunction(float x) = 0;
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction(int x) override;
+virtual void myFunction(float x) override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, OverrideAlreadyExisting) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction() = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+  virtual void myFunction() override;
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction() = 0;
+};
+
+class MyDerived : public MyBase {
+  virtual void myFunction() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, TwoOverloadsOnlyOnePureVirtual) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction(int x) = 0;
+  virtual void myFunction(float x);
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction(int x) = 0;
+  virtual void myFunction(float x);
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction(int x) override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, DerivedClassHasNonOverridingFunction) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction(int x) = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+  void myFunction(float x);
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction(int x) = 0;
+};
+
+class MyDerived : public MyBase {
+  void myFunction(float x);
+virtual void myFunction(int x) override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, PureVirtualFunctionWithNoexcept) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction() noexcept = 0;
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction() noexcept = 0;
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction() noexcept override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, MixOfVirtualAndNonVirtualMemberFunctions) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+    void myFunction2();
+};
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+    void myFunction2();
+};
+
+class MyDerived : public MyBase {
+virtual void myFunction1() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, PureVirtualFunctionWithBody) {
+  const char *Test = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+void MyBase::myFunction1() {
+}
+
+class MyDerived : pub^lic MyBase {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase {
+  virtual void myFunction1() = 0;
+};
+
+void MyBase::myFunction1() {
+}
+
+class MyDerived : public MyBase {
+virtual void myFunction1() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, TraversalOrder) {
+  const char *Test = R"cpp(
+class MyBase0 {
+  virtual void myFunction0() = 0;
+};
+
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyBase2 : MyBase0, MyBase1 {
+  virtual void myFunction2() = 0;
+};
+
+class MyBase3 {
+  virtual void myFunction3() = 0;
+};
+
+class MyBase4 : MyBase2, MyBase3 {
+  virtual void myFunction4() = 0;
+};
+
+class MyDerived : pub^lic MyBase4 {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase0 {
+  virtual void myFunction0() = 0;
+};
+
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyBase2 : MyBase0, MyBase1 {
+  virtual void myFunction2() = 0;
+};
+
+class MyBase3 {
+  virtual void myFunction3() = 0;
+};
+
+class MyBase4 : MyBase2, MyBase3 {
+  virtual void myFunction4() = 0;
+};
+
+class MyDerived : public MyBase4 {
+virtual void myFunction0() override;
+virtual void myFunction1() override;
+virtual void myFunction2() override;
+virtual void myFunction3() override;
+virtual void myFunction4() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+TEST_F(DeclarePureVirtualsTest, AllBaseClassSpecifiers) {
+  const char *Test = R"cpp(
+class MyBase0 {
+  virtual void myFunction0() = 0;
+};
+
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class My^Derived : MyBase0, MyBase1 {
+};
+  )cpp";
+
+  const char *Expected = R"cpp(
+class MyBase0 {
+  virtual void myFunction0() = 0;
+};
+
+class MyBase1 {
+  virtual void myFunction1() = 0;
+};
+
+class MyDerived : MyBase0, MyBase1 {
+virtual void myFunction0() override;
+virtual void myFunction1() override;
+};
+  )cpp";
+
+  EXPECT_EQ(apply(Test), Expected);
+}
+
+// This test should fail since MyBase is incomplete. No idea how to test that.
+// TEST_F(DeclarePureVirtualsTest, IncompleteBassClass) {
+//   const char *Test = R"cpp(
+// class MyBase;
+//
+// class MyDerived : My^Base {
+// };
+//   )cpp";
+//
+//   const char *Expected = R"cpp(
+// class MyBase;
+//
+// class MyDerived : MyBase {
+// };
+//   )cpp";
+//
+//   EXPECT_EQ(apply(Test), Expected);
+// }
+
+// This test should fail since MyBase is incomplete. No idea how to test that.
+// This is a different failure mode since it probably fails to apply but not to
+// prepare.
+// TEST_F(DeclarePureVirtualsTest, IncompleteBassClass) {
+//   const char *Test = R"cpp(
+// class MyBase;
+//
+// class My^Derived : MyBase {
+// };
+//   )cpp";
+//
+//   const char *Expected = R"cpp(
+// class MyBase;
+//
+// class MyDerived : MyBase {
+// };
+//   )cpp";
+//
+//   EXPECT_EQ(apply(Test), Expected);
+// }
+
+// This test should fail since MyBase is unknown. No idea how to test that.
+// TEST_F(DeclarePureVirtualsTest, UnknownBaseSpecifier) {
+//   const char *Test = R"cpp(
+// class MyDerived : My^Base {
+// };
+//   )cpp";
+//
+//   const char *Expected = R"cpp(
+// class MyDerived : MyBase {
+// };
+//   )cpp";
+//
+//   EXPECT_EQ(apply(Test), Expected);
+// }
+
+// This test should fail since MyBase is unknown. No idea how to test that.
+// It's a different failure mode since it will probably fail to apply but not to
+// prepare.
+// TEST_F(DeclarePureVirtualsTest, UnknownBaseSpecifier) {
+//   const char *Test = R"cpp(
+// class My^Derived : MyBase {
+// };
+//   )cpp";
+//
+//   const char *Expected = R"cpp(
+// class MyDerived : MyBase {
+// };
+//   )cpp";
+//
+//   EXPECT_EQ(apply(Test), Expected);
+// }
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/unittests/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/unittests/CMakeLists.txt
+++ clang-tools-extra/clangd/unittests/CMakeLists.txt
@@ -118,6 +118,7 @@
   tweaks/AnnotateHighlightingsTests.cpp
   tweaks/DefineInlineTests.cpp
   tweaks/DefineOutlineTests.cpp
+  tweaks/DeclarePureVirtualsTests.cpp
   tweaks/DumpASTTests.cpp
   tweaks/DumpRecordLayoutTests.cpp
   tweaks/DumpSymbolTests.cpp
Index: clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp
@@ -0,0 +1,220 @@
+//===--- DeclarePureVirtuals.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 "Selection.h"
+#include "SourceCode.h"
+#include "refactor/Tweak.h"
+#include "clang/AST/ASTContextAllocate.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/AttributeCommonInfo.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/ASTSelection.h"
+#include "clang/Tooling/Syntax/Tokens.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+// Determines if the class which provided the final overrider map has a non-pure
+// virtual override of Method.
+bool hasNonPureVirtOverrideFor(const CXXFinalOverriderMap &Map,
+                               const CXXMethodDecl *Method) {
+  auto const It = Map.find(Method);
+  if (It == Map.end()) {
+    return false;
+  }
+
+  for (const auto &Overrider : It->second) {
+    for (const auto &WhatIsThis :
+         Overrider.second) { // TODO: why is there a second level????
+      return !WhatIsThis.Method->isPure();
+    }
+  }
+
+  assert(false && "override was found but overrider list was empty");
+}
+
+// Appends a declaration to To which overrides Method.
+void appendDeclForOverride(std::string &To, const CXXMethodDecl *Method,
+                           const SourceManager &SM,
+                           const syntax::TokenBuffer &TokBuf) {
+  const SourceRange MethodDeclRange{Method->getBeginLoc(), Method->getEndLoc()};
+
+  const llvm::ArrayRef<syntax::Token> Tokens =
+      TokBuf.expandedTokens(MethodDeclRange);
+  const auto EqTok =
+      llvm::find_if(llvm::reverse(Tokens), [](const syntax::Token &Tok) {
+        return Tok.kind() == tok::equal;
+      });
+  assert(EqTok != Tokens.rend());
+
+  To.append(SM.getCharacterData(MethodDeclRange.getBegin()),
+            SM.getCharacterData(EqTok->location()));
+  To.append("override;\n");
+}
+
+// Returns a string with override declarations for all virtual functions in the
+// inheritance hierarchy of Start (including Start itself) which are still pure
+// virtual in Target. Start can be the same class as Target. Target must be the
+// same as or derived from Start.
+std::string collectPureVirtualFuncOverrideDecls(
+    const CXXRecordDecl &Target, const CXXRecordDecl &Start,
+    const SourceManager &SM, const syntax::TokenBuffer &TokBuf) {
+  std::string Additions;
+
+  assert(&Target == &Start || Target.isDerivedFrom(&Start));
+
+  CXXFinalOverriderMap FinalOverriderMapOfStart;
+  Start.getFinalOverriders(FinalOverriderMapOfStart);
+
+  CXXFinalOverriderMap FinalOverriderMapOfTarget;
+  // If &Target == &Start then Target doesn't already have any
+  // overrides for functions that are pure in Start. The map remains empty,
+  // which means hasOverrideFor will return false below, which is OK since the
+  // function is then counted as pure virtual.
+  if (&Target != &Start) {
+    Target.getFinalOverriders(FinalOverriderMapOfTarget);
+  }
+
+  for (const auto &bar : FinalOverriderMapOfStart) { // TODO: see line 44
+    // const CXXMethodDecl *Method = bar.first;
+    const OverridingMethods &Overrides = bar.second;
+
+    for (const std::pair<unsigned int,
+                         llvm::SmallVector<UniqueVirtualMethod, 4>>
+             &meow : // TODO
+         Overrides) {
+      const auto &IdontKnowWhatThisIs = meow.second; // TODO
+      for (const UniqueVirtualMethod &Override : IdontKnowWhatThisIs) {
+        // TODO: conjunction of all overrides? or rely on there's only one?
+        if (Override.Method->isPure() &&
+            !hasNonPureVirtOverrideFor(FinalOverriderMapOfTarget,
+                                       Override.Method)) {
+          appendDeclForOverride(Additions, Override.Method, SM, TokBuf);
+        }
+      }
+    }
+  }
+
+  return Additions;
+}
+
+// Finds the CXXBaseSpecifier in/under the selection, if any.
+const CXXBaseSpecifier *findBaseSpecifier(const SelectionTree::Node *Node) {
+  if (!Node)
+    return nullptr;
+
+  const DynTypedNode &ASTNode = Node->ASTNode;
+  const CXXBaseSpecifier *BaseSpec = ASTNode.get<CXXBaseSpecifier>();
+  if (BaseSpec)
+    return BaseSpec;
+
+  const SelectionTree::Node *const Parent = Node->Parent;
+  if (Parent)
+    return Parent->ASTNode.get<CXXBaseSpecifier>();
+
+  return nullptr;
+}
+
+/// Declares overrides for all pure virtual function in a class hierarchy,
+/// starting with a base class specifier.
+///
+/// Before:
+///   class Base    { virtual void foo() = 0; };
+///   class Derived {};
+///
+/// After:
+///   class Base    { virtual void foo() = 0; };
+///   class Derived { virtual void foo() override; };
+class DeclarePureVirtuals : public Tweak {
+public:
+  const char *id() const override;
+
+  std::string title() const override {
+    return "Override pure virtual functions";
+  }
+
+  llvm::StringLiteral kind() const override {
+    return CodeAction::QUICKFIX_KIND;
+  }
+
+  bool prepare(const Selection &Sel) override {
+    const SelectionTree::Node *const CommonAncestor =
+        Sel.ASTSelection.commonAncestor();
+    if (!CommonAncestor)
+      return false;
+
+    // maybe selected a class, in which case override functions of all bases
+    SelectedDerivedClass = CommonAncestor->ASTNode.get<CXXRecordDecl>();
+    if (SelectedDerivedClass) {
+      return !SelectedDerivedClass->bases().empty();
+    }
+
+    // maybe selected a base class specifier, in which case only override those
+    // bases's functions
+    const DeclContext &DC = CommonAncestor->getDeclContext();
+    SelectedDerivedClass = dyn_cast<CXXRecordDecl>(&DC);
+    if (SelectedDerivedClass) {
+      SelectedBaseSpecifier =
+          findBaseSpecifier(Sel.ASTSelection.commonAncestor());
+      return true;
+    }
+
+    return false;
+  }
+
+  const CXXRecordDecl *SelectedDerivedClass = nullptr;
+  const CXXBaseSpecifier *SelectedBaseSpecifier = nullptr;
+
+  Expected<Effect> apply(const Selection &Sel) override {
+    assert(SelectedDerivedClass); // prepare must have been called and returned
+                                  // true
+
+    std::string Additions;
+    SourceManager &SM = Sel.AST->getSourceManager();
+
+    if (SelectedBaseSpecifier) {
+      // TODO: can getType return null?
+      auto const *Start =
+          SelectedBaseSpecifier->getType()->getAsCXXRecordDecl();
+      if (!Start) {
+        return llvm::createStringError(
+            llvm::inconvertibleErrorCode(),
+            "selected base class specifier does not refer to a C++ class");
+      }
+      Additions = collectPureVirtualFuncOverrideDecls(
+          *SelectedDerivedClass, *Start, SM, Sel.AST->getTokens());
+    } else {
+      Additions += collectPureVirtualFuncOverrideDecls(
+          *SelectedDerivedClass, *SelectedDerivedClass, SM,
+          Sel.AST->getTokens());
+    }
+
+    // TODO: can we apply this tweak in a header if we apply the effect to the
+    // "main file"?
+    return Effect::mainFileEdit(
+        SM, tooling::Replacements(tooling::Replacement(
+                SM, SelectedDerivedClass->getEndLoc(), 0, Additions)));
+  }
+};
+
+REGISTER_TWEAK(DeclarePureVirtuals)
+
+} // 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
@@ -29,6 +29,7 @@
   RemoveUsingNamespace.cpp
   SpecialMembers.cpp
   SwapIfBranches.cpp
+  DeclarePureVirtuals.cpp
 
   LINK_LIBS
   clangAST
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to