mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
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
@ -768,7 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
|
||||
let trace =
|
||||
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();
|
||||
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
|
||||
.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> {
|
||||
pub fn new_fully_qualified(
|
||||
span: Span,
|
||||
@ -1157,3 +1145,380 @@ pub struct OpaqueCapturesLifetime<'tcx> {
|
||||
pub opaque_ty_span: Span,
|
||||
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::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
|
||||
|
||||
use crate::errors;
|
||||
use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
|
||||
use crate::infer;
|
||||
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
||||
use crate::infer::ExpectedFound;
|
||||
use crate::traits::error_reporting::report_object_safety_error;
|
||||
use crate::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
PredicateObligation,
|
||||
@ -90,6 +89,28 @@ pub use need_type_info::TypeAnnotationNeeded;
|
||||
|
||||
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`
|
||||
/// field is only populated during an in-progress typeck.
|
||||
/// Get an instance by calling `InferCtxt::err` or `FnCtxt::infer_err`.
|
||||
@ -1899,233 +1920,182 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
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(
|
||||
&self,
|
||||
trace: TypeTrace<'tcx>,
|
||||
terr: TypeError<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
use crate::traits::ObligationCauseCode::MatchExpressionArm;
|
||||
|
||||
debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
|
||||
|
||||
let span = trace.cause.span();
|
||||
let failure_code = trace.cause.as_failure_code(terr);
|
||||
let mut diag = match failure_code {
|
||||
FailureCode::Error0038(did) => {
|
||||
let violations = self.tcx.object_safety_violations(did);
|
||||
report_object_safety_error(self.tcx, span, did, violations)
|
||||
}
|
||||
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)
|
||||
}
|
||||
};
|
||||
let failure_code = trace.cause.as_failure_code_diag(
|
||||
terr,
|
||||
span,
|
||||
self.type_error_additional_suggestions(&trace, terr),
|
||||
);
|
||||
let mut diag = self.tcx.sess.create_err(failure_code);
|
||||
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
|
||||
diag
|
||||
}
|
||||
|
||||
fn emit_tuple_wrap_err(
|
||||
fn suggest_wrap_to_build_a_tuple(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
span: Span,
|
||||
found: Ty<'tcx>,
|
||||
expected_fields: &List<Ty<'tcx>>,
|
||||
) {
|
||||
let [expected_tup_elem] = expected_fields[..] else { return };
|
||||
) -> Option<TypeErrorAdditionalDiags> {
|
||||
let [expected_tup_elem] = expected_fields[..] else { return None};
|
||||
|
||||
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)
|
||||
else { return };
|
||||
else { return None };
|
||||
|
||||
let msg = "use a trailing comma to create a tuple with one element";
|
||||
if code.starts_with('(') && code.ends_with(')') {
|
||||
let sugg = if code.starts_with('(') && code.ends_with(')') {
|
||||
let before_close = span.hi() - BytePos::from_u32(1);
|
||||
err.span_suggestion(
|
||||
span.with_hi(before_close).shrink_to_hi(),
|
||||
msg,
|
||||
",",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
TypeErrorAdditionalDiags::TupleOnlyComma {
|
||||
span: span.with_hi(before_close).shrink_to_hi(),
|
||||
}
|
||||
} else {
|
||||
err.multipart_suggestion(
|
||||
msg,
|
||||
vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
TypeErrorAdditionalDiags::TupleAlsoParentheses {
|
||||
span_low: span.shrink_to_lo(),
|
||||
span_high: span.shrink_to_hi(),
|
||||
}
|
||||
};
|
||||
Some(sugg)
|
||||
}
|
||||
|
||||
fn values_str(
|
||||
@ -2828,57 +2798,91 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
|
||||
pub enum FailureCode {
|
||||
Error0038(DefId),
|
||||
Error0317(&'static str),
|
||||
Error0580(&'static str),
|
||||
Error0308(&'static str),
|
||||
Error0644(&'static str),
|
||||
Error0317,
|
||||
Error0580,
|
||||
Error0308,
|
||||
Error0644,
|
||||
}
|
||||
|
||||
pub trait ObligationCauseExt<'tcx> {
|
||||
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;
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
||||
fn as_failure_code(&self, terr: TypeError<'tcx>) -> 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::*;
|
||||
match self.code() {
|
||||
CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
|
||||
Error0308("method not compatible with trait")
|
||||
ObligationCauseFailureCode::MethodCompat { span, subdiags }
|
||||
}
|
||||
CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
|
||||
Error0308("type not compatible with trait")
|
||||
ObligationCauseFailureCode::TypeCompat { span, subdiags }
|
||||
}
|
||||
CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
|
||||
Error0308("const not compatible with trait")
|
||||
ObligationCauseFailureCode::ConstCompat { span, subdiags }
|
||||
}
|
||||
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
|
||||
Error0308(match source {
|
||||
hir::MatchSource::TryDesugar => "`?` operator has incompatible types",
|
||||
_ => "`match` arms have incompatible types",
|
||||
})
|
||||
}
|
||||
IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
|
||||
IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
|
||||
LetElse => Error0308("`else` clause of `let...else` does not diverge"),
|
||||
MainFunctionType => Error0580("`main` function has wrong type"),
|
||||
StartFunctionType => Error0308("`#[start]` function has wrong type"),
|
||||
IntrinsicType => Error0308("intrinsic has wrong type"),
|
||||
MethodReceiver => Error0308("mismatched `self` parameter type"),
|
||||
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
|
||||
hir::MatchSource::TryDesugar => {
|
||||
ObligationCauseFailureCode::TryCompat { span, subdiags }
|
||||
}
|
||||
_ => ObligationCauseFailureCode::MatchCompat { span, subdiags },
|
||||
},
|
||||
IfExpression { .. } => ObligationCauseFailureCode::IfElseDifferent { span, subdiags },
|
||||
IfExpressionWithNoElse => ObligationCauseFailureCode::NoElse { span },
|
||||
LetElse => ObligationCauseFailureCode::NoDiverge { span, subdiags },
|
||||
MainFunctionType => ObligationCauseFailureCode::FnMainCorrectType { span },
|
||||
StartFunctionType => ObligationCauseFailureCode::FnStartCorrectType { span, subdiags },
|
||||
IntrinsicType => ObligationCauseFailureCode::IntristicCorrectType { span, subdiags },
|
||||
MethodReceiver => ObligationCauseFailureCode::MethodCorrectType { span, subdiags },
|
||||
|
||||
// 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("closure/generator type that references itself")
|
||||
ObligationCauseFailureCode::ClosureSelfref { span }
|
||||
}
|
||||
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::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
@ -13,11 +13,20 @@ use rustc_span::{sym, BytePos, Span};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
|
||||
use crate::errors::{
|
||||
ConsiderAddingAwait, SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding,
|
||||
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
||||
FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
|
||||
SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
|
||||
SuggestTuplePatternOne, TypeErrorAdditionalDiags,
|
||||
};
|
||||
|
||||
use super::TypeErrCtxt;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SuggestAsRefKind {
|
||||
Option,
|
||||
Result,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub(super) fn suggest_remove_semi_or_return_binding(
|
||||
&self,
|
||||
@ -72,25 +81,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
return_sp: Span,
|
||||
arm_spans: impl Iterator<Item = Span>,
|
||||
) {
|
||||
err.multipart_suggestion(
|
||||
"you could change the return type to be a boxed trait object",
|
||||
vec![
|
||||
(return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
|
||||
(return_sp.shrink_to_hi(), ">".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = arm_spans
|
||||
.flat_map(|sp| {
|
||||
[(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
|
||||
.into_iter()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
err.multipart_suggestion(
|
||||
"if you change the return type to expect trait objects, box the returned expressions",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = SuggestBoxingForReturnImplTrait::ChangeReturnType {
|
||||
start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)),
|
||||
end_sp: return_sp.shrink_to_hi(),
|
||||
};
|
||||
err.subdiagnostic(sugg);
|
||||
|
||||
let mut starts = Vec::new();
|
||||
let mut ends = Vec::new();
|
||||
for span in arm_spans {
|
||||
starts.push(span.shrink_to_lo());
|
||||
ends.push(span.shrink_to_hi());
|
||||
}
|
||||
let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends };
|
||||
err.subdiagnostic(sugg);
|
||||
}
|
||||
|
||||
pub(super) fn suggest_tuple_pattern(
|
||||
@ -130,30 +134,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
match &compatible_variants[..] {
|
||||
[] => {}
|
||||
[variant] => {
|
||||
diag.multipart_suggestion_verbose(
|
||||
&format!("try wrapping the pattern in `{}`", variant),
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = SuggestTuplePatternOne {
|
||||
variant: variant.to_owned(),
|
||||
span_low: cause.span.shrink_to_lo(),
|
||||
span_high: cause.span.shrink_to_hi(),
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
diag.multipart_suggestions(
|
||||
&format!(
|
||||
"try wrapping the pattern in a variant of `{}`",
|
||||
self.tcx.def_path_str(expected_adt.did())
|
||||
),
|
||||
compatible_variants.into_iter().map(|variant| {
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
}),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = SuggestTuplePatternMany {
|
||||
path: self.tcx.def_path_str(expected_adt.did()),
|
||||
cause_span: cause.span,
|
||||
compatible_variants,
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
@ -291,21 +277,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let suggestion = if expected_def.is_struct() {
|
||||
format!("{}.{}", snippet, name)
|
||||
SuggestAccessingField::Safe { span, snippet, name, ty }
|
||||
} else if expected_def.is_union() {
|
||||
format!("unsafe {{ {}.{} }}", snippet, name)
|
||||
SuggestAccessingField::Unsafe { span, snippet, name, ty }
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"you might have meant to use field `{}` whose type is `{}`",
|
||||
name, ty
|
||||
),
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.subdiagnostic(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,15 +299,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||
&& let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
|
||||
&& let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
msg,
|
||||
// HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
|
||||
format!("{}.as_ref()", snippet.trim_start_matches('&')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,31 +340,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
let (msg, sug) = match (expected.is_ref(), found.is_ref()) {
|
||||
(true, false) => {
|
||||
let msg = "consider using a reference";
|
||||
let sug = format!("&{fn_name}");
|
||||
(msg, sug)
|
||||
}
|
||||
(false, true) => {
|
||||
let msg = "consider removing the reference";
|
||||
let sug = format!("{fn_name}");
|
||||
(msg, sug)
|
||||
}
|
||||
let sugg = match (expected.is_ref(), found.is_ref()) {
|
||||
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
|
||||
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
|
||||
(true, true) => {
|
||||
diag.note("fn items are distinct from fn pointers");
|
||||
let msg = "consider casting to a fn pointer";
|
||||
let sug = format!("&({fn_name} as {sig})");
|
||||
(msg, sug)
|
||||
diag.subdiagnostic(FnItemsAreDistinct);
|
||||
FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
|
||||
}
|
||||
(false, false) => {
|
||||
diag.note("fn items are distinct from fn pointers");
|
||||
let msg = "consider casting to a fn pointer";
|
||||
let sug = format!("{fn_name} as {sig}");
|
||||
(msg, sug)
|
||||
diag.subdiagnostic(FnItemsAreDistinct);
|
||||
FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
|
||||
}
|
||||
};
|
||||
diag.span_suggestion_verbose(span, msg, sug, Applicability::MaybeIncorrect);
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
|
||||
let expected_sig =
|
||||
@ -395,7 +361,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
|
||||
|
||||
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)
|
||||
@ -409,16 +375,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
|
||||
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 {
|
||||
format!("{fn_name} as {found_sig}")
|
||||
FunctionPointerSuggestion::CastBoth {
|
||||
span,
|
||||
fn_name,
|
||||
found_sig: *found_sig,
|
||||
expected_sig: *expected_sig,
|
||||
}
|
||||
};
|
||||
|
||||
let msg = format!(
|
||||
"consider casting both fn items to fn pointers using `as {expected_sig}`"
|
||||
);
|
||||
|
||||
diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect);
|
||||
diag.subdiagnostic(sug);
|
||||
}
|
||||
(ty::FnDef(did, substs), ty::FnPtr(sig)) => {
|
||||
let expected_sig =
|
||||
@ -437,7 +409,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
format!("{fn_name} as {found_sig}")
|
||||
};
|
||||
|
||||
diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting));
|
||||
diag.subdiagnostic(FnConsiderCasting { casting });
|
||||
}
|
||||
_ => {
|
||||
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, _)) =
|
||||
(expected.kind(), found.kind())
|
||||
{
|
||||
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
|
||||
if exp_def == &found_def {
|
||||
let have_as_ref = &[
|
||||
(
|
||||
sym::Option,
|
||||
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||
`.as_ref()`",
|
||||
),
|
||||
(
|
||||
sym::Result,
|
||||
"you can convert from `&Result<T, E>` to \
|
||||
`Result<&T, &E>` using `.as_ref()`",
|
||||
),
|
||||
(sym::Option, SuggestAsRefKind::Option),
|
||||
(sym::Result, SuggestAsRefKind::Result),
|
||||
];
|
||||
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||
@ -495,15 +463,28 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
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`
|
||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||
pub(super) fn suggest_let_for_letchains(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
cause: &ObligationCause<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
) -> Option<TypeErrorAdditionalDiags> {
|
||||
let hir = self.tcx.hir();
|
||||
if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
|
||||
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 };
|
||||
visitor.visit_body(&body);
|
||||
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 rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::LangItem;
|
||||
@ -864,8 +865,8 @@ impl<'tcx> PolyTraitRef<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl rustc_errors::IntoDiagnosticArg for PolyTraitRef<'_> {
|
||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
||||
impl<'tcx> IntoDiagnosticArg for TraitRef<'tcx> {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
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>>;
|
||||
|
||||
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(HashStable)]
|
||||
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> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
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>>;
|
||||
|
||||
impl<'tcx> PolyFnSig<'tcx> {
|
||||
|
Loading…
Reference in New Issue
Block a user