[AMDGPU] Infer amdgpu-no-flat-scratch-init attribute in AMDGPUAttributor (#94647)
[llvm-project.git] / clang-tools-extra / clangd / refactor / tweaks / MemberwiseConstructor.cpp
blob9bb8ca96a8b61e8f0a2fcada3310c2f21b9b7586
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"
21 namespace clang {
22 namespace clangd {
23 namespace {
25 // A tweak that adds a C++ constructor which initializes each member.
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)) {}
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;
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)
46 return false;
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;
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;
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;
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;
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;
94 return true;
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;
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},
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)});
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
129 FieldAction considerField(const FieldDecl *Field) const {
130 if (Field->hasInClassInitializer())
131 return Skip;
132 if (!Field->getIdentifier())
133 return Fail;
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;
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;
148 FieldAction VisitBuiltinType(const BuiltinType *T) {
149 if (T->isInteger() || T->isFloatingPoint() || T->isNullPtrType())
150 return Copy;
151 return Fail;
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);
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);
174 #undef ALWAYS
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())
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;
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;
219 std::string buildCode() const {
220 std::string S;
221 llvm::raw_string_ostream OS(S);
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 = ", ";
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 = ", ";
249 OS << " {}\n";
251 return S;
254 llvm::StringRef paramName(const FieldDecl *Field) const {
255 return Field->getName().trim("_");
258 const CXXRecordDecl *Class = nullptr;
259 struct FieldInfo {
260 const FieldDecl *Field;
261 FieldAction Action;
263 std::vector<FieldInfo> Fields;
265 REGISTER_TWEAK(MemberwiseConstructor)
267 } // namespace
268 } // namespace clangd
269 } // namespace clang