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,66 @@ 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. Inspired from HasNameMatcher::matchesNodeFullSlow in +// clang/lib/ASTMatchers/ASTMatchersInternal.cpp. +AST_MATCHER_P(NamedDecl, hasAnyNameIgnoringTemplates, std::vector, + Names) { + const auto FullName = "::" + Node.getQualifiedNameAsString(); + + std::string FullNameTrimmed; + int count = 0; + for (const auto &character : FullName) { + if (character == '<') { + ++count; + } else if (character == '>') { + --count; + } else if (count == 0) { + FullNameTrimmed.append(1, character); + } + } + + const auto FullNameTrimmedRef = StringRef(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 +85,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 +111,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: @@ -53,6 +129,13 @@ on(hasType(cxxRecordDecl(hasAnyName(SmallVector( ContainersWithPushBack.begin(), ContainersWithPushBack.end())))))); + 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 @@ -74,8 +157,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())); @@ -86,7 +170,7 @@ cxxConstructExpr( unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument, InitializerListAsArgument, NewExprAsArgument, - ConstructingDerived, IsPrivateCtor))) + ConstructingDerived, IsPrivateOrProtectedCtor))) .bind("ctor"); auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr)); @@ -106,22 +190,63 @@ SmallVector(TupleTypes.begin(), TupleTypes.end()))))))); 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(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 auto *Call = PushBackCall ? PushBackCall : EmplacyCall; + if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 && CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange()) return; @@ -129,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(MakeCall ? MakeCall->getBeginLoc() : CtorCall->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 auto *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back("; + Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, + EmplacePrefix); + } const SourceRange CallParensRange = MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(), @@ -146,16 +279,31 @@ if (CallParensRange.getBegin().isInvalid()) return; - const SourceLocation ExprBegin = - MakeCall ? MakeCall->getExprLoc() : CtorCall->getExprLoc(); + if (PushBackCall) { + const SourceLocation ExprBegin = + MakeCall ? MakeCall->getExprLoc() : CtorCall->getExprLoc(); - // Range for constructor name and opening brace. - const auto ParamCallSourceRange = - CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin()); + // Range for constructor name and opening brace. + const auto ParamCallSourceRange = + CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin()); - Diag << FixItHint::CreateRemoval(ParamCallSourceRange) - << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( - CallParensRange.getEnd(), CallParensRange.getEnd())); + Diag << FixItHint::CreateRemoval(ParamCallSourceRange) + << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + CallParensRange.getEnd(), CallParensRange.getEnd())); + } else { + if ((MakeCall ? MakeCall->getNumArgs() : CtorCall->getNumArgs()) == 0) { + Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + MakeCall ? MakeCall->getSourceRange() : CtorCall->getSourceRange())); + } else { + Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange( + MakeCall ? MakeCall->getBeginLoc() : CtorCall->getBeginLoc(), + MakeCall ? MakeCall->getArg(0)->getBeginLoc() + : CtorCall->getArg(0)->getBeginLoc())) + << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + MakeCall ? MakeCall->getRParenLoc() : CtorCall->getEndLoc(), + MakeCall ? MakeCall->getRParenLoc() : CtorCall->getEndLoc())); + } + } } void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { @@ -168,6 +316,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/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) {} @@ -26,55 +47,231 @@ void push_back(T &&) {} template - void emplace_back(Args &&... args){}; + 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 - void emplace_back(Args &&... args){}; + 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 - void emplace_back(Args &&... args){}; + 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 pair(const pair &){}; - template pair(pair &&){}; + 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 +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"); }; }; @@ -115,10 +316,10 @@ void push_back(T &&) {} template - void emplace_back(Args &&... args){}; + 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,159 @@ 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; + + // CHECK-FIXES: v1.emplace_back("foo", "bar"); + // CHECK-FIXES: v1.emplace_back(foo, "bar"); + // CHECK-FIXES: v1.emplace(v1.begin(), "foo", "bar"); + char *foo = "bar"; + v1.emplace_back(std::make_pair("foo", "bar")); + v1.emplace_back(std::make_pair(foo, "bar")); + v1.emplace(v1.begin(), std::make_pair("foo", "bar")); + + // CHECK-FIXES: v2.emplace_back(); + // CHECK-FIXES: v2.emplace_back(13); + // CHECK-FIXES: v2.emplace_back(13, a); + int a = 31; + v2.emplace_back(Foo()); + v2.emplace_back(Foo(13)); + v2.emplace_back(Foo(13, a)); + + // CHECK-MESSAGES-NOT: [[@LINE+1]]:19: warning: unnecessary temporary object created while calling emplace_back + v2.emplace_back(std::make_pair(3, 3)); + + // CHECK-FIXES: m1.emplace(13, "foo"); + m1.emplace(std::make_pair(13, "foo")); +} + +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; + + // CHECK-FIXES: vector.emplace_back(13); + // CHECK-FIXES: vector.emplace(vector.begin(), 13); + vector.emplace_back(Foo(13)); + vector.emplace(vector.begin(), Foo(13)); + + // CHECK-FIXES: deque.emplace(deque.begin(), 13); + // CHECK-FIXES: deque.emplace_front(13); + // CHECK-FIXES: deque.emplace_back(13); + deque.emplace(deque.begin(), Foo(13)); + deque.emplace_front(Foo(13)); + deque.emplace_back(Foo(13)); + + // CHECK-FIXES: forward_list.emplace_after(forward_list.begin(), 13); + // CHECK-FIXES: forward_list.emplace_front(13); + forward_list.emplace_after(forward_list.begin(), Foo(13)); + forward_list.emplace_front(Foo(13)); + + // CHECK-FIXES: list.emplace(list.begin(), 13); + // CHECK-FIXES: list.emplace_back(13); + // CHECK-FIXES: list.emplace_front(13); + list.emplace(list.begin(), Foo(13)); + list.emplace_back(Foo(13)); + list.emplace_front(Foo(13)); + + // CHECK-FIXES: set.emplace(13); + // CHECK-FIXES: set.emplace_hint(set.begin(), 13); + set.emplace(Foo(13)); + set.emplace_hint(set.begin(), Foo(13)); + + // CHECK-FIXES: map.emplace(13, Foo(13)); + // CHECK-FIXES: map.emplace_hint(map.begin(), 13, Foo(13)); + map.emplace(std::make_pair(13, Foo(13))); + map.emplace_hint(map.begin(), std::make_pair(13, Foo(13))); + + // CHECK-FIXES: multiset.emplace(13); + // CHECK-FIXES: multiset.emplace_hint(multiset.begin(), 13); + multiset.emplace(Foo(13)); + multiset.emplace_hint(multiset.begin(), Foo(13)); + + // CHECK-FIXES: multimap.emplace(13, Foo(13)); + // CHECK-FIXES: multimap.emplace_hint(multimap.begin(), 13, Foo(13)); + multimap.emplace(std::make_pair(13, Foo(13))); + multimap.emplace_hint(multimap.begin(), std::make_pair(13, Foo(13))); + + // CHECK-FIXES: unordered_set.emplace(13); + // CHECK-FIXES: unordered_set.emplace_hint(unordered_set.begin(), 13); + unordered_set.emplace(Foo(13)); + unordered_set.emplace_hint(unordered_set.begin(), Foo(13)); + + // CHECK-FIXES: unordered_map.emplace(13, Foo(13)); + // CHECK-FIXES: unordered_map.emplace_hint(unordered_map.begin(), 13, Foo(13)); + unordered_map.emplace(std::make_pair(13, Foo(13))); + unordered_map.emplace_hint(unordered_map.begin(), std::make_pair(13, Foo(13))); + + // CHECK-FIXES: unordered_multiset.emplace(13); + // CHECK-FIXES: unordered_multiset.emplace_hint(unordered_multiset.begin(), 13); + unordered_multiset.emplace(Foo(13)); + unordered_multiset.emplace_hint(unordered_multiset.begin(), Foo(13)); + + // CHECK-FIXES: unordered_multimap.emplace(13, Foo(13)); + // CHECK-FIXES: unordered_multimap.emplace_hint(unordered_multimap.begin(), 13, Foo(13)); + unordered_multimap.emplace(std::make_pair(13, Foo(13))); + unordered_multimap.emplace_hint(unordered_multimap.begin(), std::make_pair(13, Foo(13))); + + // CHECK-FIXES: stack.emplace(13); + // CHECK-FIXES: queue.emplace(13); + // CHECK-FIXES: priority_queue.emplace(13); + stack.emplace(Foo(13)); + queue.emplace(Foo(13)); + priority_queue.emplace(Foo(13)); +} + +struct Bar { +public: + Bar(){}; + void testWithPrivateAndProtectedCtor() { + std::vector vec; + // CHECK-MESSAGES-NOT: [[@LINE+1]]:22: warning: unnecessary temporary object created while calling emplace_back + vec.emplace_back(Bar(13)); + // CHECK-MESSAGES-NOT: [[@LINE+1]]:22: warning: unnecessary temporary object created while calling emplace_back + vec.emplace_back(Bar(13, 13)); + } + +protected: + Bar(int){}; + +private: + Bar(int, int){}; +}; + +void testMoreFalsePositiveCases() { + std::unordered_map> map; + // CHECK-MESSAGES-NOT: [[@LINE+1]]:19: warning: unnecessary temporary object created while calling emplace + map.emplace(13, std::make_pair("foo", 13)); + + std::vector>> vec; + // CHECK-MESSAGES-NOT: [[@LINE+1]]:20: warning: unnecessary temporary object created while calling emplace_back + vec.emplace_back(std::make_tuple(13, 31)); +}