File: UseEmplaceCheck.cpp

package info (click to toggle)
llvm-toolchain-4.0 1%3A4.0.1-10~deb9u2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 493,332 kB
  • sloc: cpp: 2,698,100; ansic: 552,773; asm: 128,821; python: 121,589; objc: 105,054; sh: 21,174; lisp: 6,758; ml: 5,532; perl: 5,311; pascal: 5,245; makefile: 2,083; cs: 1,868; xml: 686; php: 212; csh: 117
file content (143 lines) | stat: -rw-r--r-- 5,974 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//===--- UseEmplaceCheck.cpp - clang-tidy----------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "UseEmplaceCheck.h"
#include "../utils/OptionsUtils.h"
using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace modernize {

static const auto DefaultContainersWithPushBack =
    "::std::vector; ::std::list; ::std::deque";
static const auto DefaultSmartPointers =
    "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";

namespace {
namespace impl {
// FIXME: This matcher should be replaced by a matcher from ASTMatcher.h
const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
    CXXStdInitializerListExpr> cxxStdInitializerListExpr;
} // namespace impl
} // namespace

UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
    : ClangTidyCheck(Name, Context),
      ContainersWithPushBack(utils::options::parseStringList(Options.get(
          "ContainersWithPushBack", DefaultContainersWithPushBack))),
      SmartPointers(utils::options::parseStringList(
          Options.get("SmartPointers", DefaultSmartPointers))) {}

void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus11)
    return;

  // FIXME: Bunch of functionality that could be easily added:
  // + add handling of `push_front` for std::forward_list, std::list
  // and std::deque.
  // + add handling of `push` for std::stack, std::queue, std::priority_queue
  // + add handling of `insert` for stl associative container, but be careful
  // because this requires special treatment (it could cause performance
  // regression)
  // + match for emplace calls that should be replaced with insertion
  // + match for make_pair calls.
  auto callPushBack = cxxMemberCallExpr(
      hasDeclaration(functionDecl(hasName("push_back"))),
      on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
          ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));

  // 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
  // (and destructed) as in push_back case.
  auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
      SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));

  // Bitfields binds only to consts and emplace_back take it by universal ref.
  auto bitFieldAsArgument = hasAnyArgument(
      ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));

  // Initializer list can't be passed to universal reference.
  auto initializerListAsArgument = hasAnyArgument(
      ignoringImplicit(cxxConstructExpr(isListInitialization())));

  // We could have leak of resource.
  auto newExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
  // We would call another constructor.
  auto constructingDerived =
      hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));

  // emplace_back can't access private constructor.
  auto isPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));

  auto hasInitList = anyOf(has(ignoringImplicit(initListExpr())),
                           has(impl::cxxStdInitializerListExpr()));
  // FIXME: Replace internal C++ initializer list matcher with one from
  // ASTMatchers.h

  // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
  // overloaded functions and template names.
  auto soughtConstructExpr =
      cxxConstructExpr(
          unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument,
                       initializerListAsArgument, newExprAsArgument,
                       constructingDerived, isPrivateCtor)))
          .bind("ctor");
  auto hasConstructExpr = has(ignoringImplicit(soughtConstructExpr));

  auto ctorAsArgument = materializeTemporaryExpr(
      anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr))));

  Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument),
                                       unless(isInTemplateInstantiation()))
                         .bind("call"),
                     this);
}

void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
  const auto *InnerCtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");

  auto FunctionNameSourceRange = CharSourceRange::getCharRange(
      Call->getExprLoc(), Call->getArg(0)->getExprLoc());

  auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");

  if (FunctionNameSourceRange.getBegin().isMacroID())
    return;

  Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
                                       "emplace_back(");

  auto CallParensRange = InnerCtorCall->getParenOrBraceRange();

  // Finish if there is no explicit constructor call.
  if (CallParensRange.getBegin().isInvalid())
    return;

  // Range for constructor name and opening brace.
  auto CtorCallSourceRange = CharSourceRange::getTokenRange(
      InnerCtorCall->getExprLoc(), CallParensRange.getBegin());

  Diag << FixItHint::CreateRemoval(CtorCallSourceRange)
       << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
              CallParensRange.getEnd(), CallParensRange.getEnd()));
}

void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  Options.store(Opts, "ContainersWithPushBack",
                utils::options::serializeStringList(ContainersWithPushBack));
  Options.store(Opts, "SmartPointers",
                utils::options::serializeStringList(SmartPointers));
}

} // namespace modernize
} // namespace tidy
} // namespace clang