leanil updated this revision to Diff 90569.
leanil added a comment.

Added changes according to the comments.


Repository:
  rL LLVM

https://reviews.llvm.org/D30547

Files:
  clang-tidy/misc/CMakeLists.txt
  clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp
  clang-tidy/misc/ForwardingReferenceOverloadCheck.h
  clang-tidy/misc/MiscTidyModule.cpp
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/misc-forwarding-reference-overload.rst
  test/clang-tidy/misc-forwarding-reference-overload.cpp

Index: test/clang-tidy/misc-forwarding-reference-overload.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-forwarding-reference-overload.cpp
@@ -0,0 +1,80 @@
+// RUN: %check_clang_tidy %s misc-forwarding-reference-overload %t
+
+namespace std {
+template <bool B, class T = void>
+struct enable_if {};
+
+template <class T>
+struct enable_if<true, T> { typedef T type; };
+
+template <bool B, class T = void>
+using enable_if_t = typename enable_if<B, T>::type;
+}
+
+template <typename T>
+constexpr bool just_true = true;
+
+class Person {
+public:
+  template <typename T>
+  Person(T &&n);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'Person' can hide copy and move constructors [misc-forwarding-reference-overload]
+
+  template <typename T>
+  Person(T &&n, int I = 5, ...);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: function 'Person' can hide copy and move constructors [misc-forwarding-reference-overload]
+};
+
+template <typename U>
+class Person2 {
+public:
+  // Two parameters without default value, can't act as copy / move constructor.
+  template <typename T, class V>
+  Person2(T &&n, V &&m, int i = 5, ...);
+
+  // Guarded with enable_if.
+  template <typename T>
+  Person2(T &&n, int i = 5, std::enable_if_t<sizeof(int) < sizeof(long), int> a = 5, ...);
+
+  // Guarded with enable_if.
+  template <typename T, typename X = typename std::enable_if<sizeof(int) < sizeof(long), double>::type>
+  Person2(T &&n);
+
+  // Guarded with enable_if.
+  template <typename T>
+  Person2(T &&n, std::enable_if_t<just_true<T>, int> a = 1);
+
+  // Guarded with enable_if.
+  template <typename T, typename X = typename std::enable_if<just_true<T>, int>::type *>
+  Person2(T &&n, double d = 0.0);
+
+  // Not a forwarding reference parameter.
+  template <typename T>
+  Person2(const T &&n);
+
+  // Not a forwarding reference parameter.
+  Person2(int &&x);
+
+  // Two parameters without default value, can't act as copy / move constructor.
+  template <typename T>
+  Person2(T &&n, int x);
+
+  // Not a forwarding reference parameter.
+  template <typename T>
+  Person2(U &&n);
+};
+
+// Copy and move constructors are both disabled.
+class Person3 {
+public:
+  template <typename T>
+  Person3(T &&n);
+
+  template <typename T>
+  Person3(T &&n, int I = 5, ...);
+
+  Person3(const Person3 &rhs) = delete;
+
+private:
+  Person3(Person3 &&rhs);
+};
Index: docs/clang-tidy/checks/misc-forwarding-reference-overload.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/misc-forwarding-reference-overload.rst
@@ -0,0 +1,52 @@
+.. title:: clang-tidy - misc-forwarding-reference-overload
+
+misc-forwarding-reference-overload
+==================================
+
+The check looks for perfect forwarding constructors that can hide copy or move
+constructors. If a non const lvalue reference is passed to the constructor, the 
+forwarding reference parameter will be a better match than the const reference
+parameter of the copy constructor, so the perfect forwarding constructor will be
+called, which can be confusing.
+For detailed description of this issue see: Scott Meyers, Effective Modern C++,
+Item 26.
+
+Consider the following example:
+
+  .. code-block:: c++
+  
+    class Person {
+    public:
+      // C1: perfect forwarding ctor
+      template<typename T>
+      explicit Person(T&& n) {}
+
+      // C2: perfect forwarding ctor with parameter default value
+      template<typename T>
+      explicit Person(T&& n, int x = 1) {}
+      
+      // C3: perfect forwarding ctor guarded with enable_if
+      template<typename T, typename X = enable_if_t<is_special<T>,void>>
+      explicit Person(T&& n) {}
+      
+      // (possibly compiler generated) copy ctor
+      Person(const Person& rhs); 
+    };
+    
+The check warns for constructors C1 and C2, because those can hide copy and move
+constructors. We suppress warnings if the copy and the move constructors are both
+disabled (deleted or private), because there is nothing the prefect forwarding
+constructor could hide in this case. We also suppress warnings for constructors
+like C3 that are guarded with an enable_if, assuming the programmer was aware of
+the possible hiding.
+
+Background
+----------
+
+For deciding whether a constructor is guarded with enable_if, we consider the
+default values of the type parameters and the types of the contructor
+parameters. If any part of these types contains "enable_if" in its name, we
+assume the constructor is guarded.
+
+
+
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -64,6 +64,7 @@
    misc-definitions-in-headers
    misc-fold-init-type
    misc-forward-declaration-namespace
+   misc-forwarding-reference-overload
    misc-inaccurate-erase
    misc-incorrect-roundings
    misc-inefficient-algorithm
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -77,6 +77,11 @@
 
   Finds and replaces explicit calls to the constructor in a return statement by
   a braced initializer list so that the return type is not needlessly repeated.
+  
+- New `misc-forwarding-reference-overload
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html>`_ check
+
+  Finds perfect forwarding constructors that can unintentionally hide copy or move constructors.
 
 Improvements to include-fixer
 -----------------------------
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -17,6 +17,7 @@
 #include "DefinitionsInHeadersCheck.h"
 #include "FoldInitTypeCheck.h"
 #include "ForwardDeclarationNamespaceCheck.h"
+#include "ForwardingReferenceOverloadCheck.h"
 #include "InaccurateEraseCheck.h"
 #include "IncorrectRoundings.h"
 #include "InefficientAlgorithmCheck.h"
@@ -65,6 +66,8 @@
     CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
     CheckFactories.registerCheck<AssertSideEffectCheck>(
         "misc-assert-side-effect");
+    CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
+        "misc-forwarding-reference-overload");
     CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
     CheckFactories.registerCheck<UnconventionalAssignOperatorCheck>(
         "misc-unconventional-assign-operator");
Index: clang-tidy/misc/ForwardingReferenceOverloadCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/ForwardingReferenceOverloadCheck.h
@@ -0,0 +1,42 @@
+//===--- ForwardingReferenceOverloadCheck.h - clang-tidy---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The checker looks for constructors that can act as copy or move constructors
+/// through their forwarding reference parameters. If a non const lvalue
+/// reference is passed to the constructor, the forwarding reference parameter
+/// can be a perfect match while the const reference parameter of the copy
+/// constructor can't. The forwarding reference constructor will be called,
+/// which can lead to confusion.
+/// For detailed description of this problem see: Scott Meyers, Effective Modern
+/// C++ Design, item 26.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html
+class ForwardingReferenceOverloadCheck : public ClangTidyCheck {
+public:
+  ForwardingReferenceOverloadCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
Index: clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp
@@ -0,0 +1,132 @@
+//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardingReferenceOverloadCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+// Check if the given type is related to std::enable_if.
+AST_MATCHER(QualType, isEnableIf) {
+  auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
+    if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
+      return false;
+    }
+    return Spec->getTemplateName()
+               .getAsTemplateDecl()
+               ->getTemplatedDecl()
+               ->getName()
+               .find("enable_if") != StringRef::npos;
+  };
+  const Type *BaseType = Node.getTypePtr();
+  // Case: pointer to enable_if.
+  if (BaseType->isPointerType()) {
+    BaseType = BaseType->getPointeeType().getTypePtr();
+  }
+  // Case: type parameter dependent (enable_if<is_integral<T>>).
+  if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
+    BaseType = Dependent->getQualifier()->getAsType();
+  }
+  if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
+    return true; // Case: enable_if_t< >.
+  } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
+    if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
+      if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
+        return true; // Case: enable_if< >::type.
+      }
+    }
+  }
+  return false;
+}
+AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
+              clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
+  return Node.hasDefaultArgument() &&
+         TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
+}
+}
+
+void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
+  // Forwarding references require C++11 or later.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  auto ForwardingRefParm =
+      parmVarDecl(
+          hasType(qualType(rValueReferenceType(),
+                           references(templateTypeParmType(hasDeclaration(
+                               templateTypeParmDecl().bind("type-parm-decl")))),
+                           unless(references(isConstQualified())))))
+          .bind("parm-var");
+
+  auto HasDisabledCopy = hasMethod(
+      cxxConstructorDecl(isCopyConstructor(), anyOf(isDeleted(), isPrivate())));
+  auto HasDisabledMove = hasMethod(
+      cxxConstructorDecl(isMoveConstructor(), anyOf(isDeleted(), isPrivate())));
+
+  DeclarationMatcher findOverload =
+      cxxConstructorDecl(
+          hasParameter(0, ForwardingRefParm),
+          unless(hasAnyParameter(
+              // No warning: enable_if as constructor parameter.
+              parmVarDecl(hasType(isEnableIf())))),
+          unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
+              // No warning: enable_if as type parameter.
+              hasDefaultArgument(isEnableIf())))))),
+          unless(ofClass(
+              // No warning: disabled copy and move constructor.
+              cxxRecordDecl(HasDisabledCopy, HasDisabledMove))))
+          .bind("ctor");
+  Finder->addMatcher(findOverload, this);
+}
+
+void ForwardingReferenceOverloadCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
+  const auto *TypeParmDecl =
+      Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
+
+  // Get the FunctionDecl and FunctionTemplateDecl containing the function
+  // parameter.
+  const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
+  if (!FuncForParam)
+    return;
+  const FunctionTemplateDecl *FuncTemplate =
+      FuncForParam->getDescribedFunctionTemplate();
+  if (!FuncTemplate)
+    return;
+
+  // Check that the template type parameter belongs to the same function
+  // template as the function parameter of that type. (This implies that type
+  // deduction will happen on the type.)
+  const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
+  if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
+    return;
+
+  // Every parameter after the first must have a default value.
+  if (const auto *Ctor = Result.Nodes.getNodeAs<FunctionDecl>("ctor")) {
+    for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end();
+         ++Iter) {
+      if (!(*Iter)->hasDefaultArg()) {
+        return;
+      }
+    }
+    diag(Ctor->getLocation(), "function %0 can hide copy and move constructors")
+        << Ctor;
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangTidyMiscModule
   ArgumentCommentCheck.cpp
   AssertSideEffectCheck.cpp
+  ForwardingReferenceOverloadCheck.cpp
   MisplacedConstCheck.cpp
   UnconventionalAssignOperatorCheck.cpp
   BoolPointerImplicitConversionCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to