brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.0 KiB · c1e66f2 Raw
151 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 "ForwardingReferenceOverloadCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::bugprone {16 17namespace {18// Check if the given type is related to std::enable_if.19AST_MATCHER(QualType, isEnableIf) {20  auto CheckTemplate = [](const TemplateSpecializationType *Spec) {21    if (!Spec)22      return false;23 24    const TemplateDecl *TDecl = Spec->getTemplateName().getAsTemplateDecl();25 26    return TDecl && TDecl->isInStdNamespace() &&27           (TDecl->getName() == "enable_if" ||28            TDecl->getName() == "enable_if_t");29  };30  const Type *BaseType = Node.getTypePtr();31  // Case: pointer or reference to enable_if.32  while (BaseType->isPointerType() || BaseType->isReferenceType()) {33    BaseType = BaseType->getPointeeType().getTypePtr();34  }35  // Case: type parameter dependent (enable_if<is_integral<T>>).36  if (const auto *Dependent = BaseType->getAs<DependentNameType>())37    BaseType = Dependent->getQualifier().getAsType();38  if (!BaseType)39    return false;40  if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>()))41    return true; // Case: enable_if_t< >.42  if (const auto *TT = BaseType->getAs<TypedefType>())43    if (const NestedNameSpecifier Q = TT->getQualifier();44        Q.getKind() == NestedNameSpecifier::Kind::Type)45      if (CheckTemplate(Q.getAsType()->getAs<TemplateSpecializationType>()))46        return true; // Case: enable_if< >::type.47  return false;48}49AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,50              clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {51  return Node.hasDefaultArgument() &&52         TypeMatcher.matches(53             Node.getDefaultArgument().getArgument().getAsType(), Finder,54             Builder);55}56AST_MATCHER(TemplateDecl, hasAssociatedConstraints) {57  return Node.hasAssociatedConstraints();58}59} // namespace60 61void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {62  auto ForwardingRefParm =63      parmVarDecl(64          hasType(qualType(rValueReferenceType(),65                           references(templateTypeParmType(hasDeclaration(66                               templateTypeParmDecl().bind("type-parm-decl")))),67                           unless(references(isConstQualified())))))68          .bind("parm-var");69 70  const DeclarationMatcher FindOverload =71      cxxConstructorDecl(72          hasParameter(0, ForwardingRefParm), unless(isDeleted()),73          unless(hasAnyParameter(74              // No warning: enable_if as constructor parameter.75              parmVarDecl(hasType(isEnableIf())))),76          unless(hasParent(functionTemplateDecl(anyOf(77              // No warning: has associated constraints (like requires78              // expression).79              hasAssociatedConstraints(),80              // No warning: enable_if as type parameter.81              has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),82              // No warning: enable_if as non-type template parameter.83              has(nonTypeTemplateParmDecl(84                  hasType(isEnableIf()),85                  anyOf(hasDescendant(cxxBoolLiteral()),86                        hasDescendant(cxxNullPtrLiteralExpr()),87                        hasDescendant(integerLiteral())))))))))88          .bind("ctor");89  Finder->addMatcher(FindOverload, this);90}91 92void ForwardingReferenceOverloadCheck::check(93    const MatchFinder::MatchResult &Result) {94  const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");95  const auto *TypeParmDecl =96      Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");97 98  // Get the FunctionDecl and FunctionTemplateDecl containing the function99  // parameter.100  const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());101  if (!FuncForParam)102    return;103  const FunctionTemplateDecl *FuncTemplate =104      FuncForParam->getDescribedFunctionTemplate();105  if (!FuncTemplate)106    return;107 108  // Check that the template type parameter belongs to the same function109  // template as the function parameter of that type. (This implies that type110  // deduction will happen on the type.)111  const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();112  if (!llvm::is_contained(*Params, TypeParmDecl))113    return;114 115  // Every parameter after the first must have a default value.116  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");117  for (const auto *Param : llvm::drop_begin(Ctor->parameters())) {118    if (!Param->hasDefaultArg())119      return;120  }121  bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,122       DisabledMove = false;123  for (const auto *OtherCtor : Ctor->getParent()->ctors()) {124    if (OtherCtor->isCopyOrMoveConstructor()) {125      if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)126        (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;127      else128        (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;129    }130  }131  const bool Copy =132      (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;133  const bool Move = !DisabledMove || EnabledMove;134  if (!Copy && !Move)135    return;136  diag(Ctor->getLocation(),137       "constructor accepting a forwarding reference can "138       "hide the %select{copy|move|copy and move}0 constructor%s1")139      << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;140  for (const auto *OtherCtor : Ctor->getParent()->ctors()) {141    if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&142        OtherCtor->getAccess() != AS_private) {143      diag(OtherCtor->getLocation(),144           "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)145          << OtherCtor->isMoveConstructor();146    }147  }148}149 150} // namespace clang::tidy::bugprone151