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"
33 class MemberwiseConstructor :
public Tweak {
35 const char *id() const override final;
36 llvm::StringLiteral kind()
const override {
39 std::string title()
const override {
40 return llvm::formatv(
"Define constructor");
43 bool prepare(
const Selection &
Inputs)
override {
45 if (!
Inputs.AST->getLangOpts().CPlusPlus11)
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());
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());
67 for (
const CXXConstructorDecl *CCD : Class->ctors()) {
68 if (!CCD->isDefaultConstructor() && !CCD->isCopyOrMoveConstructor()) {
69 dlog(
" conflicting constructor");
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});
90 dlog(
" no fields to initialize");
97 Expected<Effect> apply(
const Selection &
Inputs)
override {
98 std::string
Code = buildCode();
100 std::vector<Anchor> Anchors = {
103 if (
const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(
D))
104 return CCD->isDefaultConstructor();
109 {[](
const Decl *
D) {
return llvm::isa<CXXConstructorDecl>(
D); },
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)});
129 FieldAction considerField(
const FieldDecl *Field)
const {
130 if (
Field->hasInClassInitializer())
132 if (!
Field->getIdentifier())
136 class Visitor :
public TypeVisitor<Visitor, FieldAction> {
138 Visitor(
const ASTContext &
Ctx) :
Ctx(
Ctx) {}
139 const ASTContext &
Ctx;
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; }
163 ALWAYS(MemberPointer, Copy);
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());
180 static FieldAction considerClassValue(
const CXXRecordDecl &
C) {
181 if (!
C.hasDefinition())
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())
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());
234 paramName(
Info.Field));
239 for (
const FieldInfo &Info : Fields) {
240 OS << Sep <<
Info.Field->getName() <<
"(";
241 if (
Info.Action == Move)
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;
263 std::vector<FieldInfo> Fields;