ForwardingReferenceOverloadCheck.cpp 5.8 KB

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