diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index b02eae19fce..c97fcc92856 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -768,7 +768,7 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator>) -> 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; } diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 1e43644be89..4d4a7880b00 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -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` to `Option<&T>` using `.as_ref()` +infer_sarwa_result = you can convert from `&Result` 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}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 6bbd3fd3e6e..b129621130d 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -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, + #[suggestion_part(code = ")")] + ends: Vec, + }, +} + +#[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, +} + +impl AddToDiagnostic for SuggestTuplePatternMany { + fn add_to_diagnostic_with(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, + }, + #[diag(infer_oc_type_compat, code = "E0308")] + TypeCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_const_compat, code = "E0308")] + ConstCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_try_compat, code = "E0308")] + TryCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_match_compat, code = "E0308")] + MatchCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_if_else_different, code = "E0308")] + IfElseDifferent { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[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, + }, + #[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, + }, + #[diag(infer_oc_intristic_correct_type, code = "E0308")] + IntristicCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_method_correct_type, code = "E0308")] + MethodCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[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, + }, + #[diag(infer_oc_generic, code = "E0308")] + Generic { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 86fca9797d0..7901bc94021 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -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 { + 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 { + 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>, - ) { - let [expected_tup_elem] = expected_fields[..] else { return }; + ) -> Option { + 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, + ) -> 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, + ) -> 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 }, }, } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 8ad143247e8..b5aeca12a1f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -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, ) { - 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".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::>(); - 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 to Option<&T>, remove the extra `&` - format!("{}.as_ref()", snippet.trim_start_matches('&')), - Applicability::MachineApplicable, - ); + // HACK: fix issue# 100605, suggesting convert from &Option 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 { 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` to `Option<&T>` using \ - `.as_ref()`", - ), - ( - sym::Result, - "you can convert from `&Result` 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` to `Option<&T>` using \ + `.as_ref()`", + ), + Some(SuggestAsRefKind::Result) => Some( + "you can convert from `&Result` 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 { 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 } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 5bbd396d6f3..24cbe8e8281 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -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> { } } +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> {