clang-tools  8.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 
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
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
AST_MATCHER_P(FunctionDecl, throws, internal::Matcher< Type >, InnerMatcher)