1 //===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "ForwardingReferenceOverloadCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::bugprone
{
18 // Check if the given type is related to std::enable_if.
19 AST_MATCHER(QualType
, isEnableIf
) {
20 auto CheckTemplate
= [](const TemplateSpecializationType
*Spec
) {
24 const TemplateDecl
*TDecl
= Spec
->getTemplateName().getAsTemplateDecl();
26 return TDecl
&& TDecl
->isInStdNamespace() &&
27 (TDecl
->getName() == "enable_if" ||
28 TDecl
->getName() == "enable_if_t");
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();
35 // Case: type parameter dependent (enable_if<is_integral<T>>).
36 if (const auto *Dependent
= BaseType
->getAs
<DependentNameType
>()) {
37 BaseType
= Dependent
->getQualifier()->getAsType();
41 if (CheckTemplate(BaseType
->getAs
<TemplateSpecializationType
>()))
42 return true; // Case: enable_if_t< >.
43 if (const auto *Elaborated
= BaseType
->getAs
<ElaboratedType
>()) {
44 if (const auto *Q
= Elaborated
->getQualifier())
45 if (const auto *Qualifier
= Q
->getAsType()) {
46 if (CheckTemplate(Qualifier
->getAs
<TemplateSpecializationType
>())) {
47 return true; // Case: enable_if< >::type.
53 AST_MATCHER_P(TemplateTypeParmDecl
, hasDefaultArgument
,
54 clang::ast_matchers::internal::Matcher
<QualType
>, TypeMatcher
) {
55 return Node
.hasDefaultArgument() &&
57 Node
.getDefaultArgument().getArgument().getAsType(), Finder
,
60 AST_MATCHER(TemplateDecl
, hasAssociatedConstraints
) {
61 return Node
.hasAssociatedConstraints();
65 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder
*Finder
) {
66 auto ForwardingRefParm
=
68 hasType(qualType(rValueReferenceType(),
69 references(templateTypeParmType(hasDeclaration(
70 templateTypeParmDecl().bind("type-parm-decl")))),
71 unless(references(isConstQualified())))))
74 DeclarationMatcher FindOverload
=
76 hasParameter(0, ForwardingRefParm
), unless(isDeleted()),
77 unless(hasAnyParameter(
78 // No warning: enable_if as constructor parameter.
79 parmVarDecl(hasType(isEnableIf())))),
80 unless(hasParent(functionTemplateDecl(anyOf(
81 // No warning: has associated constraints (like requires
83 hasAssociatedConstraints(),
84 // No warning: enable_if as type parameter.
85 has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),
86 // No warning: enable_if as non-type template parameter.
87 has(nonTypeTemplateParmDecl(
88 hasType(isEnableIf()),
89 anyOf(hasDescendant(cxxBoolLiteral()),
90 hasDescendant(cxxNullPtrLiteralExpr()),
91 hasDescendant(integerLiteral())))))))))
93 Finder
->addMatcher(FindOverload
, this);
96 void ForwardingReferenceOverloadCheck::check(
97 const MatchFinder::MatchResult
&Result
) {
98 const auto *ParmVar
= Result
.Nodes
.getNodeAs
<ParmVarDecl
>("parm-var");
99 const auto *TypeParmDecl
=
100 Result
.Nodes
.getNodeAs
<TemplateTypeParmDecl
>("type-parm-decl");
102 // Get the FunctionDecl and FunctionTemplateDecl containing the function
104 const auto *FuncForParam
= dyn_cast
<FunctionDecl
>(ParmVar
->getDeclContext());
107 const FunctionTemplateDecl
*FuncTemplate
=
108 FuncForParam
->getDescribedFunctionTemplate();
112 // Check that the template type parameter belongs to the same function
113 // template as the function parameter of that type. (This implies that type
114 // deduction will happen on the type.)
115 const TemplateParameterList
*Params
= FuncTemplate
->getTemplateParameters();
116 if (!llvm::is_contained(*Params
, TypeParmDecl
))
119 // Every parameter after the first must have a default value.
120 const auto *Ctor
= Result
.Nodes
.getNodeAs
<CXXConstructorDecl
>("ctor");
121 for (const auto *Param
: llvm::drop_begin(Ctor
->parameters())) {
122 if (!Param
->hasDefaultArg())
125 bool EnabledCopy
= false, DisabledCopy
= false, EnabledMove
= false,
126 DisabledMove
= false;
127 for (const auto *OtherCtor
: Ctor
->getParent()->ctors()) {
128 if (OtherCtor
->isCopyOrMoveConstructor()) {
129 if (OtherCtor
->isDeleted() || OtherCtor
->getAccess() == AS_private
)
130 (OtherCtor
->isCopyConstructor() ? DisabledCopy
: DisabledMove
) = true;
132 (OtherCtor
->isCopyConstructor() ? EnabledCopy
: EnabledMove
) = true;
135 bool Copy
= (!EnabledMove
&& !DisabledMove
&& !DisabledCopy
) || EnabledCopy
;
136 bool Move
= !DisabledMove
|| EnabledMove
;
139 diag(Ctor
->getLocation(),
140 "constructor accepting a forwarding reference can "
141 "hide the %select{copy|move|copy and move}0 constructor%s1")
142 << (Copy
&& Move
? 2 : (Copy
? 0 : 1)) << Copy
+ Move
;
143 for (const auto *OtherCtor
: Ctor
->getParent()->ctors()) {
144 if (OtherCtor
->isCopyOrMoveConstructor() && !OtherCtor
->isDeleted() &&
145 OtherCtor
->getAccess() != AS_private
) {
146 diag(OtherCtor
->getLocation(),
147 "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note
)
148 << OtherCtor
->isMoveConstructor();
153 } // namespace clang::tidy::bugprone