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 bugprone {
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 bugprone
147 } // namespace tidy
148 } // namespace clang
149