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
|
//===--- 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";
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 = has(ignoringImplicit(initListExpr()));
// 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
|