xref: /llvm-project/clang-tools-extra/clangd/refactor/tweaks/MemberwiseConstructor.cpp (revision 95a932fb15960e66fdc43dc9821685addd5ee44d)
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;
kind() const36   llvm::StringLiteral kind() const override {
37     return CodeAction::REFACTOR_KIND;
38   }
title() const39   std::string title() const override {
40     return llvm::formatv("Define constructor");
41   }
42 
prepare(const Selection & Inputs)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 
apply(const Selection & Inputs)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         {[](const Decl *D) {
103            if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D))
104              return CCD->isDefaultConstructor();
105            return false;
106          },
107          Anchor::Below},
108         // Above other constructors
109         {[](const Decl *D) { return llvm::isa<CXXConstructorDecl>(D); },
110          Anchor::Above},
111         // At the top of the public section
112         {[](const Decl *D) { return true; }, Anchor::Above},
113     };
114     auto Edit = insertDecl(Code, *Class, std::move(Anchors), AS_public);
115     if (!Edit)
116       return Edit.takeError();
117     return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
118                                 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   };
considerField(const FieldDecl * Field) const129   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(*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->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(Field->getType().getCanonicalType().getTypePtr());
177   }
178 
179   // Decide what to do with a field of type C.
considerClassValue(const CXXRecordDecl & 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 
buildCode() const219   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(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(Info.Field);
244       if (Info.Action == Move)
245         OS << ")";
246       OS << ")";
247       Sep = ", ";
248     }
249     OS << " {}\n";
250 
251     return S;
252   }
253 
paramName(const FieldDecl * Field) const254   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