1 //===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ForwardingReferenceOverloadCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include <algorithm> 14 15 using namespace clang::ast_matchers; 16 17 namespace clang { 18 namespace tidy { 19 namespace bugprone { 20 21 namespace { 22 // Check if the given type is related to std::enable_if. 23 AST_MATCHER(QualType, isEnableIf) { 24 auto CheckTemplate = [](const TemplateSpecializationType *Spec) { 25 if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) { 26 return false; 27 } 28 const NamedDecl *TypeDecl = 29 Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(); 30 return TypeDecl->isInStdNamespace() && 31 (TypeDecl->getName().equals("enable_if") || 32 TypeDecl->getName().equals("enable_if_t")); 33 }; 34 const Type *BaseType = Node.getTypePtr(); 35 // Case: pointer or reference to enable_if. 36 while (BaseType->isPointerType() || BaseType->isReferenceType()) { 37 BaseType = BaseType->getPointeeType().getTypePtr(); 38 } 39 // Case: type parameter dependent (enable_if<is_integral<T>>). 40 if (const auto *Dependent = BaseType->getAs<DependentNameType>()) { 41 BaseType = Dependent->getQualifier()->getAsType(); 42 } 43 if (!BaseType) 44 return false; 45 if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) { 46 return true; // Case: enable_if_t< >. 47 } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) { 48 if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) { 49 if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) { 50 return true; // Case: enable_if< >::type. 51 } 52 } 53 } 54 return false; 55 } 56 AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument, 57 clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) { 58 return Node.hasDefaultArgument() && 59 TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder); 60 } 61 } // namespace 62 63 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) { 64 // Forwarding references require C++11 or later. 65 if (!getLangOpts().CPlusPlus11) 66 return; 67 68 auto ForwardingRefParm = 69 parmVarDecl( 70 hasType(qualType(rValueReferenceType(), 71 references(templateTypeParmType(hasDeclaration( 72 templateTypeParmDecl().bind("type-parm-decl")))), 73 unless(references(isConstQualified()))))) 74 .bind("parm-var"); 75 76 DeclarationMatcher findOverload = 77 cxxConstructorDecl( 78 hasParameter(0, ForwardingRefParm), 79 unless(hasAnyParameter( 80 // No warning: enable_if as constructor parameter. 81 parmVarDecl(hasType(isEnableIf())))), 82 unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl( 83 // No warning: enable_if as type parameter. 84 hasDefaultArgument(isEnableIf()))))))) 85 .bind("ctor"); 86 Finder->addMatcher(findOverload, this); 87 } 88 89 void ForwardingReferenceOverloadCheck::check( 90 const MatchFinder::MatchResult &Result) { 91 const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var"); 92 const auto *TypeParmDecl = 93 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl"); 94 95 // Get the FunctionDecl and FunctionTemplateDecl containing the function 96 // parameter. 97 const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext()); 98 if (!FuncForParam) 99 return; 100 const FunctionTemplateDecl *FuncTemplate = 101 FuncForParam->getDescribedFunctionTemplate(); 102 if (!FuncTemplate) 103 return; 104 105 // Check that the template type parameter belongs to the same function 106 // template as the function parameter of that type. (This implies that type 107 // deduction will happen on the type.) 108 const TemplateParameterList *Params = FuncTemplate->getTemplateParameters(); 109 if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end()) 110 return; 111 112 // Every parameter after the first must have a default value. 113 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); 114 for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) { 115 if (!(*Iter)->hasDefaultArg()) 116 return; 117 } 118 bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false, 119 DisabledMove = false; 120 for (const auto *OtherCtor : Ctor->getParent()->ctors()) { 121 if (OtherCtor->isCopyOrMoveConstructor()) { 122 if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private) 123 (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true; 124 else 125 (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true; 126 } 127 } 128 bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy; 129 bool Move = !DisabledMove || EnabledMove; 130 if (!Copy && !Move) 131 return; 132 diag(Ctor->getLocation(), 133 "constructor accepting a forwarding reference can " 134 "hide the %select{copy|move|copy and move}0 constructor%s1") 135 << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move; 136 for (const auto *OtherCtor : Ctor->getParent()->ctors()) { 137 if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() && 138 OtherCtor->getAccess() != AS_private) { 139 diag(OtherCtor->getLocation(), 140 "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note) 141 << OtherCtor->isMoveConstructor(); 142 } 143 } 144 } 145 146 } // namespace bugprone 147 } // namespace tidy 148 } // namespace clang 149