clang-tools  15.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 
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 override 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  },
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  };
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 };
265 REGISTER_TWEAK(MemberwiseConstructor)
266 
267 } // namespace
268 } // namespace clangd
269 } // namespace clang
dlog
#define dlog(...)
Definition: Logger.h:101
clang::clangd::Anchor::Below
@ Below
Definition: InsertionPoint.h:25
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:85
llvm
Some operations such as code completion produce a set of candidates.
Definition: YAMLGenerator.cpp:28
clang::clangd::insertDecl
llvm::Expected< tooling::Replacement > insertDecl(llvm::StringRef Code, const DeclContext &DC, llvm::ArrayRef< Anchor > Anchors)
Definition: InsertionPoint.cpp:111
Ctx
Context Ctx
Definition: TUScheduler.cpp:495
ALWAYS
#define ALWAYS(T, Action)
clang::clangd::printType
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder)
Returns a QualType as string.
Definition: AST.cpp:354
Inputs
ParseInputs Inputs
Definition: TUScheduler.cpp:492
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
Code
std::string Code
Definition: FindTargetTests.cpp:67
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
Tweak.h
Logger.h
InsertionPoint.h
Info
FunctionInfo Info
Definition: FunctionSizeCheck.cpp:121
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
clang::clangd::Anchor::Above
@ Above
Definition: InsertionPoint.h:25
clang::clangd::CodeAction::REFACTOR_KIND
const static llvm::StringLiteral REFACTOR_KIND
Definition: Protocol.h:1000
Field
const FieldDecl * Field
Definition: MemberwiseConstructor.cpp:260
REGISTER_TWEAK
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:129
AST.h
ParsedAST.h
Action
FieldAction Action
Definition: MemberwiseConstructor.cpp:261