add_clang_library(clangTidyMiscModule
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
+ ForwardingReferenceOverloadCheck.cpp
MisplacedConstCheck.cpp
UnconventionalAssignOperatorCheck.cpp
BoolPointerImplicitConversionCheck.cpp
--- /dev/null
+//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//\r
+//\r
+// The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+\r
+#include "ForwardingReferenceOverloadCheck.h"\r
+#include "clang/AST/ASTContext.h"\r
+#include "clang/ASTMatchers/ASTMatchFinder.h"\r
+#include <algorithm>\r
+\r
+using namespace clang::ast_matchers;\r
+\r
+namespace clang {\r
+namespace tidy {\r
+namespace misc {\r
+\r
+namespace {\r
+// Check if the given type is related to std::enable_if.\r
+AST_MATCHER(QualType, isEnableIf) {\r
+ auto CheckTemplate = [](const TemplateSpecializationType *Spec) {\r
+ if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {\r
+ return false;\r
+ }\r
+ const NamedDecl *TypeDecl =\r
+ Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();\r
+ return TypeDecl->isInStdNamespace() &&\r
+ (TypeDecl->getName().equals("enable_if") ||\r
+ TypeDecl->getName().equals("enable_if_t"));\r
+ };\r
+ const Type *BaseType = Node.getTypePtr();\r
+ // Case: pointer or reference to enable_if.\r
+ while (BaseType->isPointerType() || BaseType->isReferenceType()) {\r
+ BaseType = BaseType->getPointeeType().getTypePtr();\r
+ }\r
+ // Case: type parameter dependent (enable_if<is_integral<T>>).\r
+ if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {\r
+ BaseType = Dependent->getQualifier()->getAsType();\r
+ }\r
+ if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {\r
+ return true; // Case: enable_if_t< >.\r
+ } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {\r
+ if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {\r
+ if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {\r
+ return true; // Case: enable_if< >::type.\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+}\r
+AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,\r
+ clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {\r
+ return Node.hasDefaultArgument() &&\r
+ TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);\r
+}\r
+}\r
+\r
+void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {\r
+ // Forwarding references require C++11 or later.\r
+ if (!getLangOpts().CPlusPlus11)\r
+ return;\r
+\r
+ auto ForwardingRefParm =\r
+ parmVarDecl(\r
+ hasType(qualType(rValueReferenceType(),\r
+ references(templateTypeParmType(hasDeclaration(\r
+ templateTypeParmDecl().bind("type-parm-decl")))),\r
+ unless(references(isConstQualified())))))\r
+ .bind("parm-var");\r
+\r
+ DeclarationMatcher findOverload =\r
+ cxxConstructorDecl(\r
+ hasParameter(0, ForwardingRefParm),\r
+ unless(hasAnyParameter(\r
+ // No warning: enable_if as constructor parameter.\r
+ parmVarDecl(hasType(isEnableIf())))),\r
+ unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(\r
+ // No warning: enable_if as type parameter.\r
+ hasDefaultArgument(isEnableIf())))))))\r
+ .bind("ctor");\r
+ Finder->addMatcher(findOverload, this);\r
+}\r
+\r
+void ForwardingReferenceOverloadCheck::check(\r
+ const MatchFinder::MatchResult &Result) {\r
+ const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");\r
+ const auto *TypeParmDecl =\r
+ Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");\r
+\r
+ // Get the FunctionDecl and FunctionTemplateDecl containing the function\r
+ // parameter.\r
+ const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());\r
+ if (!FuncForParam)\r
+ return;\r
+ const FunctionTemplateDecl *FuncTemplate =\r
+ FuncForParam->getDescribedFunctionTemplate();\r
+ if (!FuncTemplate)\r
+ return;\r
+\r
+ // Check that the template type parameter belongs to the same function\r
+ // template as the function parameter of that type. (This implies that type\r
+ // deduction will happen on the type.)\r
+ const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();\r
+ if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())\r
+ return;\r
+\r
+ // Every parameter after the first must have a default value.\r
+ const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");\r
+ for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {\r
+ if (!(*Iter)->hasDefaultArg())\r
+ return;\r
+ }\r
+ bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,\r
+ DisabledMove = false;\r
+ for (const auto *OtherCtor : Ctor->getParent()->ctors()) {\r
+ if (OtherCtor->isCopyOrMoveConstructor()) {\r
+ if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)\r
+ (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;\r
+ else\r
+ (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;\r
+ }\r
+ }\r
+ bool Copy = !DisabledCopy || EnabledCopy, Move = !DisabledMove || EnabledMove;\r
+ if (!Copy && !Move)\r
+ return;\r
+ diag(Ctor->getLocation(),\r
+ "constructor accepting a forwarding reference can "\r
+ "hide the %select{copy|move|copy and move}0 constructor%s1")\r
+ << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;\r
+ for (const auto *OtherCtor : Ctor->getParent()->ctors()) {\r
+ if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&\r
+ OtherCtor->getAccess() != AS_private) {\r
+ diag(OtherCtor->getLocation(),\r
+ "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)\r
+ << OtherCtor->isMoveConstructor();\r
+ }\r
+ }\r
+}\r
+\r
+} // namespace misc\r
+} // namespace tidy\r
+} // namespace clang\r
--- /dev/null
+//===--- 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
#include "DefinitionsInHeadersCheck.h"
#include "FoldInitTypeCheck.h"
#include "ForwardDeclarationNamespaceCheck.h"
+#include "ForwardingReferenceOverloadCheck.h"
#include "InaccurateEraseCheck.h"
#include "IncorrectRoundings.h"
#include "InefficientAlgorithmCheck.h"
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");
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.
- Support clang-formatting of the code around applied fixes (``-format-style``
command-line option).
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
--- /dev/null
+.. 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 perfect 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 is std::enable_if or std::enable_if_t, we
+assume the constructor is guarded.
--- /dev/null
+// 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 <class T>
+struct enable_if_nice { typedef T type; };
+}
+
+namespace foo {
+template <class T>
+struct enable_if { typedef T type; };
+}
+
+template <typename T>
+constexpr bool just_true = true;
+
+class Test1 {
+public:
+ template <typename T>
+ Test1(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+ template <typename T>
+ Test1(T &&n, int i = 5, ...);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+ template <typename T, typename U = typename std::enable_if_nice<T>::type>
+ Test1(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+ template <typename T>
+ Test1(T &&n, typename foo::enable_if<long>::type i = 5, ...);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+ Test1(const Test1 &other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+
+ Test1(Test1 &other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+
+ Test1(Test1 &&other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here
+};
+
+template <typename U>
+class Test2 {
+public:
+ // Two parameters without default value, can't act as copy / move constructor.
+ template <typename T, class V>
+ Test2(T &&n, V &&m, int i = 5, ...);
+
+ // Guarded with enable_if.
+ template <typename T>
+ Test2(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 &>
+ Test2(T &&n);
+
+ // Guarded with enable_if.
+ template <typename T>
+ Test2(T &&n, typename std::enable_if<just_true<T>>::type **a = nullptr);
+
+ // Guarded with enable_if.
+ template <typename T, typename X = std::enable_if_t<just_true<T>> *&&>
+ Test2(T &&n, double d = 0.0);
+
+ // Not a forwarding reference parameter.
+ template <typename T>
+ Test2(const T &&n);
+
+ // Not a forwarding reference parameter.
+ Test2(int &&x);
+
+ // Two parameters without default value, can't act as copy / move constructor.
+ template <typename T>
+ Test2(T &&n, int x);
+
+ // Not a forwarding reference parameter.
+ template <typename T>
+ Test2(U &&n);
+};
+
+// The copy and move constructors are both disabled.
+class Test3 {
+public:
+ template <typename T>
+ Test3(T &&n);
+
+ template <typename T>
+ Test3(T &&n, int I = 5, ...);
+
+ Test3(const Test3 &rhs) = delete;
+
+private:
+ Test3(Test3 &&rhs);
+};
+
+// Both the copy and the (compiler generated) move constructors can be hidden.
+class Test4 {
+public:
+ template <typename T>
+ Test4(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+ Test4(const Test4 &rhs);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+};
+
+// Only the (compiler generated) copy constructor can be hidden.
+class Test5 {
+public:
+ template <typename T>
+ Test5(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy constructor [misc-forwarding-reference-overload]
+
+ Test5(Test5 &&rhs) = delete;
+};
+
+// Only the move constructor can be hidden.
+class Test6 {
+public:
+ template <typename T>
+ Test6(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the move constructor [misc-forwarding-reference-overload]
+
+ Test6(Test6 &&rhs);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here
+private:
+ Test6(const Test6 &rhs);
+};