1 | //===--- MemberwiseConstructor.cpp - Generate C++ constructor -------------===// |
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 "AST.h" |
9 | #include "ParsedAST.h" |
10 | #include "refactor/InsertionPoint.h" |
11 | #include "refactor/Tweak.h" |
12 | #include "support/Logger.h" |
13 | #include "clang/AST/DeclCXX.h" |
14 | #include "clang/AST/TypeVisitor.h" |
15 | #include "clang/Basic/SourceManager.h" |
16 | #include "clang/Tooling/Core/Replacement.h" |
17 | #include "llvm/ADT/StringRef.h" |
18 | #include "llvm/Support/Casting.h" |
19 | #include "llvm/Support/Error.h" |
20 | |
21 | namespace clang { |
22 | namespace clangd { |
23 | namespace { |
24 | |
25 | // A tweak that adds a C++ constructor which initializes each member. |
26 | // |
27 | // Given: |
28 | // struct S{ int x; unique_ptr<double> y; }; |
29 | // the tweak inserts the constructor: |
30 | // S(int x, unique_ptr<double> y) : x(x), y(std::move(y)) {} |
31 | // |
32 | // We place the constructor inline, other tweaks are available to outline it. |
33 | class MemberwiseConstructor : public Tweak { |
34 | public: |
35 | const char *id() const final; |
36 | llvm::StringLiteral kind() const override { |
37 | return CodeAction::REFACTOR_KIND; |
38 | } |
39 | std::string title() const override { |
40 | return llvm::formatv(Fmt: "Define constructor" ); |
41 | } |
42 | |
43 | bool prepare(const Selection &Inputs) override { |
44 | // This tweak assumes move semantics. |
45 | if (!Inputs.AST->getLangOpts().CPlusPlus11) |
46 | return false; |
47 | |
48 | // Trigger only on class definitions. |
49 | if (auto *N = Inputs.ASTSelection.commonAncestor()) |
50 | Class = N->ASTNode.get<CXXRecordDecl>(); |
51 | if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion() || |
52 | Class->getDeclName().isEmpty()) |
53 | return false; |
54 | |
55 | dlog("MemberwiseConstructor for {0}?" , Class->getName()); |
56 | // For now, don't support nontrivial initialization of bases. |
57 | for (const CXXBaseSpecifier &Base : Class->bases()) { |
58 | const auto *BaseClass = Base.getType()->getAsCXXRecordDecl(); |
59 | if (!BaseClass || !BaseClass->hasDefaultConstructor()) { |
60 | dlog(" can't construct base {0}" , Base.getType().getAsString()); |
61 | return false; |
62 | } |
63 | } |
64 | |
65 | // We don't want to offer the tweak if there's a similar constructor. |
66 | // For now, only offer it if all constructors are special members. |
67 | for (const CXXConstructorDecl *CCD : Class->ctors()) { |
68 | if (!CCD->isDefaultConstructor() && !CCD->isCopyOrMoveConstructor()) { |
69 | dlog(" conflicting constructor" ); |
70 | return false; |
71 | } |
72 | } |
73 | |
74 | // Examine the fields to see which ones we should initialize. |
75 | for (const FieldDecl *D : Class->fields()) { |
76 | switch (FieldAction A = considerField(D)) { |
77 | case Fail: |
78 | dlog(" difficult field {0}" , D->getName()); |
79 | return false; |
80 | case Skip: |
81 | dlog(" (skipping field {0})" , D->getName()); |
82 | break; |
83 | default: |
84 | Fields.push_back({D, A}); |
85 | break; |
86 | } |
87 | } |
88 | // Only offer the tweak if we have some fields to initialize. |
89 | if (Fields.empty()) { |
90 | dlog(" no fields to initialize" ); |
91 | return false; |
92 | } |
93 | |
94 | return true; |
95 | } |
96 | |
97 | Expected<Effect> apply(const Selection &Inputs) override { |
98 | std::string Code = buildCode(); |
99 | // Prefer to place the new constructor... |
100 | std::vector<Anchor> Anchors = { |
101 | // Below special constructors. |
102 | {.Match: [](const Decl *D) { |
103 | if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(Val: D)) |
104 | return CCD->isDefaultConstructor(); |
105 | return false; |
106 | }, |
107 | .Direction: Anchor::Below}, |
108 | // Above other constructors |
109 | {.Match: [](const Decl *D) { return llvm::isa<CXXConstructorDecl>(Val: D); }, |
110 | .Direction: Anchor::Above}, |
111 | // At the top of the public section |
112 | {.Match: [](const Decl *D) { return true; }, .Direction: Anchor::Above}, |
113 | }; |
114 | auto Edit = insertDecl(Code, InClass: *Class, Anchors: std::move(Anchors), Protection: AS_public); |
115 | if (!Edit) |
116 | return Edit.takeError(); |
117 | return Effect::mainFileEdit(SM: Inputs.AST->getSourceManager(), |
118 | Replacements: tooling::Replacements{std::move(*Edit)}); |
119 | } |
120 | |
121 | private: |
122 | enum FieldAction { |
123 | Fail, // Disallow the tweak, we can't handle this field. |
124 | Skip, // Do not initialize this field, but allow the tweak anyway. |
125 | Move, // Pass by value and std::move into place |
126 | Copy, // Pass by value and copy into place |
127 | CopyRef, // Pass by const ref and copy into place |
128 | }; |
129 | FieldAction considerField(const FieldDecl *Field) const { |
130 | if (Field->hasInClassInitializer()) |
131 | return Skip; |
132 | if (!Field->getIdentifier()) |
133 | return Fail; |
134 | |
135 | // Decide what to do based on the field type. |
136 | class Visitor : public TypeVisitor<Visitor, FieldAction> { |
137 | public: |
138 | Visitor(const ASTContext &Ctx) : Ctx(Ctx) {} |
139 | const ASTContext &Ctx; |
140 | |
141 | // If we don't understand the type, assume we can't handle it. |
142 | FieldAction VisitType(const Type *T) { return Fail; } |
143 | FieldAction VisitRecordType(const RecordType *T) { |
144 | if (const auto *D = T->getAsCXXRecordDecl()) |
145 | return considerClassValue(C: *D); |
146 | return Fail; |
147 | } |
148 | FieldAction VisitBuiltinType(const BuiltinType *T) { |
149 | if (T->isInteger() || T->isFloatingPoint() || T->isNullPtrType()) |
150 | return Copy; |
151 | return Fail; |
152 | } |
153 | FieldAction VisitObjCObjectPointerType(const ObjCObjectPointerType *) { |
154 | return Ctx.getLangOpts().ObjCAutoRefCount ? Copy : Fail; |
155 | } |
156 | FieldAction VisitAttributedType(const AttributedType *T) { |
157 | return Visit(T: T->getModifiedType().getCanonicalType().getTypePtr()); |
158 | } |
159 | #define ALWAYS(T, Action) \ |
160 | FieldAction Visit##T##Type(const T##Type *) { return Action; } |
161 | // Trivially copyable types (pointers and numbers). |
162 | ALWAYS(Pointer, Copy); |
163 | ALWAYS(MemberPointer, Copy); |
164 | ALWAYS(Reference, Copy); |
165 | ALWAYS(Complex, Copy); |
166 | ALWAYS(Enum, Copy); |
167 | // These types are dependent (when canonical) and likely to be classes. |
168 | // Move is a reasonable generic option. |
169 | ALWAYS(DependentName, Move); |
170 | ALWAYS(UnresolvedUsing, Move); |
171 | ALWAYS(TemplateTypeParm, Move); |
172 | ALWAYS(TemplateSpecialization, Move); |
173 | }; |
174 | #undef ALWAYS |
175 | return Visitor(Class->getASTContext()) |
176 | .Visit(T: Field->getType().getCanonicalType().getTypePtr()); |
177 | } |
178 | |
179 | // Decide what to do with a field of type C. |
180 | static FieldAction considerClassValue(const CXXRecordDecl &C) { |
181 | if (!C.hasDefinition()) |
182 | return Skip; |
183 | // We can't always tell if C is copyable/movable without doing Sema work. |
184 | // We assume operations are possible unless we can prove not. |
185 | bool CanCopy = C.hasUserDeclaredCopyConstructor() || |
186 | C.needsOverloadResolutionForCopyConstructor() || |
187 | !C.defaultedCopyConstructorIsDeleted(); |
188 | bool CanMove = C.hasUserDeclaredMoveConstructor() || |
189 | (C.needsOverloadResolutionForMoveConstructor() || |
190 | !C.defaultedMoveConstructorIsDeleted()); |
191 | bool CanDefaultConstruct = C.hasDefaultConstructor(); |
192 | if (C.hasUserDeclaredCopyConstructor() || |
193 | C.hasUserDeclaredMoveConstructor()) { |
194 | for (const CXXConstructorDecl *CCD : C.ctors()) { |
195 | bool IsUsable = !CCD->isDeleted() && CCD->getAccess() == AS_public; |
196 | if (CCD->isCopyConstructor()) |
197 | CanCopy = CanCopy && IsUsable; |
198 | if (CCD->isMoveConstructor()) |
199 | CanMove = CanMove && IsUsable; |
200 | if (CCD->isDefaultConstructor()) |
201 | CanDefaultConstruct = IsUsable; |
202 | } |
203 | } |
204 | dlog(" {0} CanCopy={1} CanMove={2} TriviallyCopyable={3}" , C.getName(), |
205 | CanCopy, CanMove, C.isTriviallyCopyable()); |
206 | if (CanCopy && C.isTriviallyCopyable()) |
207 | return Copy; |
208 | if (CanMove) |
209 | return Move; |
210 | if (CanCopy) |
211 | return CopyRef; |
212 | // If it's neither copyable nor movable, then default construction is |
213 | // likely to make sense (example: std::mutex). |
214 | if (CanDefaultConstruct) |
215 | return Skip; |
216 | return Fail; |
217 | } |
218 | |
219 | std::string buildCode() const { |
220 | std::string S; |
221 | llvm::raw_string_ostream OS(S); |
222 | |
223 | if (Fields.size() == 1) |
224 | OS << "explicit " ; |
225 | OS << Class->getName() << "(" ; |
226 | const char *Sep = "" ; |
227 | for (const FieldInfo &Info : Fields) { |
228 | OS << Sep; |
229 | QualType ParamType = Info.Field->getType().getLocalUnqualifiedType(); |
230 | if (Info.Action == CopyRef) |
231 | ParamType = Class->getASTContext().getLValueReferenceType( |
232 | ParamType.withConst()); |
233 | OS << printType(ParamType, *Class, |
234 | /*Placeholder=*/paramName(Field: Info.Field)); |
235 | Sep = ", " ; |
236 | } |
237 | OS << ")" ; |
238 | Sep = " : " ; |
239 | for (const FieldInfo &Info : Fields) { |
240 | OS << Sep << Info.Field->getName() << "(" ; |
241 | if (Info.Action == Move) |
242 | OS << "std::move(" ; // FIXME: #include <utility> too |
243 | OS << paramName(Field: Info.Field); |
244 | if (Info.Action == Move) |
245 | OS << ")" ; |
246 | OS << ")" ; |
247 | Sep = ", " ; |
248 | } |
249 | OS << " {}\n" ; |
250 | |
251 | return S; |
252 | } |
253 | |
254 | llvm::StringRef paramName(const FieldDecl *Field) const { |
255 | return Field->getName().trim("_" ); |
256 | } |
257 | |
258 | const CXXRecordDecl *Class = nullptr; |
259 | struct FieldInfo { |
260 | const FieldDecl *Field; |
261 | FieldAction Action; |
262 | }; |
263 | std::vector<FieldInfo> Fields; |
264 | }; |
265 | REGISTER_TWEAK(MemberwiseConstructor) |
266 | |
267 | } // namespace |
268 | } // namespace clangd |
269 | } // namespace clang |
270 | |