1
Fork 0

Rollup merge of #116841 - chenyukang:yukang-suggest-unwrap-expect, r=b-naber

Suggest unwrap/expect for let binding type mismatch

Found it when investigating https://github.com/rust-lang/rust/issues/116738
I'm not sure whether it's a good style to suggest `unwrap`, seems it's may helpful for newcomers.

#116738 needs another fix to improve it.
This commit is contained in:
Matthias Krüger 2023-10-24 19:29:55 +02:00 committed by GitHub
commit 7a0a2d2d23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 327 additions and 1 deletions

View file

@ -58,7 +58,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_into(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected)
|| self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
|| self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty)
|| self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty);
if !suggested {
self.note_source_of_type_mismatch_constraint(

View file

@ -6,6 +6,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{
@ -1738,4 +1739,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the field is hygienic it must come from the same syntax context.
&& self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
}
pub(crate) fn suggest_missing_unwrap_expect(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) -> bool {
let ty::Adt(adt, args) = found.kind() else { return false };
let ret_ty_matches = |diagnostic_item| {
if let Some(ret_ty) = self
.ret_coercion
.as_ref()
.map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
&& let ty::Adt(kind, _) = ret_ty.kind()
&& self.tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
{
true
} else {
false
}
};
// don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`,
// `None.unwrap()` etc.
let is_ctor = matches!(
expr.kind,
hir::ExprKind::Call(
hir::Expr {
kind: hir::ExprKind::Path(hir::QPath::Resolved(
None,
hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
)),
..
},
..,
) | hir::ExprKind::Path(hir::QPath::Resolved(
None,
hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
)),
);
let (article, kind, variant, sugg_operator) =
if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
("a", "Result", "Err", ret_ty_matches(sym::Result))
} else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
("an", "Option", "None", ret_ty_matches(sym::Option))
} else {
return false;
};
if is_ctor || !self.can_coerce(args.type_at(0), expected) {
return false;
}
let (msg, sugg) = if sugg_operator {
(
format!(
"use the `?` operator to extract the `{found}` value, propagating \
{article} `{kind}::{variant}` value to the caller"
),
"?",
)
} else {
(
format!(
"consider using `{kind}::expect` to unwrap the `{found}` value, \
panicking if the value is {article} `{kind}::{variant}`"
),
".expect(\"REASON\")",
)
};
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
msg,
sugg,
Applicability::HasPlaceholders,
);
return true;
}
}