@@ -15,6 +15,69 @@ namespace tidy {
15
15
namespace modernize {
16
16
17
17
namespace {
18
+ // Identical to hasAnyName, except it does not take template specifiers into
19
+ // account. This is used to match the functions names as in
20
+ // DefaultEmplacyFunctions below without caring about the template types of the
21
+ // containers.
22
+ AST_MATCHER_P (NamedDecl, hasAnyNameIgnoringTemplates, std::vector<StringRef>,
23
+ Names) {
24
+ const std::string FullName = " ::" + Node.getQualifiedNameAsString ();
25
+
26
+ // This loop removes template specifiers by only keeping characters not within
27
+ // template brackets. We keep a depth count to handle nested templates. For
28
+ // example, it'll transform a::b<c<d>>::e<f> to simply a::b::e.
29
+ std::string FullNameTrimmed;
30
+ int Depth = 0 ;
31
+ for (const auto &Character : FullName) {
32
+ if (Character == ' <' ) {
33
+ ++Depth;
34
+ } else if (Character == ' >' ) {
35
+ --Depth;
36
+ } else if (Depth == 0 ) {
37
+ FullNameTrimmed.append (1 , Character);
38
+ }
39
+ }
40
+
41
+ // This loop is taken from HasNameMatcher::matchesNodeFullSlow in
42
+ // clang/lib/ASTMatchers/ASTMatchersInternal.cpp and checks whether
43
+ // FullNameTrimmed matches any of the given Names.
44
+ const StringRef FullNameTrimmedRef = FullNameTrimmed;
45
+ for (const StringRef Pattern : Names) {
46
+ if (Pattern.startswith (" ::" )) {
47
+ if (FullNameTrimmed == Pattern)
48
+ return true ;
49
+ } else if (FullNameTrimmedRef.endswith (Pattern) &&
50
+ FullNameTrimmedRef.drop_back (Pattern.size ()).endswith (" ::" )) {
51
+ return true ;
52
+ }
53
+ }
54
+
55
+ return false ;
56
+ }
57
+
58
+ // Checks if the given matcher is the last argument of the given CallExpr.
59
+ AST_MATCHER_P (CallExpr, hasLastArgument,
60
+ clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
61
+ if (Node.getNumArgs () == 0 )
62
+ return false ;
63
+
64
+ return InnerMatcher.matches (*Node.getArg (Node.getNumArgs () - 1 ), Finder,
65
+ Builder);
66
+ }
67
+
68
+ // Checks if the given member call has the same number of arguments as the
69
+ // function had parameters defined (this is useful to check if there is only one
70
+ // variadic argument).
71
+ AST_MATCHER (CXXMemberCallExpr, hasSameNumArgsAsDeclNumParams) {
72
+ if (Node.getMethodDecl ()->isFunctionTemplateSpecialization ())
73
+ return Node.getNumArgs () == Node.getMethodDecl ()
74
+ ->getPrimaryTemplate ()
75
+ ->getTemplatedDecl ()
76
+ ->getNumParams ();
77
+
78
+ return Node.getNumArgs () == Node.getMethodDecl ()->getNumParams ();
79
+ }
80
+
18
81
AST_MATCHER (DeclRefExpr, hasExplicitTemplateArgs) {
19
82
return Node.hasExplicitTemplateArgs ();
20
83
}
@@ -25,6 +88,20 @@ const auto DefaultSmartPointers =
25
88
" ::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr" ;
26
89
const auto DefaultTupleTypes = " ::std::pair; ::std::tuple" ;
27
90
const auto DefaultTupleMakeFunctions = " ::std::make_pair; ::std::make_tuple" ;
91
+ const auto DefaultEmplacyFunctions =
92
+ " vector::emplace_back; vector::emplace;"
93
+ " deque::emplace; deque::emplace_front; deque::emplace_back;"
94
+ " forward_list::emplace_after; forward_list::emplace_front;"
95
+ " list::emplace; list::emplace_back; list::emplace_front;"
96
+ " set::emplace; set::emplace_hint;"
97
+ " map::emplace; map::emplace_hint;"
98
+ " multiset::emplace; multiset::emplace_hint;"
99
+ " multimap::emplace; multimap::emplace_hint;"
100
+ " unordered_set::emplace; unordered_set::emplace_hint;"
101
+ " unordered_map::emplace; unordered_map::emplace_hint;"
102
+ " unordered_multiset::emplace; unordered_multiset::emplace_hint;"
103
+ " unordered_multimap::emplace; unordered_multimap::emplace_hint;"
104
+ " stack::emplace; queue::emplace; priority_queue::emplace" ;
28
105
} // namespace
29
106
30
107
UseEmplaceCheck::UseEmplaceCheck (StringRef Name, ClangTidyContext *Context)
@@ -37,7 +114,9 @@ UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
37
114
TupleTypes(utils::options::parseStringList(
38
115
Options.get(" TupleTypes" , DefaultTupleTypes))),
39
116
TupleMakeFunctions(utils::options::parseStringList(
40
- Options.get(" TupleMakeFunctions" , DefaultTupleMakeFunctions))) {}
117
+ Options.get(" TupleMakeFunctions" , DefaultTupleMakeFunctions))),
118
+ EmplacyFunctions(utils::options::parseStringList(
119
+ Options.get(" EmplacyFunctions" , DefaultEmplacyFunctions))) {}
41
120
42
121
void UseEmplaceCheck::registerMatchers (MatchFinder *Finder) {
43
122
// FIXME: Bunch of functionality that could be easily added:
@@ -52,6 +131,13 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
52
131
hasDeclaration (functionDecl (hasName (" push_back" ))),
53
132
on (hasType (cxxRecordDecl (hasAnyName (ContainersWithPushBack)))));
54
133
134
+ auto CallEmplacy = cxxMemberCallExpr (
135
+ hasDeclaration (
136
+ functionDecl (hasAnyNameIgnoringTemplates (EmplacyFunctions))),
137
+ on (hasType (cxxRecordDecl (has (typedefNameDecl (
138
+ hasName (" value_type" ), hasType (type (hasUnqualifiedDesugaredType (
139
+ recordType ().bind (" value_type" ))))))))));
140
+
55
141
// We can't replace push_backs of smart pointer because
56
142
// if emplacement fails (f.e. bad_alloc in vector) we will have leak of
57
143
// passed pointer because smart pointer won't be constructed
@@ -73,8 +159,9 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
73
159
auto ConstructingDerived =
74
160
hasParent (implicitCastExpr (hasCastKind (CastKind::CK_DerivedToBase)));
75
161
76
- // emplace_back can't access private constructor.
77
- auto IsPrivateCtor = hasDeclaration (cxxConstructorDecl (isPrivate ()));
162
+ // emplace_back can't access private or protected constructors.
163
+ auto IsPrivateOrProtectedCtor =
164
+ hasDeclaration (cxxConstructorDecl (anyOf (isPrivate (), isProtected ())));
78
165
79
166
auto HasInitList = anyOf (has (ignoringImplicit (initListExpr ())),
80
167
has (cxxStdInitializerListExpr ()));
@@ -85,7 +172,7 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
85
172
cxxConstructExpr (
86
173
unless (anyOf (IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
87
174
InitializerListAsArgument, NewExprAsArgument,
88
- ConstructingDerived, IsPrivateCtor )))
175
+ ConstructingDerived, IsPrivateOrProtectedCtor )))
89
176
.bind (" ctor" );
90
177
auto HasConstructExpr = has (ignoringImplicit (SoughtConstructExpr));
91
178
@@ -102,36 +189,86 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
102
189
hasDeclaration (cxxConstructorDecl (ofClass (hasAnyName (TupleTypes))))));
103
190
104
191
auto SoughtParam = materializeTemporaryExpr (
105
- anyOf (has (MakeTuple), has (MakeTupleCtor),
106
- HasConstructExpr, has (cxxFunctionalCastExpr (HasConstructExpr))));
192
+ anyOf (has (MakeTuple), has (MakeTupleCtor), HasConstructExpr,
193
+ has (cxxFunctionalCastExpr (HasConstructExpr))));
194
+
195
+ auto HasConstructExprWithValueTypeType =
196
+ has (ignoringImplicit (cxxConstructExpr (
197
+ SoughtConstructExpr, hasType (type (hasUnqualifiedDesugaredType (
198
+ type (equalsBoundNode (" value_type" ))))))));
199
+
200
+ auto HasConstructExprWithValueTypeTypeAsLastArgument =
201
+ hasLastArgument (materializeTemporaryExpr (anyOf (
202
+ HasConstructExprWithValueTypeType,
203
+ has (cxxFunctionalCastExpr (HasConstructExprWithValueTypeType)))));
107
204
108
205
Finder->addMatcher (
109
206
traverse (TK_AsIs, cxxMemberCallExpr (CallPushBack, has (SoughtParam),
110
207
unless (isInTemplateInstantiation ()))
111
- .bind (" call" )),
208
+ .bind (" push_back_call" )),
209
+ this );
210
+
211
+ Finder->addMatcher (
212
+ traverse (TK_AsIs,
213
+ cxxMemberCallExpr (
214
+ CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
215
+ hasSameNumArgsAsDeclNumParams (),
216
+ unless (isInTemplateInstantiation ()))
217
+ .bind (" emplacy_call" )),
218
+ this );
219
+
220
+ Finder->addMatcher (
221
+ traverse (
222
+ TK_AsIs,
223
+ cxxMemberCallExpr (
224
+ CallEmplacy,
225
+ on (hasType (cxxRecordDecl (has (typedefNameDecl (
226
+ hasName (" value_type" ),
227
+ hasType (type (
228
+ hasUnqualifiedDesugaredType (recordType (hasDeclaration (
229
+ cxxRecordDecl (hasAnyName (SmallVector<StringRef, 2 >(
230
+ TupleTypes.begin (), TupleTypes.end ()))))))))))))),
231
+ has (MakeTuple), hasSameNumArgsAsDeclNumParams (),
232
+ unless (isInTemplateInstantiation ()))
233
+ .bind (" emplacy_call" )),
112
234
this );
113
235
}
114
236
115
237
void UseEmplaceCheck::check (const MatchFinder::MatchResult &Result) {
116
- const auto *Call = Result.Nodes .getNodeAs <CXXMemberCallExpr>(" call" );
238
+ const auto *PushBackCall =
239
+ Result.Nodes .getNodeAs <CXXMemberCallExpr>(" push_back_call" );
240
+ const auto *EmplacyCall =
241
+ Result.Nodes .getNodeAs <CXXMemberCallExpr>(" emplacy_call" );
117
242
const auto *CtorCall = Result.Nodes .getNodeAs <CXXConstructExpr>(" ctor" );
118
243
const auto *MakeCall = Result.Nodes .getNodeAs <CallExpr>(" make" );
244
+
245
+ assert ((PushBackCall || EmplacyCall) && " No call matched" );
119
246
assert ((CtorCall || MakeCall) && " No push_back parameter matched" );
120
247
248
+ const CXXMemberCallExpr *Call = PushBackCall ? PushBackCall : EmplacyCall;
249
+
121
250
if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs () >= 1 &&
122
251
CtorCall->getArg (0 )->getSourceRange () == CtorCall->getSourceRange ())
123
252
return ;
124
253
125
254
const auto FunctionNameSourceRange = CharSourceRange::getCharRange (
126
255
Call->getExprLoc (), Call->getArg (0 )->getExprLoc ());
127
256
128
- auto Diag = diag (Call->getExprLoc (), " use emplace_back instead of push_back" );
257
+ auto Diag =
258
+ PushBackCall
259
+ ? diag (Call->getExprLoc (), " use emplace_back instead of push_back" )
260
+ : diag (CtorCall ? CtorCall->getBeginLoc () : MakeCall->getBeginLoc (),
261
+ " unnecessary temporary object created while calling " +
262
+ Call->getMethodDecl ()->getName ().str ());
129
263
130
264
if (FunctionNameSourceRange.getBegin ().isMacroID ())
131
265
return ;
132
266
133
- const auto *EmplacePrefix = MakeCall ? " emplace_back" : " emplace_back(" ;
134
- Diag << FixItHint::CreateReplacement (FunctionNameSourceRange, EmplacePrefix);
267
+ if (PushBackCall) {
268
+ const char *EmplacePrefix = MakeCall ? " emplace_back" : " emplace_back(" ;
269
+ Diag << FixItHint::CreateReplacement (FunctionNameSourceRange,
270
+ EmplacePrefix);
271
+ }
135
272
136
273
const SourceRange CallParensRange =
137
274
MakeCall ? SourceRange (MakeCall->getCallee ()->getEndLoc (),
@@ -143,15 +280,22 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
143
280
return ;
144
281
145
282
const SourceLocation ExprBegin =
146
- MakeCall ? MakeCall ->getExprLoc () : CtorCall ->getExprLoc ();
283
+ CtorCall ? CtorCall ->getExprLoc () : MakeCall ->getExprLoc ();
147
284
148
285
// Range for constructor name and opening brace.
149
286
const auto ParamCallSourceRange =
150
287
CharSourceRange::getTokenRange (ExprBegin, CallParensRange.getBegin ());
151
288
152
289
Diag << FixItHint::CreateRemoval (ParamCallSourceRange)
153
290
<< FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
154
- CallParensRange.getEnd (), CallParensRange.getEnd ()));
291
+ CallParensRange.getEnd (), CallParensRange.getEnd ()));
292
+
293
+ if (MakeCall && EmplacyCall) {
294
+ // Remove extra left parenthesis
295
+ Diag << FixItHint::CreateRemoval (
296
+ CharSourceRange::getCharRange (MakeCall->getCallee ()->getEndLoc (),
297
+ MakeCall->getArg (0 )->getBeginLoc ()));
298
+ }
155
299
}
156
300
157
301
void UseEmplaceCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
@@ -164,6 +308,8 @@ void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
164
308
utils::options::serializeStringList (TupleTypes));
165
309
Options.store (Opts, " TupleMakeFunctions" ,
166
310
utils::options::serializeStringList (TupleMakeFunctions));
311
+ Options.store (Opts, " EmplacyFunctions" ,
312
+ utils::options::serializeStringList (EmplacyFunctions));
167
313
}
168
314
169
315
} // namespace modernize
0 commit comments