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:
bors 2023-04-10 19:38:48 +00:00
commit 88fb1b922b
6 changed files with 774 additions and 363 deletions

View file

@ -768,7 +768,7 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
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;
}

View file

@ -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}`

View file

@ -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>,
},
}

View file

@ -49,11 +49,10 @@
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 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 @@ enum Similar<'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 @@ fn report_inference_failure(
}
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 },
},
}
}

View file

@ -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_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 @@ pub(super) fn suggest_boxing_for_return_impl_trait(
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 @@ pub(super) fn suggest_tuple_pattern(
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 @@ pub(super) fn suggest_await_on_expect_found(
}
}
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 @@ pub(super) fn suggest_accessing_field_where_appropriate(
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 @@ pub(super) fn suggest_as_ref_where_appropriate(
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 @@ pub(super) fn suggest_function_pointers(
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 @@ pub(super) fn suggest_function_pointers(
&(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 @@ pub(super) fn suggest_function_pointers(
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 @@ pub(super) fn suggest_function_pointers(
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 @@ pub(super) fn suggest_function_pointers(
};
}
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 @@ pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Opti
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 @@ fn visit_body(&mut self, body: &'v hir::Body<'v>) {
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
}
}

View file

@ -15,6 +15,7 @@
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 @@ pub fn def_id(&self) -> DefId {
}
}
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 @@ pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::TraitRef
}
}
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 @@ pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::PolyTrai
}
}
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 @@ pub fn iter(self) -> impl Iterator<Item = ty::Binder<'tcx, T::Item>> {
}
}
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 @@ fn fake() -> 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> {