@@ -113,6 +113,7 @@ use rustc_hir::{
113
113
use rustc_lexer:: { TokenKind , tokenize} ;
114
114
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
115
115
use rustc_middle:: hir:: place:: PlaceBase ;
116
+ use rustc_middle:: mir:: { AggregateKind , Operand , RETURN_PLACE , Rvalue , StatementKind , TerminatorKind } ;
116
117
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
117
118
use rustc_middle:: ty:: fast_reject:: SimplifiedType ;
118
119
use rustc_middle:: ty:: layout:: IntegerExt ;
@@ -919,22 +920,101 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
919
920
}
920
921
921
922
/// 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 {
923
928
if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind
924
929
&& let Some ( repl_def_id) = cx. qpath_res ( repl_func_qpath, repl_func. hir_id ) . opt_def_id ( )
925
930
&& ( is_diag_trait_item ( cx, repl_def_id, sym:: Default )
926
931
|| is_default_equivalent_ctor ( cx, repl_def_id, repl_func_qpath) )
927
932
{
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
+ }
931
1011
}
1012
+ false
932
1013
}
933
1014
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.
935
1016
///
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.
938
1018
pub fn is_default_equivalent ( cx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> bool {
939
1019
match & e. kind {
940
1020
ExprKind :: Lit ( lit) => match lit. node {
@@ -955,7 +1035,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
955
1035
false
956
1036
}
957
1037
} ,
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 ) ) ,
959
1039
ExprKind :: Call ( from_func, [ arg] ) => is_default_equivalent_from ( cx, from_func, arg) ,
960
1040
ExprKind :: Path ( qpath) => is_res_lang_ctor ( cx, cx. qpath_res ( qpath, e. hir_id ) , OptionNone ) ,
961
1041
ExprKind :: AddrOf ( rustc_hir:: BorrowKind :: Ref , _, expr) => matches ! ( expr. kind, ExprKind :: Array ( [ ] ) ) ,
0 commit comments