@@ -113,6 +113,7 @@ use rustc_hir::{
113113use rustc_lexer:: { TokenKind , tokenize} ;
114114use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
115115use rustc_middle:: hir:: place:: PlaceBase ;
116+ use rustc_middle:: mir:: { AggregateKind , Operand , RETURN_PLACE , Rvalue , StatementKind , TerminatorKind } ;
116117use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
117118use rustc_middle:: ty:: fast_reject:: SimplifiedType ;
118119use rustc_middle:: ty:: layout:: IntegerExt ;
@@ -919,22 +920,101 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
919920}
920921
921922/// Returns true if the expr is equal to `Default::default` when evaluated.
922- pub fn is_default_equivalent_call ( cx : & LateContext < ' _ > , repl_func : & Expr < ' _ > ) -> bool {
923+ pub fn is_default_equivalent_call (
924+ cx : & LateContext < ' _ > ,
925+ repl_func : & Expr < ' _ > ,
926+ whole_call_expr : Option < & Expr < ' _ > > ,
927+ ) -> bool {
923928 if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind
924929 && let Some ( repl_def_id) = cx. qpath_res ( repl_func_qpath, repl_func. hir_id ) . opt_def_id ( )
925930 && ( is_diag_trait_item ( cx, repl_def_id, sym:: Default )
926931 || is_default_equivalent_ctor ( cx, repl_def_id, repl_func_qpath) )
927932 {
928- true
929- } else {
930- false
933+ return true ;
934+ }
935+
936+ // Get the type of the whole method call expression, find the exact method definition, look at
937+ // its body and check if it is similar to the corresponding `Default::default()` body.
938+ let Some ( e) = whole_call_expr else { return false } ;
939+ let Some ( default_fn_def_id) = cx. tcx . get_diagnostic_item ( sym:: default_fn) else {
940+ return false ;
941+ } ;
942+ let Some ( ty) = cx. tcx . typeck ( e. hir_id . owner . def_id ) . expr_ty_adjusted_opt ( e) else {
943+ return false ;
944+ } ;
945+ let args = rustc_ty:: GenericArgs :: for_item ( cx. tcx , default_fn_def_id, |param, _| {
946+ if let rustc_ty:: GenericParamDefKind :: Lifetime = param. kind {
947+ cx. tcx . lifetimes . re_erased . into ( )
948+ } else if param. index == 0 && param. name == kw:: SelfUpper {
949+ ty. into ( )
950+ } else {
951+ param. to_error ( cx. tcx )
952+ }
953+ } ) ;
954+ let instance = rustc_ty:: Instance :: try_resolve ( cx. tcx , cx. typing_env ( ) , default_fn_def_id, args) ;
955+
956+ let Ok ( Some ( instance) ) = instance else { return false } ;
957+ if let rustc_ty:: InstanceKind :: Item ( def) = instance. def
958+ && !cx. tcx . is_mir_available ( def)
959+ {
960+ return false ;
961+ }
962+ let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind else {
963+ return false ;
964+ } ;
965+ let Some ( repl_def_id) = cx. qpath_res ( repl_func_qpath, repl_func. hir_id ) . opt_def_id ( ) else {
966+ return false ;
967+ } ;
968+
969+ // Get the MIR Body for the `<Ty as Default>::default()` function.
970+ // If it is a value or call (either fn or ctor), we compare its `DefId` against the one for the
971+ // resolution of the expression we had in the path. This lets us identify, for example, that
972+ // the body of `<Vec<T> as Default>::default()` is a `Vec::new()`, and the field was being
973+ // initialized to `Vec::new()` as well.
974+ let body = cx. tcx . instance_mir ( instance. def ) ;
975+ for block_data in body. basic_blocks . iter ( ) {
976+ if block_data. statements . len ( ) == 1
977+ && let StatementKind :: Assign ( assign) = & block_data. statements [ 0 ] . kind
978+ && assign. 0 . local == RETURN_PLACE
979+ && let Rvalue :: Aggregate ( kind, _places) = & assign. 1
980+ && let AggregateKind :: Adt ( did, variant_index, _, _, _) = & * * kind
981+ && let def = cx. tcx . adt_def ( did)
982+ && let variant = & def. variant ( * variant_index)
983+ && variant. fields . is_empty ( )
984+ && let Some ( ( _, did) ) = variant. ctor
985+ && did == repl_def_id
986+ {
987+ return true ;
988+ } else if block_data. statements . is_empty ( )
989+ && let Some ( term) = & block_data. terminator
990+ {
991+ match & term. kind {
992+ TerminatorKind :: Call {
993+ func : Operand :: Constant ( c) ,
994+ ..
995+ } if let rustc_ty:: FnDef ( did, _args) = c. ty ( ) . kind ( )
996+ && * did == repl_def_id =>
997+ {
998+ return true ;
999+ } ,
1000+ TerminatorKind :: TailCall {
1001+ func : Operand :: Constant ( c) ,
1002+ ..
1003+ } if let rustc_ty:: FnDef ( did, _args) = c. ty ( ) . kind ( )
1004+ && * did == repl_def_id =>
1005+ {
1006+ return true ;
1007+ } ,
1008+ _ => { } ,
1009+ }
1010+ }
9311011 }
1012+ false
9321013}
9331014
934- /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
1015+ /// Returns true if the expr is equal to `Default::default()` of its type when evaluated.
9351016///
936- /// It doesn't cover all cases, for example indirect function calls (some of std
937- /// functions are supported) but it is the best we have.
1017+ /// It doesn't cover all cases, like struct literals, but it is a close approximation.
9381018pub fn is_default_equivalent ( cx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> bool {
9391019 match & e. kind {
9401020 ExprKind :: Lit ( lit) => match lit. node {
@@ -955,7 +1035,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
9551035 false
9561036 }
9571037 } ,
958- ExprKind :: Call ( repl_func, [ ] ) => is_default_equivalent_call ( cx, repl_func) ,
1038+ ExprKind :: Call ( repl_func, [ ] ) => is_default_equivalent_call ( cx, repl_func, Some ( e ) ) ,
9591039 ExprKind :: Call ( from_func, [ arg] ) => is_default_equivalent_from ( cx, from_func, arg) ,
9601040 ExprKind :: Path ( qpath) => is_res_lang_ctor ( cx, cx. qpath_res ( qpath, e. hir_id ) , OptionNone ) ,
9611041 ExprKind :: AddrOf ( rustc_hir:: BorrowKind :: Ref , _, expr) => matches ! ( expr. kind, ExprKind :: Array ( [ ] ) ) ,
0 commit comments