1//===--- SpecialMembers.cpp - Generate C++ special member functions -------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include "ParsedAST.h"
9#include "refactor/InsertionPoint.h"
10#include "refactor/Tweak.h"
11#include "clang/AST/DeclCXX.h"
12#include "clang/Sema/Sema.h"
13#include "clang/Tooling/Core/Replacement.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Support/Casting.h"
16#include "llvm/Support/Error.h"
17
18namespace clang {
19namespace clangd {
20namespace {
21
22// Returns code to declare missing copy/move constructors/assignment operators.
23// They will be deleted or defaulted to match the class's current state.
24std::string buildSpecialMemberDeclarations(const CXXRecordDecl &Class) {
25 struct Members {
26 const CXXMethodDecl *Copy = nullptr;
27 const CXXMethodDecl *Move = nullptr;
28 } Ctor, Assign;
29
30 for (const auto &M : Class.methods()) {
31 if (M->isCopyAssignmentOperator())
32 Assign.Copy = M;
33 else if (M->isMoveAssignmentOperator())
34 Assign.Move = M;
35 if (const auto *C = llvm::dyn_cast<CXXConstructorDecl>(Val: M)) {
36 if (C->isCopyConstructor())
37 Ctor.Copy = C;
38 else if (C->isMoveConstructor())
39 Ctor.Move = C;
40 }
41 }
42
43 std::string S;
44 llvm::raw_string_ostream OS(S);
45
46 auto PrintMember = [&](const CXXMethodDecl *D, const char *MemberPattern,
47 const char *ParmPattern) {
48 if (D && !D->isImplicit())
49 return;
50 bool Delete = !D || D->isDeleted();
51 OS << llvm::formatv(
52 "{0} = {1};\n",
53 llvm::formatv(MemberPattern, Class.getName(),
54 llvm::formatv(ParmPattern, Class.getName())),
55 Delete ? "delete" : "default");
56 };
57 auto PrintMembers = [&](const Members &M, const char *MemberPattern) {
58 PrintMember(M.Copy, MemberPattern, /*ParmPattern=*/"const {0}&");
59 PrintMember(M.Move, MemberPattern, /*ParmPattern=*/"{0}&&");
60 };
61 PrintMembers(Ctor, /*MemberPattern=*/"{0}({1})");
62 PrintMembers(Assign, /*MemberPattern=*/"{0} &operator=({1})");
63
64 return S;
65}
66
67// A tweak that adds missing declarations of copy & move constructors.
68//
69// e.g. given `struct ^S{};`, produces:
70// struct S {
71// S(const S&) = default;
72// S(S&&) = default;
73// S &operator=(const S&) = default;
74// S &operator=(S&&) = default;
75// };
76//
77// Added members are defaulted or deleted to approximately preserve semantics.
78// (May not be a strict no-op when they were not implicitly declared).
79//
80// Having these spelled out is useful:
81// - to understand the implicit behavior
82// - to avoid relying on the implicit behavior
83// - as a baseline for explicit modification
84class SpecialMembers : public Tweak {
85public:
86 const char *id() const final;
87 llvm::StringLiteral kind() const override {
88 return CodeAction::REFACTOR_KIND;
89 }
90 std::string title() const override {
91 return llvm::formatv(Fmt: "Declare implicit {0} members",
92 Vals: NeedCopy ? NeedMove ? "copy/move" : "copy" : "move");
93 }
94
95 bool prepare(const Selection &Inputs) override {
96 // This tweak relies on =default and =delete.
97 if (!Inputs.AST->getLangOpts().CPlusPlus11)
98 return false;
99
100 // Trigger only on class definitions.
101 if (auto *N = Inputs.ASTSelection.commonAncestor())
102 Class = const_cast<CXXRecordDecl *>(N->ASTNode.get<CXXRecordDecl>());
103 if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion())
104 return false;
105
106 // Tweak is only available if some members are missing.
107 NeedCopy = !Class->hasUserDeclaredCopyConstructor() ||
108 !Class->hasUserDeclaredCopyAssignment();
109 NeedMove = !Class->hasUserDeclaredMoveAssignment() ||
110 !Class->hasUserDeclaredMoveConstructor();
111 return NeedCopy || NeedMove;
112 }
113
114 Expected<Effect> apply(const Selection &Inputs) override {
115 // Implicit special members are created lazily by clang.
116 // We need them so we can tell whether they should be =default or =delete.
117 Inputs.AST->getSema().ForceDeclarationOfImplicitMembers(Class);
118 std::string Code = buildSpecialMemberDeclarations(Class: *Class);
119
120 // Prefer to place the new members...
121 std::vector<Anchor> Anchors = {
122 // Below the default constructor
123 {.Match: [](const Decl *D) {
124 if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(Val: D))
125 return CCD->isDefaultConstructor();
126 return false;
127 },
128 .Direction: Anchor::Below},
129 // Above existing constructors
130 {.Match: [](const Decl *D) { return llvm::isa<CXXConstructorDecl>(Val: D); },
131 .Direction: Anchor::Above},
132 // At the top of the public section
133 {.Match: [](const Decl *D) { return true; }, .Direction: Anchor::Above},
134 };
135 auto Edit = insertDecl(Code, InClass: *Class, Anchors: std::move(Anchors), Protection: AS_public);
136 if (!Edit)
137 return Edit.takeError();
138 return Effect::mainFileEdit(SM: Inputs.AST->getSourceManager(),
139 Replacements: tooling::Replacements{std::move(*Edit)});
140 }
141
142private:
143 bool NeedCopy = false, NeedMove = false;
144 CXXRecordDecl *Class = nullptr;
145};
146REGISTER_TWEAK(SpecialMembers)
147
148} // namespace
149} // namespace clangd
150} // namespace clang
151

source code of clang-tools-extra/clangd/refactor/tweaks/SpecialMembers.cpp