a.cpp:
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<pair<int, int>> a;
a.push_back(make_pair(1, 2)); //caught
vector<vector<pair<int, int>>> b(1);
b[0].push_back(make_pair(1, 2)); //not caught
return 0;
}
clang-tidy -config="{Checks: 'modernize-use-emplace'}" a.cpp
a.cpp:6:4: warning: use emplace_back instead of push_back [modernize-use-emplace]
a.push_back(make_pair(1, 2)); //caught
^~~~~~~~~~~~~~~~~~~ ~
emplace_back
clang-tidy --version
LLVM (http://llvm.org/):
LLVM version 14.0.6
Optimized build.
Default target: x86_64-pc-linux-gnu
Host CPU: skylake
Short answer: This behavior is due to bug 56721 in clang-tidy
.
Long answer, based on my comment on that bug:
The bug relates to how reference
is declared in the container. That in turn causes clang-tidy
to not realize that the return value of the accessor is an instance of a relevant container.
The core of UseEmplaceCheck.cpp
is:
auto CallPushBack = cxxMemberCallExpr(
hasDeclaration(functionDecl(hasName("push_back"))),
on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));
Using the example from the question, the following simplified matcher (which can be passed to clang-query
) will report the non-nested case, as expected:
cxxMemberCallExpr(
hasDeclaration(functionDecl(hasName("push_back"))),
on(hasType(cxxRecordDecl(hasName("std::vector"))))
)
Reports:
vector<pair<int, int>> a;
a.push_back(make_pair(1, 2)); //caught
However, to get it to match the nested case (when compiling with GCC-9.3.0 headers), it must be changed to:
cxxMemberCallExpr(
hasDeclaration(functionDecl(hasName("push_back"))),
on(hasType(namedDecl(hasName("::__gnu_cxx::__alloc_traits<std::allocator<std::vector<std::pair<int, int>>>, std::vector<std::pair<int, int>>>::value_type"))))
)
Reports:
vector<vector<pair<int, int>>> b(1);
b[0].push_back(make_pair(1, 2)); //not caught
That is, the type is no longer a cxxRecordDecl
, and its name is a long template-id. That template-id is derived from the code used to declare std::vector::reference
in the headers. It should be semantically equivalent to std::pair<int, int>
, but that equivalence is evidently obscured from clang-tidy
here.