diff --git a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.h b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.h --- a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.h @@ -40,6 +40,7 @@ const std::vector SmartPointers; const std::vector TupleTypes; const std::vector TupleMakeFunctions; + const std::vector EmplacyFunctions; }; } // namespace modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseEmplaceCheck.cpp @@ -15,6 +15,69 @@ namespace modernize { namespace { +// Identical to hasAnyName, except it does not take template specifiers into +// account. This is used to match the functions names as in +// DefaultEmplacyFunctions below without caring about the template types of the +// containers. +AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector, + Names) { + const std::string FullName = "::" + Node.getQualifiedNameAsString(); + + // This loop removes template specifiers by only keeping characters not within + // template brackets. We keep a depth count to handle nested templates. For + // example, it'll transform a::b>::e to simply a::b::e. + std::string FullNameTrimmed; + int Depth = 0; + for (const auto &Character : FullName) { + if (Character == '<') { + ++Depth; + } else if (Character == '>') { + --Depth; + } else if (Depth == 0) { + FullNameTrimmed.append(1, Character); + } + } + + // This loop is taken from HasNameMatcher::matchesNodeFullSlow in + // clang/lib/ASTMatchers/ASTMatchersInternal.cpp and checks whether + // FullNameTrimmed matches any of the given Names. + const StringRef FullNameTrimmedRef = FullNameTrimmed; + for (const StringRef Pattern : Names) { + if (Pattern.startswith("::")) { + if (FullNameTrimmed == Pattern) + return true; + } else if (FullNameTrimmedRef.endswith(Pattern) && + FullNameTrimmedRef.drop_back(Pattern.size()).endswith("::")) { + return true; + } + } + + return false; +} + +// Checks if the given matcher is the last argument of the given CallExpr. +AST_MATCHER_P(CallExpr, hasLastArgument, + clang::ast_matchers::internal::Matcher, InnerMatcher) { + if (Node.getNumArgs() == 0) + return false; + + return InnerMatcher.matches(*Node.getArg(Node.getNumArgs() - 1), Finder, + Builder); +} + +// Checks if the given member call has the same number of arguments as the +// function had parameters defined (this is useful to check if there is only one +// variadic argument). +AST_MATCHER(CXXMemberCallExpr, hasSameNumArgsAsDeclNumParams) { + if (Node.getMethodDecl()->isFunctionTemplateSpecialization()) + return Node.getNumArgs() == Node.getMethodDecl() + ->getPrimaryTemplate() + ->getTemplatedDecl() + ->getNumParams(); + + return Node.getNumArgs() == Node.getMethodDecl()->getNumParams(); +} + AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) { return Node.hasExplicitTemplateArgs(); } @@ -25,6 +88,20 @@ "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr"; const auto DefaultTupleTypes = "::std::pair; ::std::tuple"; const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple"; +const auto DefaultEmplacyFunctions = + "vector::emplace_back; vector::emplace;" + "deque::emplace; deque::emplace_front; deque::emplace_back;" + "forward_list::emplace_after; forward_list::emplace_front;" + "list::emplace; list::emplace_back; list::emplace_front;" + "set::emplace; set::emplace_hint;" + "map::emplace; map::emplace_hint;" + "multiset::emplace; multiset::emplace_hint;" + "multimap::emplace; multimap::emplace_hint;" + "unordered_set::emplace; unordered_set::emplace_hint;" + "unordered_map::emplace; unordered_map::emplace_hint;" + "unordered_multiset::emplace; unordered_multiset::emplace_hint;" + "unordered_multimap::emplace; unordered_multimap::emplace_hint;" + "stack::emplace; queue::emplace; priority_queue::emplace"; } // namespace UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context) @@ -37,7 +114,9 @@ TupleTypes(utils::options::parseStringList( Options.get("TupleTypes", DefaultTupleTypes))), TupleMakeFunctions(utils::options::parseStringList( - Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))) {} + Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))), + EmplacyFunctions(utils::options::parseStringList( + Options.get("EmplacyFunctions", DefaultEmplacyFunctions))) {} void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { // FIXME: Bunch of functionality that could be easily added: @@ -52,6 +131,13 @@ hasDeclaration(functionDecl(hasName("push_back"))), on(hasType(cxxRecordDecl(hasAnyName(ContainersWithPushBack))))); + auto CallEmplacy = cxxMemberCallExpr( + hasDeclaration( + functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))), + on(hasType(cxxRecordDecl(has(typedefNameDecl( + hasName("value_type"), hasType(type(hasUnqualifiedDesugaredType( + recordType().bind("value_type")))))))))); + // We can't replace push_backs of smart pointer because // if emplacement fails (f.e. bad_alloc in vector) we will have leak of // passed pointer because smart pointer won't be constructed @@ -73,8 +159,9 @@ auto ConstructingDerived = hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase))); - // emplace_back can't access private constructor. - auto IsPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate())); + // emplace_back can't access private or protected constructors. + auto IsPrivateOrProtectedCtor = + hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected()))); auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())), has(cxxStdInitializerListExpr())); @@ -85,7 +172,7 @@ cxxConstructExpr( unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument, InitializerListAsArgument, NewExprAsArgument, - ConstructingDerived, IsPrivateCtor))) + ConstructingDerived, IsPrivateOrProtectedCtor))) .bind("ctor"); auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr)); @@ -102,22 +189,64 @@ hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes)))))); auto SoughtParam = materializeTemporaryExpr( - anyOf(has(MakeTuple), has(MakeTupleCtor), - HasConstructExpr, has(cxxFunctionalCastExpr(HasConstructExpr)))); + anyOf(has(MakeTuple), has(MakeTupleCtor), HasConstructExpr, + has(cxxFunctionalCastExpr(HasConstructExpr)))); + + auto HasConstructExprWithValueTypeType = + has(ignoringImplicit(cxxConstructExpr( + SoughtConstructExpr, hasType(type(hasUnqualifiedDesugaredType( + type(equalsBoundNode("value_type")))))))); + + auto HasConstructExprWithValueTypeTypeAsLastArgument = + hasLastArgument(materializeTemporaryExpr(anyOf( + HasConstructExprWithValueTypeType, + has(cxxFunctionalCastExpr(HasConstructExprWithValueTypeType))))); Finder->addMatcher( traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam), unless(isInTemplateInstantiation())) - .bind("call")), + .bind("push_back_call")), + this); + + Finder->addMatcher( + traverse(TK_AsIs, + cxxMemberCallExpr( + CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument, + hasSameNumArgsAsDeclNumParams(), + unless(isInTemplateInstantiation())) + .bind("emplacy_call")), + this); + + Finder->addMatcher( + traverse( + TK_AsIs, + cxxMemberCallExpr( + CallEmplacy, + on(hasType(cxxRecordDecl(has(typedefNameDecl( + hasName("value_type"), + hasType(type( + hasUnqualifiedDesugaredType(recordType(hasDeclaration( + cxxRecordDecl(hasAnyName(SmallVector( + TupleTypes.begin(), TupleTypes.end()))))))))))))), + has(MakeTuple), hasSameNumArgsAsDeclNumParams(), + unless(isInTemplateInstantiation())) + .bind("emplacy_call")), this); } void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) { - const auto *Call = Result.Nodes.getNodeAs("call"); + const auto *PushBackCall = + Result.Nodes.getNodeAs("push_back_call"); + const auto *EmplacyCall = + Result.Nodes.getNodeAs("emplacy_call"); const auto *CtorCall = Result.Nodes.getNodeAs("ctor"); const auto *MakeCall = Result.Nodes.getNodeAs("make"); + + assert((PushBackCall || EmplacyCall) && "No call matched"); assert((CtorCall || MakeCall) && "No push_back parameter matched"); + const CXXMemberCallExpr *Call = PushBackCall ? PushBackCall : EmplacyCall; + if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 && CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange()) return; @@ -125,13 +254,21 @@ const auto FunctionNameSourceRange = CharSourceRange::getCharRange( Call->getExprLoc(), Call->getArg(0)->getExprLoc()); - auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back"); + auto Diag = + PushBackCall + ? diag(Call->getExprLoc(), "use emplace_back instead of push_back") + : diag(CtorCall ? CtorCall->getBeginLoc() : MakeCall->getBeginLoc(), + "unnecessary temporary object created while calling " + + Call->getMethodDecl()->getName().str()); if (FunctionNameSourceRange.getBegin().isMacroID()) return; - const auto *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back("; - Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, EmplacePrefix); + if (PushBackCall) { + const char *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back("; + Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, + EmplacePrefix); + } const SourceRange CallParensRange = MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(), @@ -143,7 +280,7 @@ return; const SourceLocation ExprBegin = - MakeCall ? MakeCall->getExprLoc() : CtorCall->getExprLoc(); + CtorCall ? CtorCall->getExprLoc() : MakeCall->getExprLoc(); // Range for constructor name and opening brace. const auto ParamCallSourceRange = @@ -151,7 +288,14 @@ Diag << FixItHint::CreateRemoval(ParamCallSourceRange) << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( - CallParensRange.getEnd(), CallParensRange.getEnd())); + CallParensRange.getEnd(), CallParensRange.getEnd())); + + if (MakeCall && EmplacyCall) { + // Remove extra left parenthesis + Diag << FixItHint::CreateRemoval( + CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(), + MakeCall->getArg(0)->getBeginLoc())); + } } void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { @@ -164,6 +308,8 @@ utils::options::serializeStringList(TupleTypes)); Options.store(Opts, "TupleMakeFunctions", utils::options::serializeStringList(TupleMakeFunctions)); + Options.store(Opts, "EmplacyFunctions", + utils::options::serializeStringList(EmplacyFunctions)); } } // namespace modernize diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize-use-emplace.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize-use-emplace.rst --- a/clang-tools-extra/docs/clang-tidy/checks/modernize-use-emplace.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize-use-emplace.rst @@ -15,17 +15,29 @@ By default only ``std::vector``, ``std::deque``, ``std::list`` are considered. This list can be modified using the :option:`ContainersWithPushBack` option. +This check also reports when an ``emplace``-like method is improperly used, +for example using ``emplace_back`` while also calling a constructor. This +creates a temporary that requires at best a move and at worst a copy. Almost all +``emplace``-like functions in the STL are covered by this, with ``try_emplace`` +on ``std::map`` and ``std::unordered_map`` being the exception as it behaves +slightly differently than all the others. More containers can be added with the +:option:`EmplacyFunctions` option, so long as the container defines a +``value_type`` type, and the ``emplace``-like functions construct a +``value_type`` object. + Before: .. code-block:: c++ std::vector v; v.push_back(MyClass(21, 37)); + v.emplace_back(MyClass(21, 37)); std::vector> w; w.push_back(std::pair(21, 37)); w.push_back(std::make_pair(21L, 37L)); + w.emplace_back(std::make_pair(21L, 37L)); After: @@ -33,10 +45,12 @@ std::vector v; v.emplace_back(21, 37); + v.emplace_back(21, 37); std::vector> w; w.emplace_back(21, 37); w.emplace_back(21L, 37L); + w.emplace_back(21L, 37L); By default, the check is able to remove unnecessary ``std::make_pair`` and ``std::make_tuple`` calls from ``push_back`` calls on containers of @@ -128,6 +142,13 @@ function calls will be removed from ``push_back`` calls and turned into ``emplace_back``. +.. option:: EmplacyFunctions + + Semicolon-separated list of containers without their template parameters + and some ``emplace``-like method of the container. Example: + ``vector::emplace_back``. Those methods will be checked for improper use and + the check will report when a temporary is unnecessarily created. + Example ^^^^^^^ @@ -135,6 +156,7 @@ std::vector> x; x.push_back(MakeMyTuple(1, false, 'x')); + x.emplace_back(MakeMyTuple(1, false, 'x')); transforms to: @@ -142,6 +164,8 @@ std::vector> x; x.emplace_back(1, false, 'x'); + x.emplace_back(1, false, 'x'); -when :option:`TupleTypes` is set to ``MyTuple`` and :option:`TupleMakeFunctions` -is set to ``MakeMyTuple``. +when :option:`TupleTypes` is set to ``MyTuple``, :option:`TupleMakeFunctions` +is set to ``MakeMyTuple``, and :option:`EmplacyFunctions` is set to +``vector::emplace_back``. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-emplace.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-emplace.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-emplace.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-emplace.cpp @@ -10,15 +10,36 @@ namespace std { template -class initializer_list -{ +class initializer_list { public: initializer_list() noexcept {} }; +template +class pair { +public: + pair() = default; + pair(const pair &) = default; + pair(pair &&) = default; + + pair(const T1 &, const T2 &) {} + pair(T1 &&, T2 &&) {} + + template + pair(const pair &){}; + template + pair(pair &&){}; +}; + template class vector { public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + vector() = default; vector(initializer_list) {} @@ -27,54 +48,230 @@ template void emplace_back(Args &&... args){}; + template + iterator emplace(const_iterator pos, Args &&...args){}; ~vector(); }; + template class list { public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + void push_back(const T &) {} void push_back(T &&) {} + template + iterator emplace(const_iterator pos, Args &&...args){}; template void emplace_back(Args &&... args){}; + template + void emplace_front(Args &&...args){}; ~list(); }; template class deque { public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + void push_back(const T &) {} void push_back(T &&) {} + template + iterator emplace(const_iterator pos, Args &&...args){}; template void emplace_back(Args &&... args){}; + template + void emplace_front(Args &&...args){}; ~deque(); }; -template struct remove_reference { using type = T; }; -template struct remove_reference { using type = T; }; -template struct remove_reference { using type = T; }; +template +class forward_list { +public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace_front(Args &&...args){}; + template + iterator emplace_after(const_iterator pos, Args &&...args){}; +}; -template class pair { +template +class set { public: - pair() = default; - pair(const pair &) = default; - pair(pair &&) = default; + using value_type = T; - pair(const T1 &, const T2 &) {} - pair(T1 &&, T2 &&) {} + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class map { +public: + using value_type = std::pair; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class multiset { +public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class multimap { +public: + using value_type = std::pair; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class unordered_set { +public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class unordered_map { +public: + using value_type = std::pair; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class unordered_multiset { +public: + using value_type = T; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class unordered_multimap { +public: + using value_type = std::pair; + + class iterator {}; + class const_iterator {}; + const_iterator begin() { return const_iterator{}; } + + template + void emplace(Args &&...args){}; + template + iterator emplace_hint(const_iterator pos, Args &&...args){}; +}; + +template +class stack { +public: + using value_type = T; + + template + void emplace(Args &&...args){}; +}; - template pair(const pair &){}; - template pair(pair &&){}; +template +class queue { +public: + using value_type = T; + + template + void emplace(Args &&...args){}; }; +template +class priority_queue { +public: + using value_type = T; + + template + void emplace(Args &&...args){}; +}; + +template +struct remove_reference { using type = T; }; +template +struct remove_reference { using type = T; }; +template +struct remove_reference { using type = T; }; + template pair::type, typename remove_reference::type> make_pair(T1 &&, T2 &&) { return {}; }; -template class tuple { +template +class tuple { public: tuple() = default; tuple(const tuple &) = default; @@ -83,13 +280,17 @@ tuple(const Ts &...) {} tuple(Ts &&...) {} - template tuple(const tuple &){}; - template tuple(tuple &&) {} + template + tuple(const tuple &){}; + template + tuple(tuple &&) {} - template tuple(const pair &) { + template + tuple(const pair &) { static_assert(sizeof...(Ts) == 2, "Wrong tuple size"); }; - template tuple(pair &&) { + template + tuple(pair &&) { static_assert(sizeof...(Ts) == 2, "Wrong tuple size"); }; }; @@ -118,7 +319,7 @@ void emplace_back(Args &&... args){}; }; -} // llvm +} // namespace llvm void testInts() { std::vector v; @@ -375,7 +576,7 @@ // make_pair cannot be removed here, as X is not constructible with two ints. struct Y { - Y(std::pair&&) {} + Y(std::pair &&) {} }; std::vector y; y.push_back(std::make_pair(2, 3)); @@ -402,7 +603,8 @@ } namespace test { -template struct Single { +template +struct Single { Single() = default; Single(const Single &) = default; Single(Single &&) = default; @@ -410,11 +612,15 @@ Single(const T &) {} Single(T &&) {} - template Single(const Single &) {} - template Single(Single &&) {} + template + Single(const Single &) {} + template + Single(Single &&) {} - template Single(const std::tuple &) {} - template Single(std::tuple &&) {} + template + Single(const std::tuple &) {} + template + Single(std::tuple &&) {} }; template @@ -605,3 +811,240 @@ x.push_back(PairIntVector(3, {4})); x.push_back({5, {6}}); } + +class Foo { +public: + Foo(){}; + Foo(int){}; + Foo(int, int){}; + Foo(std::pair){}; + +protected: + Foo(char *) : Foo(){}; +}; + +void testSomeEmplaceCases() { + std::vector> v1; + std::vector v2; + std::unordered_map m1; + + v1.emplace_back(std::make_pair("foo", "bar")); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: v1.emplace_back("foo", "bar"); + + char *foo = "bar"; + v1.emplace_back(std::make_pair(foo, "bar")); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: v1.emplace_back(foo, "bar"); + + v1.emplace(v1.begin(), std::make_pair("foo", "bar")); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: v1.emplace(v1.begin(), "foo", "bar"); + + v2.emplace_back(Foo()); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: v2.emplace_back(); + + v2.emplace_back(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: v2.emplace_back(13); + + v2.emplace_back(Foo{13}); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: v2.emplace_back(13); + + int a = 31; + v2.emplace_back(Foo(13, a)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: v2.emplace_back(13, a); + + v2.emplace_back(std::make_pair(3, 3)); + + m1.emplace(std::make_pair(13, "foo")); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: m1.emplace(13, "foo"); + + std::vector> v3; + v3.emplace_back(std::pair(13, 71)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + v3.emplace_back(std::make_pair(13, 71)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + + std::vector> v4; + v4.emplace_back(std::tuple(13, 31, 71)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + v4.emplace_back(std::make_tuple(13, 31, 71)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + + std::vector> v5; + v5.emplace_back(test::Single(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back + v5.emplace_back(test::MakeSingle(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: unnecessary temporary object created while calling emplace_back +} + +void testAllSTLEmplacyFunctions() { + std::vector vector; + std::deque deque; + std::forward_list forward_list; + std::list list; + std::set set; + std::map map; + std::multiset multiset; + std::multimap multimap; + std::unordered_set unordered_set; + std::unordered_map unordered_map; + std::unordered_multiset unordered_multiset; + std::unordered_multimap unordered_multimap; + std::stack stack; + std::queue queue; + std::priority_queue priority_queue; + + vector.emplace_back(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: vector.emplace_back(13); + + vector.emplace(vector.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: vector.emplace(vector.begin(), 13); + + deque.emplace(deque.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: deque.emplace(deque.begin(), 13); + + deque.emplace_front(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: unnecessary temporary object created while calling emplace_front + // CHECK-FIXES: deque.emplace_front(13); + + deque.emplace_back(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: deque.emplace_back(13); + + forward_list.emplace_after(forward_list.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:52: warning: unnecessary temporary object created while calling emplace_after + // CHECK-FIXES: forward_list.emplace_after(forward_list.begin(), 13); + + forward_list.emplace_front(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: unnecessary temporary object created while calling emplace_front + // CHECK-FIXES: forward_list.emplace_front(13); + + list.emplace(list.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: list.emplace(list.begin(), 13); + + list.emplace_back(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: unnecessary temporary object created while calling emplace_back + // CHECK-FIXES: list.emplace_back(13); + + list.emplace_front(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unnecessary temporary object created while calling emplace_front + // CHECK-FIXES: list.emplace_front(13); + + set.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: set.emplace(13); + + set.emplace_hint(set.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: set.emplace_hint(set.begin(), 13); + + map.emplace(std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: map.emplace(13, Foo(13)); + + map.emplace_hint(map.begin(), std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: map.emplace_hint(map.begin(), 13, Foo(13)); + + multiset.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: multiset.emplace(13); + + multiset.emplace_hint(multiset.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: multiset.emplace_hint(multiset.begin(), 13); + + multimap.emplace(std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: multimap.emplace(13, Foo(13)); + + multimap.emplace_hint(multimap.begin(), std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: multimap.emplace_hint(multimap.begin(), 13, Foo(13)); + + unordered_set.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: unordered_set.emplace(13); + + unordered_set.emplace_hint(unordered_set.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:53: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: unordered_set.emplace_hint(unordered_set.begin(), 13); + + unordered_map.emplace(std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: unordered_map.emplace(13, Foo(13)); + + unordered_map.emplace_hint(unordered_map.begin(), std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:53: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: unordered_map.emplace_hint(unordered_map.begin(), 13, Foo(13)); + + unordered_multiset.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: unordered_multiset.emplace(13); + unordered_multiset.emplace_hint(unordered_multiset.begin(), Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:63: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: unordered_multiset.emplace_hint(unordered_multiset.begin(), 13); + + unordered_multimap.emplace(std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: unordered_multimap.emplace(13, Foo(13)); + unordered_multimap.emplace_hint(unordered_multimap.begin(), std::make_pair(13, Foo(13))); + // CHECK-MESSAGES: :[[@LINE-1]]:63: warning: unnecessary temporary object created while calling emplace_hint + // CHECK-FIXES: unordered_multimap.emplace_hint(unordered_multimap.begin(), 13, Foo(13)); + + stack.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: stack.emplace(13); + + queue.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: queue.emplace(13); + + priority_queue.emplace(Foo(13)); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unnecessary temporary object created while calling emplace + // CHECK-FIXES: priority_queue.emplace(13); +} + +struct Bar { +public: + Bar(){}; + void testWithPrivateAndProtectedCtor() { + std::vector vec; + + vec.emplace_back(Bar(13)); + vec.emplace_back(Bar(13, 13)); + } + +protected: + Bar(int){}; + +private: + Bar(int, int){}; +}; + +void testPossibleFalsePositives() { + struct Y { + Y(std::pair &&) {} + }; + std::vector y; + y.emplace_back(std::make_pair(2, 3)); + + std::vector> v; + v.emplace_back(std::make_pair(0, 3)); + + struct D { + D(...) {} + operator char() const { return 0; } + }; + v.emplace_back(std::make_pair(Something(), 2)); +}