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