@@ -112,7 +112,7 @@ use rustc_hir::{
112
112
use rustc_lexer:: { TokenKind , tokenize} ;
113
113
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
114
114
use rustc_middle:: hir:: place:: PlaceBase ;
115
- use rustc_middle:: mir:: Const ;
115
+ use rustc_middle:: mir:: { AggregateKind , Const , Operand , RETURN_PLACE , Rvalue , StatementKind , TerminatorKind } ;
116
116
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
117
117
use rustc_middle:: ty:: fast_reject:: SimplifiedType ;
118
118
use rustc_middle:: ty:: layout:: IntegerExt ;
@@ -897,22 +897,102 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
897
897
}
898
898
899
899
/// 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 {
901
905
if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind
902
906
&& let Some ( repl_def_id) = cx. qpath_res ( repl_func_qpath, repl_func. hir_id ) . opt_def_id ( )
903
907
&& ( is_diag_trait_item ( cx, repl_def_id, sym:: Default )
904
908
|| is_default_equivalent_ctor ( cx, repl_def_id, repl_func_qpath) )
905
909
{
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
+ }
909
989
}
990
+ false
910
991
}
911
992
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.
913
994
///
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.
916
996
pub fn is_default_equivalent ( cx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> bool {
917
997
match & e. kind {
918
998
ExprKind :: Lit ( lit) => match lit. node {
@@ -933,7 +1013,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
933
1013
false
934
1014
}
935
1015
} ,
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 ) ) ,
937
1017
ExprKind :: Call ( from_func, [ arg] ) => is_default_equivalent_from ( cx, from_func, arg) ,
938
1018
ExprKind :: Path ( qpath) => is_res_lang_ctor ( cx, cx. qpath_res ( qpath, e. hir_id ) , OptionNone ) ,
939
1019
ExprKind :: AddrOf ( rustc_hir:: BorrowKind :: Ref , _, expr) => matches ! ( expr. kind, ExprKind :: Array ( [ ] ) ) ,
0 commit comments