| 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; |
| 36 | llvm::StringLiteral kind() const override { |
| 37 | return CodeAction::REFACTOR_KIND; |
| 38 | } |
| 39 | std::string title() const override { |
| 40 | return llvm::formatv(Fmt: "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 | {.Match: [](const Decl *D) { |
| 103 | if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(Val: D)) |
| 104 | return CCD->isDefaultConstructor(); |
| 105 | return false; |
| 106 | }, |
| 107 | .Direction: Anchor::Below}, |
| 108 | // Above other constructors |
| 109 | {.Match: [](const Decl *D) { return llvm::isa<CXXConstructorDecl>(Val: D); }, |
| 110 | .Direction: Anchor::Above}, |
| 111 | // At the top of the public section |
| 112 | {.Match: [](const Decl *D) { return true; }, .Direction: Anchor::Above}, |
| 113 | }; |
| 114 | auto Edit = insertDecl(Code, InClass: *Class, Anchors: std::move(Anchors), Protection: AS_public); |
| 115 | if (!Edit) |
| 116 | return Edit.takeError(); |
| 117 | return Effect::mainFileEdit(SM: Inputs.AST->getSourceManager(), |
| 118 | Replacements: 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(C: *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: 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(T: 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(Field: 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(Field: 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 |
| 270 | |