#include "ParsedAST.h"
#include "refactor/InsertionPoint.h"
#include "refactor/Tweak.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Sema/Sema.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
namespace clang {
namespace clangd {
namespace {
std::string buildSpecialMemberDeclarations(const CXXRecordDecl &Class) {
struct Members {
const CXXMethodDecl *Copy = nullptr;
const CXXMethodDecl *Move = nullptr;
} Ctor, Assign;
for (const auto &M : Class.methods()) {
if (M->isCopyAssignmentOperator())
Assign.Copy = M;
else if (M->isMoveAssignmentOperator())
Assign.Move = M;
if (const auto *C = llvm::dyn_cast<CXXConstructorDecl>(M)) {
if (C->isCopyConstructor())
Ctor.Copy = C;
else if (C->isMoveConstructor())
Ctor.Move = C;
}
}
std::string S;
llvm::raw_string_ostream OS(S);
auto PrintMember = [&](const CXXMethodDecl *D, const char *MemberPattern,
const char *ParmPattern) {
if (D && !D->isImplicit())
return;
bool Delete = !D || D->isDeleted();
OS << llvm::formatv(
"{0} = {1};\n",
llvm::formatv(MemberPattern, Class.getName(),
llvm::formatv(ParmPattern, Class.getName())),
Delete ? "delete" : "default");
};
auto PrintMembers = [&](const Members &M, const char *MemberPattern) {
PrintMember(M.Copy, MemberPattern, "const {0}&");
PrintMember(M.Move, MemberPattern, "{0}&&");
};
PrintMembers(Ctor, "{0}({1})");
PrintMembers(Assign, "{0} &operator=({1})");
return S;
}
class SpecialMembers : public Tweak {
public:
const char *id() const final;
llvm::StringLiteral kind() const override {
return CodeAction::REFACTOR_KIND;
}
std::string title() const override {
return llvm::formatv("Declare implicit {0} members",
NeedCopy ? NeedMove ? "copy/move" : "copy" : "move");
}
bool prepare(const Selection &Inputs) override {
if (!Inputs.AST->getLangOpts().CPlusPlus11)
return false;
if (auto *N = Inputs.ASTSelection.commonAncestor())
Class = const_cast<CXXRecordDecl *>(N->ASTNode.get<CXXRecordDecl>());
if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion())
return false;
NeedCopy = !Class->hasUserDeclaredCopyConstructor() ||
!Class->hasUserDeclaredCopyAssignment();
NeedMove = !Class->hasUserDeclaredMoveAssignment() ||
!Class->hasUserDeclaredMoveConstructor();
return NeedCopy || NeedMove;
}
Expected<Effect> apply(const Selection &Inputs) override {
Inputs.AST->getSema().ForceDeclarationOfImplicitMembers(Class);
std::string Code = buildSpecialMemberDeclarations(*Class);
std::vector<Anchor> Anchors = {
{[](const Decl *D) {
if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D))
return CCD->isDefaultConstructor();
return false;
},
Anchor::Below},
{[](const Decl *D) { return llvm::isa<CXXConstructorDecl>(D); },
Anchor::Above},
{[](const Decl *D) { return true; }, Anchor::Above},
};
auto Edit = insertDecl(Code, *Class, std::move(Anchors), AS_public);
if (!Edit)
return Edit.takeError();
return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
tooling::Replacements{std::move(*Edit)});
}
private:
bool NeedCopy = false, NeedMove = false;
CXXRecordDecl *Class = nullptr;
};
REGISTER_TWEAK(SpecialMembers)
}
}
}