@@ -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