diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 15 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 85 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/astconv/mod.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/demand.rs | 106 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/collect.rs | 12 |
7 files changed, 147 insertions, 109 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 550c1c43530..232cf4bdb7f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -304,6 +304,21 @@ impl Diagnostic { ) } + /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. + /// In other words, multiple changes need to be applied as part of this suggestion. + pub fn multipart_suggestion_verbose( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + self.multipart_suggestion_with_style( + msg, + suggestion, + applicability, + SuggestionStyle::ShowAlways, + ) + } /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( &mut self, diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index c498ce7dbe4..6f84b0d400e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -258,6 +258,20 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::multipart_suggestion()`]. + pub fn multipart_suggestion_verbose( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + if !self.0.allow_suggestions { + return self; + } + self.0.diagnostic.multipart_suggestion_verbose(msg, suggestion, applicability); + self + } + /// See [`Diagnostic::tool_only_multipart_suggestion()`]. pub fn tool_only_multipart_suggestion( &mut self, diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 94ca70d3f95..4fccfc287fd 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1633,50 +1633,57 @@ impl<'a> Parser<'a> { { let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; - let (ident, self_sugg, param_sugg, type_sugg) = match pat.kind { - PatKind::Ident(_, ident, _) => ( - ident, - format!("self: {}", ident), - format!("{}: TypeName", ident), - format!("_: {}", ident), - ), - // Also catches `fn foo(&a)`. - PatKind::Ref(ref pat, mutab) - if matches!(pat.clone().into_inner().kind, PatKind::Ident(..)) => - { - match pat.clone().into_inner().kind { - PatKind::Ident(_, ident, _) => { - let mutab = mutab.prefix_str(); - ( - ident, - format!("self: &{}{}", mutab, ident), - format!("{}: &{}TypeName", ident, mutab), - format!("_: &{}{}", mutab, ident), - ) + let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) = + match pat.kind { + PatKind::Ident(_, ident, _) => ( + ident, + "self: ".to_string(), + ": TypeName".to_string(), + "_: ".to_string(), + pat.span.shrink_to_lo(), + pat.span.shrink_to_hi(), + pat.span.shrink_to_lo(), + ), + // Also catches `fn foo(&a)`. + PatKind::Ref(ref inner_pat, mutab) + if matches!(inner_pat.clone().into_inner().kind, PatKind::Ident(..)) => + { + match inner_pat.clone().into_inner().kind { + PatKind::Ident(_, ident, _) => { + let mutab = mutab.prefix_str(); + ( + ident, + "self: ".to_string(), + format!("{}: &{}TypeName", ident, mutab), + "_: ".to_string(), + pat.span.shrink_to_lo(), + pat.span, + pat.span.shrink_to_lo(), + ) + } + _ => unreachable!(), } - _ => unreachable!(), - } - } - _ => { - // Otherwise, try to get a type and emit a suggestion. - if let Some(ty) = pat.to_ty() { - err.span_suggestion_verbose( - pat.span, - "explicitly ignore the parameter name", - format!("_: {}", pprust::ty_to_string(&ty)), - Applicability::MachineApplicable, - ); - err.note(rfc_note); } + _ => { + // Otherwise, try to get a type and emit a suggestion. + if let Some(ty) = pat.to_ty() { + err.span_suggestion_verbose( + pat.span, + "explicitly ignore the parameter name", + format!("_: {}", pprust::ty_to_string(&ty)), + Applicability::MachineApplicable, + ); + err.note(rfc_note); + } - return None; - } - }; + return None; + } + }; // `fn foo(a, b) {}`, `fn foo(a<x>, b<y>) {}` or `fn foo(usize, usize) {}` if first_param { err.span_suggestion( - pat.span, + self_span, "if this is a `self` type, give it a parameter name", self_sugg, Applicability::MaybeIncorrect, @@ -1686,14 +1693,14 @@ impl<'a> Parser<'a> { // `fn foo(HashMap: TypeName<u32>)`. if self.token != token::Lt { err.span_suggestion( - pat.span, + param_span, "if this is a parameter name, give it a type", param_sugg, Applicability::HasPlaceholders, ); } err.span_suggestion( - pat.span, + type_span, "if this is a type, explicitly ignore the parameter name", type_sugg, Applicability::MachineApplicable, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a11d7a69c54..19136c6ceeb 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -12,7 +12,7 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, SuggestionStyle}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; @@ -1960,11 +1960,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { introduce_suggestion.push((*span, formatter(<_name))); } } - err.multipart_suggestion_with_style( + err.multipart_suggestion_verbose( &msg, introduce_suggestion, Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); } @@ -1976,14 +1975,13 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { }) .map(|(formatter, span)| (*span, formatter(name))) .collect(); - err.multipart_suggestion_with_style( + err.multipart_suggestion_verbose( &format!( "consider using the `{}` lifetime", lifetime_names.iter().next().unwrap() ), spans_suggs, Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); }; let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec<Option<String>>| { @@ -2074,11 +2072,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { }; spans_suggs.push((span, sugg.to_string())); } - err.multipart_suggestion_with_style( + err.multipart_suggestion_verbose( "consider using the `'static` lifetime", spans_suggs, Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); continue; } @@ -2163,11 +2160,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { .unwrap_or((span, sugg)); introduce_suggestion.push((span, sugg.to_string())); } - err.multipart_suggestion_with_style( + err.multipart_suggestion_verbose( &msg, introduce_suggestion, Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, ); if should_break { break; @@ -2243,11 +2239,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { if spans_suggs.len() > 0 { // This happens when we have `Foo<T>` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. - err.multipart_suggestion_with_style( + err.multipart_suggestion_verbose( "consider using one of the available lifetimes here", spans_suggs, Applicability::HasPlaceholders, - SuggestionStyle::ShowAlways, ); } } diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 51bb09e4c54..2f2223ee822 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -1687,14 +1687,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constraint=constraint, )); } else { - err.span_suggestion( - span, + err.span_suggestion_verbose( + span.with_hi(assoc_name.span.lo()), "use fully qualified syntax to disambiguate", format!( - "<{} as {}>::{}", + "<{} as {}>::", ty_param_name(), bound.print_only_trait_path(), - assoc_name, ), Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 6c94b5acec7..a86db2d31b3 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -17,7 +17,6 @@ use rustc_span::{BytePos, Span}; use super::method::probe; -use std::fmt; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -772,9 +771,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // For now, don't suggest casting with `as`. let can_cast = false; - let prefix = if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Struct(_, fields, _), - .. + let mut sugg = vec![]; + + if let Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Struct(_, fields, _), .. })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { // `expr` is a literal field for a struct, only suggest if appropriate @@ -783,12 +783,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand) { // This is a field literal - Some(field) => format!("{}: ", field.ident), + Some(field) => { + sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); + } // Likely a field was meant, but this field wasn't found. Do not suggest anything. None => return false, } - } else { - String::new() }; if let hir::ExprKind::Call(path, args) = &expr.kind { @@ -843,28 +843,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty, expected_ty, ); - let with_opt_paren: fn(&dyn fmt::Display) -> String = - if expr.precedence().order() < PREC_POSTFIX { - |s| format!("({})", s) - } else { - |s| s.to_string() - }; + let close_paren = if expr.precedence().order() < PREC_POSTFIX { + sugg.push((expr.span.shrink_to_lo(), "(".to_string())); + ")" + } else { + "" + }; - let cast_suggestion = format!("{}{} as {}", prefix, with_opt_paren(&src), expected_ty); - let into_suggestion = format!("{}{}.into()", prefix, with_opt_paren(&src)); - let suffix_suggestion = with_opt_paren(&format_args!( - "{}{}", + let mut cast_suggestion = sugg.clone(); + cast_suggestion + .push((expr.span.shrink_to_hi(), format!("{} as {}", close_paren, expected_ty))); + let mut into_suggestion = sugg.clone(); + into_suggestion.push((expr.span.shrink_to_hi(), format!("{}.into()", close_paren))); + let mut suffix_suggestion = sugg.clone(); + suffix_suggestion.push(( if matches!( (&expected_ty.kind(), &checked_ty.kind()), (ty::Int(_) | ty::Uint(_), ty::Float(_)) ) { // Remove fractional part from literal, for example `42.0f32` into `42` let src = src.trim_end_matches(&checked_ty.to_string()); - src.split('.').next().unwrap() + let len = src.split('.').next().unwrap().len(); + expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) } else { - src.trim_end_matches(&checked_ty.to_string()) + let len = src.trim_end_matches(&checked_ty.to_string()).len(); + expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) + }, + if expr.precedence().order() < PREC_POSTFIX { + // Readd `)` + format!("{})", expected_ty) + } else { + expected_ty.to_string() }, - expected_ty, )); let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| { if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false } @@ -891,22 +901,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .ok() .map(|src| (expr, src)) }); - let (span, msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) = + let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) = (lhs_expr_and_src, exp_to_found_is_fallible) { let msg = format!( "you can convert `{}` from `{}` to `{}`, matching the type of `{}`", lhs_src, expected_ty, checked_ty, src ); - let suggestion = format!("{}::from({})", checked_ty, lhs_src); - (lhs_expr.span, msg, suggestion) + let suggestion = vec![ + (lhs_expr.span.shrink_to_lo(), format!("{}::from(", checked_ty)), + (lhs_expr.span.shrink_to_hi(), ")".to_string()), + ]; + (msg, suggestion) } else { let msg = format!("{} and panic if the converted value doesn't fit", msg); - let suggestion = - format!("{}{}.try_into().unwrap()", prefix, with_opt_paren(&src)); - (expr.span, msg, suggestion) + let mut suggestion = sugg.clone(); + suggestion.push(( + expr.span.shrink_to_hi(), + format!("{}.try_into().unwrap()", close_paren), + )); + (msg, suggestion) }; - err.span_suggestion(span, &msg, suggestion, Applicability::MachineApplicable); + err.multipart_suggestion_verbose( + &msg, + suggestion, + Applicability::MachineApplicable, + ); }; let suggest_to_change_suffix_or_into = @@ -944,7 +964,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { into_suggestion.clone() }; - err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable); + err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable); }; match (&expected_ty.kind(), &checked_ty.kind()) { @@ -998,16 +1018,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if found.bit_width() < exp.bit_width() { suggest_to_change_suffix_or_into(err, false, true); } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else if can_cast { // Missing try_into implementation for `f64` to `f32` - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &format!("{}, producing the closest possible value", cast_msg), cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion @@ -1017,16 +1035,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else if can_cast { // Missing try_into implementation for `{float}` to `{integer}` - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &format!("{}, rounding the float towards zero", msg), cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion @@ -1037,8 +1053,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Float(ref exp), &ty::Uint(ref found)) => { // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &format!( "{}, producing the floating point representation of the integer", msg, @@ -1047,16 +1062,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else { // Missing try_into implementation for `{integer}` to `{float}` - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &format!( "{}, producing the floating point representation of the integer, rounded if necessary", @@ -1071,8 +1084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (&ty::Float(ref exp), &ty::Int(ref found)) => { // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &format!( "{}, producing the floating point representation of the integer", &msg, @@ -1081,16 +1093,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &lit_msg, suffix_suggestion, Applicability::MachineApplicable, ); } else { // Missing try_into implementation for `{integer}` to `{float}` - err.span_suggestion( - expr.span, + err.multipart_suggestion_verbose( &format!( "{}, producing the floating point representation of the integer, \ rounded if necessary", diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 8b1d1e450d0..1bc7bc3e063 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -450,9 +450,9 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { let suggestions = vec![ (lt_sp, sugg), ( - span, + span.with_hi(item_segment.ident.span.lo()), format!( - "{}::{}", + "{}::", // Replace the existing lifetimes with a new named lifetime. self.tcx .replace_late_bound_regions(poly_trait_ref, |_| { @@ -465,7 +465,6 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { )) }) .0, - item_segment.ident ), ), ]; @@ -487,14 +486,13 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { | hir::Node::ForeignItem(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => { - err.span_suggestion( - span, + err.span_suggestion_verbose( + span.with_hi(item_segment.ident.span.lo()), "use a fully qualified path with inferred lifetimes", format!( - "{}::{}", + "{}::", // Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`. self.tcx.anonymize_late_bound_regions(poly_trait_ref).skip_binder(), - item_segment.ident ), Applicability::MaybeIncorrect, ); |
