clang-tools  14.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 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace bugprone {
19 
20 namespace {
21 // Check if the given type is related to std::enable_if.
22 AST_MATCHER(QualType, isEnableIf) {
23  auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
24  if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
25  return false;
26  }
27  const NamedDecl *TypeDecl =
28  Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
29  return TypeDecl->isInStdNamespace() &&
30  (TypeDecl->getName().equals("enable_if") ||
31  TypeDecl->getName().equals("enable_if_t"));
32  };
33  const Type *BaseType = Node.getTypePtr();
34  // Case: pointer or reference to enable_if.
35  while (BaseType->isPointerType() || BaseType->isReferenceType()) {
36  BaseType = BaseType->getPointeeType().getTypePtr();
37  }
38  // Case: type parameter dependent (enable_if<is_integral<T>>).
39  if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
40  BaseType = Dependent->getQualifier()->getAsType();
41  }
42  if (!BaseType)
43  return false;
44  if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>()))
45  return true; // Case: enable_if_t< >.
46  if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
47  if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
48  if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
49  return true; // Case: enable_if< >::type.
50  }
51  }
52  }
53  return false;
54 }
55 AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
56  clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
57  return Node.hasDefaultArgument() &&
58  TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
59 }
60 } // namespace
61 
62 void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
63  auto ForwardingRefParm =
64  parmVarDecl(
65  hasType(qualType(rValueReferenceType(),
66  references(templateTypeParmType(hasDeclaration(
67  templateTypeParmDecl().bind("type-parm-decl")))),
68  unless(references(isConstQualified())))))
69  .bind("parm-var");
70 
71  DeclarationMatcher FindOverload =
72  cxxConstructorDecl(
73  hasParameter(0, ForwardingRefParm),
74  unless(hasAnyParameter(
75  // No warning: enable_if as constructor parameter.
76  parmVarDecl(hasType(isEnableIf())))),
77  unless(hasParent(functionTemplateDecl(anyOf(
78  // No warning: enable_if as type parameter.
79  has(templateTypeParmDecl(hasDefaultArgument(isEnableIf()))),
80  // No warning: enable_if as non-type template parameter.
81  has(nonTypeTemplateParmDecl(
82  hasType(isEnableIf()),
83  anyOf(hasDescendant(cxxBoolLiteral()),
84  hasDescendant(cxxNullPtrLiteralExpr()),
85  hasDescendant(integerLiteral())))))))))
86  .bind("ctor");
87  Finder->addMatcher(FindOverload, this);
88 }
89 
91  const MatchFinder::MatchResult &Result) {
92  const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
93  const auto *TypeParmDecl =
94  Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
95 
96  // Get the FunctionDecl and FunctionTemplateDecl containing the function
97  // parameter.
98  const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
99  if (!FuncForParam)
100  return;
101  const FunctionTemplateDecl *FuncTemplate =
102  FuncForParam->getDescribedFunctionTemplate();
103  if (!FuncTemplate)
104  return;
105 
106  // Check that the template type parameter belongs to the same function
107  // template as the function parameter of that type. (This implies that type
108  // deduction will happen on the type.)
109  const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
110  if (!llvm::is_contained(*Params, TypeParmDecl))
111  return;
112 
113  // Every parameter after the first must have a default value.
114  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
115  for (auto *Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end();
116  ++Iter) {
117  if (!(*Iter)->hasDefaultArg())
118  return;
119  }
120  bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
121  DisabledMove = false;
122  for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
123  if (OtherCtor->isCopyOrMoveConstructor()) {
124  if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
125  (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
126  else
127  (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
128  }
129  }
130  bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
131  bool Move = !DisabledMove || EnabledMove;
132  if (!Copy && !Move)
133  return;
134  diag(Ctor->getLocation(),
135  "constructor accepting a forwarding reference can "
136  "hide the %select{copy|move|copy and move}0 constructor%s1")
137  << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
138  for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
139  if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
140  OtherCtor->getAccess() != AS_private) {
141  diag(OtherCtor->getLocation(),
142  "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
143  << OtherCtor->isMoveConstructor();
144  }
145  }
146 }
147 
148 } // namespace bugprone
149 } // namespace tidy
150 } // namespace clang
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::ast_matchers
Definition: AbseilMatcher.h:14
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:19
clang::clangd::check
bool check(llvm::StringRef File, llvm::function_ref< bool(const Position &)> ShouldCheckLine, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:259
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
ForwardingReferenceOverloadCheck.h
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
Definition: EasilySwappableParametersCheck.cpp:1877