1 //===--- MemberwiseConstructor.cpp - Generate C++ constructor -------------===//
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
7 //===----------------------------------------------------------------------===//
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"
25 // A tweak that adds a C++ constructor which initializes each member.
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)) {}
32 // We place the constructor inline, other tweaks are available to outline it.
33 class MemberwiseConstructor
: public Tweak
{
35 const char *id() const final
;
36 llvm::StringLiteral
kind() const override
{
37 return CodeAction::REFACTOR_KIND
;
39 std::string
title() const override
{
40 return llvm::formatv("Define constructor");
43 bool prepare(const Selection
&Inputs
) override
{
44 // This tweak assumes move semantics.
45 if (!Inputs
.AST
->getLangOpts().CPlusPlus11
)
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())
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());
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");
74 // Examine the fields to see which ones we should initialize.
75 for (const FieldDecl
*D
: Class
->fields()) {
76 switch (FieldAction A
= considerField(D
)) {
78 dlog(" difficult field {0}", D
->getName());
81 dlog(" (skipping field {0})", D
->getName());
84 Fields
.push_back({D
, A
});
88 // Only offer the tweak if we have some fields to initialize.
90 dlog(" no fields to initialize");
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.
103 if (const auto *CCD
= llvm::dyn_cast
<CXXConstructorDecl
>(D
))
104 return CCD
->isDefaultConstructor();
108 // Above other constructors
109 {[](const Decl
*D
) { return llvm::isa
<CXXConstructorDecl
>(D
); },
111 // At the top of the public section
112 {[](const Decl
*D
) { return true; }, Anchor::Above
},
114 auto Edit
= insertDecl(Code
, *Class
, std::move(Anchors
), AS_public
);
116 return Edit
.takeError();
117 return Effect::mainFileEdit(Inputs
.AST
->getSourceManager(),
118 tooling::Replacements
{std::move(*Edit
)});
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
129 FieldAction
considerField(const FieldDecl
*Field
) const {
130 if (Field
->hasInClassInitializer())
132 if (!Field
->getIdentifier())
135 // Decide what to do based on the field type.
136 class Visitor
: public TypeVisitor
<Visitor
, FieldAction
> {
138 Visitor(const ASTContext
&Ctx
) : Ctx(Ctx
) {}
139 const ASTContext
&Ctx
;
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(*D
);
148 FieldAction
VisitBuiltinType(const BuiltinType
*T
) {
149 if (T
->isInteger() || T
->isFloatingPoint() || T
->isNullPtrType())
153 FieldAction
VisitObjCObjectPointerType(const ObjCObjectPointerType
*) {
154 return Ctx
.getLangOpts().ObjCAutoRefCount
? Copy
: Fail
;
156 FieldAction
VisitAttributedType(const AttributedType
*T
) {
157 return Visit(T
->getModifiedType().getCanonicalType().getTypePtr());
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
);
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
);
175 return Visitor(Class
->getASTContext())
176 .Visit(Field
->getType().getCanonicalType().getTypePtr());
179 // Decide what to do with a field of type C.
180 static FieldAction
considerClassValue(const CXXRecordDecl
&C
) {
181 if (!C
.hasDefinition())
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
;
204 dlog(" {0} CanCopy={1} CanMove={2} TriviallyCopyable={3}", C
.getName(),
205 CanCopy
, CanMove
, C
.isTriviallyCopyable());
206 if (CanCopy
&& C
.isTriviallyCopyable())
212 // If it's neither copyable nor movable, then default construction is
213 // likely to make sense (example: std::mutex).
214 if (CanDefaultConstruct
)
219 std::string
buildCode() const {
221 llvm::raw_string_ostream
OS(S
);
223 if (Fields
.size() == 1)
225 OS
<< Class
->getName() << "(";
226 const char *Sep
= "";
227 for (const FieldInfo
&Info
: Fields
) {
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(Info
.Field
));
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(Info
.Field
);
244 if (Info
.Action
== Move
)
254 llvm::StringRef
paramName(const FieldDecl
*Field
) const {
255 return Field
->getName().trim("_");
258 const CXXRecordDecl
*Class
= nullptr;
260 const FieldDecl
*Field
;
263 std::vector
<FieldInfo
> Fields
;
265 REGISTER_TWEAK(MemberwiseConstructor
)
268 } // namespace clangd