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