clang-tools 20.0.0git
ForwardingReferenceOverloadCheck.cpp
Go to the documentation of this file.
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//===----------------------------------------------------------------------===//
8
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include <algorithm>
13
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::bugprone {
17
18namespace {
19// Check if the given type is related to std::enable_if.
20AST_MATCHER(QualType, isEnableIf) {
21 auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
22 if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
23 return false;
24 }
25 const NamedDecl *TypeDecl =
26 Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
27 return TypeDecl->isInStdNamespace() &&
28 (TypeDecl->getName() == "enable_if" ||
29 TypeDecl->getName() == "enable_if_t");
30 };
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();
35 }
36 // Case: type parameter dependent (enable_if<is_integral<T>>).
37 if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
38 BaseType = Dependent->getQualifier()->getAsType();
39 }
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.
49 }
50 }
51 }
52 return false;
53}
54AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
55 clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
56 return Node.hasDefaultArgument() &&
57 TypeMatcher.matches(
58 Node.getDefaultArgument().getArgument().getAsType(), Finder,
59 Builder);
60}
61AST_MATCHER(TemplateDecl, hasAssociatedConstraints) {
62 return Node.hasAssociatedConstraints();
63}
64} // namespace
65
67 auto ForwardingRefParm =
68 parmVarDecl(
69 hasType(qualType(rValueReferenceType(),
70 references(templateTypeParmType(hasDeclaration(
71 templateTypeParmDecl().bind("type-parm-decl")))),
72 unless(references(isConstQualified())))))
73 .bind("parm-var");
74
75 DeclarationMatcher FindOverload =
76 cxxConstructorDecl(
77 hasParameter(0, ForwardingRefParm), unless(isDeleted()),
78 unless(hasAnyParameter(
79 // No warning: enable_if as constructor parameter.
80 parmVarDecl(hasType(isEnableIf())))),
81 unless(hasParent(functionTemplateDecl(anyOf(
82 // No warning: has associated constraints (like requires
83 // expression).
84 hasAssociatedConstraints(),
85 // No warning: enable_if as type parameter.
86 has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),
87 // No warning: enable_if as non-type template parameter.
88 has(nonTypeTemplateParmDecl(
89 hasType(isEnableIf()),
90 anyOf(hasDescendant(cxxBoolLiteral()),
91 hasDescendant(cxxNullPtrLiteralExpr()),
92 hasDescendant(integerLiteral())))))))))
93 .bind("ctor");
94 Finder->addMatcher(FindOverload, this);
95}
96
98 const MatchFinder::MatchResult &Result) {
99 const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
100 const auto *TypeParmDecl =
101 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
102
103 // Get the FunctionDecl and FunctionTemplateDecl containing the function
104 // parameter.
105 const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
106 if (!FuncForParam)
107 return;
108 const FunctionTemplateDecl *FuncTemplate =
109 FuncForParam->getDescribedFunctionTemplate();
110 if (!FuncTemplate)
111 return;
112
113 // Check that the template type parameter belongs to the same function
114 // template as the function parameter of that type. (This implies that type
115 // deduction will happen on the type.)
116 const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
117 if (!llvm::is_contained(*Params, TypeParmDecl))
118 return;
119
120 // Every parameter after the first must have a default value.
121 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
122 for (const auto *Param : llvm::drop_begin(Ctor->parameters())) {
123 if (!Param->hasDefaultArg())
124 return;
125 }
126 bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
127 DisabledMove = false;
128 for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
129 if (OtherCtor->isCopyOrMoveConstructor()) {
130 if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
131 (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
132 else
133 (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
134 }
135 }
136 bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
137 bool Move = !DisabledMove || EnabledMove;
138 if (!Copy && !Move)
139 return;
140 diag(Ctor->getLocation(),
141 "constructor accepting a forwarding reference can "
142 "hide the %select{copy|move|copy and move}0 constructor%s1")
143 << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
144 for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
145 if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
146 OtherCtor->getAccess() != AS_private) {
147 diag(OtherCtor->getLocation(),
148 "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
149 << OtherCtor->isMoveConstructor();
150 }
151 }
152}
153
154} // namespace clang::tidy::bugprone
MemberFunctionInfo Copy
CodeCompletionBuilder Builder
NodeType Type
::clang::DynTypedNode Node
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)