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"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::bugprone
{
19 // Check if the given type is related to std::enable_if.
20 AST_MATCHER(QualType
, isEnableIf
) {
21 auto CheckTemplate
= [](const TemplateSpecializationType
*Spec
) {
22 if (!Spec
|| !Spec
->getTemplateName().getAsTemplateDecl()) {
25 const NamedDecl
*TypeDecl
=
26 Spec
->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
27 return TypeDecl
->isInStdNamespace() &&
28 (TypeDecl
->getName().equals("enable_if") ||
29 TypeDecl
->getName().equals("enable_if_t"));
31 const Type
*BaseType
= Node
.getTypePtr();
32 // Case: pointer or reference to enable_if.
33 while (BaseType
->isPointerType() || BaseType
->isReferenceType()) {
34 BaseType
= BaseType
->getPointeeType().getTypePtr();
36 // Case: type parameter dependent (enable_if<is_integral<T>>).
37 if (const auto *Dependent
= BaseType
->getAs
<DependentNameType
>()) {
38 BaseType
= Dependent
->getQualifier()->getAsType();
42 if (CheckTemplate(BaseType
->getAs
<TemplateSpecializationType
>()))
43 return true; // Case: enable_if_t< >.
44 if (const auto *Elaborated
= BaseType
->getAs
<ElaboratedType
>()) {
45 if (const auto *Q
= Elaborated
->getQualifier())
46 if (const auto *Qualifier
= Q
->getAsType()) {
47 if (CheckTemplate(Qualifier
->getAs
<TemplateSpecializationType
>())) {
48 return true; // Case: enable_if< >::type.
54 AST_MATCHER_P(TemplateTypeParmDecl
, hasDefaultArgument
,
55 clang::ast_matchers::internal::Matcher
<QualType
>, TypeMatcher
) {
56 return Node
.hasDefaultArgument() &&
57 TypeMatcher
.matches(Node
.getDefaultArgument(), Finder
, Builder
);
59 AST_MATCHER(TemplateDecl
, hasAssociatedConstraints
) {
60 return Node
.hasAssociatedConstraints();
64 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder
*Finder
) {
65 auto ForwardingRefParm
=
67 hasType(qualType(rValueReferenceType(),
68 references(templateTypeParmType(hasDeclaration(
69 templateTypeParmDecl().bind("type-parm-decl")))),
70 unless(references(isConstQualified())))))
73 DeclarationMatcher FindOverload
=
75 hasParameter(0, ForwardingRefParm
),
76 unless(hasAnyParameter(
77 // No warning: enable_if as constructor parameter.
78 parmVarDecl(hasType(isEnableIf())))),
79 unless(hasParent(functionTemplateDecl(anyOf(
80 // No warning: has associated constraints (like requires
82 hasAssociatedConstraints(),
83 // No warning: enable_if as type parameter.
84 has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),
85 // No warning: enable_if as non-type template parameter.
86 has(nonTypeTemplateParmDecl(
87 hasType(isEnableIf()),
88 anyOf(hasDescendant(cxxBoolLiteral()),
89 hasDescendant(cxxNullPtrLiteralExpr()),
90 hasDescendant(integerLiteral())))))))))
92 Finder
->addMatcher(FindOverload
, this);
95 void ForwardingReferenceOverloadCheck::check(
96 const MatchFinder::MatchResult
&Result
) {
97 const auto *ParmVar
= Result
.Nodes
.getNodeAs
<ParmVarDecl
>("parm-var");
98 const auto *TypeParmDecl
=
99 Result
.Nodes
.getNodeAs
<TemplateTypeParmDecl
>("type-parm-decl");
101 // Get the FunctionDecl and FunctionTemplateDecl containing the function
103 const auto *FuncForParam
= dyn_cast
<FunctionDecl
>(ParmVar
->getDeclContext());
106 const FunctionTemplateDecl
*FuncTemplate
=
107 FuncForParam
->getDescribedFunctionTemplate();
111 // Check that the template type parameter belongs to the same function
112 // template as the function parameter of that type. (This implies that type
113 // deduction will happen on the type.)
114 const TemplateParameterList
*Params
= FuncTemplate
->getTemplateParameters();
115 if (!llvm::is_contained(*Params
, TypeParmDecl
))
118 // Every parameter after the first must have a default value.
119 const auto *Ctor
= Result
.Nodes
.getNodeAs
<CXXConstructorDecl
>("ctor");
120 for (const auto *Param
: llvm::drop_begin(Ctor
->parameters())) {
121 if (!Param
->hasDefaultArg())
124 bool EnabledCopy
= false, DisabledCopy
= false, EnabledMove
= false,
125 DisabledMove
= false;
126 for (const auto *OtherCtor
: Ctor
->getParent()->ctors()) {
127 if (OtherCtor
->isCopyOrMoveConstructor()) {
128 if (OtherCtor
->isDeleted() || OtherCtor
->getAccess() == AS_private
)
129 (OtherCtor
->isCopyConstructor() ? DisabledCopy
: DisabledMove
) = true;
131 (OtherCtor
->isCopyConstructor() ? EnabledCopy
: EnabledMove
) = true;
134 bool Copy
= (!EnabledMove
&& !DisabledMove
&& !DisabledCopy
) || EnabledCopy
;
135 bool Move
= !DisabledMove
|| EnabledMove
;
138 diag(Ctor
->getLocation(),
139 "constructor accepting a forwarding reference can "
140 "hide the %select{copy|move|copy and move}0 constructor%s1")
141 << (Copy
&& Move
? 2 : (Copy
? 0 : 1)) << Copy
+ Move
;
142 for (const auto *OtherCtor
: Ctor
->getParent()->ctors()) {
143 if (OtherCtor
->isCopyOrMoveConstructor() && !OtherCtor
->isDeleted() &&
144 OtherCtor
->getAccess() != AS_private
) {
145 diag(OtherCtor
->getLocation(),
146 "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note
)
147 << OtherCtor
->isMoveConstructor();
152 } // namespace clang::tidy::bugprone