@@ -112,7 +112,7 @@ use rustc_hir::{
112112use rustc_lexer:: { TokenKind , tokenize} ;
113113use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
114114use rustc_middle:: hir:: place:: PlaceBase ;
115- use rustc_middle:: mir:: Const ;
115+ use rustc_middle:: mir:: { AggregateKind , Const , Operand , RETURN_PLACE , Rvalue , StatementKind , TerminatorKind } ;
116116use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
117117use rustc_middle:: ty:: fast_reject:: SimplifiedType ;
118118use rustc_middle:: ty:: layout:: IntegerExt ;
@@ -897,22 +897,102 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
897897}
898898
899899/// Returns true if the expr is equal to `Default::default` when evaluated.
900- pub fn is_default_equivalent_call ( cx : & LateContext < ' _ > , repl_func : & Expr < ' _ > ) -> bool {
900+ pub fn is_default_equivalent_call (
901+ cx : & LateContext < ' _ > ,
902+ repl_func : & Expr < ' _ > ,
903+ whole_call_expr : Option < & Expr < ' _ > > ,
904+ ) -> bool {
901905 if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind
902906 && let Some ( repl_def_id) = cx. qpath_res ( repl_func_qpath, repl_func. hir_id ) . opt_def_id ( )
903907 && ( is_diag_trait_item ( cx, repl_def_id, sym:: Default )
904908 || is_default_equivalent_ctor ( cx, repl_def_id, repl_func_qpath) )
905909 {
906- true
907- } else {
908- false
910+ return true ;
911+ }
912+
913+ // Get the type of the whole method call expression, find the exact method definition, look at
914+ // its body and check if it is similar to the corresponding `Default::default()` body.
915+ let Some ( e) = whole_call_expr else { return false } ;
916+ let Some ( default_fn_def_id) = cx. tcx . get_diagnostic_item ( sym:: default_fn) else {
917+ return false ;
918+ } ;
919+ let Some ( ty) = cx. tcx . typeck ( e. hir_id . owner . def_id ) . expr_ty_adjusted_opt ( e) else {
920+ return false ;
921+ } ;
922+ let args = rustc_ty:: GenericArgs :: for_item ( cx. tcx , default_fn_def_id, |param, _| {
923+ if let rustc_ty:: GenericParamDefKind :: Lifetime = param. kind {
924+ cx. tcx . lifetimes . re_erased . into ( )
925+ } else if param. index == 0 && param. name == kw:: SelfUpper {
926+ ty. into ( )
927+ } else {
928+ param. to_error ( cx. tcx )
929+ }
930+ } ) ;
931+ let instance = rustc_ty:: Instance :: try_resolve ( cx. tcx , cx. typing_env ( ) , default_fn_def_id, args) ;
932+
933+ let Ok ( Some ( instance) ) = instance else { return false } ;
934+ if let rustc_ty:: InstanceKind :: Item ( def) = instance. def
935+ && !cx. tcx . is_mir_available ( def)
936+ {
937+ // Avoid ICE while running rustdoc for not providing `optimized_mir` query.
938+ return false ;
939+ }
940+ let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind else {
941+ return false ;
942+ } ;
943+ let Some ( repl_def_id) = cx. qpath_res ( repl_func_qpath, repl_func. hir_id ) . opt_def_id ( ) else {
944+ return false ;
945+ } ;
946+
947+ // Get the MIR Body for the `<Ty as Default>::default()` function.
948+ // If it is a value or call (either fn or ctor), we compare its `DefId` against the one for the
949+ // resolution of the expression we had in the path. This lets us identify, for example, that
950+ // the body of `<Vec<T> as Default>::default()` is a `Vec::new()`, and the field was being
951+ // initialized to `Vec::new()` as well.
952+ let body = cx. tcx . instance_mir ( instance. def ) ;
953+ for block_data in body. basic_blocks . iter ( ) {
954+ if block_data. statements . len ( ) == 1
955+ && let StatementKind :: Assign ( assign) = & block_data. statements [ 0 ] . kind
956+ && assign. 0 . local == RETURN_PLACE
957+ && let Rvalue :: Aggregate ( kind, _places) = & assign. 1
958+ && let AggregateKind :: Adt ( did, variant_index, _, _, _) = & * * kind
959+ && let def = cx. tcx . adt_def ( did)
960+ && let variant = & def. variant ( * variant_index)
961+ && variant. fields . is_empty ( )
962+ && let Some ( ( _, did) ) = variant. ctor
963+ && did == repl_def_id
964+ {
965+ return true ;
966+ } else if block_data. statements . is_empty ( )
967+ && let Some ( term) = & block_data. terminator
968+ {
969+ match & term. kind {
970+ TerminatorKind :: Call {
971+ func : Operand :: Constant ( c) ,
972+ ..
973+ } if let rustc_ty:: FnDef ( did, _args) = c. ty ( ) . kind ( )
974+ && * did == repl_def_id =>
975+ {
976+ return true ;
977+ } ,
978+ TerminatorKind :: TailCall {
979+ func : Operand :: Constant ( c) ,
980+ ..
981+ } if let rustc_ty:: FnDef ( did, _args) = c. ty ( ) . kind ( )
982+ && * did == repl_def_id =>
983+ {
984+ return true ;
985+ } ,
986+ _ => { } ,
987+ }
988+ }
909989 }
990+ false
910991}
911992
912- /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
993+ /// Returns true if the expr is equal to `Default::default()` of its type when evaluated.
913994///
914- /// It doesn't cover all cases, for example indirect function calls (some of std
915- /// functions are supported) but it is the best we have.
995+ /// It doesn't cover all cases, like struct literals, but it is a close approximation.
916996pub fn is_default_equivalent ( cx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> bool {
917997 match & e. kind {
918998 ExprKind :: Lit ( lit) => match lit. node {
@@ -933,7 +1013,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
9331013 false
9341014 }
9351015 } ,
936- ExprKind :: Call ( repl_func, [ ] ) => is_default_equivalent_call ( cx, repl_func) ,
1016+ ExprKind :: Call ( repl_func, [ ] ) => is_default_equivalent_call ( cx, repl_func, Some ( e ) ) ,
9371017 ExprKind :: Call ( from_func, [ arg] ) => is_default_equivalent_from ( cx, from_func, arg) ,
9381018 ExprKind :: Path ( qpath) => is_res_lang_ctor ( cx, cx. qpath_res ( qpath, e. hir_id ) , OptionNone ) ,
9391019 ExprKind :: AddrOf ( rustc_hir:: BorrowKind :: Ref , _, expr) => matches ! ( expr. kind, ExprKind :: Array ( [ ] ) ) ,
0 commit comments