| //===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "ForwardingReferenceOverloadCheck.h" | 
 | #include "clang/AST/ASTContext.h" | 
 | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
 | #include <algorithm> | 
 |  | 
 | using namespace clang::ast_matchers; | 
 |  | 
 | namespace clang { | 
 | namespace tidy { | 
 | namespace bugprone { | 
 |  | 
 | namespace { | 
 | // Check if the given type is related to std::enable_if. | 
 | AST_MATCHER(QualType, isEnableIf) { | 
 |   auto CheckTemplate = [](const TemplateSpecializationType *Spec) { | 
 |     if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) { | 
 |       return false; | 
 |     } | 
 |     const NamedDecl *TypeDecl = | 
 |         Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(); | 
 |     return TypeDecl->isInStdNamespace() && | 
 |            (TypeDecl->getName().equals("enable_if") || | 
 |             TypeDecl->getName().equals("enable_if_t")); | 
 |   }; | 
 |   const Type *BaseType = Node.getTypePtr(); | 
 |   // Case: pointer or reference to enable_if. | 
 |   while (BaseType->isPointerType() || BaseType->isReferenceType()) { | 
 |     BaseType = BaseType->getPointeeType().getTypePtr(); | 
 |   } | 
 |   // Case: type parameter dependent (enable_if<is_integral<T>>). | 
 |   if (const auto *Dependent = BaseType->getAs<DependentNameType>()) { | 
 |     BaseType = Dependent->getQualifier()->getAsType(); | 
 |   } | 
 |   if (!BaseType) | 
 |     return false; | 
 |   if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) { | 
 |     return true; // Case: enable_if_t< >. | 
 |   } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) { | 
 |     if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) { | 
 |       if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) { | 
 |         return true; // Case: enable_if< >::type. | 
 |       } | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 | AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument, | 
 |               clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) { | 
 |   return Node.hasDefaultArgument() && | 
 |          TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder); | 
 | } | 
 | } // namespace | 
 |  | 
 | void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) { | 
 |   // Forwarding references require C++11 or later. | 
 |   if (!getLangOpts().CPlusPlus11) | 
 |     return; | 
 |  | 
 |   auto ForwardingRefParm = | 
 |       parmVarDecl( | 
 |           hasType(qualType(rValueReferenceType(), | 
 |                            references(templateTypeParmType(hasDeclaration( | 
 |                                templateTypeParmDecl().bind("type-parm-decl")))), | 
 |                            unless(references(isConstQualified()))))) | 
 |           .bind("parm-var"); | 
 |  | 
 |   DeclarationMatcher findOverload = | 
 |       cxxConstructorDecl( | 
 |           hasParameter(0, ForwardingRefParm), | 
 |           unless(hasAnyParameter( | 
 |               // No warning: enable_if as constructor parameter. | 
 |               parmVarDecl(hasType(isEnableIf())))), | 
 |           unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl( | 
 |               // No warning: enable_if as type parameter. | 
 |               hasDefaultArgument(isEnableIf()))))))) | 
 |           .bind("ctor"); | 
 |   Finder->addMatcher(findOverload, this); | 
 | } | 
 |  | 
 | void ForwardingReferenceOverloadCheck::check( | 
 |     const MatchFinder::MatchResult &Result) { | 
 |   const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var"); | 
 |   const auto *TypeParmDecl = | 
 |       Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl"); | 
 |  | 
 |   // Get the FunctionDecl and FunctionTemplateDecl containing the function | 
 |   // parameter. | 
 |   const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext()); | 
 |   if (!FuncForParam) | 
 |     return; | 
 |   const FunctionTemplateDecl *FuncTemplate = | 
 |       FuncForParam->getDescribedFunctionTemplate(); | 
 |   if (!FuncTemplate) | 
 |     return; | 
 |  | 
 |   // Check that the template type parameter belongs to the same function | 
 |   // template as the function parameter of that type. (This implies that type | 
 |   // deduction will happen on the type.) | 
 |   const TemplateParameterList *Params = FuncTemplate->getTemplateParameters(); | 
 |   if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end()) | 
 |     return; | 
 |  | 
 |   // Every parameter after the first must have a default value. | 
 |   const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); | 
 |   for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) { | 
 |     if (!(*Iter)->hasDefaultArg()) | 
 |       return; | 
 |   } | 
 |   bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false, | 
 |        DisabledMove = false; | 
 |   for (const auto *OtherCtor : Ctor->getParent()->ctors()) { | 
 |     if (OtherCtor->isCopyOrMoveConstructor()) { | 
 |       if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private) | 
 |         (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true; | 
 |       else | 
 |         (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true; | 
 |     } | 
 |   } | 
 |   bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy; | 
 |   bool Move = !DisabledMove || EnabledMove; | 
 |   if (!Copy && !Move) | 
 |     return; | 
 |   diag(Ctor->getLocation(), | 
 |        "constructor accepting a forwarding reference can " | 
 |        "hide the %select{copy|move|copy and move}0 constructor%s1") | 
 |       << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move; | 
 |   for (const auto *OtherCtor : Ctor->getParent()->ctors()) { | 
 |     if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() && | 
 |         OtherCtor->getAccess() != AS_private) { | 
 |       diag(OtherCtor->getLocation(), | 
 |            "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note) | 
 |           << OtherCtor->isMoveConstructor(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | } // namespace bugprone | 
 | } // namespace tidy | 
 | } // namespace clang |