135 :
ClangTidyCheck(Name, Context), IgnoreImplicitConstructors(Options.get(
136 "IgnoreImplicitConstructors", false)),
137 ContainersWithPushBack(
utils::options::parseStringList(Options.get(
138 "ContainersWithPushBack", DefaultContainersWithPushBack))),
139 ContainersWithPush(
utils::options::parseStringList(
140 Options.get(
"ContainersWithPush", DefaultContainersWithPush))),
141 ContainersWithPushFront(
utils::options::parseStringList(Options.get(
142 "ContainersWithPushFront", DefaultContainersWithPushFront))),
143 SmartPointers(
utils::options::parseStringList(
144 Options.get(
"SmartPointers", DefaultSmartPointers))),
145 TupleTypes(
utils::options::parseStringList(
146 Options.get(
"TupleTypes", DefaultTupleTypes))),
147 TupleMakeFunctions(
utils::options::parseStringList(
148 Options.get(
"TupleMakeFunctions", DefaultTupleMakeFunctions))),
149 EmplacyFunctions(
utils::options::parseStringList(
150 Options.get(
"EmplacyFunctions", DefaultEmplacyFunctions))) {}
159 cxxMemberCallExprOnContainer(
"push_back", ContainersWithPushBack);
160 auto CallPush = cxxMemberCallExprOnContainer(
"push", ContainersWithPush);
162 cxxMemberCallExprOnContainer(
"push_front", ContainersWithPushFront);
164 auto CallEmplacy = cxxMemberCallExpr(
166 functionDecl(hasAnyNameIgnoringTemplates(EmplacyFunctions))),
167 on(hasTypeOrPointeeType(
168 hasCanonicalType(hasDeclaration(has(typedefNameDecl(
169 hasName(
"value_type"),
170 hasType(hasCanonicalType(recordType().bind(
"value_type"))))))))));
176 auto IsCtorOfSmartPtr =
177 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(SmartPointers))));
180 auto BitFieldAsArgument = hasAnyArgument(
181 ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
184 auto InitializerListAsArgument = hasAnyArgument(
185 ignoringImplicit(allOf(cxxConstructExpr(isListInitialization()),
186 unless(cxxTemporaryObjectExpr()))));
189 auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
191 auto ConstructingDerived =
192 hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
195 auto IsPrivateOrProtectedCtor =
196 hasDeclaration(cxxConstructorDecl(anyOf(isPrivate(), isProtected())));
198 auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
199 has(cxxStdInitializerListExpr()));
203 auto SoughtConstructExpr =
205 unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
206 InitializerListAsArgument, NewExprAsArgument,
207 ConstructingDerived, IsPrivateOrProtectedCtor)))
209 auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
212 auto HasConstructInitListExpr = has(initListExpr(
213 initCountLeq(1), anyOf(allOf(has(SoughtConstructExpr),
214 has(cxxConstructExpr(argumentCountIs(0)))),
215 has(cxxBindTemporaryExpr(
216 has(SoughtConstructExpr),
217 has(cxxConstructExpr(argumentCountIs(0))))))));
218 auto HasBracedInitListExpr =
219 anyOf(has(cxxBindTemporaryExpr(HasConstructInitListExpr)),
220 HasConstructInitListExpr);
222 auto MakeTuple = ignoringImplicit(
223 callExpr(callee(expr(ignoringImplicit(declRefExpr(
224 unless(hasExplicitTemplateArgs()),
225 to(functionDecl(hasAnyName(TupleMakeFunctions))))))))
230 auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
231 has(materializeTemporaryExpr(MakeTuple)),
232 hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(TupleTypes))))));
235 materializeTemporaryExpr(
236 anyOf(has(MakeTuple), has(MakeTupleCtor), HasConstructExpr,
237 HasBracedInitListExpr,
238 has(cxxFunctionalCastExpr(HasConstructExpr)),
239 has(cxxFunctionalCastExpr(HasBracedInitListExpr))))
240 .bind(
"temporary_expr");
242 auto HasConstructExprWithValueTypeType =
243 has(ignoringImplicit(cxxConstructExpr(
245 hasType(hasCanonicalType(type(equalsBoundNode(
"value_type")))))));
247 auto HasBracedInitListWithValueTypeType = anyOf(
248 allOf(HasConstructInitListExpr,
249 has(initListExpr(hasType(
250 hasCanonicalType(type(equalsBoundNode(
"value_type"))))))),
251 has(cxxBindTemporaryExpr(HasConstructInitListExpr,
252 has(initListExpr(hasType(hasCanonicalType(
253 type(equalsBoundNode(
"value_type")))))))));
255 auto HasConstructExprWithValueTypeTypeAsLastArgument = hasLastArgument(
256 materializeTemporaryExpr(
257 anyOf(HasConstructExprWithValueTypeType,
258 HasBracedInitListWithValueTypeType,
259 has(cxxFunctionalCastExpr(HasConstructExprWithValueTypeType)),
260 has(cxxFunctionalCastExpr(HasBracedInitListWithValueTypeType))))
261 .bind(
"temporary_expr"));
264 traverse(TK_AsIs, cxxMemberCallExpr(CallPushBack, has(SoughtParam),
265 unless(isInTemplateInstantiation()))
266 .bind(
"push_back_call")),
270 traverse(TK_AsIs, cxxMemberCallExpr(CallPush, has(SoughtParam),
271 unless(isInTemplateInstantiation()))
276 traverse(TK_AsIs, cxxMemberCallExpr(CallPushFront, has(SoughtParam),
277 unless(isInTemplateInstantiation()))
278 .bind(
"push_front_call")),
284 CallEmplacy, HasConstructExprWithValueTypeTypeAsLastArgument,
285 hasSameNumArgsAsDeclNumParams(),
286 unless(isInTemplateInstantiation()))
287 .bind(
"emplacy_call")),
294 on(hasType(cxxRecordDecl(has(typedefNameDecl(
295 hasName(
"value_type"),
296 hasType(hasCanonicalType(recordType(hasDeclaration(
297 cxxRecordDecl(hasAnyName(SmallVector<StringRef, 2>(
298 TupleTypes.begin(), TupleTypes.end())))))))))))),
299 has(MakeTuple), hasSameNumArgsAsDeclNumParams(),
300 unless(isInTemplateInstantiation()))
301 .bind(
"emplacy_call")),
306 const auto *PushBackCall =
307 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_back_call");
308 const auto *PushCall = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_call");
309 const auto *PushFrontCall =
310 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"push_front_call");
311 const auto *EmplacyCall =
312 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"emplacy_call");
313 const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>(
"ctor");
314 const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>(
"make");
315 const auto *TemporaryExpr =
316 Result.Nodes.getNodeAs<MaterializeTemporaryExpr>(
"temporary_expr");
318 const CXXMemberCallExpr *Call = [&]() {
326 return PushFrontCall;
331 assert(Call &&
"No call matched");
332 assert((CtorCall || MakeCall) &&
"No push_back parameter matched");
334 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs() >= 1 &&
335 CtorCall->getArg(0)->getSourceRange() == CtorCall->getSourceRange())
338 const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
339 Call->getExprLoc(), Call->getArg(0)->getExprLoc());
343 ? diag(TemporaryExpr ? TemporaryExpr->getBeginLoc()
344 : CtorCall ? CtorCall->getBeginLoc()
345 : MakeCall->getBeginLoc(),
346 "unnecessary temporary object created while calling %0")
347 : diag(Call->getExprLoc(),
"use emplace%select{|_back|_front}0 "
348 "instead of push%select{|_back|_front}0");
350 Diag << Call->getMethodDecl()->getName();
353 else if (PushBackCall)
358 if (FunctionNameSourceRange.getBegin().isMacroID())
362 const char *EmplacePrefix = MakeCall ?
"emplace_back" :
"emplace_back(";
363 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
365 }
else if (PushCall) {
366 const char *EmplacePrefix = MakeCall ?
"emplace" :
"emplace(";
367 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
369 }
else if (PushFrontCall) {
370 const char *EmplacePrefix = MakeCall ?
"emplace_front" :
"emplace_front(";
371 Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
375 const SourceRange CallParensRange =
376 MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(),
377 MakeCall->getRParenLoc())
378 : CtorCall->getParenOrBraceRange();
381 if (CallParensRange.getBegin().isInvalid())
385 const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc()
386 : CtorCall ? CtorCall->getExprLoc()
387 : MakeCall->getExprLoc();
390 const auto ParamCallSourceRange =
391 CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
394 const auto EndCallSourceRange = CharSourceRange::getTokenRange(
395 CallParensRange.getEnd(),
396 TemporaryExpr ? TemporaryExpr->getEndLoc() : CallParensRange.getEnd());
398 Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
399 << FixItHint::CreateRemoval(EndCallSourceRange);
401 if (MakeCall && EmplacyCall) {
403 Diag << FixItHint::CreateRemoval(
404 CharSourceRange::getCharRange(MakeCall->getCallee()->getEndLoc(),
405 MakeCall->getArg(0)->getBeginLoc()));