brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.7 KiB · 851392f Raw
252 lines · cpp
1//===----------------------------------------------------------------------===//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 9#include "SpecialMemberFunctionsCheck.h"10 11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "llvm/ADT/StringExtras.h"14 15#define DEBUG_TYPE "clang-tidy"16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::cppcoreguidelines {20 21namespace {22AST_MATCHER(CXXRecordDecl, isInMacro) {23  return Node.getBeginLoc().isMacroID() && Node.getEndLoc().isMacroID();24}25} // namespace26 27SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(28    StringRef Name, ClangTidyContext *Context)29    : ClangTidyCheck(Name, Context), AllowMissingMoveFunctions(Options.get(30                                         "AllowMissingMoveFunctions", false)),31      AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", false)),32      AllowMissingMoveFunctionsWhenCopyIsDeleted(33          Options.get("AllowMissingMoveFunctionsWhenCopyIsDeleted", false)),34      AllowImplicitlyDeletedCopyOrMove(35          Options.get("AllowImplicitlyDeletedCopyOrMove", false)),36      IgnoreMacros(Options.get("IgnoreMacros", true)) {}37 38void SpecialMemberFunctionsCheck::storeOptions(39    ClangTidyOptions::OptionMap &Opts) {40  Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);41  Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);42  Options.store(Opts, "AllowMissingMoveFunctionsWhenCopyIsDeleted",43                AllowMissingMoveFunctionsWhenCopyIsDeleted);44  Options.store(Opts, "AllowImplicitlyDeletedCopyOrMove",45                AllowImplicitlyDeletedCopyOrMove);46  Options.store(Opts, "IgnoreMacros", IgnoreMacros);47}48 49std::optional<TraversalKind>50SpecialMemberFunctionsCheck::getCheckTraversalKind() const {51  return AllowImplicitlyDeletedCopyOrMove ? TK_AsIs52                                          : TK_IgnoreUnlessSpelledInSource;53}54 55void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {56  const auto IsNotImplicitOrDeleted = anyOf(unless(isImplicit()), isDeleted());57  const ast_matchers::internal::Matcher<CXXRecordDecl> Anything = anything();58 59  Finder->addMatcher(60      cxxRecordDecl(61          unless(isImplicit()), IgnoreMacros ? unless(isInMacro()) : Anything,62          eachOf(has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")),63                 has(cxxConstructorDecl(isCopyConstructor(),64                                        IsNotImplicitOrDeleted)65                         .bind("copy-ctor")),66                 has(cxxMethodDecl(isCopyAssignmentOperator(),67                                   IsNotImplicitOrDeleted)68                         .bind("copy-assign")),69                 has(cxxConstructorDecl(isMoveConstructor(),70                                        IsNotImplicitOrDeleted)71                         .bind("move-ctor")),72                 has(cxxMethodDecl(isMoveAssignmentOperator(),73                                   IsNotImplicitOrDeleted)74                         .bind("move-assign"))))75          .bind("class-def"),76      this);77}78 79static llvm::StringRef80toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {81  switch (K) {82  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:83    return "a destructor";84  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::85      DefaultDestructor:86    return "a default destructor";87  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::88      NonDefaultDestructor:89    return "a non-default destructor";90  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:91    return "a copy constructor";92  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:93    return "a copy assignment operator";94  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor:95    return "a move constructor";96  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment:97    return "a move assignment operator";98  }99  llvm_unreachable("Unhandled SpecialMemberFunctionKind");100}101 102static std::string103join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,104     llvm::StringRef AndOr) {105  assert(!SMFS.empty() &&106         "List of defined or undefined members should never be empty.");107  std::string Buffer;108  llvm::raw_string_ostream Stream(Buffer);109 110  Stream << toString(SMFS[0]);111  const size_t LastIndex = SMFS.size() - 1;112  for (size_t I = 1; I < LastIndex; ++I) {113    Stream << ", " << toString(SMFS[I]);114  }115  if (LastIndex != 0) {116    Stream << AndOr << toString(SMFS[LastIndex]);117  }118  return Stream.str();119}120 121void SpecialMemberFunctionsCheck::check(122    const MatchFinder::MatchResult &Result) {123  const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");124  if (!MatchedDecl)125    return;126 127  ClassDefId ID(MatchedDecl->getLocation(),128                std::string(MatchedDecl->getName()));129 130  auto StoreMember = [this, &ID](SpecialMemberFunctionData Data) {131    llvm::SmallVectorImpl<SpecialMemberFunctionData> &Members =132        ClassWithSpecialMembers[ID];133    if (!llvm::is_contained(Members, Data))134      Members.push_back(std::move(Data));135  };136 137  if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) {138    SpecialMemberFunctionKind DestructorType =139        SpecialMemberFunctionKind::Destructor;140    if (Dtor->isDefined()) {141      DestructorType = Dtor->getDefinition()->isDefaulted()142                           ? SpecialMemberFunctionKind::DefaultDestructor143                           : SpecialMemberFunctionKind::NonDefaultDestructor;144    }145    StoreMember({DestructorType, Dtor->isDeleted()});146  }147 148  const std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>149      Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},150                  {"copy-assign", SpecialMemberFunctionKind::CopyAssignment},151                  {"move-ctor", SpecialMemberFunctionKind::MoveConstructor},152                  {"move-assign", SpecialMemberFunctionKind::MoveAssignment}};153 154  for (const auto &KV : Matchers)155    if (const auto *MethodDecl =156            Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {157      StoreMember(158          {KV.second, MethodDecl->isDeleted(), MethodDecl->isImplicit()});159    }160}161 162void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {163  for (const auto &C : ClassWithSpecialMembers) {164    checkForMissingMembers(C.first, C.second);165  }166}167 168void SpecialMemberFunctionsCheck::checkForMissingMembers(169    const ClassDefId &ID,170    llvm::ArrayRef<SpecialMemberFunctionData> DefinedMembers) {171  llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;172 173  auto HasMember = [&](SpecialMemberFunctionKind Kind) {174    return llvm::any_of(DefinedMembers, [Kind](const auto &Data) {175      return Data.FunctionKind == Kind && !Data.IsImplicit;176    });177  };178 179  auto HasImplicitDeletedMember = [&](SpecialMemberFunctionKind Kind) {180    return llvm::any_of(DefinedMembers, [Kind](const auto &Data) {181      return Data.FunctionKind == Kind && Data.IsImplicit && Data.IsDeleted;182    });183  };184 185  auto IsDeleted = [&](SpecialMemberFunctionKind Kind) {186    return llvm::any_of(DefinedMembers, [Kind](const auto &Data) {187      return Data.FunctionKind == Kind && Data.IsDeleted;188    });189  };190 191  auto RequireMembers = [&](SpecialMemberFunctionKind Kind1,192                            SpecialMemberFunctionKind Kind2) {193    if (AllowImplicitlyDeletedCopyOrMove && HasImplicitDeletedMember(Kind1) &&194        HasImplicitDeletedMember(Kind2))195      return;196 197    if (!HasMember(Kind1))198      MissingMembers.push_back(Kind1);199 200    if (!HasMember(Kind2))201      MissingMembers.push_back(Kind2);202  };203 204  const bool RequireThree =205      HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) ||206      (!AllowSoleDefaultDtor &&207       (HasMember(SpecialMemberFunctionKind::Destructor) ||208        HasMember(SpecialMemberFunctionKind::DefaultDestructor))) ||209      HasMember(SpecialMemberFunctionKind::CopyConstructor) ||210      HasMember(SpecialMemberFunctionKind::CopyAssignment) ||211      HasMember(SpecialMemberFunctionKind::MoveConstructor) ||212      HasMember(SpecialMemberFunctionKind::MoveAssignment);213 214  const bool RequireFive =215      (!AllowMissingMoveFunctions && RequireThree &&216       getLangOpts().CPlusPlus11) ||217      HasMember(SpecialMemberFunctionKind::MoveConstructor) ||218      HasMember(SpecialMemberFunctionKind::MoveAssignment);219 220  if (RequireThree) {221    if (!HasMember(SpecialMemberFunctionKind::Destructor) &&222        !HasMember(SpecialMemberFunctionKind::DefaultDestructor) &&223        !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor))224      MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);225 226    RequireMembers(SpecialMemberFunctionKind::CopyConstructor,227                   SpecialMemberFunctionKind::CopyAssignment);228  }229 230  if (RequireFive &&231      !(AllowMissingMoveFunctionsWhenCopyIsDeleted &&232        (IsDeleted(SpecialMemberFunctionKind::CopyConstructor) &&233         IsDeleted(SpecialMemberFunctionKind::CopyAssignment)))) {234    assert(RequireThree);235    RequireMembers(SpecialMemberFunctionKind::MoveConstructor,236                   SpecialMemberFunctionKind::MoveAssignment);237  }238 239  if (!MissingMembers.empty()) {240    llvm::SmallVector<SpecialMemberFunctionKind, 5> DefinedMemberKinds;241    for (const auto &Data : DefinedMembers) {242      if (!Data.IsImplicit)243        DefinedMemberKinds.push_back(Data.FunctionKind);244    }245    diag(ID.first, "class '%0' defines %1 but does not define %2")246        << ID.second << cppcoreguidelines::join(DefinedMemberKinds, " and ")247        << cppcoreguidelines::join(MissingMembers, " or ");248  }249}250 251} // namespace clang::tidy::cppcoreguidelines252