[MemProf] Templatize CallStackRadixTreeBuilder (NFC) (#117014)
[llvm-project.git] / clang-tools-extra / clang-tidy / bugprone / ForwardingReferenceOverloadCheck.cpp
blob00e8f7e514368ba8b97365c247bc051f5ef7160f
1 //===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
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-exception
6 //
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 {
17 namespace {
18 // Check if the given type is related to std::enable_if.
19 AST_MATCHER(QualType, isEnableIf) {
20 auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
21 if (!Spec)
22 return false;
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();
39 if (!BaseType)
40 return false;
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.
51 return false;
53 AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
54 clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
55 return Node.hasDefaultArgument() &&
56 TypeMatcher.matches(
57 Node.getDefaultArgument().getArgument().getAsType(), Finder,
58 Builder);
60 AST_MATCHER(TemplateDecl, hasAssociatedConstraints) {
61 return Node.hasAssociatedConstraints();
63 } // namespace
65 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
66 auto ForwardingRefParm =
67 parmVarDecl(
68 hasType(qualType(rValueReferenceType(),
69 references(templateTypeParmType(hasDeclaration(
70 templateTypeParmDecl().bind("type-parm-decl")))),
71 unless(references(isConstQualified())))))
72 .bind("parm-var");
74 DeclarationMatcher FindOverload =
75 cxxConstructorDecl(
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
82 // expression).
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())))))))))
92 .bind("ctor");
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
103 // parameter.
104 const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
105 if (!FuncForParam)
106 return;
107 const FunctionTemplateDecl *FuncTemplate =
108 FuncForParam->getDescribedFunctionTemplate();
109 if (!FuncTemplate)
110 return;
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))
117 return;
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())
123 return;
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;
131 else
132 (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
135 bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
136 bool Move = !DisabledMove || EnabledMove;
137 if (!Copy && !Move)
138 return;
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