1afa94306SSam McCall //===--- MemberwiseConstructor.cpp - Generate C++ constructor -------------===// 2afa94306SSam McCall // 3afa94306SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4afa94306SSam McCall // See https://llvm.org/LICENSE.txt for license information. 5afa94306SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6afa94306SSam McCall // 7afa94306SSam McCall //===----------------------------------------------------------------------===// 8afa94306SSam McCall #include "AST.h" 9afa94306SSam McCall #include "ParsedAST.h" 10afa94306SSam McCall #include "refactor/InsertionPoint.h" 11afa94306SSam McCall #include "refactor/Tweak.h" 12afa94306SSam McCall #include "support/Logger.h" 13afa94306SSam McCall #include "clang/AST/DeclCXX.h" 14afa94306SSam McCall #include "clang/AST/TypeVisitor.h" 15afa94306SSam McCall #include "clang/Basic/SourceManager.h" 16afa94306SSam McCall #include "clang/Tooling/Core/Replacement.h" 17afa94306SSam McCall #include "llvm/ADT/StringRef.h" 18afa94306SSam McCall #include "llvm/Support/Casting.h" 19afa94306SSam McCall #include "llvm/Support/Error.h" 20afa94306SSam McCall 21afa94306SSam McCall namespace clang { 22afa94306SSam McCall namespace clangd { 23afa94306SSam McCall namespace { 24afa94306SSam McCall 25afa94306SSam McCall // A tweak that adds a C++ constructor which initializes each member. 26afa94306SSam McCall // 27afa94306SSam McCall // Given: 28afa94306SSam McCall // struct S{ int x; unique_ptr<double> y; }; 29afa94306SSam McCall // the tweak inserts the constructor: 30afa94306SSam McCall // S(int x, unique_ptr<double> y) : x(x), y(std::move(y)) {} 31afa94306SSam McCall // 32afa94306SSam McCall // We place the constructor inline, other tweaks are available to outline it. 33afa94306SSam McCall class MemberwiseConstructor : public Tweak { 34afa94306SSam McCall public: 35*95a932fbSKazu Hirata const char *id() const final; kind() const36afa94306SSam McCall llvm::StringLiteral kind() const override { 37afa94306SSam McCall return CodeAction::REFACTOR_KIND; 38afa94306SSam McCall } title() const39afa94306SSam McCall std::string title() const override { 40afa94306SSam McCall return llvm::formatv("Define constructor"); 41afa94306SSam McCall } 42afa94306SSam McCall prepare(const Selection & Inputs)43afa94306SSam McCall bool prepare(const Selection &Inputs) override { 44afa94306SSam McCall // This tweak assumes move semantics. 45afa94306SSam McCall if (!Inputs.AST->getLangOpts().CPlusPlus11) 46afa94306SSam McCall return false; 47afa94306SSam McCall 48afa94306SSam McCall // Trigger only on class definitions. 49afa94306SSam McCall if (auto *N = Inputs.ASTSelection.commonAncestor()) 50afa94306SSam McCall Class = N->ASTNode.get<CXXRecordDecl>(); 51afa94306SSam McCall if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion() || 52afa94306SSam McCall Class->getDeclName().isEmpty()) 53afa94306SSam McCall return false; 54afa94306SSam McCall 55afa94306SSam McCall dlog("MemberwiseConstructor for {0}?", Class->getName()); 56afa94306SSam McCall // For now, don't support nontrivial initialization of bases. 57afa94306SSam McCall for (const CXXBaseSpecifier &Base : Class->bases()) { 58afa94306SSam McCall const auto *BaseClass = Base.getType()->getAsCXXRecordDecl(); 59afa94306SSam McCall if (!BaseClass || !BaseClass->hasDefaultConstructor()) { 60afa94306SSam McCall dlog(" can't construct base {0}", Base.getType().getAsString()); 61afa94306SSam McCall return false; 62afa94306SSam McCall } 63afa94306SSam McCall } 64afa94306SSam McCall 65afa94306SSam McCall // We don't want to offer the tweak if there's a similar constructor. 66afa94306SSam McCall // For now, only offer it if all constructors are special members. 67afa94306SSam McCall for (const CXXConstructorDecl *CCD : Class->ctors()) { 68afa94306SSam McCall if (!CCD->isDefaultConstructor() && !CCD->isCopyOrMoveConstructor()) { 69afa94306SSam McCall dlog(" conflicting constructor"); 70afa94306SSam McCall return false; 71afa94306SSam McCall } 72afa94306SSam McCall } 73afa94306SSam McCall 74afa94306SSam McCall // Examine the fields to see which ones we should initialize. 75afa94306SSam McCall for (const FieldDecl *D : Class->fields()) { 76afa94306SSam McCall switch (FieldAction A = considerField(D)) { 77afa94306SSam McCall case Fail: 78afa94306SSam McCall dlog(" difficult field {0}", D->getName()); 79afa94306SSam McCall return false; 80afa94306SSam McCall case Skip: 81afa94306SSam McCall dlog(" (skipping field {0})", D->getName()); 82afa94306SSam McCall break; 83afa94306SSam McCall default: 84afa94306SSam McCall Fields.push_back({D, A}); 85afa94306SSam McCall break; 86afa94306SSam McCall } 87afa94306SSam McCall } 88afa94306SSam McCall // Only offer the tweak if we have some fields to initialize. 89afa94306SSam McCall if (Fields.empty()) { 90afa94306SSam McCall dlog(" no fields to initialize"); 91afa94306SSam McCall return false; 92afa94306SSam McCall } 93afa94306SSam McCall 94afa94306SSam McCall return true; 95afa94306SSam McCall } 96afa94306SSam McCall apply(const Selection & Inputs)97afa94306SSam McCall Expected<Effect> apply(const Selection &Inputs) override { 98afa94306SSam McCall std::string Code = buildCode(); 99afa94306SSam McCall // Prefer to place the new constructor... 100afa94306SSam McCall std::vector<Anchor> Anchors = { 101afa94306SSam McCall // Below special constructors. 102afa94306SSam McCall {[](const Decl *D) { 103afa94306SSam McCall if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D)) 104afa94306SSam McCall return CCD->isDefaultConstructor(); 105afa94306SSam McCall return false; 106afa94306SSam McCall }, 107afa94306SSam McCall Anchor::Below}, 108afa94306SSam McCall // Above other constructors 109afa94306SSam McCall {[](const Decl *D) { return llvm::isa<CXXConstructorDecl>(D); }, 110afa94306SSam McCall Anchor::Above}, 111afa94306SSam McCall // At the top of the public section 112afa94306SSam McCall {[](const Decl *D) { return true; }, Anchor::Above}, 113afa94306SSam McCall }; 114afa94306SSam McCall auto Edit = insertDecl(Code, *Class, std::move(Anchors), AS_public); 115afa94306SSam McCall if (!Edit) 116afa94306SSam McCall return Edit.takeError(); 117afa94306SSam McCall return Effect::mainFileEdit(Inputs.AST->getSourceManager(), 118afa94306SSam McCall tooling::Replacements{std::move(*Edit)}); 119afa94306SSam McCall } 120afa94306SSam McCall 121afa94306SSam McCall private: 122afa94306SSam McCall enum FieldAction { 123afa94306SSam McCall Fail, // Disallow the tweak, we can't handle this field. 124afa94306SSam McCall Skip, // Do not initialize this field, but allow the tweak anyway. 125afa94306SSam McCall Move, // Pass by value and std::move into place 126afa94306SSam McCall Copy, // Pass by value and copy into place 127afa94306SSam McCall CopyRef, // Pass by const ref and copy into place 128afa94306SSam McCall }; considerField(const FieldDecl * Field) const129afa94306SSam McCall FieldAction considerField(const FieldDecl *Field) const { 130afa94306SSam McCall if (Field->hasInClassInitializer()) 131afa94306SSam McCall return Skip; 132afa94306SSam McCall if (!Field->getIdentifier()) 133afa94306SSam McCall return Fail; 134afa94306SSam McCall 135afa94306SSam McCall // Decide what to do based on the field type. 136afa94306SSam McCall class Visitor : public TypeVisitor<Visitor, FieldAction> { 137afa94306SSam McCall public: 138afa94306SSam McCall Visitor(const ASTContext &Ctx) : Ctx(Ctx) {} 139afa94306SSam McCall const ASTContext &Ctx; 140afa94306SSam McCall 141afa94306SSam McCall // If we don't understand the type, assume we can't handle it. 142afa94306SSam McCall FieldAction VisitType(const Type *T) { return Fail; } 143afa94306SSam McCall FieldAction VisitRecordType(const RecordType *T) { 144afa94306SSam McCall if (const auto *D = T->getAsCXXRecordDecl()) 145afa94306SSam McCall return considerClassValue(*D); 146afa94306SSam McCall return Fail; 147afa94306SSam McCall } 148afa94306SSam McCall FieldAction VisitBuiltinType(const BuiltinType *T) { 149afa94306SSam McCall if (T->isInteger() || T->isFloatingPoint() || T->isNullPtrType()) 150afa94306SSam McCall return Copy; 151afa94306SSam McCall return Fail; 152afa94306SSam McCall } 153afa94306SSam McCall FieldAction VisitObjCObjectPointerType(const ObjCObjectPointerType *) { 154afa94306SSam McCall return Ctx.getLangOpts().ObjCAutoRefCount ? Copy : Fail; 155afa94306SSam McCall } 156afa94306SSam McCall FieldAction VisitAttributedType(const AttributedType *T) { 157afa94306SSam McCall return Visit(T->getModifiedType().getCanonicalType().getTypePtr()); 158afa94306SSam McCall } 159afa94306SSam McCall #define ALWAYS(T, Action) \ 160afa94306SSam McCall FieldAction Visit##T##Type(const T##Type *) { return Action; } 161afa94306SSam McCall // Trivially copyable types (pointers and numbers). 162afa94306SSam McCall ALWAYS(Pointer, Copy); 163afa94306SSam McCall ALWAYS(MemberPointer, Copy); 164afa94306SSam McCall ALWAYS(Reference, Copy); 165afa94306SSam McCall ALWAYS(Complex, Copy); 166afa94306SSam McCall ALWAYS(Enum, Copy); 167afa94306SSam McCall // These types are dependent (when canonical) and likely to be classes. 168afa94306SSam McCall // Move is a reasonable generic option. 169afa94306SSam McCall ALWAYS(DependentName, Move); 170afa94306SSam McCall ALWAYS(UnresolvedUsing, Move); 171afa94306SSam McCall ALWAYS(TemplateTypeParm, Move); 172afa94306SSam McCall ALWAYS(TemplateSpecialization, Move); 173afa94306SSam McCall }; 174afa94306SSam McCall #undef ALWAYS 175afa94306SSam McCall return Visitor(Class->getASTContext()) 176afa94306SSam McCall .Visit(Field->getType().getCanonicalType().getTypePtr()); 177afa94306SSam McCall } 178afa94306SSam McCall 179afa94306SSam McCall // Decide what to do with a field of type C. considerClassValue(const CXXRecordDecl & C)180afa94306SSam McCall static FieldAction considerClassValue(const CXXRecordDecl &C) { 18184051d82SHaojian Wu if (!C.hasDefinition()) 18284051d82SHaojian Wu return Skip; 183afa94306SSam McCall // We can't always tell if C is copyable/movable without doing Sema work. 184afa94306SSam McCall // We assume operations are possible unless we can prove not. 185afa94306SSam McCall bool CanCopy = C.hasUserDeclaredCopyConstructor() || 186afa94306SSam McCall C.needsOverloadResolutionForCopyConstructor() || 187afa94306SSam McCall !C.defaultedCopyConstructorIsDeleted(); 188afa94306SSam McCall bool CanMove = C.hasUserDeclaredMoveConstructor() || 189afa94306SSam McCall (C.needsOverloadResolutionForMoveConstructor() || 190afa94306SSam McCall !C.defaultedMoveConstructorIsDeleted()); 191afa94306SSam McCall bool CanDefaultConstruct = C.hasDefaultConstructor(); 192afa94306SSam McCall if (C.hasUserDeclaredCopyConstructor() || 193afa94306SSam McCall C.hasUserDeclaredMoveConstructor()) { 194afa94306SSam McCall for (const CXXConstructorDecl *CCD : C.ctors()) { 195afa94306SSam McCall bool IsUsable = !CCD->isDeleted() && CCD->getAccess() == AS_public; 196afa94306SSam McCall if (CCD->isCopyConstructor()) 197afa94306SSam McCall CanCopy = CanCopy && IsUsable; 198afa94306SSam McCall if (CCD->isMoveConstructor()) 199afa94306SSam McCall CanMove = CanMove && IsUsable; 200afa94306SSam McCall if (CCD->isDefaultConstructor()) 201afa94306SSam McCall CanDefaultConstruct = IsUsable; 202afa94306SSam McCall } 203afa94306SSam McCall } 204afa94306SSam McCall dlog(" {0} CanCopy={1} CanMove={2} TriviallyCopyable={3}", C.getName(), 205afa94306SSam McCall CanCopy, CanMove, C.isTriviallyCopyable()); 206afa94306SSam McCall if (CanCopy && C.isTriviallyCopyable()) 207afa94306SSam McCall return Copy; 208afa94306SSam McCall if (CanMove) 209afa94306SSam McCall return Move; 210afa94306SSam McCall if (CanCopy) 211afa94306SSam McCall return CopyRef; 212afa94306SSam McCall // If it's neither copyable nor movable, then default construction is 213afa94306SSam McCall // likely to make sense (example: std::mutex). 214afa94306SSam McCall if (CanDefaultConstruct) 215afa94306SSam McCall return Skip; 216afa94306SSam McCall return Fail; 217afa94306SSam McCall } 218afa94306SSam McCall buildCode() const219afa94306SSam McCall std::string buildCode() const { 220afa94306SSam McCall std::string S; 221afa94306SSam McCall llvm::raw_string_ostream OS(S); 222afa94306SSam McCall 223afa94306SSam McCall if (Fields.size() == 1) 224afa94306SSam McCall OS << "explicit "; 225afa94306SSam McCall OS << Class->getName() << "("; 226afa94306SSam McCall const char *Sep = ""; 227afa94306SSam McCall for (const FieldInfo &Info : Fields) { 228afa94306SSam McCall OS << Sep; 229afa94306SSam McCall QualType ParamType = Info.Field->getType().getLocalUnqualifiedType(); 230afa94306SSam McCall if (Info.Action == CopyRef) 231afa94306SSam McCall ParamType = Class->getASTContext().getLValueReferenceType( 232afa94306SSam McCall ParamType.withConst()); 233afa94306SSam McCall OS << printType(ParamType, *Class, 234afa94306SSam McCall /*Placeholder=*/paramName(Info.Field)); 235afa94306SSam McCall Sep = ", "; 236afa94306SSam McCall } 237afa94306SSam McCall OS << ")"; 238afa94306SSam McCall Sep = " : "; 239afa94306SSam McCall for (const FieldInfo &Info : Fields) { 240afa94306SSam McCall OS << Sep << Info.Field->getName() << "("; 241afa94306SSam McCall if (Info.Action == Move) 242afa94306SSam McCall OS << "std::move("; // FIXME: #include <utility> too 243afa94306SSam McCall OS << paramName(Info.Field); 244afa94306SSam McCall if (Info.Action == Move) 245afa94306SSam McCall OS << ")"; 246afa94306SSam McCall OS << ")"; 247afa94306SSam McCall Sep = ", "; 248afa94306SSam McCall } 249afa94306SSam McCall OS << " {}\n"; 250afa94306SSam McCall 251afa94306SSam McCall return S; 252afa94306SSam McCall } 253afa94306SSam McCall paramName(const FieldDecl * Field) const254afa94306SSam McCall llvm::StringRef paramName(const FieldDecl *Field) const { 255afa94306SSam McCall return Field->getName().trim("_"); 256afa94306SSam McCall } 257afa94306SSam McCall 258afa94306SSam McCall const CXXRecordDecl *Class = nullptr; 259afa94306SSam McCall struct FieldInfo { 260afa94306SSam McCall const FieldDecl *Field; 261afa94306SSam McCall FieldAction Action; 262afa94306SSam McCall }; 263afa94306SSam McCall std::vector<FieldInfo> Fields; 264afa94306SSam McCall }; 265afa94306SSam McCall REGISTER_TWEAK(MemberwiseConstructor) 266afa94306SSam McCall 267afa94306SSam McCall } // namespace 268afa94306SSam McCall } // namespace clangd 269afa94306SSam McCall } // namespace clang 270