Auto merge of #112116 - compiler-errors:misc-hir-typeck-mismatch-tweaks, r=WaffleLapkin
Misc HIR typeck type mismatch tweaks These are all intended to improve #112104, but I couldn't get it to actually suggest adding `as_ref` to the LHS of the equality expr without some hacks that I may play around with some more. Each commit's title should explain what it's doing except for perhaps the last one, which addresses the bogus suggestion on #112104 itself.
This commit is contained in:
commit
9c843d9fa3
20 changed files with 338 additions and 171 deletions
|
@ -25,6 +25,8 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item
|
||||||
|
|
||||||
hir_typeck_convert_to_str = try converting the passed type into a `&str`
|
hir_typeck_convert_to_str = try converting the passed type into a `&str`
|
||||||
|
|
||||||
|
hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}`
|
||||||
|
|
||||||
hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
|
hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
|
||||||
|
|
||||||
hir_typeck_expected_default_return_type = expected `()` because of default return type
|
hir_typeck_expected_default_return_type = expected `()` because of default return type
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rustc_ast::util::parser::PREC_POSTFIX;
|
||||||
use rustc_errors::MultiSpan;
|
use rustc_errors::MultiSpan;
|
||||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::CtorKind;
|
use rustc_hir::def::{CtorKind, Res};
|
||||||
use rustc_hir::intravisit::Visitor;
|
use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_hir::{is_range_literal, Node};
|
use rustc_hir::{is_range_literal, Node};
|
||||||
|
@ -91,6 +91,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty);
|
self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Really hacky heuristic to remap an `assert_eq!` error to the user
|
||||||
|
/// expressions provided to the macro.
|
||||||
|
fn adjust_expr_for_assert_eq_macro(
|
||||||
|
&self,
|
||||||
|
found_expr: &mut &'tcx hir::Expr<'tcx>,
|
||||||
|
expected_expr: &mut Option<&'tcx hir::Expr<'tcx>>,
|
||||||
|
) {
|
||||||
|
let Some(expected_expr) = expected_expr else { return; };
|
||||||
|
|
||||||
|
if !found_expr.span.eq_ctxt(expected_expr.span) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_expr
|
||||||
|
.span
|
||||||
|
.ctxt()
|
||||||
|
.outer_expn_data()
|
||||||
|
.macro_def_id
|
||||||
|
.is_some_and(|def_id| self.tcx.is_diagnostic_item(sym::assert_eq_macro, def_id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hir::ExprKind::Unary(
|
||||||
|
hir::UnOp::Deref,
|
||||||
|
hir::Expr { kind: hir::ExprKind::Path(found_path), .. },
|
||||||
|
) = found_expr.kind else { return; };
|
||||||
|
let hir::ExprKind::Unary(
|
||||||
|
hir::UnOp::Deref,
|
||||||
|
hir::Expr { kind: hir::ExprKind::Path(expected_path), .. },
|
||||||
|
) = expected_expr.kind else { return; };
|
||||||
|
|
||||||
|
for (path, name, idx, var) in [
|
||||||
|
(expected_path, "left_val", 0, expected_expr),
|
||||||
|
(found_path, "right_val", 1, found_expr),
|
||||||
|
] {
|
||||||
|
if let hir::QPath::Resolved(_, path) = path
|
||||||
|
&& let [segment] = path.segments
|
||||||
|
&& segment.ident.name.as_str() == name
|
||||||
|
&& let Res::Local(hir_id) = path.res
|
||||||
|
&& let Some((_, hir::Node::Expr(match_expr))) = self.tcx.hir().parent_iter(hir_id).nth(2)
|
||||||
|
&& let hir::ExprKind::Match(scrutinee, _, _) = match_expr.kind
|
||||||
|
&& let hir::ExprKind::Tup(exprs) = scrutinee.kind
|
||||||
|
&& let hir::ExprKind::AddrOf(_, _, macro_arg) = exprs[idx].kind
|
||||||
|
{
|
||||||
|
*var = macro_arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Requires that the two types unify, and prints an error message if
|
/// Requires that the two types unify, and prints an error message if
|
||||||
/// they don't.
|
/// they don't.
|
||||||
pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
|
pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
|
||||||
|
@ -156,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
pub fn demand_coerce(
|
pub fn demand_coerce(
|
||||||
&self,
|
&self,
|
||||||
expr: &hir::Expr<'tcx>,
|
expr: &'tcx hir::Expr<'tcx>,
|
||||||
checked_ty: Ty<'tcx>,
|
checked_ty: Ty<'tcx>,
|
||||||
expected: Ty<'tcx>,
|
expected: Ty<'tcx>,
|
||||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
|
@ -177,10 +227,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
#[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
|
#[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
|
||||||
pub fn demand_coerce_diag(
|
pub fn demand_coerce_diag(
|
||||||
&self,
|
&self,
|
||||||
expr: &hir::Expr<'tcx>,
|
mut expr: &'tcx hir::Expr<'tcx>,
|
||||||
checked_ty: Ty<'tcx>,
|
checked_ty: Ty<'tcx>,
|
||||||
expected: Ty<'tcx>,
|
expected: Ty<'tcx>,
|
||||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
allow_two_phase: AllowTwoPhase,
|
allow_two_phase: AllowTwoPhase,
|
||||||
) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) {
|
) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) {
|
||||||
let expected = self.resolve_vars_with_obligations(expected);
|
let expected = self.resolve_vars_with_obligations(expected);
|
||||||
|
@ -190,6 +240,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
Err(e) => e,
|
Err(e) => e,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.adjust_expr_for_assert_eq_macro(&mut expr, &mut expected_ty_expr);
|
||||||
|
|
||||||
self.set_tainted_by_errors(self.tcx.sess.delay_span_bug(
|
self.set_tainted_by_errors(self.tcx.sess.delay_span_bug(
|
||||||
expr.span,
|
expr.span,
|
||||||
"`TypeError` when attempting coercion but no error emitted",
|
"`TypeError` when attempting coercion but no error emitted",
|
||||||
|
|
|
@ -327,3 +327,19 @@ pub struct CtorIsPrivate {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub def: String,
|
pub def: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[multipart_suggestion(
|
||||||
|
hir_typeck_convert_using_method,
|
||||||
|
applicability = "machine-applicable",
|
||||||
|
style = "verbose"
|
||||||
|
)]
|
||||||
|
pub struct SuggestConvertViaMethod<'tcx> {
|
||||||
|
#[suggestion_part(code = "{sugg}")]
|
||||||
|
pub span: Span,
|
||||||
|
#[suggestion_part(code = "")]
|
||||||
|
pub borrow_removal_span: Option<Span>,
|
||||||
|
pub sugg: &'static str,
|
||||||
|
pub expected: Ty<'tcx>,
|
||||||
|
pub found: Ty<'tcx>,
|
||||||
|
}
|
||||||
|
|
|
@ -1404,7 +1404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// type of the place it is referencing, and not some
|
// type of the place it is referencing, and not some
|
||||||
// supertype thereof.
|
// supertype thereof.
|
||||||
let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
|
let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
|
||||||
self.demand_eqtype(init.span, local_ty, init_ty);
|
if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) {
|
||||||
|
self.emit_type_mismatch_suggestions(
|
||||||
|
&mut diag,
|
||||||
|
init.peel_drop_temps(),
|
||||||
|
init_ty,
|
||||||
|
local_ty,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
diag.emit();
|
||||||
|
}
|
||||||
init_ty
|
init_ty
|
||||||
} else {
|
} else {
|
||||||
self.check_expr_coercible_to_type(init, local_ty, None)
|
self.check_expr_coercible_to_type(init, local_ty, None)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use super::FnCtxt;
|
use super::FnCtxt;
|
||||||
|
|
||||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing};
|
use crate::errors::{
|
||||||
|
AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
|
||||||
|
};
|
||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||||
|
@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let expr = expr.peel_blocks();
|
let expr = expr.peel_blocks();
|
||||||
|
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
|
||||||
|
|
||||||
if let Some((suggestion, msg, applicability, verbose, annotation)) =
|
if let Some((suggestion, msg, applicability, verbose, annotation)) =
|
||||||
self.suggest_deref_or_ref(expr, found, expected)
|
self.suggest_deref_or_ref(expr, found, expected)
|
||||||
{
|
{
|
||||||
|
@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
|
}
|
||||||
|
|
||||||
|
if self.suggest_else_fn_with_closure(err, expr, found, expected) {
|
||||||
return true;
|
return true;
|
||||||
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
|
}
|
||||||
|
|
||||||
|
if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
|
||||||
&& let ty::FnDef(def_id, ..) = *found.kind()
|
&& let ty::FnDef(def_id, ..) = *found.kind()
|
||||||
&& let Some(sp) = self.tcx.hir().span_if_local(def_id)
|
&& let Some(sp) = self.tcx.hir().span_if_local(def_id)
|
||||||
{
|
{
|
||||||
|
@ -343,12 +351,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
err.span_label(sp, format!("{descr} `{name}` defined here"));
|
err.span_label(sp, format!("{descr} `{name}` defined here"));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
|
}
|
||||||
|
|
||||||
|
if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
|
|
||||||
if !methods.is_empty() {
|
if !methods.is_empty() {
|
||||||
let mut suggestions = methods.iter()
|
let mut suggestions = methods
|
||||||
|
.iter()
|
||||||
.filter_map(|conversion_method| {
|
.filter_map(|conversion_method| {
|
||||||
let receiver_method_ident = expr.method_ident();
|
let receiver_method_ident = expr.method_ident();
|
||||||
if let Some(method_ident) = receiver_method_ident
|
if let Some(method_ident) = receiver_method_ident
|
||||||
|
@ -380,12 +391,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
} else {
|
} else {
|
||||||
vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
|
vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
|
||||||
};
|
};
|
||||||
let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
|
let struct_pat_shorthand_field =
|
||||||
|
self.maybe_get_struct_pattern_shorthand_field(expr);
|
||||||
if let Some(name) = struct_pat_shorthand_field {
|
if let Some(name) = struct_pat_shorthand_field {
|
||||||
sugg.insert(
|
sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
|
||||||
0,
|
|
||||||
(expr.span.shrink_to_lo(), format!("{}: ", name)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Some(sugg)
|
Some(sugg)
|
||||||
})
|
})
|
||||||
|
@ -398,42 +407,100 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if let ty::Adt(found_adt, found_substs) = found.kind()
|
|
||||||
&& self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
|
|
||||||
&& let ty::Adt(expected_adt, expected_substs) = expected.kind()
|
|
||||||
&& self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
|
|
||||||
&& let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
|
|
||||||
&& inner_ty.is_str()
|
|
||||||
{
|
|
||||||
let ty = found_substs.type_at(0);
|
|
||||||
let mut peeled = ty;
|
|
||||||
let mut ref_cnt = 0;
|
|
||||||
while let ty::Ref(_, inner, _) = peeled.kind() {
|
|
||||||
peeled = *inner;
|
|
||||||
ref_cnt += 1;
|
|
||||||
}
|
}
|
||||||
if let ty::Adt(adt, _) = peeled.kind()
|
|
||||||
&& Some(adt.did()) == self.tcx.lang_items().string()
|
if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
|
||||||
|
self.deconstruct_option_or_result(found, expected)
|
||||||
|
&& let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
|
||||||
{
|
{
|
||||||
let sugg = if ref_cnt == 0 {
|
// Suggest removing any stray borrows (unless there's macro shenanigans involved).
|
||||||
".as_deref()"
|
let inner_expr = expr.peel_borrows();
|
||||||
|
if !inner_expr.span.eq_ctxt(expr.span) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
|
||||||
|
None
|
||||||
} else {
|
} else {
|
||||||
".map(|x| x.as_str())"
|
Some(expr.span.shrink_to_lo().until(inner_expr.span))
|
||||||
};
|
};
|
||||||
|
// Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
|
||||||
|
// `as_ref` and `as_deref` compatibility.
|
||||||
|
let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| {
|
||||||
|
self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected)
|
||||||
|
});
|
||||||
|
// FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
|
||||||
|
// but those checks need to be a bit more delicate and the benefit is diminishing.
|
||||||
|
if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
|
||||||
|
err.subdiagnostic(SuggestConvertViaMethod {
|
||||||
|
span: expr.span.shrink_to_hi(),
|
||||||
|
sugg: ".as_ref()",
|
||||||
|
expected,
|
||||||
|
found,
|
||||||
|
borrow_removal_span,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else if let Some((deref_ty, _)) =
|
||||||
|
self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
|
||||||
|
&& self.can_eq(self.param_env, deref_ty, peeled)
|
||||||
|
&& error_tys_equate_as_ref
|
||||||
|
{
|
||||||
|
err.subdiagnostic(SuggestConvertViaMethod {
|
||||||
|
span: expr.span.shrink_to_hi(),
|
||||||
|
sugg: ".as_deref()",
|
||||||
|
expected,
|
||||||
|
found,
|
||||||
|
borrow_removal_span,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
|
||||||
|
&& Some(adt.did()) == self.tcx.lang_items().string()
|
||||||
|
&& peeled.is_str()
|
||||||
|
// `Result::map`, conversely, does not take ref of the error type.
|
||||||
|
&& error_tys.map_or(true, |(found, expected)| {
|
||||||
|
self.can_eq(self.param_env, found, expected)
|
||||||
|
})
|
||||||
|
{
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
expr.span.shrink_to_hi(),
|
expr.span.shrink_to_hi(),
|
||||||
fluent::hir_typeck_convert_to_str,
|
fluent::hir_typeck_convert_to_str,
|
||||||
sugg,
|
".map(|x| x.as_str())",
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deconstruct_option_or_result(
|
||||||
|
&self,
|
||||||
|
found_ty: Ty<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
|
||||||
|
let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
|
||||||
|
&& self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
|
||||||
|
{
|
||||||
|
Some((found_substs.type_at(0), expected_substs.type_at(0), None))
|
||||||
|
} else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
|
||||||
|
&& self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
|
||||||
|
{
|
||||||
|
Some((
|
||||||
|
found_substs.type_at(0),
|
||||||
|
expected_substs.type_at(0),
|
||||||
|
Some((found_substs.type_at(1), expected_substs.type_at(1))),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
|
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
|
||||||
/// in the heap by calling `Box::new()`.
|
/// in the heap by calling `Box::new()`.
|
||||||
pub(in super::super) fn suggest_boxing_when_appropriate(
|
pub(in super::super) fn suggest_boxing_when_appropriate(
|
||||||
|
|
|
@ -278,9 +278,6 @@ infer_ril_introduced_by = requirement introduced by this return type
|
||||||
infer_ril_introduced_here = `'static` requirement introduced here
|
infer_ril_introduced_here = `'static` requirement introduced here
|
||||||
infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type
|
infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type
|
||||||
|
|
||||||
infer_sarwa_option = you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
|
|
||||||
infer_sarwa_result = you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
|
|
||||||
|
|
||||||
infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions
|
infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions
|
||||||
|
|
||||||
infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object
|
infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object
|
||||||
|
|
|
@ -1246,30 +1246,6 @@ pub struct FnConsiderCasting {
|
||||||
pub casting: String,
|
pub casting: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
|
||||||
pub enum SuggestAsRefWhereAppropriate<'a> {
|
|
||||||
#[suggestion(
|
|
||||||
infer_sarwa_option,
|
|
||||||
code = "{snippet}.as_ref()",
|
|
||||||
applicability = "machine-applicable"
|
|
||||||
)]
|
|
||||||
Option {
|
|
||||||
#[primary_span]
|
|
||||||
span: Span,
|
|
||||||
snippet: &'a str,
|
|
||||||
},
|
|
||||||
#[suggestion(
|
|
||||||
infer_sarwa_result,
|
|
||||||
code = "{snippet}.as_ref()",
|
|
||||||
applicability = "machine-applicable"
|
|
||||||
)]
|
|
||||||
Result {
|
|
||||||
#[primary_span]
|
|
||||||
span: Span,
|
|
||||||
snippet: &'a str,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
pub enum SuggestAccessingField<'a> {
|
pub enum SuggestAccessingField<'a> {
|
||||||
#[suggestion(
|
#[suggestion(
|
||||||
|
|
|
@ -1897,7 +1897,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
if should_suggest_fixes {
|
if should_suggest_fixes {
|
||||||
self.suggest_tuple_pattern(cause, &exp_found, diag);
|
self.suggest_tuple_pattern(cause, &exp_found, diag);
|
||||||
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
|
|
||||||
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
|
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
|
||||||
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
|
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
|
||||||
self.suggest_function_pointers(cause, span, &exp_found, diag);
|
self.suggest_function_pointers(cause, span, &exp_found, diag);
|
||||||
|
|
|
@ -374,12 +374,18 @@ impl<T> Trait<T> for X {
|
||||||
) {
|
) {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
|
|
||||||
|
// Don't suggest constraining a projection to something containing itself
|
||||||
|
if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let msg = || {
|
let msg = || {
|
||||||
format!(
|
format!(
|
||||||
"consider constraining the associated type `{}` to `{}`",
|
"consider constraining the associated type `{}` to `{}`",
|
||||||
values.expected, values.found
|
values.expected, values.found
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let body_owner = tcx.hir().get_if_local(body_owner_def_id);
|
let body_owner = tcx.hir().get_if_local(body_owner_def_id);
|
||||||
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
|
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ use rustc_span::{sym, BytePos, Span};
|
||||||
|
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
||||||
FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
|
FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait,
|
||||||
SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
|
SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne,
|
||||||
SuggestTuplePatternOne, TypeErrorAdditionalDiags,
|
TypeErrorAdditionalDiags,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::TypeErrCtxt;
|
use super::TypeErrCtxt;
|
||||||
|
@ -289,27 +289,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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_kind(exp_found.expected, exp_found.found)
|
|
||||||
{
|
|
||||||
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
|
||||||
let snippet = snippet.trim_start_matches('&');
|
|
||||||
let subdiag = match msg {
|
|
||||||
SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
|
|
||||||
SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
|
|
||||||
};
|
|
||||||
diag.subdiagnostic(subdiag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn suggest_function_pointers(
|
pub(super) fn suggest_function_pointers(
|
||||||
&self,
|
&self,
|
||||||
cause: &ObligationCause<'tcx>,
|
cause: &ObligationCause<'tcx>,
|
||||||
|
|
11
tests/ui/associated-types/dont-suggest-cyclic-constraint.rs
Normal file
11
tests/ui/associated-types/dont-suggest-cyclic-constraint.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
fn foo<I: Iterator>(mut iter: I, value: &I::Item)
|
||||||
|
where
|
||||||
|
I::Item: Eq + Debug,
|
||||||
|
{
|
||||||
|
debug_assert_eq!(iter.next(), Some(value));
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/dont-suggest-cyclic-constraint.rs:7:35
|
||||||
|
|
|
||||||
|
LL | debug_assert_eq!(iter.next(), Some(value));
|
||||||
|
| ^^^^^^^^^^^ expected `Option<<I as Iterator>::Item>`, found `Option<&<I as Iterator>::Item>`
|
||||||
|
|
|
||||||
|
= note: expected enum `Option<<I as Iterator>::Item>`
|
||||||
|
found enum `Option<&<I as Iterator>::Item>`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -84,15 +84,16 @@ LL | fn foo3(_: u32) {}
|
||||||
| ^^^^ ------
|
| ^^^^ ------
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/deref-suggestion.rs:37:5
|
--> $DIR/deref-suggestion.rs:37:22
|
||||||
|
|
|
|
||||||
LL | assert_eq!(3i32, &3i32);
|
LL | assert_eq!(3i32, &3i32);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^ expected `i32`, found `&i32`
|
||||||
| |
|
|
|
||||||
| expected `i32`, found `&i32`
|
help: consider removing the borrow
|
||||||
| expected because this is `i32`
|
|
|
||||||
|
LL - assert_eq!(3i32, &3i32);
|
||||||
|
LL + assert_eq!(3i32, 3i32);
|
||||||
|
|
|
|
||||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/deref-suggestion.rs:40:17
|
--> $DIR/deref-suggestion.rs:40:17
|
||||||
|
|
|
@ -13,10 +13,6 @@ note: function defined here
|
||||||
|
|
|
|
||||||
LL | fn takes_option(_arg: Option<&String>) {}
|
LL | fn takes_option(_arg: Option<&String>) {}
|
||||||
| ^^^^^^^^^^^^ ---------------------
|
| ^^^^^^^^^^^^ ---------------------
|
||||||
help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
|
|
||||||
|
|
|
||||||
LL | takes_option(None.as_ref());
|
|
||||||
| ~~~~~~~~~~~~~
|
|
||||||
help: consider removing the borrow
|
help: consider removing the borrow
|
||||||
|
|
|
|
||||||
LL - takes_option(&None);
|
LL - takes_option(&None);
|
||||||
|
@ -27,10 +23,8 @@ error[E0308]: mismatched types
|
||||||
--> $DIR/issue-100605.rs:8:18
|
--> $DIR/issue-100605.rs:8:18
|
||||||
|
|
|
|
||||||
LL | takes_option(&res);
|
LL | takes_option(&res);
|
||||||
| ------------ ^^^^
|
| ------------ ^^^^ expected `Option<&String>`, found `&Option<String>`
|
||||||
| | |
|
| |
|
||||||
| | expected `Option<&String>`, found `&Option<String>`
|
|
||||||
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `res.as_ref()`
|
|
||||||
| arguments to this function are incorrect
|
| arguments to this function are incorrect
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&String>`
|
= note: expected enum `Option<&String>`
|
||||||
|
@ -40,6 +34,11 @@ note: function defined here
|
||||||
|
|
|
|
||||||
LL | fn takes_option(_arg: Option<&String>) {}
|
LL | fn takes_option(_arg: Option<&String>) {}
|
||||||
| ^^^^^^^^^^^^ ---------------------
|
| ^^^^^^^^^^^^ ---------------------
|
||||||
|
help: try using `.as_ref()` to convert `&Option<String>` to `Option<&String>`
|
||||||
|
|
|
||||||
|
LL - takes_option(&res);
|
||||||
|
LL + takes_option(res.as_ref());
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,10 @@ LL | let Some(ref a): Option<&[u8]> = some else { return };
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&[u8]>`
|
= note: expected enum `Option<&[u8]>`
|
||||||
found enum `Option<Vec<u8>>`
|
found enum `Option<Vec<u8>>`
|
||||||
|
help: try using `.as_deref()` to convert `Option<Vec<u8>>` to `Option<&[u8]>`
|
||||||
|
|
|
||||||
|
LL | let Some(ref a): Option<&[u8]> = some.as_deref() else { return };
|
||||||
|
| +++++++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/let-else-ref-bindings.rs:20:38
|
--> $DIR/let-else-ref-bindings.rs:20:38
|
||||||
|
@ -15,6 +19,11 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return };
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&[u8]>`
|
= note: expected enum `Option<&[u8]>`
|
||||||
found reference `&Option<Vec<u8>>`
|
found reference `&Option<Vec<u8>>`
|
||||||
|
help: try using `.as_deref()` to convert `&Option<Vec<u8>>` to `Option<&[u8]>`
|
||||||
|
|
|
||||||
|
LL - let Some(ref a): Option<&[u8]> = &some else { return };
|
||||||
|
LL + let Some(ref a): Option<&[u8]> = some.as_deref() else { return };
|
||||||
|
|
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/let-else-ref-bindings.rs:24:34
|
--> $DIR/let-else-ref-bindings.rs:24:34
|
||||||
|
@ -26,6 +35,10 @@ LL | let Some(a): Option<&[u8]> = some else { return };
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&[u8]>`
|
= note: expected enum `Option<&[u8]>`
|
||||||
found enum `Option<Vec<u8>>`
|
found enum `Option<Vec<u8>>`
|
||||||
|
help: try using `.as_deref()` to convert `Option<Vec<u8>>` to `Option<&[u8]>`
|
||||||
|
|
|
||||||
|
LL | let Some(a): Option<&[u8]> = some.as_deref() else { return };
|
||||||
|
| +++++++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/let-else-ref-bindings.rs:27:34
|
--> $DIR/let-else-ref-bindings.rs:27:34
|
||||||
|
@ -37,6 +50,11 @@ LL | let Some(a): Option<&[u8]> = &some else { return };
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&[u8]>`
|
= note: expected enum `Option<&[u8]>`
|
||||||
found reference `&Option<Vec<u8>>`
|
found reference `&Option<Vec<u8>>`
|
||||||
|
help: try using `.as_deref()` to convert `&Option<Vec<u8>>` to `Option<&[u8]>`
|
||||||
|
|
|
||||||
|
LL - let Some(a): Option<&[u8]> = &some else { return };
|
||||||
|
LL + let Some(a): Option<&[u8]> = some.as_deref() else { return };
|
||||||
|
|
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/let-else-ref-bindings.rs:44:46
|
--> $DIR/let-else-ref-bindings.rs:44:46
|
||||||
|
|
|
@ -24,4 +24,6 @@ fn main() {
|
||||||
let multiple_ref_result = &&Ok(Foo);
|
let multiple_ref_result = &&Ok(Foo);
|
||||||
multiple_ref_result.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
|
multiple_ref_result.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
|
||||||
multiple_ref_result.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
|
multiple_ref_result.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
|
||||||
|
|
||||||
|
let _: Result<&usize, _> = &Ok(42); //~ ERROR mismatched types [E0308]
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,14 +74,16 @@ error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:13:29
|
--> $DIR/as-ref.rs:13:29
|
||||||
|
|
|
|
||||||
LL | let y: Option<&usize> = x;
|
LL | let y: Option<&usize> = x;
|
||||||
| -------------- ^
|
| -------------- ^ expected `Option<&usize>`, found `&Option<usize>`
|
||||||
| | |
|
| |
|
||||||
| | expected `Option<&usize>`, found `&Option<usize>`
|
|
||||||
| | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
|
|
||||||
| expected due to this
|
| expected due to this
|
||||||
|
|
|
|
||||||
= note: expected enum `Option<&usize>`
|
= note: expected enum `Option<&usize>`
|
||||||
found reference `&Option<usize>`
|
found reference `&Option<usize>`
|
||||||
|
help: try using `.as_ref()` to convert `&Option<usize>` to `Option<&usize>`
|
||||||
|
|
|
||||||
|
LL | let y: Option<&usize> = x.as_ref();
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:15:37
|
--> $DIR/as-ref.rs:15:37
|
||||||
|
@ -93,10 +95,10 @@ LL | let y: Result<&usize, &usize> = x;
|
||||||
|
|
|
|
||||||
= note: expected enum `Result<&usize, &usize>`
|
= note: expected enum `Result<&usize, &usize>`
|
||||||
found reference `&Result<usize, usize>`
|
found reference `&Result<usize, usize>`
|
||||||
help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
|
help: try using `.as_ref()` to convert `&Result<usize, usize>` to `Result<&usize, &usize>`
|
||||||
|
|
|
|
||||||
LL | let y: Result<&usize, &usize> = x.as_ref();
|
LL | let y: Result<&usize, &usize> = x.as_ref();
|
||||||
| ~~~~~~~~~~
|
| +++++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/as-ref.rs:19:36
|
--> $DIR/as-ref.rs:19:36
|
||||||
|
@ -181,6 +183,22 @@ help: consider using `as_ref` instead
|
||||||
LL | multiple_ref_result.as_ref().and_then(|arg| Ok(takes_ref(arg)));
|
LL | multiple_ref_result.as_ref().and_then(|arg| Ok(takes_ref(arg)));
|
||||||
| +++++++++
|
| +++++++++
|
||||||
|
|
||||||
error: aborting due to 11 previous errors
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/as-ref.rs:28:32
|
||||||
|
|
|
||||||
|
LL | let _: Result<&usize, _> = &Ok(42);
|
||||||
|
| ----------------- ^^^^^^^ expected `Result<&usize, _>`, found `&Result<{integer}, _>`
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: expected enum `Result<&usize, _>`
|
||||||
|
found reference `&Result<{integer}, _>`
|
||||||
|
help: try using `.as_ref()` to convert `&Result<{integer}, _>` to `Result<&usize, _>`
|
||||||
|
|
|
||||||
|
LL - let _: Result<&usize, _> = &Ok(42);
|
||||||
|
LL + let _: Result<&usize, _> = Ok(42).as_ref();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/dont-suggest-try_into-in-macros.rs:2:5
|
--> $DIR/dont-suggest-try_into-in-macros.rs:2:23
|
||||||
|
|
|
|
||||||
LL | assert_eq!(10u64, 10usize);
|
LL | assert_eq!(10u64, 10usize);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^ expected `u64`, found `usize`
|
||||||
| |
|
|
||||||
| expected `u64`, found `usize`
|
|
||||||
| expected because this is `u64`
|
|
||||||
|
|
|
|
||||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
help: change the type of the numeric literal from `usize` to `u64`
|
||||||
|
|
|
||||||
|
LL | assert_eq!(10u64, 10u64);
|
||||||
|
| ~~~
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ error[E0308]: mismatched types
|
||||||
--> $DIR/switched-expectations.rs:3:30
|
--> $DIR/switched-expectations.rs:3:30
|
||||||
|
|
|
|
||||||
LL | let ref string: String = var;
|
LL | let ref string: String = var;
|
||||||
| ^^^ expected `String`, found `i32`
|
| ^^^- help: try using a conversion method: `.to_string()`
|
||||||
|
| |
|
||||||
|
| expected `String`, found `i32`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ note: function defined here
|
||||||
|
|
|
|
||||||
LL | fn take_str_maybe(_: Option<&str>) { }
|
LL | fn take_str_maybe(_: Option<&str>) { }
|
||||||
| ^^^^^^^^^^^^^^ ---------------
|
| ^^^^^^^^^^^^^^ ---------------
|
||||||
help: try converting the passed type into a `&str`
|
help: try using `.as_deref()` to convert `Option<String>` to `Option<&str>`
|
||||||
|
|
|
|
||||||
LL | take_str_maybe(option.as_deref());
|
LL | take_str_maybe(option.as_deref());
|
||||||
| +++++++++++
|
| +++++++++++
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue