xref: /llvm-project/clang-tools-extra/clangd/refactor/tweaks/SpecialMembers.cpp (revision e26dad0a661e055076002d0de4ceb713b8ca6917)
168eac9a6SSam McCall //===--- SpecialMembers.cpp - Generate C++ special member functions -------===//
268eac9a6SSam McCall //
368eac9a6SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468eac9a6SSam McCall // See https://llvm.org/LICENSE.txt for license information.
568eac9a6SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668eac9a6SSam McCall //
768eac9a6SSam McCall //===----------------------------------------------------------------------===//
868eac9a6SSam McCall #include "ParsedAST.h"
968eac9a6SSam McCall #include "refactor/InsertionPoint.h"
1068eac9a6SSam McCall #include "refactor/Tweak.h"
1168eac9a6SSam McCall #include "clang/AST/DeclCXX.h"
1268eac9a6SSam McCall #include "clang/Sema/Sema.h"
1368eac9a6SSam McCall #include "clang/Tooling/Core/Replacement.h"
1468eac9a6SSam McCall #include "llvm/ADT/StringRef.h"
1568eac9a6SSam McCall #include "llvm/Support/Casting.h"
1668eac9a6SSam McCall #include "llvm/Support/Error.h"
1768eac9a6SSam McCall 
1868eac9a6SSam McCall namespace clang {
1968eac9a6SSam McCall namespace clangd {
2068eac9a6SSam McCall namespace {
2168eac9a6SSam McCall 
2268eac9a6SSam McCall // Returns code to declare missing copy/move constructors/assignment operators.
2368eac9a6SSam McCall // They will be deleted or defaulted to match the class's current state.
buildSpecialMemberDeclarations(const CXXRecordDecl & Class)2468eac9a6SSam McCall std::string buildSpecialMemberDeclarations(const CXXRecordDecl &Class) {
2568eac9a6SSam McCall   struct Members {
2668eac9a6SSam McCall     const CXXMethodDecl *Copy = nullptr;
2768eac9a6SSam McCall     const CXXMethodDecl *Move = nullptr;
2868eac9a6SSam McCall   } Ctor, Assign;
2968eac9a6SSam McCall 
3068eac9a6SSam McCall   for (const auto &M : Class.methods()) {
3168eac9a6SSam McCall     if (M->isCopyAssignmentOperator())
3268eac9a6SSam McCall       Assign.Copy = M;
3368eac9a6SSam McCall     else if (M->isMoveAssignmentOperator())
3468eac9a6SSam McCall       Assign.Move = M;
3568eac9a6SSam McCall     if (const auto *C = llvm::dyn_cast<CXXConstructorDecl>(M)) {
3668eac9a6SSam McCall       if (C->isCopyConstructor())
3768eac9a6SSam McCall         Ctor.Copy = C;
3868eac9a6SSam McCall       else if (C->isMoveConstructor())
3968eac9a6SSam McCall         Ctor.Move = C;
4068eac9a6SSam McCall     }
4168eac9a6SSam McCall   }
4268eac9a6SSam McCall 
4368eac9a6SSam McCall   std::string S;
4468eac9a6SSam McCall   llvm::raw_string_ostream OS(S);
4568eac9a6SSam McCall 
4668eac9a6SSam McCall   auto PrintMember = [&](const CXXMethodDecl *D, const char *MemberPattern,
4768eac9a6SSam McCall                          const char *ParmPattern) {
4868eac9a6SSam McCall     if (D && !D->isImplicit())
4968eac9a6SSam McCall       return;
5068eac9a6SSam McCall     bool Delete = !D || D->isDeleted();
5168eac9a6SSam McCall     OS << llvm::formatv(
5268eac9a6SSam McCall         "{0} = {1};\n",
5368eac9a6SSam McCall         llvm::formatv(MemberPattern, Class.getName(),
5468eac9a6SSam McCall                       llvm::formatv(ParmPattern, Class.getName())),
5568eac9a6SSam McCall         Delete ? "delete" : "default");
5668eac9a6SSam McCall   };
5768eac9a6SSam McCall   auto PrintMembers = [&](const Members &M, const char *MemberPattern) {
5868eac9a6SSam McCall     PrintMember(M.Copy, MemberPattern, /*ParmPattern=*/"const {0}&");
5968eac9a6SSam McCall     PrintMember(M.Move, MemberPattern, /*ParmPattern=*/"{0}&&");
6068eac9a6SSam McCall   };
6168eac9a6SSam McCall   PrintMembers(Ctor, /*MemberPattern=*/"{0}({1})");
6268eac9a6SSam McCall   PrintMembers(Assign, /*MemberPattern=*/"{0} &operator=({1})");
6368eac9a6SSam McCall 
6468eac9a6SSam McCall   return S;
6568eac9a6SSam McCall }
6668eac9a6SSam McCall 
6768eac9a6SSam McCall // A tweak that adds missing declarations of copy & move constructors.
6868eac9a6SSam McCall //
6968eac9a6SSam McCall // e.g. given `struct ^S{};`, produces:
7068eac9a6SSam McCall //   struct S {
7168eac9a6SSam McCall //     S(const S&) = default;
7268eac9a6SSam McCall //     S(S&&) = default;
7368eac9a6SSam McCall //     S &operator=(const S&) = default;
7468eac9a6SSam McCall //     S &operator=(S&&) = default;
7568eac9a6SSam McCall //   };
7668eac9a6SSam McCall //
7768eac9a6SSam McCall // Added members are defaulted or deleted to approximately preserve semantics.
7868eac9a6SSam McCall // (May not be a strict no-op when they were not implicitly declared).
7968eac9a6SSam McCall //
8068eac9a6SSam McCall // Having these spelled out is useful:
8168eac9a6SSam McCall //  - to understand the implicit behavior
8268eac9a6SSam McCall //  - to avoid relying on the implicit behavior
8368eac9a6SSam McCall //  - as a baseline for explicit modification
84*e26dad0aSKadir Cetinkaya class SpecialMembers : public Tweak {
8568eac9a6SSam McCall public:
8695a932fbSKazu Hirata   const char *id() const final;
kind() const8768eac9a6SSam McCall   llvm::StringLiteral kind() const override {
8868eac9a6SSam McCall     return CodeAction::REFACTOR_KIND;
8968eac9a6SSam McCall   }
title() const9068eac9a6SSam McCall   std::string title() const override {
9182cddb17SHaojian Wu     return llvm::formatv("Declare implicit {0} members",
9268eac9a6SSam McCall                          NeedCopy ? NeedMove ? "copy/move" : "copy" : "move");
9368eac9a6SSam McCall   }
9468eac9a6SSam McCall 
prepare(const Selection & Inputs)9568eac9a6SSam McCall   bool prepare(const Selection &Inputs) override {
9668eac9a6SSam McCall     // This tweak relies on =default and =delete.
9768eac9a6SSam McCall     if (!Inputs.AST->getLangOpts().CPlusPlus11)
9868eac9a6SSam McCall       return false;
9968eac9a6SSam McCall 
10068eac9a6SSam McCall     // Trigger only on class definitions.
10168eac9a6SSam McCall     if (auto *N = Inputs.ASTSelection.commonAncestor())
10268eac9a6SSam McCall       Class = const_cast<CXXRecordDecl *>(N->ASTNode.get<CXXRecordDecl>());
103*e26dad0aSKadir Cetinkaya     if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion())
10468eac9a6SSam McCall       return false;
10568eac9a6SSam McCall 
10668eac9a6SSam McCall     // Tweak is only available if some members are missing.
10768eac9a6SSam McCall     NeedCopy = !Class->hasUserDeclaredCopyConstructor() ||
10868eac9a6SSam McCall                !Class->hasUserDeclaredCopyAssignment();
10968eac9a6SSam McCall     NeedMove = !Class->hasUserDeclaredMoveAssignment() ||
11068eac9a6SSam McCall                !Class->hasUserDeclaredMoveConstructor();
11168eac9a6SSam McCall     return NeedCopy || NeedMove;
11268eac9a6SSam McCall   }
11368eac9a6SSam McCall 
apply(const Selection & Inputs)11468eac9a6SSam McCall   Expected<Effect> apply(const Selection &Inputs) override {
11568eac9a6SSam McCall     // Implicit special members are created lazily by clang.
11668eac9a6SSam McCall     // We need them so we can tell whether they should be =default or =delete.
11768eac9a6SSam McCall     Inputs.AST->getSema().ForceDeclarationOfImplicitMembers(Class);
11868eac9a6SSam McCall     std::string Code = buildSpecialMemberDeclarations(*Class);
11968eac9a6SSam McCall 
12068eac9a6SSam McCall     // Prefer to place the new members...
12168eac9a6SSam McCall     std::vector<Anchor> Anchors = {
12268eac9a6SSam McCall         // Below the default constructor
12368eac9a6SSam McCall         {[](const Decl *D) {
12468eac9a6SSam McCall            if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D))
12568eac9a6SSam McCall              return CCD->isDefaultConstructor();
12668eac9a6SSam McCall            return false;
12768eac9a6SSam McCall          },
12868eac9a6SSam McCall          Anchor::Below},
12968eac9a6SSam McCall         // Above existing constructors
13068eac9a6SSam McCall         {[](const Decl *D) { return llvm::isa<CXXConstructorDecl>(D); },
13168eac9a6SSam McCall          Anchor::Above},
13268eac9a6SSam McCall         // At the top of the public section
13368eac9a6SSam McCall         {[](const Decl *D) { return true; }, Anchor::Above},
13468eac9a6SSam McCall     };
13568eac9a6SSam McCall     auto Edit = insertDecl(Code, *Class, std::move(Anchors), AS_public);
13668eac9a6SSam McCall     if (!Edit)
13768eac9a6SSam McCall       return Edit.takeError();
13868eac9a6SSam McCall     return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
13968eac9a6SSam McCall                                 tooling::Replacements{std::move(*Edit)});
14068eac9a6SSam McCall   }
14168eac9a6SSam McCall 
14268eac9a6SSam McCall private:
14368eac9a6SSam McCall   bool NeedCopy = false, NeedMove = false;
14468eac9a6SSam McCall   CXXRecordDecl *Class = nullptr;
14568eac9a6SSam McCall };
146*e26dad0aSKadir Cetinkaya REGISTER_TWEAK(SpecialMembers)
14768eac9a6SSam McCall 
14868eac9a6SSam McCall } // namespace
14968eac9a6SSam McCall } // namespace clangd
15068eac9a6SSam McCall } // namespace clang
151