@@ -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,34 @@ void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
305320 this );
306321}
307322
323+ static const CXXConstructExpr *unwrapToConstructorExpr (const Expr *E) {
324+ while (E) {
325+ if (const auto *ConstructorExpr = llvm::dyn_cast<CXXConstructExpr>(E)) {
326+ return ConstructorExpr;
327+ }
328+
329+ if (const auto *BindTemp = llvm::dyn_cast<CXXBindTemporaryExpr>(E)) {
330+ E = BindTemp->getSubExpr ();
331+ continue ;
332+ }
333+
334+ if (const auto *MaterialTemp =
335+ llvm::dyn_cast<MaterializeTemporaryExpr>(E)) {
336+ E = MaterialTemp->getSubExpr ();
337+ continue ;
338+ }
339+
340+ if (const auto *Cast = llvm::dyn_cast<ImplicitCastExpr>(E)) {
341+ E = Cast->getSubExpr ();
342+ continue ;
343+ }
344+
345+ break ;
346+ }
347+
348+ return nullptr ; // No relevant sub-expression found
349+ }
350+
308351void UseEmplaceCheck::check (const MatchFinder::MatchResult &Result) {
309352 const auto *PushBackCall =
310353 Result.Nodes .getNodeAs <CXXMemberCallExpr>(" push_back_call" );
@@ -313,7 +356,8 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
313356 Result.Nodes .getNodeAs <CXXMemberCallExpr>(" push_front_call" );
314357 const auto *EmplacyCall =
315358 Result.Nodes .getNodeAs <CXXMemberCallExpr>(" emplacy_call" );
316- const auto *CtorCall = Result.Nodes .getNodeAs <CXXConstructExpr>(" ctor" );
359+ auto *CtorCall = Result.Nodes .getNodeAs <CXXConstructExpr>(" ctor" );
360+ auto *AggInitCall = Result.Nodes .getNodeAs <InitListExpr>(" agg_init" );
317361 const auto *MakeCall = Result.Nodes .getNodeAs <CallExpr>(" make" );
318362 const auto *TemporaryExpr =
319363 Result.Nodes .getNodeAs <MaterializeTemporaryExpr>(" temporary_expr" );
@@ -332,19 +376,42 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
332376 }();
333377
334378 assert (Call && " No call matched" );
335- assert ((CtorCall || MakeCall) && " No push_back parameter matched" );
379+ assert ((CtorCall || MakeCall || AggInitCall) &&
380+ " No push_back parameter matched" );
336381
337382 if (IgnoreImplicitConstructors && CtorCall && CtorCall->getNumArgs () >= 1 &&
338383 CtorCall->getArg (0 )->getSourceRange () == CtorCall->getSourceRange ())
339384 return ;
340385
386+ if (IgnoreImplicitConstructors && AggInitCall &&
387+ AggInitCall->getNumInits () >= 1 &&
388+ AggInitCall->getInit (0 )->getSourceRange () ==
389+ AggInitCall->getSourceRange ())
390+ return ;
391+
392+ if (getLangOpts ().LangStd >= LangStandard::lang_cxx20 && AggInitCall) {
393+ for (const auto *Init : AggInitCall->inits ()) {
394+ if (const auto *InnerConstructorExpr = unwrapToConstructorExpr (Init)) {
395+ // consume all args if it's an empty constructor call so that we can ->
396+ // T{} -> emplace_back()
397+ if (InnerConstructorExpr && InnerConstructorExpr->getNumArgs () == 0 ) {
398+
399+ CtorCall = InnerConstructorExpr;
400+ AggInitCall = nullptr ;
401+ break ;
402+ }
403+ }
404+ }
405+ }
406+
341407 const auto FunctionNameSourceRange = CharSourceRange::getCharRange (
342408 Call->getExprLoc (), Call->getArg (0 )->getExprLoc ());
343409
344410 auto Diag =
345411 EmplacyCall
346412 ? diag (TemporaryExpr ? TemporaryExpr->getBeginLoc ()
347413 : CtorCall ? CtorCall->getBeginLoc ()
414+ : AggInitCall ? AggInitCall->getBeginLoc ()
348415 : MakeCall->getBeginLoc (),
349416 " unnecessary temporary object created while calling %0" )
350417 : diag (Call->getExprLoc (), " use emplace%select{|_back|_front}0 "
@@ -376,9 +443,10 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
376443 }
377444
378445 const SourceRange CallParensRange =
379- MakeCall ? SourceRange (MakeCall->getCallee ()->getEndLoc (),
380- MakeCall->getRParenLoc ())
381- : CtorCall->getParenOrBraceRange ();
446+ MakeCall ? SourceRange (MakeCall->getCallee ()->getEndLoc (),
447+ MakeCall->getRParenLoc ())
448+ : CtorCall ? CtorCall->getParenOrBraceRange ()
449+ : AggInitCall->getSourceRange ();
382450
383451 // Finish if there is no explicit constructor call.
384452 if (CallParensRange.getBegin ().isInvalid ())
@@ -387,6 +455,7 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
387455 // FIXME: Will there ever be a CtorCall, if there is no TemporaryExpr?
388456 const SourceLocation ExprBegin = TemporaryExpr ? TemporaryExpr->getExprLoc ()
389457 : CtorCall ? CtorCall->getExprLoc ()
458+ : AggInitCall ? AggInitCall->getExprLoc ()
390459 : MakeCall->getExprLoc ();
391460
392461 // Range for constructor name and opening brace.
0 commit comments