[Workflow] Try to fix code-formatter failing to find changes in some cases.
[llvm-project.git] / clang-tools-extra / clang-tidy / bugprone / ForwardingReferenceOverloadCheck.cpp
blobc608fe713f9f5b8d9f34dcff8b26e2655da595a2
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"
12 #include <algorithm>
14 using namespace clang::ast_matchers;
16 namespace clang::tidy::bugprone {
18 namespace {
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()) {
23 return false;
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();
40 if (!BaseType)
41 return false;
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.
52 return false;
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();
62 } // namespace
64 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
65 auto ForwardingRefParm =
66 parmVarDecl(
67 hasType(qualType(rValueReferenceType(),
68 references(templateTypeParmType(hasDeclaration(
69 templateTypeParmDecl().bind("type-parm-decl")))),
70 unless(references(isConstQualified())))))
71 .bind("parm-var");
73 DeclarationMatcher FindOverload =
74 cxxConstructorDecl(
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
81 // expression).
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())))))))))
91 .bind("ctor");
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
102 // parameter.
103 const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
104 if (!FuncForParam)
105 return;
106 const FunctionTemplateDecl *FuncTemplate =
107 FuncForParam->getDescribedFunctionTemplate();
108 if (!FuncTemplate)
109 return;
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))
116 return;
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())
122 return;
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;
130 else
131 (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
134 bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
135 bool Move = !DisabledMove || EnabledMove;
136 if (!Copy && !Move)
137 return;
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