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"
33class MemberwiseConstructor :
public Tweak {
35 const char *id() const final;
36 llvm::StringLiteral kind()
const override {
39 std::string title()
const override {
40 return llvm::formatv(
"Define constructor");
43 bool prepare(
const Selection &Inputs)
override {
45 if (!Inputs.AST->getLangOpts().CPlusPlus11)
49 if (
auto *N = Inputs.ASTSelection.commonAncestor())
50 Class = N->ASTNode.get<CXXRecordDecl>();
51 if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion() ||
52 Class->getDeclName().isEmpty())
55 dlog(
"MemberwiseConstructor for {0}?", Class->getName());
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());
67 for (
const CXXConstructorDecl *CCD : Class->ctors()) {
68 if (!CCD->isDefaultConstructor() && !CCD->isCopyOrMoveConstructor()) {
69 dlog(
" conflicting constructor");
75 for (
const FieldDecl *D : Class->fields()) {
76 switch (FieldAction A = considerField(D)) {
78 dlog(
" difficult field {0}", D->getName());
81 dlog(
" (skipping field {0})", D->getName());
84 Fields.push_back({D,
A});
90 dlog(
" no fields to initialize");
97 Expected<Effect> apply(
const Selection &Inputs)
override {
98 std::string
Code = buildCode();
100 std::vector<Anchor> Anchors = {
103 if (
const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D))
104 return CCD->isDefaultConstructor();
109 {[](
const Decl *D) {
return llvm::isa<CXXConstructorDecl>(D); },
114 auto Edit =
insertDecl(
Code, *Class, std::move(Anchors), AS_public);
116 return Edit.takeError();
117 return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
118 tooling::Replacements{std::move(*Edit)});
129 FieldAction considerField(
const FieldDecl *
Field)
const {
130 if (
Field->hasInClassInitializer())
132 if (!
Field->getIdentifier())
136 class Visitor :
public TypeVisitor<Visitor, FieldAction> {
138 Visitor(
const ASTContext &Ctx) : Ctx(Ctx) {}
139 const ASTContext &Ctx;
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);
148 FieldAction VisitBuiltinType(
const BuiltinType *T) {
149 if (
T->isInteger() ||
T->isFloatingPoint() ||
T->isNullPtrType())
153 FieldAction VisitObjCObjectPointerType(
const ObjCObjectPointerType *) {
154 return Ctx.getLangOpts().ObjCAutoRefCount ?
Copy : Fail;
156 FieldAction VisitAttributedType(
const AttributedType *T) {
157 return Visit(
T->getModifiedType().getCanonicalType().getTypePtr());
159#define ALWAYS(T, Action) \
160 FieldAction Visit##T##Type(const T##Type *) { return Action; }
175 return Visitor(Class->getASTContext())
176 .Visit(
Field->getType().getCanonicalType().getTypePtr());
180 static FieldAction considerClassValue(
const CXXRecordDecl &
C) {
181 if (!
C.hasDefinition())
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;
204 dlog(
" {0} CanCopy={1} CanMove={2} TriviallyCopyable={3}",
C.getName(),
205 CanCopy, CanMove,
C.isTriviallyCopyable());
206 if (CanCopy &&
C.isTriviallyCopyable())
214 if (CanDefaultConstruct)
219 std::string buildCode()
const {
221 llvm::raw_string_ostream
OS(S);
223 if (Fields.size() == 1)
225 OS << Class->getName() <<
"(";
226 const char *Sep =
"";
227 for (
const FieldInfo &
Info : Fields) {
229 QualType ParamType =
Info.Field->getType().getLocalUnqualifiedType();
230 if (
Info.Action == CopyRef)
231 ParamType = Class->getASTContext().getLValueReferenceType(
232 ParamType.withConst());
234 paramName(
Info.Field));
239 for (
const FieldInfo &
Info : Fields) {
240 OS << Sep <<
Info.Field->getName() <<
"(";
241 if (
Info.Action == Move)
243 OS << paramName(
Info.Field);
244 if (
Info.Action == Move)
254 llvm::StringRef paramName(
const FieldDecl *
Field)
const {
255 return Field->getName().trim(
"_");
258 const CXXRecordDecl *Class =
nullptr;
263 std::vector<FieldInfo> Fields;
const FunctionDecl * Decl
#define ALWAYS(T, Action)
llvm::raw_string_ostream OS
#define REGISTER_TWEAK(Subclass)
@ Info
An information message.
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder)
Returns a QualType as string.
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