brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · 5ef72ea Raw
182 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 "CrtpConstructorAccessibilityCheck.h"10#include "../utils/LexerUtils.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::bugprone {16 17static bool hasPrivateConstructor(const CXXRecordDecl *RD) {18  return llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) {19    return Ctor->getAccess() == AS_private;20  });21}22 23static bool isDerivedParameterBefriended(const CXXRecordDecl *CRTP,24                                         const NamedDecl *Param) {25  return llvm::any_of(CRTP->friends(), [&](const FriendDecl *Friend) {26    const TypeSourceInfo *const FriendType = Friend->getFriendType();27    if (!FriendType) {28      return false;29    }30 31    const auto *const TTPT =32        dyn_cast<TemplateTypeParmType>(FriendType->getType());33 34    return TTPT && TTPT->getDecl() == Param;35  });36}37 38static bool isDerivedClassBefriended(const CXXRecordDecl *CRTP,39                                     const CXXRecordDecl *Derived) {40  return llvm::any_of(CRTP->friends(), [&](const FriendDecl *Friend) {41    const TypeSourceInfo *const FriendType = Friend->getFriendType();42    if (!FriendType) {43      return false;44    }45 46    return declaresSameEntity(FriendType->getType()->getAsCXXRecordDecl(),47                              Derived);48  });49}50 51static const NamedDecl *52getDerivedParameter(const ClassTemplateSpecializationDecl *CRTP,53                    const CXXRecordDecl *Derived) {54  size_t Idx = 0;55  const bool AnyOf = llvm::any_of(56      CRTP->getTemplateArgs().asArray(), [&](const TemplateArgument &Arg) {57        ++Idx;58        return Arg.getKind() == TemplateArgument::Type &&59               declaresSameEntity(Arg.getAsType()->getAsCXXRecordDecl(),60                                  Derived);61      });62 63  return AnyOf ? CRTP->getSpecializedTemplate()64                     ->getTemplateParameters()65                     ->getParam(Idx - 1)66               : nullptr;67}68 69static std::vector<FixItHint>70hintMakeCtorPrivate(const CXXConstructorDecl *Ctor,71                    const std::string &OriginalAccess) {72  std::vector<FixItHint> Hints;73 74  Hints.emplace_back(FixItHint::CreateInsertion(75      Ctor->getBeginLoc().getLocWithOffset(-1), "private:\n"));76 77  const ASTContext &ASTCtx = Ctor->getASTContext();78  const SourceLocation CtorEndLoc =79      Ctor->isExplicitlyDefaulted()80          ? utils::lexer::findNextTerminator(Ctor->getEndLoc(),81                                             ASTCtx.getSourceManager(),82                                             ASTCtx.getLangOpts())83          : Ctor->getEndLoc();84  Hints.emplace_back(FixItHint::CreateInsertion(85      CtorEndLoc.getLocWithOffset(1), '\n' + OriginalAccess + ':' + '\n'));86 87  return Hints;88}89 90void CrtpConstructorAccessibilityCheck::registerMatchers(MatchFinder *Finder) {91  Finder->addMatcher(92      classTemplateSpecializationDecl(93          decl().bind("crtp"),94          hasAnyTemplateArgument(refersToType(recordType(hasDeclaration(95              cxxRecordDecl(96                  isDerivedFrom(cxxRecordDecl(equalsBoundNode("crtp"))))97                  .bind("derived")))))),98      this);99}100 101void CrtpConstructorAccessibilityCheck::check(102    const MatchFinder::MatchResult &Result) {103  const auto *CRTPInstantiation =104      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("crtp");105  const auto *DerivedRecord = Result.Nodes.getNodeAs<CXXRecordDecl>("derived");106  const CXXRecordDecl *CRTPDeclaration =107      CRTPInstantiation->getSpecializedTemplate()->getTemplatedDecl();108 109  if (!CRTPDeclaration->hasDefinition()) {110    return;111  }112 113  const auto *DerivedTemplateParameter =114      getDerivedParameter(CRTPInstantiation, DerivedRecord);115 116  assert(DerivedTemplateParameter &&117         "No template parameter corresponds to the derived class of the CRTP.");118 119  const bool NeedsFriend =120      !isDerivedParameterBefriended(CRTPDeclaration,121                                    DerivedTemplateParameter) &&122      !isDerivedClassBefriended(CRTPDeclaration, DerivedRecord);123 124  const FixItHint HintFriend = FixItHint::CreateInsertion(125      CRTPDeclaration->getBraceRange().getEnd(),126      "friend " + DerivedTemplateParameter->getNameAsString() + ';' + '\n');127 128  if (hasPrivateConstructor(CRTPDeclaration) && NeedsFriend) {129    diag(CRTPDeclaration->getLocation(),130         "the CRTP cannot be constructed from the derived class; consider "131         "declaring the derived class as friend")132        << HintFriend;133  }134 135  auto WithFriendHintIfNeeded = [&](const DiagnosticBuilder &Diag,136                                    bool NeedsFriend) {137    if (NeedsFriend)138      Diag << HintFriend;139  };140 141  if (!CRTPDeclaration->hasUserDeclaredConstructor()) {142    const bool IsStruct = CRTPDeclaration->isStruct();143 144    WithFriendHintIfNeeded(145        diag(CRTPDeclaration->getLocation(),146             "the implicit default constructor of the CRTP is publicly "147             "accessible; consider making it private%select{| and declaring "148             "the derived class as friend}0")149            << NeedsFriend150            << FixItHint::CreateInsertion(151                   CRTPDeclaration->getBraceRange().getBegin().getLocWithOffset(152                       1),153                   (IsStruct ? "\nprivate:\n" : "\n") +154                       CRTPDeclaration->getNameAsString() + "() = default;\n" +155                       (IsStruct ? "public:\n" : "")),156        NeedsFriend);157  }158 159  for (auto &&Ctor : CRTPDeclaration->ctors()) {160    if (Ctor->getAccess() == AS_private || Ctor->isDeleted())161      continue;162 163    const bool IsPublic = Ctor->getAccess() == AS_public;164    const std::string Access = IsPublic ? "public" : "protected";165 166    WithFriendHintIfNeeded(167        diag(Ctor->getLocation(),168             "%0 constructor allows the CRTP to be %select{inherited "169             "from|constructed}1 as a regular template class; consider making "170             "it private%select{| and declaring the derived class as friend}2")171            << Access << IsPublic << NeedsFriend172            << hintMakeCtorPrivate(Ctor, Access),173        NeedsFriend);174  }175}176 177bool CrtpConstructorAccessibilityCheck::isLanguageVersionSupported(178    const LangOptions &LangOpts) const {179  return LangOpts.CPlusPlus11;180}181} // namespace clang::tidy::bugprone182