Auto merge of #108698 - IntQuant:issue-100717-infer-6, r=davidtwco
Migrating rustc_infer to session diagnostics (part 5) `@rustbot` label +A-translation cc https://github.com/rust-lang/rust/issues/100717
This commit is contained in:
commit
88fb1b922b
6 changed files with 774 additions and 363 deletions
|
@ -768,7 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
|
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
|
||||||
let trace =
|
let trace =
|
||||||
mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
|
mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
|
||||||
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) {
|
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
|
||||||
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
|
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,3 +348,47 @@ infer_prlf_known_limitation = this is a known limitation that will be removed in
|
||||||
|
|
||||||
infer_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds
|
infer_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds
|
||||||
.label = opaque type defined here
|
.label = opaque type defined here
|
||||||
|
|
||||||
|
infer_fps_use_ref = consider using a reference
|
||||||
|
infer_fps_remove_ref = consider removing the reference
|
||||||
|
infer_fps_cast = consider casting to a fn pointer
|
||||||
|
infer_fps_items_are_distinct = fn items are distinct from fn pointers
|
||||||
|
infer_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`
|
||||||
|
|
||||||
|
infer_fn_uniq_types = different fn items have unique types, even if their signatures are the same
|
||||||
|
infer_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
|
||||||
|
|
||||||
|
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_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}`
|
||||||
|
|
||||||
|
infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object
|
||||||
|
infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions
|
||||||
|
|
||||||
|
infer_stp_wrap_one = try wrapping the pattern in `{$variant}`
|
||||||
|
infer_stp_wrap_many = try wrapping the pattern in a variant of `{$path}`
|
||||||
|
|
||||||
|
infer_tuple_trailing_comma = use a trailing comma to create a tuple with one element
|
||||||
|
|
||||||
|
infer_oc_method_compat = method not compatible with trait
|
||||||
|
infer_oc_type_compat = type not compatible with trait
|
||||||
|
infer_oc_const_compat = const not compatible with trait
|
||||||
|
infer_oc_try_compat = `?` operator has incompatible types
|
||||||
|
infer_oc_match_compat = `match` arms have incompatible types
|
||||||
|
infer_oc_if_else_different = `if` and `else` have incompatible types
|
||||||
|
infer_oc_no_else = `if` may be missing an `else` clause
|
||||||
|
infer_oc_no_diverge = `else` clause of `let...else` does not diverge
|
||||||
|
infer_oc_fn_main_correct_type = `main` function has wrong type
|
||||||
|
infer_oc_fn_start_correct_type = `#[start]` function has wrong type
|
||||||
|
infer_oc_intristic_correct_type = intrinsic has wrong type
|
||||||
|
infer_oc_method_correct_type = mismatched `self` parameter type
|
||||||
|
infer_oc_closure_selfref = closure/generator type that references itself
|
||||||
|
infer_oc_cant_coerce = cannot coerce intrinsics to function pointers
|
||||||
|
infer_oc_generic = mismatched types
|
||||||
|
|
||||||
|
infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b`
|
||||||
|
infer_meant_char_literal = if you meant to write a `char` literal, use single quotes
|
||||||
|
infer_meant_str_literal = if you meant to write a `str` literal, use double quotes
|
||||||
|
infer_consider_specifying_length = consider specifying the actual array length
|
||||||
|
infer_try_cannot_convert = `?` operator cannot convert from `{$found}` to `{$expected}`
|
||||||
|
|
|
@ -184,18 +184,6 @@ pub enum SourceKindMultiSuggestion<'a> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
|
||||||
#[suggestion(
|
|
||||||
infer_suggest_add_let_for_letchains,
|
|
||||||
style = "verbose",
|
|
||||||
applicability = "machine-applicable",
|
|
||||||
code = "let "
|
|
||||||
)]
|
|
||||||
pub(crate) struct SuggAddLetForLetChains {
|
|
||||||
#[primary_span]
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SourceKindMultiSuggestion<'a> {
|
impl<'a> SourceKindMultiSuggestion<'a> {
|
||||||
pub fn new_fully_qualified(
|
pub fn new_fully_qualified(
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -1157,3 +1145,380 @@ pub struct OpaqueCapturesLifetime<'tcx> {
|
||||||
pub opaque_ty_span: Span,
|
pub opaque_ty_span: Span,
|
||||||
pub opaque_ty: Ty<'tcx>,
|
pub opaque_ty: Ty<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
pub enum FunctionPointerSuggestion<'a> {
|
||||||
|
#[suggestion(
|
||||||
|
infer_fps_use_ref,
|
||||||
|
code = "&{fn_name}",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
UseRef {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[skip_arg]
|
||||||
|
fn_name: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_fps_remove_ref,
|
||||||
|
code = "{fn_name}",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
RemoveRef {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[skip_arg]
|
||||||
|
fn_name: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_fps_cast,
|
||||||
|
code = "&({fn_name} as {sig})",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
CastRef {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[skip_arg]
|
||||||
|
fn_name: String,
|
||||||
|
#[skip_arg]
|
||||||
|
sig: Binder<'a, FnSig<'a>>,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_fps_cast,
|
||||||
|
code = "{fn_name} as {sig}",
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
Cast {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[skip_arg]
|
||||||
|
fn_name: String,
|
||||||
|
#[skip_arg]
|
||||||
|
sig: Binder<'a, FnSig<'a>>,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_fps_cast_both,
|
||||||
|
code = "{fn_name} as {found_sig}",
|
||||||
|
style = "hidden",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
CastBoth {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[skip_arg]
|
||||||
|
fn_name: String,
|
||||||
|
#[skip_arg]
|
||||||
|
found_sig: Binder<'a, FnSig<'a>>,
|
||||||
|
expected_sig: Binder<'a, FnSig<'a>>,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_fps_cast_both,
|
||||||
|
code = "&({fn_name} as {found_sig})",
|
||||||
|
style = "hidden",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
CastBothRef {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[skip_arg]
|
||||||
|
fn_name: String,
|
||||||
|
#[skip_arg]
|
||||||
|
found_sig: Binder<'a, FnSig<'a>>,
|
||||||
|
expected_sig: Binder<'a, FnSig<'a>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[note(infer_fps_items_are_distinct)]
|
||||||
|
pub struct FnItemsAreDistinct;
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[note(infer_fn_uniq_types)]
|
||||||
|
pub struct FnUniqTypes;
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[help(infer_fn_consider_casting)]
|
||||||
|
pub struct FnConsiderCasting {
|
||||||
|
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)]
|
||||||
|
pub enum SuggestAccessingField<'a> {
|
||||||
|
#[suggestion(
|
||||||
|
infer_suggest_accessing_field,
|
||||||
|
code = "{snippet}.{name}",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
Safe {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
snippet: String,
|
||||||
|
name: Symbol,
|
||||||
|
ty: Ty<'a>,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_suggest_accessing_field,
|
||||||
|
code = "unsafe {{ {snippet}.{name} }}",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
Unsafe {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
snippet: String,
|
||||||
|
name: Symbol,
|
||||||
|
ty: Ty<'a>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
pub enum SuggestBoxingForReturnImplTrait {
|
||||||
|
#[multipart_suggestion(infer_sbfrit_change_return_type, applicability = "maybe-incorrect")]
|
||||||
|
ChangeReturnType {
|
||||||
|
#[suggestion_part(code = "Box<dyn")]
|
||||||
|
start_sp: Span,
|
||||||
|
#[suggestion_part(code = ">")]
|
||||||
|
end_sp: Span,
|
||||||
|
},
|
||||||
|
#[multipart_suggestion(infer_sbfrit_box_return_expr, applicability = "maybe-incorrect")]
|
||||||
|
BoxReturnExpr {
|
||||||
|
#[suggestion_part(code = "Box::new(")]
|
||||||
|
starts: Vec<Span>,
|
||||||
|
#[suggestion_part(code = ")")]
|
||||||
|
ends: Vec<Span>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[multipart_suggestion(infer_stp_wrap_one, applicability = "maybe-incorrect")]
|
||||||
|
pub struct SuggestTuplePatternOne {
|
||||||
|
pub variant: String,
|
||||||
|
#[suggestion_part(code = "{variant}(")]
|
||||||
|
pub span_low: Span,
|
||||||
|
#[suggestion_part(code = ")")]
|
||||||
|
pub span_high: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SuggestTuplePatternMany {
|
||||||
|
pub path: String,
|
||||||
|
pub cause_span: Span,
|
||||||
|
pub compatible_variants: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddToDiagnostic for SuggestTuplePatternMany {
|
||||||
|
fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, f: F)
|
||||||
|
where
|
||||||
|
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||||
|
{
|
||||||
|
diag.set_arg("path", self.path);
|
||||||
|
let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into());
|
||||||
|
diag.multipart_suggestions(
|
||||||
|
message,
|
||||||
|
self.compatible_variants.into_iter().map(|variant| {
|
||||||
|
vec![
|
||||||
|
(self.cause_span.shrink_to_lo(), format!("{}(", variant)),
|
||||||
|
(self.cause_span.shrink_to_hi(), ")".to_string()),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
rustc_errors::Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
pub enum TypeErrorAdditionalDiags {
|
||||||
|
#[suggestion(
|
||||||
|
infer_meant_byte_literal,
|
||||||
|
code = "b'{code}'",
|
||||||
|
applicability = "machine-applicable"
|
||||||
|
)]
|
||||||
|
MeantByteLiteral {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
code: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_meant_char_literal,
|
||||||
|
code = "'{code}'",
|
||||||
|
applicability = "machine-applicable"
|
||||||
|
)]
|
||||||
|
MeantCharLiteral {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
code: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_meant_str_literal,
|
||||||
|
code = "\"{code}\"",
|
||||||
|
applicability = "machine-applicable"
|
||||||
|
)]
|
||||||
|
MeantStrLiteral {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
code: String,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_consider_specifying_length,
|
||||||
|
code = "{length}",
|
||||||
|
applicability = "maybe-incorrect"
|
||||||
|
)]
|
||||||
|
ConsiderSpecifyingLength {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
length: u64,
|
||||||
|
},
|
||||||
|
#[note(infer_try_cannot_convert)]
|
||||||
|
TryCannotConvert { found: String, expected: String },
|
||||||
|
#[suggestion(infer_tuple_trailing_comma, code = ",", applicability = "machine-applicable")]
|
||||||
|
TupleOnlyComma {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
#[multipart_suggestion(infer_tuple_trailing_comma, applicability = "machine-applicable")]
|
||||||
|
TupleAlsoParentheses {
|
||||||
|
#[suggestion_part(code = "(")]
|
||||||
|
span_low: Span,
|
||||||
|
#[suggestion_part(code = ",)")]
|
||||||
|
span_high: Span,
|
||||||
|
},
|
||||||
|
#[suggestion(
|
||||||
|
infer_suggest_add_let_for_letchains,
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "machine-applicable",
|
||||||
|
code = "let "
|
||||||
|
)]
|
||||||
|
AddLetForLetChains {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
pub enum ObligationCauseFailureCode {
|
||||||
|
#[diag(infer_oc_method_compat, code = "E0308")]
|
||||||
|
MethodCompat {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_type_compat, code = "E0308")]
|
||||||
|
TypeCompat {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_const_compat, code = "E0308")]
|
||||||
|
ConstCompat {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_try_compat, code = "E0308")]
|
||||||
|
TryCompat {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_match_compat, code = "E0308")]
|
||||||
|
MatchCompat {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_if_else_different, code = "E0308")]
|
||||||
|
IfElseDifferent {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_no_else, code = "E0317")]
|
||||||
|
NoElse {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_no_diverge, code = "E0308")]
|
||||||
|
NoDiverge {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_fn_main_correct_type, code = "E0580")]
|
||||||
|
FnMainCorrectType {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_fn_start_correct_type, code = "E0308")]
|
||||||
|
FnStartCorrectType {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_intristic_correct_type, code = "E0308")]
|
||||||
|
IntristicCorrectType {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_method_correct_type, code = "E0308")]
|
||||||
|
MethodCorrectType {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_closure_selfref, code = "E0644")]
|
||||||
|
ClosureSelfref {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_cant_coerce, code = "E0308")]
|
||||||
|
CantCoerce {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
#[diag(infer_oc_generic, code = "E0308")]
|
||||||
|
Generic {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -49,11 +49,10 @@ use super::lexical_region_resolve::RegionResolutionError;
|
||||||
use super::region_constraints::GenericKind;
|
use super::region_constraints::GenericKind;
|
||||||
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
|
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
|
||||||
|
|
||||||
use crate::errors;
|
use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
|
||||||
use crate::infer;
|
use crate::infer;
|
||||||
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
||||||
use crate::infer::ExpectedFound;
|
use crate::infer::ExpectedFound;
|
||||||
use crate::traits::error_reporting::report_object_safety_error;
|
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||||
PredicateObligation,
|
PredicateObligation,
|
||||||
|
@ -90,6 +89,28 @@ pub use need_type_info::TypeAnnotationNeeded;
|
||||||
|
|
||||||
pub mod nice_region_error;
|
pub mod nice_region_error;
|
||||||
|
|
||||||
|
/// Makes a valid string literal from a string by escaping special characters (" and \),
|
||||||
|
/// unless they are already escaped.
|
||||||
|
fn escape_literal(s: &str) -> String {
|
||||||
|
let mut escaped = String::with_capacity(s.len());
|
||||||
|
let mut chrs = s.chars().peekable();
|
||||||
|
while let Some(first) = chrs.next() {
|
||||||
|
match (first, chrs.peek()) {
|
||||||
|
('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => {
|
||||||
|
escaped.push('\\');
|
||||||
|
escaped.push(delim);
|
||||||
|
chrs.next();
|
||||||
|
}
|
||||||
|
('"' | '\'', _) => {
|
||||||
|
escaped.push('\\');
|
||||||
|
escaped.push(first)
|
||||||
|
}
|
||||||
|
(c, _) => escaped.push(c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
escaped
|
||||||
|
}
|
||||||
|
|
||||||
/// A helper for building type related errors. The `typeck_results`
|
/// A helper for building type related errors. The `typeck_results`
|
||||||
/// field is only populated during an in-progress typeck.
|
/// field is only populated during an in-progress typeck.
|
||||||
/// Get an instance by calling `InferCtxt::err` or `FnCtxt::infer_err`.
|
/// Get an instance by calling `InferCtxt::err` or `FnCtxt::infer_err`.
|
||||||
|
@ -1899,233 +1920,182 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
debug!(?diag);
|
debug!(?diag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_error_additional_suggestions(
|
||||||
|
&self,
|
||||||
|
trace: &TypeTrace<'tcx>,
|
||||||
|
terr: TypeError<'tcx>,
|
||||||
|
) -> Vec<TypeErrorAdditionalDiags> {
|
||||||
|
use crate::traits::ObligationCauseCode::MatchExpressionArm;
|
||||||
|
let mut suggestions = Vec::new();
|
||||||
|
let span = trace.cause.span();
|
||||||
|
let values = self.resolve_vars_if_possible(trace.values);
|
||||||
|
if let Some((expected, found)) = values.ty() {
|
||||||
|
match (expected.kind(), found.kind()) {
|
||||||
|
(ty::Tuple(_), ty::Tuple(_)) => {}
|
||||||
|
// If a tuple of length one was expected and the found expression has
|
||||||
|
// parentheses around it, perhaps the user meant to write `(expr,)` to
|
||||||
|
// build a tuple (issue #86100)
|
||||||
|
(ty::Tuple(fields), _) => {
|
||||||
|
suggestions.extend(self.suggest_wrap_to_build_a_tuple( span, found, fields))
|
||||||
|
}
|
||||||
|
// If a byte was expected and the found expression is a char literal
|
||||||
|
// containing a single ASCII character, perhaps the user meant to write `b'c'` to
|
||||||
|
// specify a byte literal
|
||||||
|
(ty::Uint(ty::UintTy::U8), ty::Char) => {
|
||||||
|
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
|
||||||
|
&& let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
|
||||||
|
&& !code.starts_with("\\u") // forbid all Unicode escapes
|
||||||
|
&& code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII
|
||||||
|
{
|
||||||
|
suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral { span, code: escape_literal(code) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If a character was expected and the found expression is a string literal
|
||||||
|
// containing a single character, perhaps the user meant to write `'c'` to
|
||||||
|
// specify a character literal (issue #92479)
|
||||||
|
(ty::Char, ty::Ref(_, r, _)) if r.is_str() => {
|
||||||
|
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
|
||||||
|
&& let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
|
||||||
|
&& code.chars().count() == 1
|
||||||
|
{
|
||||||
|
suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral { span, code: escape_literal(code) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If a string was expected and the found expression is a character literal,
|
||||||
|
// perhaps the user meant to write `"s"` to specify a string literal.
|
||||||
|
(ty::Ref(_, r, _), ty::Char) if r.is_str() => {
|
||||||
|
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
|
||||||
|
if let Some(code) =
|
||||||
|
code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
|
||||||
|
{
|
||||||
|
suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { span, code: escape_literal(code) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
|
||||||
|
// we try to suggest to add the missing `let` for `if let Some(..) = expr`
|
||||||
|
(ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
|
||||||
|
suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span));
|
||||||
|
}
|
||||||
|
(ty::Array(_, _), ty::Array(_, _)) => suggestions.extend(self.suggest_specify_actual_length(terr, trace, span)),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let code = trace.cause.code();
|
||||||
|
if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code
|
||||||
|
&& let hir::MatchSource::TryDesugar = source
|
||||||
|
&& let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
|
||||||
|
{
|
||||||
|
suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() });
|
||||||
|
}
|
||||||
|
suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suggest_specify_actual_length(
|
||||||
|
&self,
|
||||||
|
terr: TypeError<'_>,
|
||||||
|
trace: &TypeTrace<'_>,
|
||||||
|
span: Span,
|
||||||
|
) -> Option<TypeErrorAdditionalDiags> {
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
let TypeError::FixedArraySize(sz) = terr else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let tykind = match hir.find_by_def_id(trace.cause.body_id) {
|
||||||
|
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => {
|
||||||
|
let body = hir.body(*body_id);
|
||||||
|
struct LetVisitor<'v> {
|
||||||
|
span: Span,
|
||||||
|
result: Option<&'v hir::Ty<'v>>,
|
||||||
|
}
|
||||||
|
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
||||||
|
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
|
||||||
|
if self.result.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Find a local statement where the initializer has
|
||||||
|
// the same span as the error and the type is specified.
|
||||||
|
if let hir::Stmt {
|
||||||
|
kind: hir::StmtKind::Local(hir::Local {
|
||||||
|
init: Some(hir::Expr {
|
||||||
|
span: init_span,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
ty: Some(array_ty),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
..
|
||||||
|
} = s
|
||||||
|
&& init_span == &self.span {
|
||||||
|
self.result = Some(*array_ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut visitor = LetVisitor { span, result: None };
|
||||||
|
visitor.visit_body(body);
|
||||||
|
visitor.result.map(|r| &r.peel_refs().kind)
|
||||||
|
}
|
||||||
|
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) => {
|
||||||
|
Some(&ty.peel_refs().kind)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(tykind) = tykind
|
||||||
|
&& let hir::TyKind::Array(_, length) = tykind
|
||||||
|
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
||||||
|
&& let Some(span) = self.tcx.hir().opt_span(*hir_id)
|
||||||
|
{
|
||||||
|
Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn report_and_explain_type_error(
|
pub fn report_and_explain_type_error(
|
||||||
&self,
|
&self,
|
||||||
trace: TypeTrace<'tcx>,
|
trace: TypeTrace<'tcx>,
|
||||||
terr: TypeError<'tcx>,
|
terr: TypeError<'tcx>,
|
||||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||||
use crate::traits::ObligationCauseCode::MatchExpressionArm;
|
|
||||||
|
|
||||||
debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
|
debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
|
||||||
|
|
||||||
let span = trace.cause.span();
|
let span = trace.cause.span();
|
||||||
let failure_code = trace.cause.as_failure_code(terr);
|
let failure_code = trace.cause.as_failure_code_diag(
|
||||||
let mut diag = match failure_code {
|
terr,
|
||||||
FailureCode::Error0038(did) => {
|
span,
|
||||||
let violations = self.tcx.object_safety_violations(did);
|
self.type_error_additional_suggestions(&trace, terr),
|
||||||
report_object_safety_error(self.tcx, span, did, violations)
|
);
|
||||||
}
|
let mut diag = self.tcx.sess.create_err(failure_code);
|
||||||
FailureCode::Error0317(failure_str) => {
|
|
||||||
struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
|
|
||||||
}
|
|
||||||
FailureCode::Error0580(failure_str) => {
|
|
||||||
struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
|
|
||||||
}
|
|
||||||
FailureCode::Error0308(failure_str) => {
|
|
||||||
fn escape_literal(s: &str) -> String {
|
|
||||||
let mut escaped = String::with_capacity(s.len());
|
|
||||||
let mut chrs = s.chars().peekable();
|
|
||||||
while let Some(first) = chrs.next() {
|
|
||||||
match (first, chrs.peek()) {
|
|
||||||
('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => {
|
|
||||||
escaped.push('\\');
|
|
||||||
escaped.push(delim);
|
|
||||||
chrs.next();
|
|
||||||
}
|
|
||||||
('"' | '\'', _) => {
|
|
||||||
escaped.push('\\');
|
|
||||||
escaped.push(first)
|
|
||||||
}
|
|
||||||
(c, _) => escaped.push(c),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
escaped
|
|
||||||
}
|
|
||||||
let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str);
|
|
||||||
let values = self.resolve_vars_if_possible(trace.values);
|
|
||||||
if let Some((expected, found)) = values.ty() {
|
|
||||||
match (expected.kind(), found.kind()) {
|
|
||||||
(ty::Tuple(_), ty::Tuple(_)) => {}
|
|
||||||
// If a tuple of length one was expected and the found expression has
|
|
||||||
// parentheses around it, perhaps the user meant to write `(expr,)` to
|
|
||||||
// build a tuple (issue #86100)
|
|
||||||
(ty::Tuple(fields), _) => {
|
|
||||||
self.emit_tuple_wrap_err(&mut err, span, found, fields)
|
|
||||||
}
|
|
||||||
// If a byte was expected and the found expression is a char literal
|
|
||||||
// containing a single ASCII character, perhaps the user meant to write `b'c'` to
|
|
||||||
// specify a byte literal
|
|
||||||
(ty::Uint(ty::UintTy::U8), ty::Char) => {
|
|
||||||
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
|
|
||||||
&& let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
|
|
||||||
&& !code.starts_with("\\u") // forbid all Unicode escapes
|
|
||||||
&& code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII
|
|
||||||
{
|
|
||||||
err.span_suggestion(
|
|
||||||
span,
|
|
||||||
"if you meant to write a byte literal, prefix with `b`",
|
|
||||||
format!("b'{}'", escape_literal(code)),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If a character was expected and the found expression is a string literal
|
|
||||||
// containing a single character, perhaps the user meant to write `'c'` to
|
|
||||||
// specify a character literal (issue #92479)
|
|
||||||
(ty::Char, ty::Ref(_, r, _)) if r.is_str() => {
|
|
||||||
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
|
|
||||||
&& let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
|
|
||||||
&& code.chars().count() == 1
|
|
||||||
{
|
|
||||||
err.span_suggestion(
|
|
||||||
span,
|
|
||||||
"if you meant to write a `char` literal, use single quotes",
|
|
||||||
format!("'{}'", escape_literal(code)),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If a string was expected and the found expression is a character literal,
|
|
||||||
// perhaps the user meant to write `"s"` to specify a string literal.
|
|
||||||
(ty::Ref(_, r, _), ty::Char) if r.is_str() => {
|
|
||||||
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
|
|
||||||
if let Some(code) =
|
|
||||||
code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
|
|
||||||
{
|
|
||||||
err.span_suggestion(
|
|
||||||
span,
|
|
||||||
"if you meant to write a `str` literal, use double quotes",
|
|
||||||
format!("\"{}\"", escape_literal(code)),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
|
|
||||||
// we try to suggest to add the missing `let` for `if let Some(..) = expr`
|
|
||||||
(ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
|
|
||||||
self.suggest_let_for_letchains(&mut err, &trace.cause, span);
|
|
||||||
}
|
|
||||||
(ty::Array(_, _), ty::Array(_, _)) => 'block: {
|
|
||||||
let hir = self.tcx.hir();
|
|
||||||
let TypeError::FixedArraySize(sz) = terr else {
|
|
||||||
break 'block;
|
|
||||||
};
|
|
||||||
let tykind = match hir.find_by_def_id(trace.cause.body_id) {
|
|
||||||
Some(hir::Node::Item(hir::Item {
|
|
||||||
kind: hir::ItemKind::Fn(_, _, body_id),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
let body = hir.body(*body_id);
|
|
||||||
struct LetVisitor<'v> {
|
|
||||||
span: Span,
|
|
||||||
result: Option<&'v hir::Ty<'v>>,
|
|
||||||
}
|
|
||||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
|
||||||
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
|
|
||||||
if self.result.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Find a local statement where the initializer has
|
|
||||||
// the same span as the error and the type is specified.
|
|
||||||
if let hir::Stmt {
|
|
||||||
kind: hir::StmtKind::Local(hir::Local {
|
|
||||||
init: Some(hir::Expr {
|
|
||||||
span: init_span,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
ty: Some(array_ty),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
..
|
|
||||||
} = s
|
|
||||||
&& init_span == &self.span {
|
|
||||||
self.result = Some(*array_ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut visitor = LetVisitor {span, result: None};
|
|
||||||
visitor.visit_body(body);
|
|
||||||
visitor.result.map(|r| &r.peel_refs().kind)
|
|
||||||
}
|
|
||||||
Some(hir::Node::Item(hir::Item {
|
|
||||||
kind: hir::ItemKind::Const(ty, _),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
Some(&ty.peel_refs().kind)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(tykind) = tykind
|
|
||||||
&& let hir::TyKind::Array(_, length) = tykind
|
|
||||||
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
|
||||||
&& let Some(span) = self.tcx.hir().opt_span(*hir_id)
|
|
||||||
{
|
|
||||||
err.span_suggestion(
|
|
||||||
span,
|
|
||||||
"consider specifying the actual array length",
|
|
||||||
sz.found,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let code = trace.cause.code();
|
|
||||||
if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code
|
|
||||||
&& let hir::MatchSource::TryDesugar = source
|
|
||||||
&& let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
|
|
||||||
{
|
|
||||||
err.note(&format!(
|
|
||||||
"`?` operator cannot convert from `{}` to `{}`",
|
|
||||||
found_ty.content(),
|
|
||||||
expected_ty.content(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
err
|
|
||||||
}
|
|
||||||
FailureCode::Error0644(failure_str) => {
|
|
||||||
struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
|
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
|
||||||
diag
|
diag
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_tuple_wrap_err(
|
fn suggest_wrap_to_build_a_tuple(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
|
||||||
span: Span,
|
span: Span,
|
||||||
found: Ty<'tcx>,
|
found: Ty<'tcx>,
|
||||||
expected_fields: &List<Ty<'tcx>>,
|
expected_fields: &List<Ty<'tcx>>,
|
||||||
) {
|
) -> Option<TypeErrorAdditionalDiags> {
|
||||||
let [expected_tup_elem] = expected_fields[..] else { return };
|
let [expected_tup_elem] = expected_fields[..] else { return None};
|
||||||
|
|
||||||
if !self.same_type_modulo_infer(expected_tup_elem, found) {
|
if !self.same_type_modulo_infer(expected_tup_elem, found) {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
|
let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
|
||||||
else { return };
|
else { return None };
|
||||||
|
|
||||||
let msg = "use a trailing comma to create a tuple with one element";
|
let sugg = if code.starts_with('(') && code.ends_with(')') {
|
||||||
if code.starts_with('(') && code.ends_with(')') {
|
|
||||||
let before_close = span.hi() - BytePos::from_u32(1);
|
let before_close = span.hi() - BytePos::from_u32(1);
|
||||||
err.span_suggestion(
|
TypeErrorAdditionalDiags::TupleOnlyComma {
|
||||||
span.with_hi(before_close).shrink_to_hi(),
|
span: span.with_hi(before_close).shrink_to_hi(),
|
||||||
msg,
|
}
|
||||||
",",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
err.multipart_suggestion(
|
TypeErrorAdditionalDiags::TupleAlsoParentheses {
|
||||||
msg,
|
span_low: span.shrink_to_lo(),
|
||||||
vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())],
|
span_high: span.shrink_to_hi(),
|
||||||
Applicability::MachineApplicable,
|
}
|
||||||
);
|
};
|
||||||
}
|
Some(sugg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn values_str(
|
fn values_str(
|
||||||
|
@ -2828,57 +2798,91 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FailureCode {
|
pub enum FailureCode {
|
||||||
Error0038(DefId),
|
Error0317,
|
||||||
Error0317(&'static str),
|
Error0580,
|
||||||
Error0580(&'static str),
|
Error0308,
|
||||||
Error0308(&'static str),
|
Error0644,
|
||||||
Error0644(&'static str),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ObligationCauseExt<'tcx> {
|
pub trait ObligationCauseExt<'tcx> {
|
||||||
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode;
|
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode;
|
||||||
|
|
||||||
|
fn as_failure_code_diag(
|
||||||
|
&self,
|
||||||
|
terr: TypeError<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
) -> ObligationCauseFailureCode;
|
||||||
fn as_requirement_str(&self) -> &'static str;
|
fn as_requirement_str(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
||||||
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode {
|
fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode {
|
||||||
use self::FailureCode::*;
|
use self::FailureCode::*;
|
||||||
|
use crate::traits::ObligationCauseCode::*;
|
||||||
|
match self.code() {
|
||||||
|
IfExpressionWithNoElse => Error0317,
|
||||||
|
MainFunctionType => Error0580,
|
||||||
|
CompareImplItemObligation { .. }
|
||||||
|
| MatchExpressionArm(_)
|
||||||
|
| IfExpression { .. }
|
||||||
|
| LetElse
|
||||||
|
| StartFunctionType
|
||||||
|
| IntrinsicType
|
||||||
|
| MethodReceiver => Error0308,
|
||||||
|
|
||||||
|
// In the case where we have no more specific thing to
|
||||||
|
// say, also take a look at the error code, maybe we can
|
||||||
|
// tailor to that.
|
||||||
|
_ => match terr {
|
||||||
|
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => Error0644,
|
||||||
|
TypeError::IntrinsicCast => Error0308,
|
||||||
|
_ => Error0308,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn as_failure_code_diag(
|
||||||
|
&self,
|
||||||
|
terr: TypeError<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
|
) -> ObligationCauseFailureCode {
|
||||||
use crate::traits::ObligationCauseCode::*;
|
use crate::traits::ObligationCauseCode::*;
|
||||||
match self.code() {
|
match self.code() {
|
||||||
CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
|
CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
|
||||||
Error0308("method not compatible with trait")
|
ObligationCauseFailureCode::MethodCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
|
CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
|
||||||
Error0308("type not compatible with trait")
|
ObligationCauseFailureCode::TypeCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
|
CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
|
||||||
Error0308("const not compatible with trait")
|
ObligationCauseFailureCode::ConstCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
|
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
|
||||||
Error0308(match source {
|
hir::MatchSource::TryDesugar => {
|
||||||
hir::MatchSource::TryDesugar => "`?` operator has incompatible types",
|
ObligationCauseFailureCode::TryCompat { span, subdiags }
|
||||||
_ => "`match` arms have incompatible types",
|
}
|
||||||
})
|
_ => ObligationCauseFailureCode::MatchCompat { span, subdiags },
|
||||||
}
|
},
|
||||||
IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
|
IfExpression { .. } => ObligationCauseFailureCode::IfElseDifferent { span, subdiags },
|
||||||
IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
|
IfExpressionWithNoElse => ObligationCauseFailureCode::NoElse { span },
|
||||||
LetElse => Error0308("`else` clause of `let...else` does not diverge"),
|
LetElse => ObligationCauseFailureCode::NoDiverge { span, subdiags },
|
||||||
MainFunctionType => Error0580("`main` function has wrong type"),
|
MainFunctionType => ObligationCauseFailureCode::FnMainCorrectType { span },
|
||||||
StartFunctionType => Error0308("`#[start]` function has wrong type"),
|
StartFunctionType => ObligationCauseFailureCode::FnStartCorrectType { span, subdiags },
|
||||||
IntrinsicType => Error0308("intrinsic has wrong type"),
|
IntrinsicType => ObligationCauseFailureCode::IntristicCorrectType { span, subdiags },
|
||||||
MethodReceiver => Error0308("mismatched `self` parameter type"),
|
MethodReceiver => ObligationCauseFailureCode::MethodCorrectType { span, subdiags },
|
||||||
|
|
||||||
// In the case where we have no more specific thing to
|
// In the case where we have no more specific thing to
|
||||||
// say, also take a look at the error code, maybe we can
|
// say, also take a look at the error code, maybe we can
|
||||||
// tailor to that.
|
// tailor to that.
|
||||||
_ => match terr {
|
_ => match terr {
|
||||||
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
|
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
|
||||||
Error0644("closure/generator type that references itself")
|
ObligationCauseFailureCode::ClosureSelfref { span }
|
||||||
}
|
}
|
||||||
TypeError::IntrinsicCast => {
|
TypeError::IntrinsicCast => {
|
||||||
Error0308("cannot coerce intrinsics to function pointers")
|
ObligationCauseFailureCode::CantCoerce { span, subdiags }
|
||||||
}
|
}
|
||||||
_ => Error0308("mismatched types"),
|
_ => ObligationCauseFailureCode::Generic { span, subdiags },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use hir::def::CtorKind;
|
use hir::def::CtorKind;
|
||||||
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
use rustc_errors::{Applicability, Diagnostic};
|
use rustc_errors::Diagnostic;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_middle::traits::{
|
use rustc_middle::traits::{
|
||||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||||
|
@ -13,11 +13,20 @@ use rustc_span::{sym, BytePos, Span};
|
||||||
use rustc_target::abi::FieldIdx;
|
use rustc_target::abi::FieldIdx;
|
||||||
|
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
ConsiderAddingAwait, SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding,
|
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
||||||
|
FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
|
||||||
|
SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
|
||||||
|
SuggestTuplePatternOne, TypeErrorAdditionalDiags,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::TypeErrCtxt;
|
use super::TypeErrCtxt;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum SuggestAsRefKind {
|
||||||
|
Option,
|
||||||
|
Result,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
pub(super) fn suggest_remove_semi_or_return_binding(
|
pub(super) fn suggest_remove_semi_or_return_binding(
|
||||||
&self,
|
&self,
|
||||||
|
@ -72,25 +81,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
return_sp: Span,
|
return_sp: Span,
|
||||||
arm_spans: impl Iterator<Item = Span>,
|
arm_spans: impl Iterator<Item = Span>,
|
||||||
) {
|
) {
|
||||||
err.multipart_suggestion(
|
let sugg = SuggestBoxingForReturnImplTrait::ChangeReturnType {
|
||||||
"you could change the return type to be a boxed trait object",
|
start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)),
|
||||||
vec![
|
end_sp: return_sp.shrink_to_hi(),
|
||||||
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
|
};
|
||||||
(return_sp.shrink_to_hi(), ">".to_string()),
|
err.subdiagnostic(sugg);
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
let mut starts = Vec::new();
|
||||||
);
|
let mut ends = Vec::new();
|
||||||
let sugg = arm_spans
|
for span in arm_spans {
|
||||||
.flat_map(|sp| {
|
starts.push(span.shrink_to_lo());
|
||||||
[(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
|
ends.push(span.shrink_to_hi());
|
||||||
.into_iter()
|
}
|
||||||
})
|
let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends };
|
||||||
.collect::<Vec<_>>();
|
err.subdiagnostic(sugg);
|
||||||
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(
|
pub(super) fn suggest_tuple_pattern(
|
||||||
|
@ -130,30 +134,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
match &compatible_variants[..] {
|
match &compatible_variants[..] {
|
||||||
[] => {}
|
[] => {}
|
||||||
[variant] => {
|
[variant] => {
|
||||||
diag.multipart_suggestion_verbose(
|
let sugg = SuggestTuplePatternOne {
|
||||||
&format!("try wrapping the pattern in `{}`", variant),
|
variant: variant.to_owned(),
|
||||||
vec![
|
span_low: cause.span.shrink_to_lo(),
|
||||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
span_high: cause.span.shrink_to_hi(),
|
||||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
};
|
||||||
],
|
diag.subdiagnostic(sugg);
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// More than one matching variant.
|
// More than one matching variant.
|
||||||
diag.multipart_suggestions(
|
let sugg = SuggestTuplePatternMany {
|
||||||
&format!(
|
path: self.tcx.def_path_str(expected_adt.did()),
|
||||||
"try wrapping the pattern in a variant of `{}`",
|
cause_span: cause.span,
|
||||||
self.tcx.def_path_str(expected_adt.did())
|
compatible_variants,
|
||||||
),
|
};
|
||||||
compatible_variants.into_iter().map(|variant| {
|
diag.subdiagnostic(sugg);
|
||||||
vec![
|
|
||||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
|
||||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,15 +251,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn suggest_await_on_future(&self, diag: &mut Diagnostic, sp: Span) {
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
sp.shrink_to_hi(),
|
|
||||||
"consider `await`ing on the `Future`",
|
|
||||||
".await",
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn suggest_accessing_field_where_appropriate(
|
pub(super) fn suggest_accessing_field_where_appropriate(
|
||||||
&self,
|
&self,
|
||||||
cause: &ObligationCause<'tcx>,
|
cause: &ObligationCause<'tcx>,
|
||||||
|
@ -291,21 +277,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||||
let suggestion = if expected_def.is_struct() {
|
let suggestion = if expected_def.is_struct() {
|
||||||
format!("{}.{}", snippet, name)
|
SuggestAccessingField::Safe { span, snippet, name, ty }
|
||||||
} else if expected_def.is_union() {
|
} else if expected_def.is_union() {
|
||||||
format!("unsafe {{ {}.{} }}", snippet, name)
|
SuggestAccessingField::Unsafe { span, snippet, name, ty }
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
diag.span_suggestion(
|
diag.subdiagnostic(suggestion);
|
||||||
span,
|
|
||||||
&format!(
|
|
||||||
"you might have meant to use field `{}` whose type is `{}`",
|
|
||||||
name, ty
|
|
||||||
),
|
|
||||||
suggestion,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,15 +299,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
diag: &mut Diagnostic,
|
diag: &mut Diagnostic,
|
||||||
) {
|
) {
|
||||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
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)
|
&& let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found)
|
||||||
{
|
{
|
||||||
diag.span_suggestion(
|
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
||||||
span,
|
let snippet = snippet.trim_start_matches('&');
|
||||||
msg,
|
let subdiag = match msg {
|
||||||
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
|
||||||
format!("{}.as_ref()", snippet.trim_start_matches('&')),
|
SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
|
||||||
Applicability::MachineApplicable,
|
};
|
||||||
);
|
diag.subdiagnostic(subdiag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,31 +340,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (msg, sug) = match (expected.is_ref(), found.is_ref()) {
|
let sugg = match (expected.is_ref(), found.is_ref()) {
|
||||||
(true, false) => {
|
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
|
||||||
let msg = "consider using a reference";
|
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
|
||||||
let sug = format!("&{fn_name}");
|
|
||||||
(msg, sug)
|
|
||||||
}
|
|
||||||
(false, true) => {
|
|
||||||
let msg = "consider removing the reference";
|
|
||||||
let sug = format!("{fn_name}");
|
|
||||||
(msg, sug)
|
|
||||||
}
|
|
||||||
(true, true) => {
|
(true, true) => {
|
||||||
diag.note("fn items are distinct from fn pointers");
|
diag.subdiagnostic(FnItemsAreDistinct);
|
||||||
let msg = "consider casting to a fn pointer";
|
FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
|
||||||
let sug = format!("&({fn_name} as {sig})");
|
|
||||||
(msg, sug)
|
|
||||||
}
|
}
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
diag.note("fn items are distinct from fn pointers");
|
diag.subdiagnostic(FnItemsAreDistinct);
|
||||||
let msg = "consider casting to a fn pointer";
|
FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
|
||||||
let sug = format!("{fn_name} as {sig}");
|
|
||||||
(msg, sug)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
diag.span_suggestion_verbose(span, msg, sug, Applicability::MaybeIncorrect);
|
diag.subdiagnostic(sugg);
|
||||||
}
|
}
|
||||||
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
||||||
let expected_sig =
|
let expected_sig =
|
||||||
|
@ -395,7 +361,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
|
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
|
||||||
|
|
||||||
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
|
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
|
||||||
diag.note("different fn items have unique types, even if their signatures are the same");
|
diag.subdiagnostic(FnUniqTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
||||||
|
@ -409,16 +375,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
|
let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
|
||||||
let sug = if found.is_ref() {
|
let sug = if found.is_ref() {
|
||||||
format!("&({fn_name} as {found_sig})")
|
FunctionPointerSuggestion::CastBothRef {
|
||||||
|
span,
|
||||||
|
fn_name,
|
||||||
|
found_sig: *found_sig,
|
||||||
|
expected_sig: *expected_sig,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{fn_name} as {found_sig}")
|
FunctionPointerSuggestion::CastBoth {
|
||||||
|
span,
|
||||||
|
fn_name,
|
||||||
|
found_sig: *found_sig,
|
||||||
|
expected_sig: *expected_sig,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let msg = format!(
|
diag.subdiagnostic(sug);
|
||||||
"consider casting both fn items to fn pointers using `as {expected_sig}`"
|
|
||||||
);
|
|
||||||
|
|
||||||
diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect);
|
|
||||||
}
|
}
|
||||||
(ty::FnDef(did, substs), ty::FnPtr(sig)) => {
|
(ty::FnDef(did, substs), ty::FnPtr(sig)) => {
|
||||||
let expected_sig =
|
let expected_sig =
|
||||||
|
@ -437,7 +409,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
format!("{fn_name} as {found_sig}")
|
format!("{fn_name} as {found_sig}")
|
||||||
};
|
};
|
||||||
|
|
||||||
diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting));
|
diag.subdiagnostic(FnConsiderCasting { casting });
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return;
|
return;
|
||||||
|
@ -445,23 +417,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
pub fn should_suggest_as_ref_kind(
|
||||||
|
&self,
|
||||||
|
expected: Ty<'tcx>,
|
||||||
|
found: Ty<'tcx>,
|
||||||
|
) -> Option<SuggestAsRefKind> {
|
||||||
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
|
||||||
(expected.kind(), found.kind())
|
(expected.kind(), found.kind())
|
||||||
{
|
{
|
||||||
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
||||||
if exp_def == &found_def {
|
if exp_def == &found_def {
|
||||||
let have_as_ref = &[
|
let have_as_ref = &[
|
||||||
(
|
(sym::Option, SuggestAsRefKind::Option),
|
||||||
sym::Option,
|
(sym::Result, SuggestAsRefKind::Result),
|
||||||
"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)| {
|
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||||
|
@ -495,15 +463,28 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
|
||||||
|
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||||
|
match self.should_suggest_as_ref_kind(expected, found) {
|
||||||
|
Some(SuggestAsRefKind::Option) => Some(
|
||||||
|
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||||
|
`.as_ref()`",
|
||||||
|
),
|
||||||
|
Some(SuggestAsRefKind::Result) => Some(
|
||||||
|
"you can convert from `&Result<T, E>` to \
|
||||||
|
`Result<&T, &E>` using `.as_ref()`",
|
||||||
|
),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Try to find code with pattern `if Some(..) = expr`
|
/// Try to find code with pattern `if Some(..) = expr`
|
||||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
/// 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
|
/// 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(
|
pub(super) fn suggest_let_for_letchains(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
|
||||||
cause: &ObligationCause<'_>,
|
cause: &ObligationCause<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) {
|
) -> Option<TypeErrorAdditionalDiags> {
|
||||||
let hir = self.tcx.hir();
|
let hir = self.tcx.hir();
|
||||||
if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
|
if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
|
||||||
let hir::Node::Item(hir::Item {
|
let hir::Node::Item(hir::Item {
|
||||||
|
@ -550,9 +531,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
||||||
visitor.visit_body(&body);
|
visitor.visit_body(&body);
|
||||||
if visitor.result {
|
if visitor.result {
|
||||||
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
|
return Some(TypeErrorAdditionalDiags::AddLetForLetChains{span: span.shrink_to_lo()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ use hir::def::DefKind;
|
||||||
use polonius_engine::Atom;
|
use polonius_engine::Atom;
|
||||||
use rustc_data_structures::captures::Captures;
|
use rustc_data_structures::captures::Captures;
|
||||||
use rustc_data_structures::intern::Interned;
|
use rustc_data_structures::intern::Interned;
|
||||||
|
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::LangItem;
|
use rustc_hir::LangItem;
|
||||||
|
@ -864,8 +865,8 @@ impl<'tcx> PolyTraitRef<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl rustc_errors::IntoDiagnosticArg for PolyTraitRef<'_> {
|
impl<'tcx> IntoDiagnosticArg for TraitRef<'tcx> {
|
||||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
self.to_string().into_diagnostic_arg()
|
self.to_string().into_diagnostic_arg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -910,6 +911,12 @@ impl<'tcx> ExistentialTraitRef<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> IntoDiagnosticArg for ExistentialTraitRef<'tcx> {
|
||||||
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
|
self.to_string().into_diagnostic_arg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type PolyExistentialTraitRef<'tcx> = Binder<'tcx, ExistentialTraitRef<'tcx>>;
|
pub type PolyExistentialTraitRef<'tcx> = Binder<'tcx, ExistentialTraitRef<'tcx>>;
|
||||||
|
|
||||||
impl<'tcx> PolyExistentialTraitRef<'tcx> {
|
impl<'tcx> PolyExistentialTraitRef<'tcx> {
|
||||||
|
@ -926,12 +933,6 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl rustc_errors::IntoDiagnosticArg for PolyExistentialTraitRef<'_> {
|
|
||||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
|
||||||
self.to_string().into_diagnostic_arg()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
|
||||||
#[derive(HashStable)]
|
#[derive(HashStable)]
|
||||||
pub enum BoundVariableKind {
|
pub enum BoundVariableKind {
|
||||||
|
@ -1146,6 +1147,15 @@ impl<'tcx, T: IntoIterator> Binder<'tcx, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx, T> IntoDiagnosticArg for Binder<'tcx, T>
|
||||||
|
where
|
||||||
|
T: IntoDiagnosticArg,
|
||||||
|
{
|
||||||
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
|
self.0.into_diagnostic_arg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SkipBindersAt<'tcx> {
|
struct SkipBindersAt<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
index: ty::DebruijnIndex,
|
index: ty::DebruijnIndex,
|
||||||
|
@ -1362,6 +1372,12 @@ impl<'tcx> FnSig<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> IntoDiagnosticArg for FnSig<'tcx> {
|
||||||
|
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||||
|
self.to_string().into_diagnostic_arg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
|
pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
|
||||||
|
|
||||||
impl<'tcx> PolyFnSig<'tcx> {
|
impl<'tcx> PolyFnSig<'tcx> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue