Move some suggestions from error_reporting to error_reporting::suggest
This commit is contained in:
parent
25a6daccab
commit
3b9daac6a2
5 changed files with 1305 additions and 661 deletions
|
@ -56,22 +56,17 @@ use crate::infer::ExpectedFound;
|
||||||
use crate::traits::error_reporting::report_object_safety_error;
|
use crate::traits::error_reporting::report_object_safety_error;
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||||
StatementAsExpression,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::errors::SuggAddLetForLetChains;
|
|
||||||
use hir::intravisit::{walk_expr, walk_stmt};
|
|
||||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||||
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
|
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
|
||||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
|
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorKind, DefKind};
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::intravisit::Visitor;
|
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_hir::Node;
|
use rustc_hir::Node;
|
||||||
use rustc_middle::dep_graph::DepContext;
|
use rustc_middle::dep_graph::DepContext;
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
|
||||||
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
|
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
|
self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
|
||||||
|
@ -83,6 +78,7 @@ use std::ops::{ControlFlow, Deref};
|
||||||
use std::{cmp, fmt, iter};
|
use std::{cmp, fmt, iter};
|
||||||
|
|
||||||
mod note;
|
mod note;
|
||||||
|
mod suggest;
|
||||||
|
|
||||||
pub(crate) mod need_type_info;
|
pub(crate) mod need_type_info;
|
||||||
pub use need_type_info::TypeAnnotationNeeded;
|
pub use need_type_info::TypeAnnotationNeeded;
|
||||||
|
@ -806,87 +802,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_remove_semi_or_return_binding(
|
|
||||||
&self,
|
|
||||||
err: &mut Diagnostic,
|
|
||||||
first_id: Option<hir::HirId>,
|
|
||||||
first_ty: Ty<'tcx>,
|
|
||||||
first_span: Span,
|
|
||||||
second_id: Option<hir::HirId>,
|
|
||||||
second_ty: Ty<'tcx>,
|
|
||||||
second_span: Span,
|
|
||||||
) {
|
|
||||||
let remove_semicolon = [
|
|
||||||
(first_id, self.resolve_vars_if_possible(second_ty)),
|
|
||||||
(second_id, self.resolve_vars_if_possible(first_ty)),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.find_map(|(id, ty)| {
|
|
||||||
let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
|
|
||||||
self.could_remove_semicolon(blk, ty)
|
|
||||||
});
|
|
||||||
match remove_semicolon {
|
|
||||||
Some((sp, StatementAsExpression::NeedsBoxing)) => {
|
|
||||||
err.multipart_suggestion(
|
|
||||||
"consider removing this semicolon and boxing the expressions",
|
|
||||||
vec![
|
|
||||||
(first_span.shrink_to_lo(), "Box::new(".to_string()),
|
|
||||||
(first_span.shrink_to_hi(), ")".to_string()),
|
|
||||||
(second_span.shrink_to_lo(), "Box::new(".to_string()),
|
|
||||||
(second_span.shrink_to_hi(), ")".to_string()),
|
|
||||||
(sp, String::new()),
|
|
||||||
],
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some((sp, StatementAsExpression::CorrectType)) => {
|
|
||||||
err.span_suggestion_short(
|
|
||||||
sp,
|
|
||||||
"consider removing this semicolon",
|
|
||||||
"",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
|
|
||||||
if let Some(id) = id
|
|
||||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(id)
|
|
||||||
&& self.consider_returning_binding(blk, ty, err)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suggest_boxing_for_return_impl_trait(
|
|
||||||
&self,
|
|
||||||
err: &mut Diagnostic,
|
|
||||||
return_sp: Span,
|
|
||||||
arm_spans: impl Iterator<Item = Span>,
|
|
||||||
) {
|
|
||||||
err.multipart_suggestion(
|
|
||||||
"you could change the return type to be a boxed trait object",
|
|
||||||
vec![
|
|
||||||
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
|
|
||||||
(return_sp.shrink_to_hi(), ">".to_string()),
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
let sugg = arm_spans
|
|
||||||
.flat_map(|sp| {
|
|
||||||
[(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
|
|
||||||
.into_iter()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
err.multipart_suggestion(
|
|
||||||
"if you change the return type to expect trait objects, box the returned expressions",
|
|
||||||
sugg,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
|
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
|
||||||
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
|
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
|
||||||
/// populate `other_value` with `other_ty`.
|
/// populate `other_value` with `other_ty`.
|
||||||
|
@ -1940,310 +1855,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
debug!(?diag);
|
debug!(?diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_tuple_pattern(
|
|
||||||
&self,
|
|
||||||
cause: &ObligationCause<'tcx>,
|
|
||||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
|
||||||
diag: &mut Diagnostic,
|
|
||||||
) {
|
|
||||||
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
|
||||||
// some modifications due to that being in typeck and this being in infer.
|
|
||||||
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
|
||||||
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
|
|
||||||
let compatible_variants: Vec<_> = expected_adt
|
|
||||||
.variants()
|
|
||||||
.iter()
|
|
||||||
.filter(|variant| {
|
|
||||||
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
|
|
||||||
})
|
|
||||||
.filter_map(|variant| {
|
|
||||||
let sole_field = &variant.fields[0];
|
|
||||||
let sole_field_ty = sole_field.ty(self.tcx, substs);
|
|
||||||
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
|
||||||
let variant_path =
|
|
||||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
|
||||||
// FIXME #56861: DRYer prelude filtering
|
|
||||||
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
|
||||||
if let Some((_, path)) = path.split_once("::") {
|
|
||||||
return Some(path.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(variant_path)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
match &compatible_variants[..] {
|
|
||||||
[] => {}
|
|
||||||
[variant] => {
|
|
||||||
diag.multipart_suggestion_verbose(
|
|
||||||
&format!("try wrapping the pattern in `{}`", variant),
|
|
||||||
vec![
|
|
||||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
|
||||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// More than one matching variant.
|
|
||||||
diag.multipart_suggestions(
|
|
||||||
&format!(
|
|
||||||
"try wrapping the pattern in a variant of `{}`",
|
|
||||||
self.tcx.def_path_str(expected_adt.did())
|
|
||||||
),
|
|
||||||
compatible_variants.into_iter().map(|variant| {
|
|
||||||
vec![
|
|
||||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
|
||||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A possible error is to forget to add `.await` when using futures:
|
|
||||||
///
|
|
||||||
/// ```compile_fail,E0308
|
|
||||||
/// async fn make_u32() -> u32 {
|
|
||||||
/// 22
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn take_u32(x: u32) {}
|
|
||||||
///
|
|
||||||
/// async fn foo() {
|
|
||||||
/// let x = make_u32();
|
|
||||||
/// take_u32(x);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
|
||||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
|
||||||
/// `.await` to the tail of the expression.
|
|
||||||
fn suggest_await_on_expect_found(
|
|
||||||
&self,
|
|
||||||
cause: &ObligationCause<'tcx>,
|
|
||||||
exp_span: Span,
|
|
||||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
|
||||||
diag: &mut Diagnostic,
|
|
||||||
) {
|
|
||||||
debug!(
|
|
||||||
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
|
|
||||||
exp_span, exp_found.expected, exp_found.found,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match (
|
|
||||||
self.get_impl_future_output_ty(exp_found.expected),
|
|
||||||
self.get_impl_future_output_ty(exp_found.found),
|
|
||||||
) {
|
|
||||||
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
|
||||||
.code()
|
|
||||||
{
|
|
||||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
|
||||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
|
||||||
diag.multipart_suggestion(
|
|
||||||
"consider `await`ing on both `Future`s",
|
|
||||||
vec![
|
|
||||||
(then_span.shrink_to_hi(), ".await".to_string()),
|
|
||||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
|
||||||
prior_arms,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if let [.., arm_span] = &prior_arms[..] {
|
|
||||||
diag.multipart_suggestion(
|
|
||||||
"consider `await`ing on both `Future`s",
|
|
||||||
vec![
|
|
||||||
(arm_span.shrink_to_hi(), ".await".to_string()),
|
|
||||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
diag.help("consider `await`ing on both `Future`s");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
diag.help("consider `await`ing on both `Future`s");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
exp_span.shrink_to_hi(),
|
|
||||||
"consider `await`ing on the `Future`",
|
|
||||||
".await",
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
|
||||||
{
|
|
||||||
ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
then_span.shrink_to_hi(),
|
|
||||||
"consider `await`ing on the `Future`",
|
|
||||||
".await",
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
|
||||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
then_span.shrink_to_hi(),
|
|
||||||
"consider `await`ing on the `Future`",
|
|
||||||
".await",
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
|
||||||
ref prior_arms,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
diag.multipart_suggestion_verbose(
|
|
||||||
"consider `await`ing on the `Future`",
|
|
||||||
prior_arms
|
|
||||||
.iter()
|
|
||||||
.map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
|
|
||||||
.collect(),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suggest_accessing_field_where_appropriate(
|
|
||||||
&self,
|
|
||||||
cause: &ObligationCause<'tcx>,
|
|
||||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
|
||||||
diag: &mut Diagnostic,
|
|
||||||
) {
|
|
||||||
debug!(
|
|
||||||
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
|
|
||||||
cause, exp_found
|
|
||||||
);
|
|
||||||
if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
|
|
||||||
if expected_def.is_enum() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((name, ty)) = expected_def
|
|
||||||
.non_enum_variant()
|
|
||||||
.fields
|
|
||||||
.iter()
|
|
||||||
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
|
|
||||||
.map(|field| (field.name, field.ty(self.tcx, expected_substs)))
|
|
||||||
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
|
|
||||||
{
|
|
||||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
|
||||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
|
||||||
let suggestion = if expected_def.is_struct() {
|
|
||||||
format!("{}.{}", snippet, name)
|
|
||||||
} else if expected_def.is_union() {
|
|
||||||
format!("unsafe {{ {}.{} }}", snippet, name)
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
diag.span_suggestion(
|
|
||||||
span,
|
|
||||||
&format!(
|
|
||||||
"you might have meant to use field `{}` whose type is `{}`",
|
|
||||||
name, ty
|
|
||||||
),
|
|
||||||
suggestion,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
|
|
||||||
/// suggests it.
|
|
||||||
fn suggest_as_ref_where_appropriate(
|
|
||||||
&self,
|
|
||||||
span: Span,
|
|
||||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
|
||||||
diag: &mut Diagnostic,
|
|
||||||
) {
|
|
||||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
|
||||||
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
|
|
||||||
{
|
|
||||||
diag.span_suggestion(
|
|
||||||
span,
|
|
||||||
msg,
|
|
||||||
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
|
||||||
format!("{}.as_ref()", snippet.trim_start_matches('&')),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
|
||||||
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
|
||||||
(expected.kind(), found.kind())
|
|
||||||
{
|
|
||||||
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
|
||||||
if exp_def == &found_def {
|
|
||||||
let have_as_ref = &[
|
|
||||||
(
|
|
||||||
sym::Option,
|
|
||||||
"you can convert from `&Option<T>` to `Option<&T>` using \
|
|
||||||
`.as_ref()`",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
sym::Result,
|
|
||||||
"you can convert from `&Result<T, E>` to \
|
|
||||||
`Result<&T, &E>` using `.as_ref()`",
|
|
||||||
),
|
|
||||||
];
|
|
||||||
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
|
||||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
|
||||||
}) {
|
|
||||||
let mut show_suggestion = true;
|
|
||||||
for (exp_ty, found_ty) in
|
|
||||||
iter::zip(exp_substs.types(), found_substs.types())
|
|
||||||
{
|
|
||||||
match *exp_ty.kind() {
|
|
||||||
ty::Ref(_, exp_ty, _) => {
|
|
||||||
match (exp_ty.kind(), found_ty.kind()) {
|
|
||||||
(_, ty::Param(_))
|
|
||||||
| (_, ty::Infer(_))
|
|
||||||
| (ty::Param(_), _)
|
|
||||||
| (ty::Infer(_), _) => {}
|
|
||||||
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
|
|
||||||
_ => show_suggestion = false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ty::Param(_) | ty::Infer(_) => {}
|
|
||||||
_ => show_suggestion = false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if show_suggestion {
|
|
||||||
return Some(*msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn report_and_explain_type_error(
|
pub fn report_and_explain_type_error(
|
||||||
&self,
|
&self,
|
||||||
trace: TypeTrace<'tcx>,
|
trace: TypeTrace<'tcx>,
|
||||||
|
@ -2357,67 +1968,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
diag
|
diag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to find code with pattern `if Some(..) = expr`
|
|
||||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
|
||||||
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
|
||||||
fn suggest_let_for_letchains(
|
|
||||||
&self,
|
|
||||||
err: &mut Diagnostic,
|
|
||||||
cause: &ObligationCause<'_>,
|
|
||||||
span: Span,
|
|
||||||
) {
|
|
||||||
let hir = self.tcx.hir();
|
|
||||||
let fn_hir_id = hir.get_parent_node(cause.body_id);
|
|
||||||
if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
|
|
||||||
let hir::Node::Item(hir::Item {
|
|
||||||
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
|
|
||||||
}) = node {
|
|
||||||
let body = hir.body(*body_id);
|
|
||||||
|
|
||||||
/// Find the if expression with given span
|
|
||||||
struct IfVisitor {
|
|
||||||
pub result: bool,
|
|
||||||
pub found_if: bool,
|
|
||||||
pub err_span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'v> Visitor<'v> for IfVisitor {
|
|
||||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
|
||||||
if self.result { return; }
|
|
||||||
match ex.kind {
|
|
||||||
hir::ExprKind::If(cond, _, _) => {
|
|
||||||
self.found_if = true;
|
|
||||||
walk_expr(self, cond);
|
|
||||||
self.found_if = false;
|
|
||||||
}
|
|
||||||
_ => walk_expr(self, ex),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
|
||||||
if let hir::StmtKind::Local(hir::Local {
|
|
||||||
span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
|
|
||||||
}) = &ex.kind
|
|
||||||
&& self.found_if
|
|
||||||
&& span.eq(&self.err_span) {
|
|
||||||
self.result = true;
|
|
||||||
}
|
|
||||||
walk_stmt(self, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
|
||||||
hir::intravisit::walk_body(self, body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
|
||||||
visitor.visit_body(&body);
|
|
||||||
if visitor.result {
|
|
||||||
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_tuple_wrap_err(
|
fn emit_tuple_wrap_err(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
|
@ -3228,211 +2778,3 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|
||||||
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
|
|
||||||
/// is enough to fix the error.
|
|
||||||
pub fn could_remove_semicolon(
|
|
||||||
&self,
|
|
||||||
blk: &'tcx hir::Block<'tcx>,
|
|
||||||
expected_ty: Ty<'tcx>,
|
|
||||||
) -> Option<(Span, StatementAsExpression)> {
|
|
||||||
let blk = blk.innermost_block();
|
|
||||||
// Do not suggest if we have a tail expr.
|
|
||||||
if blk.expr.is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let last_stmt = blk.stmts.last()?;
|
|
||||||
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
|
|
||||||
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
|
||||||
_ if last_expr_ty.references_error() => return None,
|
|
||||||
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
|
||||||
StatementAsExpression::CorrectType
|
|
||||||
}
|
|
||||||
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
|
|
||||||
if last_def_id == exp_def_id =>
|
|
||||||
{
|
|
||||||
StatementAsExpression::CorrectType
|
|
||||||
}
|
|
||||||
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
|
|
||||||
debug!(
|
|
||||||
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
|
||||||
last_def_id, last_bounds, exp_def_id, exp_bounds
|
|
||||||
);
|
|
||||||
|
|
||||||
let last_local_id = last_def_id.as_local()?;
|
|
||||||
let exp_local_id = exp_def_id.as_local()?;
|
|
||||||
|
|
||||||
match (
|
|
||||||
&self.tcx.hir().expect_item(last_local_id).kind,
|
|
||||||
&self.tcx.hir().expect_item(exp_local_id).kind,
|
|
||||||
) {
|
|
||||||
(
|
|
||||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
|
||||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
|
||||||
) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
|
|
||||||
match (left, right) {
|
|
||||||
(
|
|
||||||
hir::GenericBound::Trait(tl, ml),
|
|
||||||
hir::GenericBound::Trait(tr, mr),
|
|
||||||
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
|
||||||
&& ml == mr =>
|
|
||||||
{
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(
|
|
||||||
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
|
|
||||||
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
|
|
||||||
) if langl == langr => {
|
|
||||||
// FIXME: consider the bounds!
|
|
||||||
debug!("{:?} {:?}", argsl, argsr);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}) =>
|
|
||||||
{
|
|
||||||
StatementAsExpression::NeedsBoxing
|
|
||||||
}
|
|
||||||
_ => StatementAsExpression::CorrectType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
let span = if last_stmt.span.from_expansion() {
|
|
||||||
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
|
||||||
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
|
||||||
} else {
|
|
||||||
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
|
|
||||||
};
|
|
||||||
Some((span, needs_box))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Suggest returning a local binding with a compatible type if the block
|
|
||||||
/// has no return expression.
|
|
||||||
pub fn consider_returning_binding(
|
|
||||||
&self,
|
|
||||||
blk: &'tcx hir::Block<'tcx>,
|
|
||||||
expected_ty: Ty<'tcx>,
|
|
||||||
err: &mut Diagnostic,
|
|
||||||
) -> bool {
|
|
||||||
let blk = blk.innermost_block();
|
|
||||||
// Do not suggest if we have a tail expr.
|
|
||||||
if blk.expr.is_some() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let mut shadowed = FxIndexSet::default();
|
|
||||||
let mut candidate_idents = vec![];
|
|
||||||
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
|
||||||
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
|
||||||
&& let Some(pat_ty) = self
|
|
||||||
.typeck_results
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
|
|
||||||
{
|
|
||||||
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
|
||||||
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
|
||||||
&& !(pat_ty, expected_ty).references_error()
|
|
||||||
&& shadowed.insert(ident.name)
|
|
||||||
{
|
|
||||||
candidate_idents.push((*ident, pat_ty));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
let hir = self.tcx.hir();
|
|
||||||
for stmt in blk.stmts.iter().rev() {
|
|
||||||
let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
|
|
||||||
local.pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
match hir.find(hir.get_parent_node(blk.hir_id)) {
|
|
||||||
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
|
|
||||||
match hir.find(hir.get_parent_node(*hir_id)) {
|
|
||||||
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
|
|
||||||
pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
Some(
|
|
||||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
|
||||||
| hir::Node::ImplItem(hir::ImplItem {
|
|
||||||
kind: hir::ImplItemKind::Fn(_, body),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| hir::Node::TraitItem(hir::TraitItem {
|
|
||||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| hir::Node::Expr(hir::Expr {
|
|
||||||
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) => {
|
|
||||||
for param in hir.body(*body).params {
|
|
||||||
param.pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(hir::Node::Expr(hir::Expr {
|
|
||||||
kind:
|
|
||||||
hir::ExprKind::If(
|
|
||||||
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
|
||||||
then_block,
|
|
||||||
_,
|
|
||||||
),
|
|
||||||
..
|
|
||||||
})) if then_block.hir_id == *hir_id => {
|
|
||||||
let_.pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match &candidate_idents[..] {
|
|
||||||
[(ident, _ty)] => {
|
|
||||||
let sm = self.tcx.sess.source_map();
|
|
||||||
if let Some(stmt) = blk.stmts.last() {
|
|
||||||
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
|
||||||
let sugg = if sm.is_multiline(blk.span)
|
|
||||||
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
|
||||||
{
|
|
||||||
format!("\n{spacing}{ident}")
|
|
||||||
} else {
|
|
||||||
format!(" {ident}")
|
|
||||||
};
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
stmt_span.shrink_to_hi(),
|
|
||||||
format!("consider returning the local binding `{ident}`"),
|
|
||||||
sugg,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let sugg = if sm.is_multiline(blk.span)
|
|
||||||
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
|
||||||
{
|
|
||||||
format!("\n{spacing} {ident}\n{spacing}")
|
|
||||||
} else {
|
|
||||||
format!(" {ident} ")
|
|
||||||
};
|
|
||||||
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
|
|
||||||
format!("consider returning the local binding `{ident}`"),
|
|
||||||
sugg,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
values if (1..3).contains(&values.len()) => {
|
|
||||||
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
|
||||||
err.span_note(spans, "consider returning one of these bindings");
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
427
compiler/rustc_infer/src/infer/error_reporting/note_region.rs
Normal file
427
compiler/rustc_infer/src/infer/error_reporting/note_region.rs
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
use crate::errors::RegionOriginNote;
|
||||||
|
use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
|
||||||
|
use crate::infer::{self, SubregionOrigin};
|
||||||
|
use rustc_errors::{
|
||||||
|
fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||||
|
};
|
||||||
|
use rustc_middle::traits::ObligationCauseCode;
|
||||||
|
use rustc_middle::ty::error::TypeError;
|
||||||
|
use rustc_middle::ty::{self, Region};
|
||||||
|
|
||||||
|
use super::ObligationCauseAsDiagArg;
|
||||||
|
|
||||||
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
|
||||||
|
match *origin {
|
||||||
|
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
|
||||||
|
span: trace.cause.span,
|
||||||
|
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
|
||||||
|
expected_found: self.values_str(trace.values),
|
||||||
|
}
|
||||||
|
.add_to_diagnostic(err),
|
||||||
|
infer::Reborrow(span) => {
|
||||||
|
RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err)
|
||||||
|
}
|
||||||
|
infer::ReborrowUpvar(span, ref upvar_id) => {
|
||||||
|
let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
|
||||||
|
RegionOriginNote::WithName {
|
||||||
|
span,
|
||||||
|
msg: fluent::infer_reborrow,
|
||||||
|
name: &var_name.to_string(),
|
||||||
|
continues: false,
|
||||||
|
}
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
infer::RelateObjectBound(span) => {
|
||||||
|
RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
infer::DataBorrowed(ty, span) => {
|
||||||
|
RegionOriginNote::WithName {
|
||||||
|
span,
|
||||||
|
msg: fluent::infer_data_borrowed,
|
||||||
|
name: &self.ty_to_string(ty),
|
||||||
|
continues: false,
|
||||||
|
}
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||||
|
RegionOriginNote::WithName {
|
||||||
|
span,
|
||||||
|
msg: fluent::infer_reference_outlives_referent,
|
||||||
|
name: &self.ty_to_string(ty),
|
||||||
|
continues: false,
|
||||||
|
}
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
infer::RelateParamBound(span, ty, opt_span) => {
|
||||||
|
RegionOriginNote::WithName {
|
||||||
|
span,
|
||||||
|
msg: fluent::infer_relate_param_bound,
|
||||||
|
name: &self.ty_to_string(ty),
|
||||||
|
continues: opt_span.is_some(),
|
||||||
|
}
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
if let Some(span) = opt_span {
|
||||||
|
RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
infer::RelateRegionParamBound(span) => {
|
||||||
|
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
infer::CompareImplItemObligation { span, .. } => {
|
||||||
|
RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
infer::CheckAssociatedTypeBounds { ref parent, .. } => {
|
||||||
|
self.note_region_origin(err, &parent);
|
||||||
|
}
|
||||||
|
infer::AscribeUserTypeProvePredicate(span) => {
|
||||||
|
RegionOriginNote::Plain {
|
||||||
|
span,
|
||||||
|
msg: fluent::infer_ascribe_user_type_prove_predicate,
|
||||||
|
}
|
||||||
|
.add_to_diagnostic(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn report_concrete_failure(
|
||||||
|
&self,
|
||||||
|
origin: SubregionOrigin<'tcx>,
|
||||||
|
sub: Region<'tcx>,
|
||||||
|
sup: Region<'tcx>,
|
||||||
|
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||||
|
match origin {
|
||||||
|
infer::Subtype(box trace) => {
|
||||||
|
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
|
||||||
|
let mut err = self.report_and_explain_type_error(trace, terr);
|
||||||
|
match (*sub, *sup) {
|
||||||
|
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
|
||||||
|
(ty::RePlaceholder(_), _) => {
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"",
|
||||||
|
sup,
|
||||||
|
" doesn't meet the lifetime requirements",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(_, ty::RePlaceholder(_)) => {
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"the required lifetime does not necessarily outlive ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
note_and_explain_region(self.tcx, &mut err, "", sup, "...", None);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"...does not necessarily outlive ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::Reborrow(span) => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx.sess,
|
||||||
|
span,
|
||||||
|
E0312,
|
||||||
|
"lifetime of reference outlives lifetime of borrowed content..."
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"...the reference is valid for ",
|
||||||
|
sub,
|
||||||
|
"...",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"...but the borrowed content is only valid for ",
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::ReborrowUpvar(span, ref upvar_id) => {
|
||||||
|
let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx.sess,
|
||||||
|
span,
|
||||||
|
E0313,
|
||||||
|
"lifetime of borrowed pointer outlives lifetime of captured variable `{}`...",
|
||||||
|
var_name
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"...the borrowed pointer is valid for ",
|
||||||
|
sub,
|
||||||
|
"...",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
&format!("...but `{}` is only valid for ", var_name),
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::RelateObjectBound(span) => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx.sess,
|
||||||
|
span,
|
||||||
|
E0476,
|
||||||
|
"lifetime of the source pointer does not outlive lifetime bound of the \
|
||||||
|
object type"
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"object type is valid for ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"source pointer is only valid for ",
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::RelateParamBound(span, ty, opt_span) => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx.sess,
|
||||||
|
span,
|
||||||
|
E0477,
|
||||||
|
"the type `{}` does not fulfill the required lifetime",
|
||||||
|
self.ty_to_string(ty)
|
||||||
|
);
|
||||||
|
match *sub {
|
||||||
|
ty::ReStatic => note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"type must satisfy ",
|
||||||
|
sub,
|
||||||
|
if opt_span.is_some() { " as required by this binding" } else { "" },
|
||||||
|
opt_span,
|
||||||
|
),
|
||||||
|
_ => note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"type must outlive ",
|
||||||
|
sub,
|
||||||
|
if opt_span.is_some() { " as required by this binding" } else { "" },
|
||||||
|
opt_span,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::RelateRegionParamBound(span) => {
|
||||||
|
let mut err =
|
||||||
|
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"lifetime parameter instantiated with ",
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"but lifetime parameter must outlive ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::DataBorrowed(ty, span) => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx.sess,
|
||||||
|
span,
|
||||||
|
E0490,
|
||||||
|
"a value of type `{}` is borrowed for too long",
|
||||||
|
self.ty_to_string(ty)
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"the type is valid for ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"but the borrow lasts for ",
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||||
|
let mut err = struct_span_err!(
|
||||||
|
self.tcx.sess,
|
||||||
|
span,
|
||||||
|
E0491,
|
||||||
|
"in type `{}`, reference has a longer lifetime than the data it references",
|
||||||
|
self.ty_to_string(ty)
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"the pointer is valid for ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"but the referenced data is only valid for ",
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
|
||||||
|
.report_extra_impl_obligation(
|
||||||
|
span,
|
||||||
|
impl_item_def_id,
|
||||||
|
trait_item_def_id,
|
||||||
|
&format!("`{}: {}`", sup, sub),
|
||||||
|
),
|
||||||
|
infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
|
||||||
|
let mut err = self.report_concrete_failure(*parent, sub, sup);
|
||||||
|
|
||||||
|
let trait_item_span = self.tcx.def_span(trait_item_def_id);
|
||||||
|
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
|
||||||
|
err.span_label(
|
||||||
|
trait_item_span,
|
||||||
|
format!("definition of `{}` from trait", item_name),
|
||||||
|
);
|
||||||
|
|
||||||
|
let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
|
||||||
|
let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
|
||||||
|
|
||||||
|
let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
|
||||||
|
impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
|
||||||
|
let clauses: Vec<_> = trait_predicates
|
||||||
|
.predicates
|
||||||
|
.into_iter()
|
||||||
|
.filter(|&(pred, _)| !impl_predicates.contains(pred))
|
||||||
|
.map(|(pred, _)| format!("{}", pred))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !clauses.is_empty() {
|
||||||
|
let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
|
||||||
|
let where_clause_span = generics.tail_span_for_predicate_suggestion();
|
||||||
|
|
||||||
|
let suggestion = format!(
|
||||||
|
"{} {}",
|
||||||
|
generics.add_where_or_trailing_comma(),
|
||||||
|
clauses.join(", "),
|
||||||
|
);
|
||||||
|
err.span_suggestion(
|
||||||
|
where_clause_span,
|
||||||
|
&format!(
|
||||||
|
"try copying {} from the trait",
|
||||||
|
if clauses.len() > 1 { "these clauses" } else { "this clause" }
|
||||||
|
),
|
||||||
|
suggestion,
|
||||||
|
rustc_errors::Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
err
|
||||||
|
}
|
||||||
|
infer::AscribeUserTypeProvePredicate(span) => {
|
||||||
|
let mut err =
|
||||||
|
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"lifetime instantiated with ",
|
||||||
|
sup,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
&mut err,
|
||||||
|
"but lifetime must outlive ",
|
||||||
|
sub,
|
||||||
|
"",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn report_placeholder_failure(
|
||||||
|
&self,
|
||||||
|
placeholder_origin: SubregionOrigin<'tcx>,
|
||||||
|
sub: Region<'tcx>,
|
||||||
|
sup: Region<'tcx>,
|
||||||
|
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||||
|
// I can't think how to do better than this right now. -nikomatsakis
|
||||||
|
debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
|
||||||
|
match placeholder_origin {
|
||||||
|
infer::Subtype(box ref trace)
|
||||||
|
if matches!(
|
||||||
|
&trace.cause.code().peel_derives(),
|
||||||
|
ObligationCauseCode::BindingObligation(..)
|
||||||
|
| ObligationCauseCode::ExprBindingObligation(..)
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
// Hack to get around the borrow checker because trace.cause has an `Rc`.
|
||||||
|
if let ObligationCauseCode::BindingObligation(_, span)
|
||||||
|
| ObligationCauseCode::ExprBindingObligation(_, span, ..) =
|
||||||
|
&trace.cause.code().peel_derives()
|
||||||
|
{
|
||||||
|
let span = *span;
|
||||||
|
let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
|
||||||
|
err.span_note(span, "the lifetime requirement is introduced here");
|
||||||
|
err
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
infer::Subtype(box trace) => {
|
||||||
|
let terr = TypeError::RegionsPlaceholderMismatch;
|
||||||
|
return self.report_and_explain_type_error(trace, terr);
|
||||||
|
}
|
||||||
|
_ => return self.report_concrete_failure(placeholder_origin, sub, sup),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
672
compiler/rustc_infer/src/infer/error_reporting/suggest.rs
Normal file
672
compiler/rustc_infer/src/infer/error_reporting/suggest.rs
Normal file
|
@ -0,0 +1,672 @@
|
||||||
|
use hir::def::CtorKind;
|
||||||
|
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||||
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
|
use rustc_errors::{Applicability, Diagnostic};
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_middle::traits::{
|
||||||
|
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||||
|
StatementAsExpression,
|
||||||
|
};
|
||||||
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
|
use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
|
||||||
|
use rustc_span::{sym, BytePos, Span};
|
||||||
|
|
||||||
|
use crate::errors::SuggAddLetForLetChains;
|
||||||
|
|
||||||
|
use super::TypeErrCtxt;
|
||||||
|
|
||||||
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
pub(super) fn suggest_remove_semi_or_return_binding(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
first_id: Option<hir::HirId>,
|
||||||
|
first_ty: Ty<'tcx>,
|
||||||
|
first_span: Span,
|
||||||
|
second_id: Option<hir::HirId>,
|
||||||
|
second_ty: Ty<'tcx>,
|
||||||
|
second_span: Span,
|
||||||
|
) {
|
||||||
|
let remove_semicolon = [
|
||||||
|
(first_id, self.resolve_vars_if_possible(second_ty)),
|
||||||
|
(second_id, self.resolve_vars_if_possible(first_ty)),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find_map(|(id, ty)| {
|
||||||
|
let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
|
||||||
|
self.could_remove_semicolon(blk, ty)
|
||||||
|
});
|
||||||
|
match remove_semicolon {
|
||||||
|
Some((sp, StatementAsExpression::NeedsBoxing)) => {
|
||||||
|
err.multipart_suggestion(
|
||||||
|
"consider removing this semicolon and boxing the expressions",
|
||||||
|
vec![
|
||||||
|
(first_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||||
|
(first_span.shrink_to_hi(), ")".to_string()),
|
||||||
|
(second_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||||
|
(second_span.shrink_to_hi(), ")".to_string()),
|
||||||
|
(sp, String::new()),
|
||||||
|
],
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some((sp, StatementAsExpression::CorrectType)) => {
|
||||||
|
err.span_suggestion_short(
|
||||||
|
sp,
|
||||||
|
"consider removing this semicolon",
|
||||||
|
"",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
|
||||||
|
if let Some(id) = id
|
||||||
|
&& let hir::Node::Block(blk) = self.tcx.hir().get(id)
|
||||||
|
&& self.consider_returning_binding(blk, ty, err)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn suggest_boxing_for_return_impl_trait(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
return_sp: Span,
|
||||||
|
arm_spans: impl Iterator<Item = Span>,
|
||||||
|
) {
|
||||||
|
err.multipart_suggestion(
|
||||||
|
"you could change the return type to be a boxed trait object",
|
||||||
|
vec![
|
||||||
|
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
|
||||||
|
(return_sp.shrink_to_hi(), ">".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
let sugg = arm_spans
|
||||||
|
.flat_map(|sp| {
|
||||||
|
[(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
|
||||||
|
.into_iter()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
err.multipart_suggestion(
|
||||||
|
"if you change the return type to expect trait objects, box the returned expressions",
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn suggest_tuple_pattern(
|
||||||
|
&self,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
|
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||||
|
diag: &mut Diagnostic,
|
||||||
|
) {
|
||||||
|
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
||||||
|
// some modifications due to that being in typeck and this being in infer.
|
||||||
|
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
||||||
|
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
|
||||||
|
let compatible_variants: Vec<_> = expected_adt
|
||||||
|
.variants()
|
||||||
|
.iter()
|
||||||
|
.filter(|variant| {
|
||||||
|
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
|
||||||
|
})
|
||||||
|
.filter_map(|variant| {
|
||||||
|
let sole_field = &variant.fields[0];
|
||||||
|
let sole_field_ty = sole_field.ty(self.tcx, substs);
|
||||||
|
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
||||||
|
let variant_path =
|
||||||
|
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||||
|
// FIXME #56861: DRYer prelude filtering
|
||||||
|
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
||||||
|
if let Some((_, path)) = path.split_once("::") {
|
||||||
|
return Some(path.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(variant_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
match &compatible_variants[..] {
|
||||||
|
[] => {}
|
||||||
|
[variant] => {
|
||||||
|
diag.multipart_suggestion_verbose(
|
||||||
|
&format!("try wrapping the pattern in `{}`", variant),
|
||||||
|
vec![
|
||||||
|
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||||
|
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// More than one matching variant.
|
||||||
|
diag.multipart_suggestions(
|
||||||
|
&format!(
|
||||||
|
"try wrapping the pattern in a variant of `{}`",
|
||||||
|
self.tcx.def_path_str(expected_adt.did())
|
||||||
|
),
|
||||||
|
compatible_variants.into_iter().map(|variant| {
|
||||||
|
vec![
|
||||||
|
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||||
|
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A possible error is to forget to add `.await` when using futures:
|
||||||
|
///
|
||||||
|
/// ```compile_fail,E0308
|
||||||
|
/// async fn make_u32() -> u32 {
|
||||||
|
/// 22
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn take_u32(x: u32) {}
|
||||||
|
///
|
||||||
|
/// async fn foo() {
|
||||||
|
/// let x = make_u32();
|
||||||
|
/// take_u32(x);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||||
|
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||||
|
/// `.await` to the tail of the expression.
|
||||||
|
pub(super) fn suggest_await_on_expect_found(
|
||||||
|
&self,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
|
exp_span: Span,
|
||||||
|
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||||
|
diag: &mut Diagnostic,
|
||||||
|
) {
|
||||||
|
debug!(
|
||||||
|
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
|
||||||
|
exp_span, exp_found.expected, exp_found.found,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (
|
||||||
|
self.get_impl_future_output_ty(exp_found.expected),
|
||||||
|
self.get_impl_future_output_ty(exp_found.found),
|
||||||
|
) {
|
||||||
|
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
||||||
|
.code()
|
||||||
|
{
|
||||||
|
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||||
|
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
"consider `await`ing on both `Future`s",
|
||||||
|
vec![
|
||||||
|
(then_span.shrink_to_hi(), ".await".to_string()),
|
||||||
|
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||||
|
prior_arms,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if let [.., arm_span] = &prior_arms[..] {
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
"consider `await`ing on both `Future`s",
|
||||||
|
vec![
|
||||||
|
(arm_span.shrink_to_hi(), ".await".to_string()),
|
||||||
|
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
diag.help("consider `await`ing on both `Future`s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
diag.help("consider `await`ing on both `Future`s");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
exp_span.shrink_to_hi(),
|
||||||
|
"consider `await`ing on the `Future`",
|
||||||
|
".await",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
||||||
|
{
|
||||||
|
ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
then_span.shrink_to_hi(),
|
||||||
|
"consider `await`ing on the `Future`",
|
||||||
|
".await",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||||
|
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
then_span.shrink_to_hi(),
|
||||||
|
"consider `await`ing on the `Future`",
|
||||||
|
".await",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||||
|
ref prior_arms,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
diag.multipart_suggestion_verbose(
|
||||||
|
"consider `await`ing on the `Future`",
|
||||||
|
prior_arms
|
||||||
|
.iter()
|
||||||
|
.map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
|
||||||
|
.collect(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn suggest_accessing_field_where_appropriate(
|
||||||
|
&self,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
|
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||||
|
diag: &mut Diagnostic,
|
||||||
|
) {
|
||||||
|
debug!(
|
||||||
|
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
|
||||||
|
cause, exp_found
|
||||||
|
);
|
||||||
|
if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
|
||||||
|
if expected_def.is_enum() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((name, ty)) = expected_def
|
||||||
|
.non_enum_variant()
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
|
||||||
|
.map(|field| (field.name, field.ty(self.tcx, expected_substs)))
|
||||||
|
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
|
||||||
|
{
|
||||||
|
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||||
|
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||||
|
let suggestion = if expected_def.is_struct() {
|
||||||
|
format!("{}.{}", snippet, name)
|
||||||
|
} else if expected_def.is_union() {
|
||||||
|
format!("unsafe {{ {}.{} }}", snippet, name)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
diag.span_suggestion(
|
||||||
|
span,
|
||||||
|
&format!(
|
||||||
|
"you might have meant to use field `{}` whose type is `{}`",
|
||||||
|
name, ty
|
||||||
|
),
|
||||||
|
suggestion,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
|
||||||
|
/// suggests it.
|
||||||
|
pub(super) fn suggest_as_ref_where_appropriate(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||||
|
diag: &mut Diagnostic,
|
||||||
|
) {
|
||||||
|
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||||
|
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
|
||||||
|
{
|
||||||
|
diag.span_suggestion(
|
||||||
|
span,
|
||||||
|
msg,
|
||||||
|
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
||||||
|
format!("{}.as_ref()", snippet.trim_start_matches('&')),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||||
|
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
||||||
|
(expected.kind(), found.kind())
|
||||||
|
{
|
||||||
|
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
||||||
|
if exp_def == &found_def {
|
||||||
|
let have_as_ref = &[
|
||||||
|
(
|
||||||
|
sym::Option,
|
||||||
|
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||||
|
`.as_ref()`",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
sym::Result,
|
||||||
|
"you can convert from `&Result<T, E>` to \
|
||||||
|
`Result<&T, &E>` using `.as_ref()`",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||||
|
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||||
|
}) {
|
||||||
|
let mut show_suggestion = true;
|
||||||
|
for (exp_ty, found_ty) in
|
||||||
|
std::iter::zip(exp_substs.types(), found_substs.types())
|
||||||
|
{
|
||||||
|
match *exp_ty.kind() {
|
||||||
|
ty::Ref(_, exp_ty, _) => {
|
||||||
|
match (exp_ty.kind(), found_ty.kind()) {
|
||||||
|
(_, ty::Param(_))
|
||||||
|
| (_, ty::Infer(_))
|
||||||
|
| (ty::Param(_), _)
|
||||||
|
| (ty::Infer(_), _) => {}
|
||||||
|
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
|
||||||
|
_ => show_suggestion = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ty::Param(_) | ty::Infer(_) => {}
|
||||||
|
_ => show_suggestion = false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if show_suggestion {
|
||||||
|
return Some(*msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to find code with pattern `if Some(..) = expr`
|
||||||
|
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||||
|
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||||
|
pub(super) fn suggest_let_for_letchains(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
cause: &ObligationCause<'_>,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
let fn_hir_id = hir.get_parent_node(cause.body_id);
|
||||||
|
if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
|
||||||
|
let hir::Node::Item(hir::Item {
|
||||||
|
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
|
||||||
|
}) = node {
|
||||||
|
let body = hir.body(*body_id);
|
||||||
|
|
||||||
|
/// Find the if expression with given span
|
||||||
|
struct IfVisitor {
|
||||||
|
pub result: bool,
|
||||||
|
pub found_if: bool,
|
||||||
|
pub err_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'v> Visitor<'v> for IfVisitor {
|
||||||
|
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||||
|
if self.result { return; }
|
||||||
|
match ex.kind {
|
||||||
|
hir::ExprKind::If(cond, _, _) => {
|
||||||
|
self.found_if = true;
|
||||||
|
walk_expr(self, cond);
|
||||||
|
self.found_if = false;
|
||||||
|
}
|
||||||
|
_ => walk_expr(self, ex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||||
|
if let hir::StmtKind::Local(hir::Local {
|
||||||
|
span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
|
||||||
|
}) = &ex.kind
|
||||||
|
&& self.found_if
|
||||||
|
&& span.eq(&self.err_span) {
|
||||||
|
self.result = true;
|
||||||
|
}
|
||||||
|
walk_stmt(self, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
||||||
|
hir::intravisit::walk_body(self, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
||||||
|
visitor.visit_body(&body);
|
||||||
|
if visitor.result {
|
||||||
|
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
|
||||||
|
/// is enough to fix the error.
|
||||||
|
pub fn could_remove_semicolon(
|
||||||
|
&self,
|
||||||
|
blk: &'tcx hir::Block<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
) -> Option<(Span, StatementAsExpression)> {
|
||||||
|
let blk = blk.innermost_block();
|
||||||
|
// Do not suggest if we have a tail expr.
|
||||||
|
if blk.expr.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let last_stmt = blk.stmts.last()?;
|
||||||
|
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
|
||||||
|
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
||||||
|
_ if last_expr_ty.references_error() => return None,
|
||||||
|
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
||||||
|
StatementAsExpression::CorrectType
|
||||||
|
}
|
||||||
|
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
|
||||||
|
if last_def_id == exp_def_id =>
|
||||||
|
{
|
||||||
|
StatementAsExpression::CorrectType
|
||||||
|
}
|
||||||
|
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
|
||||||
|
debug!(
|
||||||
|
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
||||||
|
last_def_id, last_bounds, exp_def_id, exp_bounds
|
||||||
|
);
|
||||||
|
|
||||||
|
let last_local_id = last_def_id.as_local()?;
|
||||||
|
let exp_local_id = exp_def_id.as_local()?;
|
||||||
|
|
||||||
|
match (
|
||||||
|
&self.tcx.hir().expect_item(last_local_id).kind,
|
||||||
|
&self.tcx.hir().expect_item(exp_local_id).kind,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
||||||
|
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
||||||
|
) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
|
||||||
|
match (left, right) {
|
||||||
|
(
|
||||||
|
hir::GenericBound::Trait(tl, ml),
|
||||||
|
hir::GenericBound::Trait(tr, mr),
|
||||||
|
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
||||||
|
&& ml == mr =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(
|
||||||
|
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
|
||||||
|
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
|
||||||
|
) if langl == langr => {
|
||||||
|
// FIXME: consider the bounds!
|
||||||
|
debug!("{:?} {:?}", argsl, argsr);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}) =>
|
||||||
|
{
|
||||||
|
StatementAsExpression::NeedsBoxing
|
||||||
|
}
|
||||||
|
_ => StatementAsExpression::CorrectType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let span = if last_stmt.span.from_expansion() {
|
||||||
|
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
||||||
|
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
||||||
|
} else {
|
||||||
|
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
|
||||||
|
};
|
||||||
|
Some((span, needs_box))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suggest returning a local binding with a compatible type if the block
|
||||||
|
/// has no return expression.
|
||||||
|
pub fn consider_returning_binding(
|
||||||
|
&self,
|
||||||
|
blk: &'tcx hir::Block<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
) -> bool {
|
||||||
|
let blk = blk.innermost_block();
|
||||||
|
// Do not suggest if we have a tail expr.
|
||||||
|
if blk.expr.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut shadowed = FxIndexSet::default();
|
||||||
|
let mut candidate_idents = vec![];
|
||||||
|
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
||||||
|
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
||||||
|
&& let Some(pat_ty) = self
|
||||||
|
.typeck_results
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
|
||||||
|
{
|
||||||
|
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
||||||
|
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
||||||
|
&& !(pat_ty, expected_ty).references_error()
|
||||||
|
&& shadowed.insert(ident.name)
|
||||||
|
{
|
||||||
|
candidate_idents.push((*ident, pat_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
for stmt in blk.stmts.iter().rev() {
|
||||||
|
let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
|
||||||
|
local.pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
match hir.find(hir.get_parent_node(blk.hir_id)) {
|
||||||
|
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
|
||||||
|
match hir.find(hir.get_parent_node(*hir_id)) {
|
||||||
|
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
|
||||||
|
pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
Some(
|
||||||
|
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
||||||
|
| hir::Node::ImplItem(hir::ImplItem {
|
||||||
|
kind: hir::ImplItemKind::Fn(_, body),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| hir::Node::TraitItem(hir::TraitItem {
|
||||||
|
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| hir::Node::Expr(hir::Expr {
|
||||||
|
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
) => {
|
||||||
|
for param in hir.body(*body).params {
|
||||||
|
param.pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(hir::Node::Expr(hir::Expr {
|
||||||
|
kind:
|
||||||
|
hir::ExprKind::If(
|
||||||
|
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
||||||
|
then_block,
|
||||||
|
_,
|
||||||
|
),
|
||||||
|
..
|
||||||
|
})) if then_block.hir_id == *hir_id => {
|
||||||
|
let_.pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match &candidate_idents[..] {
|
||||||
|
[(ident, _ty)] => {
|
||||||
|
let sm = self.tcx.sess.source_map();
|
||||||
|
if let Some(stmt) = blk.stmts.last() {
|
||||||
|
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
||||||
|
let sugg = if sm.is_multiline(blk.span)
|
||||||
|
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
||||||
|
{
|
||||||
|
format!("\n{spacing}{ident}")
|
||||||
|
} else {
|
||||||
|
format!(" {ident}")
|
||||||
|
};
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
stmt_span.shrink_to_hi(),
|
||||||
|
format!("consider returning the local binding `{ident}`"),
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let sugg = if sm.is_multiline(blk.span)
|
||||||
|
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
||||||
|
{
|
||||||
|
format!("\n{spacing} {ident}\n{spacing}")
|
||||||
|
} else {
|
||||||
|
format!(" {ident} ")
|
||||||
|
};
|
||||||
|
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
|
||||||
|
format!("consider returning the local binding `{ident}`"),
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
values if (1..3).contains(&values.len()) => {
|
||||||
|
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
||||||
|
err.span_note(spans, "consider returning one of these bindings");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
203
compiler/rustc_infer/src/infer/note.rs
Normal file
203
compiler/rustc_infer/src/infer/note.rs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
fn note_error_origin(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
|
exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
|
||||||
|
terr: TypeError<'tcx>,
|
||||||
|
) {
|
||||||
|
match *cause.code() {
|
||||||
|
ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
|
||||||
|
let ty = self.resolve_vars_if_possible(root_ty);
|
||||||
|
if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)))
|
||||||
|
{
|
||||||
|
// don't show type `_`
|
||||||
|
if span.desugaring_kind() == Some(DesugaringKind::ForLoop)
|
||||||
|
&& let ty::Adt(def, substs) = ty.kind()
|
||||||
|
&& Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
|
||||||
|
{
|
||||||
|
err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0)));
|
||||||
|
} else {
|
||||||
|
err.span_label(span, format!("this expression has type `{}`", ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
|
||||||
|
&& ty.is_box() && ty.boxed_ty() == found
|
||||||
|
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||||
|
{
|
||||||
|
err.span_suggestion(
|
||||||
|
span,
|
||||||
|
"consider dereferencing the boxed value",
|
||||||
|
format!("*{}", snippet),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
|
||||||
|
err.span_label(span, "expected due to this");
|
||||||
|
}
|
||||||
|
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||||
|
arm_block_id,
|
||||||
|
arm_span,
|
||||||
|
arm_ty,
|
||||||
|
prior_arm_block_id,
|
||||||
|
prior_arm_span,
|
||||||
|
prior_arm_ty,
|
||||||
|
source,
|
||||||
|
ref prior_arms,
|
||||||
|
scrut_hir_id,
|
||||||
|
opt_suggest_box_span,
|
||||||
|
scrut_span,
|
||||||
|
..
|
||||||
|
}) => match source {
|
||||||
|
hir::MatchSource::TryDesugar => {
|
||||||
|
if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
|
||||||
|
let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
|
||||||
|
let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
|
||||||
|
let arg_expr = args.first().expect("try desugaring call w/out arg");
|
||||||
|
self.typeck_results.as_ref().and_then(|typeck_results| {
|
||||||
|
typeck_results.expr_ty_opt(arg_expr)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bug!("try desugaring w/out call expr as scrutinee");
|
||||||
|
};
|
||||||
|
|
||||||
|
match scrut_ty {
|
||||||
|
Some(ty) if expected == ty => {
|
||||||
|
let source_map = self.tcx.sess.source_map();
|
||||||
|
err.span_suggestion(
|
||||||
|
source_map.end_point(cause.span),
|
||||||
|
"try removing this `?`",
|
||||||
|
"",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// `prior_arm_ty` can be `!`, `expected` will have better info when present.
|
||||||
|
let t = self.resolve_vars_if_possible(match exp_found {
|
||||||
|
Some(ty::error::ExpectedFound { expected, .. }) => expected,
|
||||||
|
_ => prior_arm_ty,
|
||||||
|
});
|
||||||
|
let source_map = self.tcx.sess.source_map();
|
||||||
|
let mut any_multiline_arm = source_map.is_multiline(arm_span);
|
||||||
|
if prior_arms.len() <= 4 {
|
||||||
|
for sp in prior_arms {
|
||||||
|
any_multiline_arm |= source_map.is_multiline(*sp);
|
||||||
|
err.span_label(*sp, format!("this is found to be of type `{}`", t));
|
||||||
|
}
|
||||||
|
} else if let Some(sp) = prior_arms.last() {
|
||||||
|
any_multiline_arm |= source_map.is_multiline(*sp);
|
||||||
|
err.span_label(
|
||||||
|
*sp,
|
||||||
|
format!("this and all prior arms are found to be of type `{}`", t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let outer_error_span = if any_multiline_arm {
|
||||||
|
// Cover just `match` and the scrutinee expression, not
|
||||||
|
// the entire match body, to reduce diagram noise.
|
||||||
|
cause.span.shrink_to_lo().to(scrut_span)
|
||||||
|
} else {
|
||||||
|
cause.span
|
||||||
|
};
|
||||||
|
let msg = "`match` arms have incompatible types";
|
||||||
|
err.span_label(outer_error_span, msg);
|
||||||
|
self.suggest_remove_semi_or_return_binding(
|
||||||
|
err,
|
||||||
|
prior_arm_block_id,
|
||||||
|
prior_arm_ty,
|
||||||
|
prior_arm_span,
|
||||||
|
arm_block_id,
|
||||||
|
arm_ty,
|
||||||
|
arm_span,
|
||||||
|
);
|
||||||
|
if let Some(ret_sp) = opt_suggest_box_span {
|
||||||
|
// Get return type span and point to it.
|
||||||
|
self.suggest_boxing_for_return_impl_trait(
|
||||||
|
err,
|
||||||
|
ret_sp,
|
||||||
|
prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ObligationCauseCode::IfExpression(box IfExpressionCause {
|
||||||
|
then_id,
|
||||||
|
else_id,
|
||||||
|
then_ty,
|
||||||
|
else_ty,
|
||||||
|
outer_span,
|
||||||
|
opt_suggest_box_span,
|
||||||
|
}) => {
|
||||||
|
let then_span = self.find_block_span_from_hir_id(then_id);
|
||||||
|
let else_span = self.find_block_span_from_hir_id(else_id);
|
||||||
|
err.span_label(then_span, "expected because of this");
|
||||||
|
if let Some(sp) = outer_span {
|
||||||
|
err.span_label(sp, "`if` and `else` have incompatible types");
|
||||||
|
}
|
||||||
|
self.suggest_remove_semi_or_return_binding(
|
||||||
|
err,
|
||||||
|
Some(then_id),
|
||||||
|
then_ty,
|
||||||
|
then_span,
|
||||||
|
Some(else_id),
|
||||||
|
else_ty,
|
||||||
|
else_span,
|
||||||
|
);
|
||||||
|
if let Some(ret_sp) = opt_suggest_box_span {
|
||||||
|
self.suggest_boxing_for_return_impl_trait(
|
||||||
|
err,
|
||||||
|
ret_sp,
|
||||||
|
[then_span, else_span].into_iter(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObligationCauseCode::LetElse => {
|
||||||
|
err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
|
||||||
|
err.help("...or use `match` instead of `let...else`");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let ObligationCauseCode::BindingObligation(_, span)
|
||||||
|
| ObligationCauseCode::ExprBindingObligation(_, span, ..)
|
||||||
|
= cause.code().peel_derives()
|
||||||
|
&& let TypeError::RegionsPlaceholderMismatch = terr
|
||||||
|
{
|
||||||
|
err.span_note( * span,
|
||||||
|
"the lifetime requirement is introduced here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> InferCtxt<'tcx> {
|
||||||
|
/// Given a [`hir::Block`], get the span of its last expression or
|
||||||
|
/// statement, peeling off any inner blocks.
|
||||||
|
pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
|
||||||
|
let block = block.innermost_block();
|
||||||
|
if let Some(expr) = &block.expr {
|
||||||
|
expr.span
|
||||||
|
} else if let Some(stmt) = block.stmts.last() {
|
||||||
|
// possibly incorrect trailing `;` in the else arm
|
||||||
|
stmt.span
|
||||||
|
} else {
|
||||||
|
// empty block; point at its entirety
|
||||||
|
block.span
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a [`hir::HirId`] for a block, get the span of its last expression
|
||||||
|
/// or statement, peeling off any inner blocks.
|
||||||
|
pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
|
||||||
|
match self.tcx.hir().get(hir_id) {
|
||||||
|
hir::Node::Block(blk) => self.find_block_span(blk),
|
||||||
|
// The parser was in a weird state if either of these happen, but
|
||||||
|
// it's better not to panic.
|
||||||
|
hir::Node::Expr(e) => e.span,
|
||||||
|
_ => rustc_span::DUMMY_SP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,13 +12,13 @@ extern crate tracing;
|
||||||
extern crate rustc_middle;
|
extern crate rustc_middle;
|
||||||
|
|
||||||
mod chalk;
|
mod chalk;
|
||||||
|
mod codegen;
|
||||||
mod dropck_outlives;
|
mod dropck_outlives;
|
||||||
mod evaluate_obligation;
|
mod evaluate_obligation;
|
||||||
mod implied_outlives_bounds;
|
mod implied_outlives_bounds;
|
||||||
mod normalize_erasing_regions;
|
mod normalize_erasing_regions;
|
||||||
mod normalize_projection_ty;
|
mod normalize_projection_ty;
|
||||||
mod type_op;
|
mod type_op;
|
||||||
mod codegen;
|
|
||||||
|
|
||||||
pub use type_op::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause};
|
pub use type_op::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue