clang-tools 19.0.0git
MemberwiseConstructor.cpp
Go to the documentation of this file.
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"
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
21namespace clang {
22namespace clangd {
23namespace {
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.
33class MemberwiseConstructor : public Tweak {
34public:
35 const char *id() const final;
36 llvm::StringLiteral kind() const override {
38 }
39 std::string title() const override {
40 return llvm::formatv("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 {[](const Decl *D) {
103 if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D))
104 return CCD->isDefaultConstructor();
105 return false;
106 },
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},
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
121private:
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(*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.
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(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
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};
265REGISTER_TWEAK(MemberwiseConstructor)
266
267} // namespace
268} // namespace clangd
269} // namespace clang
const FunctionDecl * Decl
MemberFunctionInfo Move
MemberFunctionInfo Copy
std::string Code
const Criteria C
NodeType Type
#define dlog(...)
Definition: Logger.h:101
FieldAction Action
#define ALWAYS(T, Action)
const FieldDecl * Field
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:129
@ Info
An information message.
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder)
Returns a QualType as string.
Definition: AST.cpp:394
llvm::Expected< tooling::Replacement > insertDecl(llvm::StringRef Code, const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
static const llvm::StringLiteral REFACTOR_KIND
Definition: Protocol.h:1071