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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits