brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.2 KiB · 0b86b48 Raw
151 lines · cpp
1//===--- SpecialMembers.cpp - Generate C++ special member functions -------===//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-exception6//7//===----------------------------------------------------------------------===//8#include "ParsedAST.h"9#include "refactor/InsertionPoint.h"10#include "refactor/Tweak.h"11#include "clang/AST/DeclCXX.h"12#include "clang/Sema/Sema.h"13#include "clang/Tooling/Core/Replacement.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/Support/Casting.h"16#include "llvm/Support/Error.h"17 18namespace clang {19namespace clangd {20namespace {21 22// Returns code to declare missing copy/move constructors/assignment operators.23// They will be deleted or defaulted to match the class's current state.24std::string buildSpecialMemberDeclarations(const CXXRecordDecl &Class) {25  struct Members {26    const CXXMethodDecl *Copy = nullptr;27    const CXXMethodDecl *Move = nullptr;28  } Ctor, Assign;29 30  for (const auto &M : Class.methods()) {31    if (M->isCopyAssignmentOperator())32      Assign.Copy = M;33    else if (M->isMoveAssignmentOperator())34      Assign.Move = M;35    if (const auto *C = llvm::dyn_cast<CXXConstructorDecl>(M)) {36      if (C->isCopyConstructor())37        Ctor.Copy = C;38      else if (C->isMoveConstructor())39        Ctor.Move = C;40    }41  }42 43  std::string S;44  llvm::raw_string_ostream OS(S);45 46  auto PrintMember = [&](const CXXMethodDecl *D, const char *MemberPattern,47                         const char *ParmPattern) {48    if (D && !D->isImplicit())49      return;50    bool Delete = !D || D->isDeleted();51    OS << llvm::formatv(52        "{0} = {1};\n",53        llvm::formatv(MemberPattern, Class.getName(),54                      llvm::formatv(ParmPattern, Class.getName())),55        Delete ? "delete" : "default");56  };57  auto PrintMembers = [&](const Members &M, const char *MemberPattern) {58    PrintMember(M.Copy, MemberPattern, /*ParmPattern=*/"const {0}&");59    PrintMember(M.Move, MemberPattern, /*ParmPattern=*/"{0}&&");60  };61  PrintMembers(Ctor, /*MemberPattern=*/"{0}({1})");62  PrintMembers(Assign, /*MemberPattern=*/"{0} &operator=({1})");63 64  return S;65}66 67// A tweak that adds missing declarations of copy & move constructors.68//69// e.g. given `struct ^S{};`, produces:70//   struct S {71//     S(const S&) = default;72//     S(S&&) = default;73//     S &operator=(const S&) = default;74//     S &operator=(S&&) = default;75//   };76//77// Added members are defaulted or deleted to approximately preserve semantics.78// (May not be a strict no-op when they were not implicitly declared).79//80// Having these spelled out is useful:81//  - to understand the implicit behavior82//  - to avoid relying on the implicit behavior83//  - as a baseline for explicit modification84class SpecialMembers : public Tweak {85public:86  const char *id() const final;87  llvm::StringLiteral kind() const override {88    return CodeAction::REFACTOR_KIND;89  }90  std::string title() const override {91    return llvm::formatv("Declare implicit {0} members",92                         NeedCopy ? NeedMove ? "copy/move" : "copy" : "move");93  }94 95  bool prepare(const Selection &Inputs) override {96    // This tweak relies on =default and =delete.97    if (!Inputs.AST->getLangOpts().CPlusPlus11)98      return false;99 100    // Trigger only on class definitions.101    if (auto *N = Inputs.ASTSelection.commonAncestor())102      Class = const_cast<CXXRecordDecl *>(N->ASTNode.get<CXXRecordDecl>());103    if (!Class || !Class->isThisDeclarationADefinition() || Class->isUnion())104      return false;105 106    // Tweak is only available if some members are missing.107    NeedCopy = !Class->hasUserDeclaredCopyConstructor() ||108               !Class->hasUserDeclaredCopyAssignment();109    NeedMove = !Class->hasUserDeclaredMoveAssignment() ||110               !Class->hasUserDeclaredMoveConstructor();111    return NeedCopy || NeedMove;112  }113 114  Expected<Effect> apply(const Selection &Inputs) override {115    // Implicit special members are created lazily by clang.116    // We need them so we can tell whether they should be =default or =delete.117    Inputs.AST->getSema().ForceDeclarationOfImplicitMembers(Class);118    std::string Code = buildSpecialMemberDeclarations(*Class);119 120    // Prefer to place the new members...121    std::vector<Anchor> Anchors = {122        // Below the default constructor123        {[](const Decl *D) {124           if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(D))125             return CCD->isDefaultConstructor();126           return false;127         },128         Anchor::Below},129        // Above existing constructors130        {[](const Decl *D) { return llvm::isa<CXXConstructorDecl>(D); },131         Anchor::Above},132        // At the top of the public section133        {[](const Decl *D) { return true; }, Anchor::Above},134    };135    auto Edit = insertDecl(Code, *Class, std::move(Anchors), AS_public);136    if (!Edit)137      return Edit.takeError();138    return Effect::mainFileEdit(Inputs.AST->getSourceManager(),139                                tooling::Replacements{std::move(*Edit)});140  }141 142private:143  bool NeedCopy = false, NeedMove = false;144  CXXRecordDecl *Class = nullptr;145};146REGISTER_TWEAK(SpecialMembers)147 148} // namespace149} // namespace clangd150} // namespace clang151