forked from OSchip/llvm-project
[clang-tidy] Add proper emplace checks to modernize-use-emplace
modernize-use-emplace only recommends going from a push_back to an emplace_back, but does not provide a recommendation when emplace_back is improperly used. This adds the functionality of warning the user when an unecessary temporary is created while calling emplace_back or other "emplacy" functions from the STL containers. Reviewed By: kuhar, ivanmurashko Differential Revision: https://reviews.llvm.org/D101471
This commit is contained in:
parent
006334470d
commit
987f9cb6b9
|
@ -15,6 +15,69 @@ namespace tidy {
|
|||
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<StringRef>,
|
||||
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<c<d>>::e<f> 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<Expr>, 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 @@ const auto DefaultSmartPointers =
|
|||
"::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 @@ UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
|
|||
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 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
|
|||
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 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
|
|||
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 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
|
|||
cxxConstructExpr(
|
||||
unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
|
||||
InitializerListAsArgument, NewExprAsArgument,
|
||||
ConstructingDerived, IsPrivateCtor)))
|
||||
ConstructingDerived, IsPrivateOrProtectedCtor)))
|
||||
.bind("ctor");
|
||||
auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
|
||||
|
||||
|
@ -102,22 +189,64 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
|
|||
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<StringRef, 2>(
|
||||
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<CXXMemberCallExpr>("call");
|
||||
const auto *PushBackCall =
|
||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("push_back_call");
|
||||
const auto *EmplacyCall =
|
||||
Result.Nodes.getNodeAs<CXXMemberCallExpr>("emplacy_call");
|
||||
const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
|
||||
const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("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 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
|
|||
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 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
|
|||
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 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
|
|||
|
||||
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 @@ void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|||
utils::options::serializeStringList(TupleTypes));
|
||||
Options.store(Opts, "TupleMakeFunctions",
|
||||
utils::options::serializeStringList(TupleMakeFunctions));
|
||||
Options.store(Opts, "EmplacyFunctions",
|
||||
utils::options::serializeStringList(EmplacyFunctions));
|
||||
}
|
||||
|
||||
} // namespace modernize
|
||||
|
|
|
@ -40,6 +40,7 @@ private:
|
|||
const std::vector<StringRef> SmartPointers;
|
||||
const std::vector<StringRef> TupleTypes;
|
||||
const std::vector<StringRef> TupleMakeFunctions;
|
||||
const std::vector<StringRef> EmplacyFunctions;
|
||||
};
|
||||
|
||||
} // namespace modernize
|
||||
|
|
|
@ -15,17 +15,29 @@ because replacing ``insert`` with ``emplace`` may result in
|
|||
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<MyClass> v;
|
||||
v.push_back(MyClass(21, 37));
|
||||
v.emplace_back(MyClass(21, 37));
|
||||
|
||||
std::vector<std::pair<int, int>> w;
|
||||
|
||||
w.push_back(std::pair<int, int>(21, 37));
|
||||
w.push_back(std::make_pair(21L, 37L));
|
||||
w.emplace_back(std::make_pair(21L, 37L));
|
||||
|
||||
After:
|
||||
|
||||
|
@ -33,10 +45,12 @@ After:
|
|||
|
||||
std::vector<MyClass> v;
|
||||
v.emplace_back(21, 37);
|
||||
v.emplace_back(21, 37);
|
||||
|
||||
std::vector<std::pair<int, int>> 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 @@ Options
|
|||
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 @@ Example
|
|||
|
||||
std::vector<MyTuple<int, bool, char>> x;
|
||||
x.push_back(MakeMyTuple(1, false, 'x'));
|
||||
x.emplace_back(MakeMyTuple(1, false, 'x'));
|
||||
|
||||
transforms to:
|
||||
|
||||
|
@ -142,6 +164,8 @@ transforms to:
|
|||
|
||||
std::vector<MyTuple<int, bool, char>> 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``.
|
||||
|
|
|
@ -10,52 +10,13 @@
|
|||
|
||||
namespace std {
|
||||
template <typename>
|
||||
class initializer_list
|
||||
{
|
||||
class initializer_list {
|
||||
public:
|
||||
initializer_list() noexcept {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class vector {
|
||||
public:
|
||||
vector() = default;
|
||||
vector(initializer_list<T>) {}
|
||||
|
||||
void push_back(const T &) {}
|
||||
void push_back(T &&) {}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args){};
|
||||
~vector();
|
||||
};
|
||||
template <typename T>
|
||||
class list {
|
||||
public:
|
||||
void push_back(const T &) {}
|
||||
void push_back(T &&) {}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args){};
|
||||
~list();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class deque {
|
||||
public:
|
||||
void push_back(const T &) {}
|
||||
void push_back(T &&) {}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args){};
|
||||
~deque();
|
||||
};
|
||||
|
||||
template <typename T> struct remove_reference { using type = T; };
|
||||
template <typename T> struct remove_reference<T &> { using type = T; };
|
||||
template <typename T> struct remove_reference<T &&> { using type = T; };
|
||||
|
||||
template <typename T1, typename T2> class pair {
|
||||
template <typename T1, typename T2>
|
||||
class pair {
|
||||
public:
|
||||
pair() = default;
|
||||
pair(const pair &) = default;
|
||||
|
@ -64,17 +25,253 @@ public:
|
|||
pair(const T1 &, const T2 &) {}
|
||||
pair(T1 &&, T2 &&) {}
|
||||
|
||||
template <typename U1, typename U2> pair(const pair<U1, U2> &){};
|
||||
template <typename U1, typename U2> pair(pair<U1, U2> &&){};
|
||||
template <typename U1, typename U2>
|
||||
pair(const pair<U1, U2> &){};
|
||||
template <typename U1, typename U2>
|
||||
pair(pair<U1, U2> &&){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class vector {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
vector() = default;
|
||||
vector(initializer_list<T>) {}
|
||||
|
||||
void push_back(const T &) {}
|
||||
void push_back(T &&) {}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args){};
|
||||
template <typename... Args>
|
||||
iterator emplace(const_iterator pos, Args &&...args){};
|
||||
~vector();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
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 <typename... Args>
|
||||
iterator emplace(const_iterator pos, Args &&...args){};
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args){};
|
||||
template <typename... Args>
|
||||
void emplace_front(Args &&...args){};
|
||||
~list();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
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 <typename... Args>
|
||||
iterator emplace(const_iterator pos, Args &&...args){};
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&... args){};
|
||||
template <typename... Args>
|
||||
void emplace_front(Args &&...args){};
|
||||
~deque();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class forward_list {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_front(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_after(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class set {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename Key, typename T>
|
||||
class map {
|
||||
public:
|
||||
using value_type = std::pair<Key, T>;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class multiset {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename Key, typename T>
|
||||
class multimap {
|
||||
public:
|
||||
using value_type = std::pair<Key, T>;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class unordered_set {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename Key, typename T>
|
||||
class unordered_map {
|
||||
public:
|
||||
using value_type = std::pair<Key, T>;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class unordered_multiset {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename Key, typename T>
|
||||
class unordered_multimap {
|
||||
public:
|
||||
using value_type = std::pair<Key, T>;
|
||||
|
||||
class iterator {};
|
||||
class const_iterator {};
|
||||
const_iterator begin() { return const_iterator{}; }
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
template <typename... Args>
|
||||
iterator emplace_hint(const_iterator pos, Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class stack {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class queue {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class priority_queue {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args &&...args){};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct remove_reference { using type = T; };
|
||||
template <typename T>
|
||||
struct remove_reference<T &> { using type = T; };
|
||||
template <typename T>
|
||||
struct remove_reference<T &&> { using type = T; };
|
||||
|
||||
template <typename T1, typename T2>
|
||||
pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type>
|
||||
make_pair(T1 &&, T2 &&) {
|
||||
return {};
|
||||
};
|
||||
|
||||
template <typename... Ts> class tuple {
|
||||
template <typename... Ts>
|
||||
class tuple {
|
||||
public:
|
||||
tuple() = default;
|
||||
tuple(const tuple &) = default;
|
||||
|
@ -83,13 +280,17 @@ public:
|
|||
tuple(const Ts &...) {}
|
||||
tuple(Ts &&...) {}
|
||||
|
||||
template <typename... Us> tuple(const tuple<Us...> &){};
|
||||
template <typename... Us> tuple(tuple<Us...> &&) {}
|
||||
template <typename... Us>
|
||||
tuple(const tuple<Us...> &){};
|
||||
template <typename... Us>
|
||||
tuple(tuple<Us...> &&) {}
|
||||
|
||||
template <typename U1, typename U2> tuple(const pair<U1, U2> &) {
|
||||
template <typename U1, typename U2>
|
||||
tuple(const pair<U1, U2> &) {
|
||||
static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
|
||||
};
|
||||
template <typename U1, typename U2> tuple(pair<U1, U2> &&) {
|
||||
template <typename U1, typename U2>
|
||||
tuple(pair<U1, U2> &&) {
|
||||
static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
|
||||
};
|
||||
};
|
||||
|
@ -118,7 +319,7 @@ public:
|
|||
void emplace_back(Args &&... args){};
|
||||
};
|
||||
|
||||
} // llvm
|
||||
} // namespace llvm
|
||||
|
||||
void testInts() {
|
||||
std::vector<int> v;
|
||||
|
@ -375,7 +576,7 @@ void testMakePair() {
|
|||
// make_pair cannot be removed here, as X is not constructible with two ints.
|
||||
|
||||
struct Y {
|
||||
Y(std::pair<int, int>&&) {}
|
||||
Y(std::pair<int, int> &&) {}
|
||||
};
|
||||
std::vector<Y> y;
|
||||
y.push_back(std::make_pair(2, 3));
|
||||
|
@ -402,7 +603,8 @@ void testMakeTuple() {
|
|||
}
|
||||
|
||||
namespace test {
|
||||
template <typename T> struct Single {
|
||||
template <typename T>
|
||||
struct Single {
|
||||
Single() = default;
|
||||
Single(const Single &) = default;
|
||||
Single(Single &&) = default;
|
||||
|
@ -410,11 +612,15 @@ template <typename T> struct Single {
|
|||
Single(const T &) {}
|
||||
Single(T &&) {}
|
||||
|
||||
template <typename U> Single(const Single<U> &) {}
|
||||
template <typename U> Single(Single<U> &&) {}
|
||||
template <typename U>
|
||||
Single(const Single<U> &) {}
|
||||
template <typename U>
|
||||
Single(Single<U> &&) {}
|
||||
|
||||
template <typename U> Single(const std::tuple<U> &) {}
|
||||
template <typename U> Single(std::tuple<U> &&) {}
|
||||
template <typename U>
|
||||
Single(const std::tuple<U> &) {}
|
||||
template <typename U>
|
||||
Single(std::tuple<U> &&) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -605,3 +811,240 @@ void testInitializerList() {
|
|||
x.push_back(PairIntVector(3, {4}));
|
||||
x.push_back({5, {6}});
|
||||
}
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
Foo(){};
|
||||
Foo(int){};
|
||||
Foo(int, int){};
|
||||
Foo(std::pair<int, int>){};
|
||||
|
||||
protected:
|
||||
Foo(char *) : Foo(){};
|
||||
};
|
||||
|
||||
void testSomeEmplaceCases() {
|
||||
std::vector<std::pair<char *, char *>> v1;
|
||||
std::vector<Foo> v2;
|
||||
std::unordered_map<int, char *> 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<std::pair<int, int>> v3;
|
||||
v3.emplace_back(std::pair<int, int>(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<std::tuple<int, int, int>> v4;
|
||||
v4.emplace_back(std::tuple<int, int, int>(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<test::Single<int>> v5;
|
||||
v5.emplace_back(test::Single<int>(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<Foo> vector;
|
||||
std::deque<Foo> deque;
|
||||
std::forward_list<Foo> forward_list;
|
||||
std::list<Foo> list;
|
||||
std::set<Foo> set;
|
||||
std::map<int, Foo> map;
|
||||
std::multiset<Foo> multiset;
|
||||
std::multimap<int, Foo> multimap;
|
||||
std::unordered_set<Foo> unordered_set;
|
||||
std::unordered_map<int, Foo> unordered_map;
|
||||
std::unordered_multiset<Foo> unordered_multiset;
|
||||
std::unordered_multimap<int, Foo> unordered_multimap;
|
||||
std::stack<Foo> stack;
|
||||
std::queue<Foo> queue;
|
||||
std::priority_queue<Foo> 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<Bar> 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<int, int> &&) {}
|
||||
};
|
||||
std::vector<Y> y;
|
||||
y.emplace_back(std::make_pair(2, 3));
|
||||
|
||||
std::vector<std::pair<int, int>> v;
|
||||
v.emplace_back(std::make_pair<char, char>(0, 3));
|
||||
|
||||
struct D {
|
||||
D(...) {}
|
||||
operator char() const { return 0; }
|
||||
};
|
||||
v.emplace_back(std::make_pair<D, int>(Something(), 2));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue