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.