@@ -98,8 +98,8 @@ auto hasWantedType(llvm::ArrayRef<StringRef> TypeNames) {
9898
9999// Matches member call expressions of the named method on the listed container
100100// types.
101- auto cxxMemberCallExprOnContainer (
102- StringRef MethodName, llvm::ArrayRef<StringRef> ContainerNames) {
101+ auto cxxMemberCallExprOnContainer (StringRef MethodName,
102+ llvm::ArrayRef<StringRef> ContainerNames) {
103103 return cxxMemberCallExpr (
104104 hasDeclaration (functionDecl (hasName (MethodName))),
105105 on (hasTypeOrPointeeType (hasWantedType (ContainerNames))));
@@ -174,19 +174,19 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
174174 // passed pointer because smart pointer won't be constructed
175175 // (and destructed) as in push_back case.
176176 auto IsCtorOfSmartPtr =
177- hasDeclaration ( cxxConstructorDecl (ofClass (hasAnyName (SmartPointers) )));
177+ cxxConstructorDecl (ofClass (hasAnyName (SmartPointers)));
178178
179179 // Bitfields binds only to consts and emplace_back take it by universal ref.
180- auto BitFieldAsArgument = hasAnyArgument (
181- ignoringImplicit (memberExpr (hasDeclaration (fieldDecl (isBitField ()))))) ;
180+ auto BitFieldAsArgument =
181+ ignoringImplicit (memberExpr (hasDeclaration (fieldDecl (isBitField ()))));
182182
183183 // Initializer list can't be passed to universal reference.
184- auto InitializerListAsArgument = hasAnyArgument (
184+ auto InitializerListAsArgument =
185185 ignoringImplicit (allOf (cxxConstructExpr (isListInitialization ()),
186- unless (cxxTemporaryObjectExpr ())))) ;
186+ unless (cxxTemporaryObjectExpr ())));
187187
188188 // We could have leak of resource.
189- auto NewExprAsArgument = hasAnyArgument ( ignoringImplicit (cxxNewExpr () ));
189+ auto NewExprAsArgument = ignoringImplicit (cxxNewExpr ());
190190 // We would call another constructor.
191191 auto ConstructingDerived =
192192 hasParent (implicitCastExpr (hasCastKind (CastKind::CK_DerivedToBase)));
@@ -202,11 +202,26 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
202202 // overloaded functions and template names.
203203 auto SoughtConstructExpr =
204204 cxxConstructExpr (
205- unless (anyOf (IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
206- InitializerListAsArgument, NewExprAsArgument,
207- ConstructingDerived, IsPrivateOrProtectedCtor)))
205+ unless (anyOf (hasDeclaration (IsCtorOfSmartPtr), HasInitList,
206+ hasAnyArgument (BitFieldAsArgument),
207+ hasAnyArgument (InitializerListAsArgument),
208+ hasAnyArgument (NewExprAsArgument), ConstructingDerived,
209+ IsPrivateOrProtectedCtor)))
208210 .bind (" ctor" );
209- auto HasConstructExpr = has (ignoringImplicit (SoughtConstructExpr));
211+
212+ auto IsPrimitiveType = hasType (builtinType ());
213+
214+ auto AggregateInitExpr =
215+ getLangOpts ().CPlusPlus20
216+ ? initListExpr (unless (anyOf (HasInitList, has (IsCtorOfSmartPtr),
217+ has (BitFieldAsArgument),
218+ has (InitializerListAsArgument),
219+ has (NewExprAsArgument), IsPrimitiveType)))
220+ .bind (" agg_init" )
221+ : unless (anything ());
222+
223+ auto HasConstructExpr =
224+ has (ignoringImplicit (anyOf (SoughtConstructExpr, AggregateInitExpr)));
210225
211226 // allow for T{} to be replaced, even if no CTOR is declared
212227 auto HasConstructInitListExpr = has (initListExpr (
@@ -305,6 +320,36 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
305320 this );
306321}
307322
323+ static const CXXConstructExpr *unwrapConstructorExpr (const Expr *E) {
324+
325+ while (E) {
326+
327+ if (const auto *ConstructorExpr = llvm::dyn_cast<CXXConstructExpr>(E)) {
328+ return ConstructorExpr;
329+ }
330+
331+ if (const auto *BindTemp = llvm::dyn_cast<CXXBindTemporaryExpr>(E)) {
332+ E = BindTemp->getSubExpr ();
333+ continue ;
334+ }
335+
336+ if (const auto *MaterialTemp =
337+ llvm::dyn_cast<MaterializeTemporaryExpr>(E)) {
338+ E = MaterialTemp->getSubExpr ();
339+ continue ;
340+ }
341+
342+ if (const auto *Cast = llvm::dyn_cast<ImplicitCastExpr>(E)) {
343+ E = Cast->getSubExpr ();
344+ continue ;
345+ }
346+
347+ break ;
348+ }
349+
350+ return nullptr ; // No relevant sub-expression found
351+ }
352+
308353void UseEmplaceCheck::check (const MatchFinder::MatchResult &Result) {
309354 const auto *PushBackCall =
310355 Result.Nodes .getNodeAs <CXXMemberCallExpr>(" push_back_call" );
@@ -313,7 +358,8 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
313358 Result.Nodes .getNodeAs <CXXMemberCallExpr>(" push_front_call" );
314359 const auto *EmplacyCall =
315360 Result.Nodes .getNodeAs <CXXMemberCallExpr>(" emplacy_call" );
316- const auto *CtorCall = Result.Nodes .getNodeAs <CXXConstructExpr>(" ctor" );
361+ auto *CtorCall = Result.Nodes .getNodeAs <CXXConstructExpr>(" ctor" );
362+ auto *AggInitCall = Result.Nodes .getNodeAs <InitListExpr>(" agg_init" );
317363 const auto *MakeCall = Result.Nodes .getNodeAs <CallExpr>(" make" );
318364 const auto *TemporaryExpr =
319365 Result.Nodes .getNodeAs <MaterializeTemporaryExpr>(" temporary_expr" );
@@ -332,19 +378,42 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
332378 }();
333379
334380 assert (Call && " No call matched" );
335- assert ((CtorCall || MakeCall) && " No push_back parameter matched" );
381+ assert ((CtorCall || MakeCall || AggInitCall) &&
382+ " No push_back parameter matched" );
336383
337384 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs () >= 1 &&
338385 CtorCall->getArg (0 )->getSourceRange () == CtorCall->getSourceRange ())
339386 return ;
340387
388+ if (IgnoreImplicitConstructors && AggInitCall &&
389+ AggInitCall->getNumInits () >= 1 &&
390+ AggInitCall->getInit (0 )->getSourceRange () ==
391+ AggInitCall->getSourceRange ())
392+ return ;
393+
394+ if (getLangOpts ().LangStd >= LangStandard::lang_cxx20 && AggInitCall) {
395+ for (const auto *Init : AggInitCall->inits ()) {
396+ if (const auto *InnerConstructorExpr = unwrapConstructorExpr (Init)) {
397+ // consume all args if it's an empty constructor call so that we can ->
398+ // T{} -> emplace_back()
399+ if (InnerConstructorExpr && InnerConstructorExpr->getNumArgs () == 0 ) {
400+
401+ CtorCall = InnerConstructorExpr;
402+ AggInitCall = nullptr ;
403+ break ;
404+ }
405+ }
406+ }
407+ }
408+
341409 const auto FunctionNameSourceRange = CharSourceRange::getCharRange (
342410 Call->getExprLoc (), Call->getArg (0 )->getExprLoc ());
343411
344412 auto Diag =
345413 EmplacyCall
346414 ? diag (TemporaryExpr ? TemporaryExpr->getBeginLoc ()
347415 : CtorCall ? CtorCall->getBeginLoc ()
416+ : AggInitCall ? AggInitCall->getBeginLoc ()
348417 : MakeCall->getBeginLoc (),
349418 " unnecessary temporary object created while calling %0" )
350419 : diag (Call->getExprLoc (), " use emplace%select{|_back|_front}0 "
@@ -376,9 +445,10 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
376445 }
377446
378447 const SourceRange CallParensRange =
379- MakeCall ? SourceRange (MakeCall->getCallee ()->getEndLoc (),
380- MakeCall->getRParenLoc ())
381- : CtorCall->getParenOrBraceRange ();
448+ MakeCall ? SourceRange (MakeCall->getCallee ()->getEndLoc (),
449+ MakeCall->getRParenLoc ())
450+ : CtorCall ? CtorCall->getParenOrBraceRange ()
451+ : AggInitCall->getSourceRange ();
382452
383453 // Finish if there is no explicit constructor call.
384454 if (CallParensRange.getBegin ().isInvalid ())
@@ -387,6 +457,7 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
387457 // FIXME: Will there ever be a CtorCall, if there is no TemporaryExpr?
388458 const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc ()
389459 : CtorCall ? CtorCall->getExprLoc ()
460+ : AggInitCall ? AggInitCall->getExprLoc ()
390461 : MakeCall->getExprLoc ();
391462
392463 // Range for constructor name and opening brace.
0 commit comments