Use smaller spans for some structured suggestions
Use more accurate suggestion spans for * argument parse error * fully qualified path * missing code block type * numeric casts * E0212
This commit is contained in:
@@ -298,6 +298,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,
|
||||
|
||||
@@ -257,6 +257,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,
|
||||
|
||||
@@ -1618,50 +1618,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,
|
||||
@@ -1671,14 +1678,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,
|
||||
|
||||
@@ -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};
|
||||
@@ -1950,11 +1950,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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1966,14 +1965,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>>| {
|
||||
@@ -2064,11 +2062,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;
|
||||
}
|
||||
@@ -2088,11 +2085,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
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;
|
||||
@@ -2167,11 +2163,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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1648,14 +1648,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,
|
||||
);
|
||||
|
||||
@@ -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> {
|
||||
@@ -771,9 +770,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
|
||||
@@ -782,12 +782,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 {
|
||||
@@ -842,28 +842,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 }
|
||||
@@ -890,22 +900,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 =
|
||||
@@ -943,7 +963,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()) {
|
||||
@@ -997,16 +1017,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
|
||||
@@ -1016,16 +1034,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
|
||||
@@ -1036,8 +1052,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,
|
||||
@@ -1046,16 +1061,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",
|
||||
@@ -1070,8 +1083,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,
|
||||
@@ -1080,16 +1092,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",
|
||||
|
||||
@@ -452,9 +452,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, |_| {
|
||||
@@ -467,7 +467,6 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
|
||||
))
|
||||
})
|
||||
.0,
|
||||
item_segment.ident
|
||||
),
|
||||
),
|
||||
];
|
||||
@@ -489,14 +488,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,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user