1

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

clang-tidy wiki

SebastianWilke
  • 470
  • 1
  • 4
  • 15

1 Answers1

0

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.

Scott McPeak
  • 8,803
  • 2
  • 40
  • 79