clang-tools 6.0.0

ForwardingReferenceOverloadCheck.cpp
Go to the documentation of this file.
1 //===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "ForwardingReferenceOverloadCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include <algorithm>
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace misc {
20
21 namespace {
22 // Check if the given type is related to std::enable_if.
23 AST_MATCHER(QualType, isEnableIf) {
24 auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
25 if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
26 return false;
27 }
28 const NamedDecl *TypeDecl =
29 Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
30 return TypeDecl->isInStdNamespace() &&
31 (TypeDecl->getName().equals("enable_if") ||
32 TypeDecl->getName().equals("enable_if_t"));
33 };
34 const Type *BaseType = Node.getTypePtr();
35 // Case: pointer or reference to enable_if.
36 while (BaseType->isPointerType() || BaseType->isReferenceType()) {
37 BaseType = BaseType->getPointeeType().getTypePtr();
38 }
39 // Case: type parameter dependent (enable_if<is_integral<T>>).
40 if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
41 BaseType = Dependent->getQualifier()->getAsType();
42 }
43 if (!BaseType)
44 return false;
45 if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
46 return true; // Case: enable_if_t< >.
47 } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
48 if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
49 if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
50 return true; // Case: enable_if< >::type.
51 }
52 }
53 }
54 return false;
55 }
56 AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
57 clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
58 return Node.hasDefaultArgument() &&
59 TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
60 }
61 } // namespace
62
63 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
64 // Forwarding references require C++11 or later.
65 if (!getLangOpts().CPlusPlus11)
66 return;
67
68 auto ForwardingRefParm =
69 parmVarDecl(
70 hasType(qualType(rValueReferenceType(),
71 references(templateTypeParmType(hasDeclaration(
72 templateTypeParmDecl().bind("type-parm-decl")))),
73 unless(references(isConstQualified())))))
74 .bind("parm-var");
75
76 DeclarationMatcher findOverload =
77 cxxConstructorDecl(
78 hasParameter(0, ForwardingRefParm),
79 unless(hasAnyParameter(
80 // No warning: enable_if as constructor parameter.
81 parmVarDecl(hasType(isEnableIf())))),
82 unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
83 // No warning: enable_if as type parameter.
84 hasDefaultArgument(isEnableIf())))))))
85 .bind("ctor");
86 Finder->addMatcher(findOverload, this);
87 }
88
89 void ForwardingReferenceOverloadCheck::check(
90 const MatchFinder::MatchResult &Result) {
91 const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
92 const auto *TypeParmDecl =
93 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
94
95 // Get the FunctionDecl and FunctionTemplateDecl containing the function
96 // parameter.
97 const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
98 if (!FuncForParam)
99 return;
100 const FunctionTemplateDecl *FuncTemplate =
101 FuncForParam->getDescribedFunctionTemplate();
102 if (!FuncTemplate)
103 return;
104
105 // Check that the template type parameter belongs to the same function
106 // template as the function parameter of that type. (This implies that type
107 // deduction will happen on the type.)
108 const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
109 if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
110 return;
111
112 // Every parameter after the first must have a default value.
113 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
114 for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {
115 if (!(*Iter)->hasDefaultArg())
116 return;
117 }
118 bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
119 DisabledMove = false;
120 for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
121 if (OtherCtor->isCopyOrMoveConstructor()) {
122 if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
123 (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
124 else
125 (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
126 }
127 }
128 bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
129 bool Move = !DisabledMove || EnabledMove;
130 if (!Copy && !Move)
131 return;
132 diag(Ctor->getLocation(),
133 "constructor accepting a forwarding reference can "
134 "hide the %select{copy|move|copy and move}0 constructor%s1")
135 << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
136 for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
137 if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
138 OtherCtor->getAccess() != AS_private) {
139 diag(OtherCtor->getLocation(),
140 "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
141 << OtherCtor->isMoveConstructor();
142 }
143 }
144 }
145
146 } // namespace misc
147 } // namespace tidy
148 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
ForwardingReferenceOverloadCheck.h
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N)
Definition: StringConstructorCheck.cpp:21
clang::ast_matchers::AST_MATCHER
AST_MATCHER(VarDecl, isAsm)
Definition: NoAssemblerCheck.cpp:18