1
Fork 0

Auto merge of #87915 - estebank:fancy-spans, r=oli-obk

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
This commit is contained in:
bors 2021-09-13 16:31:12 +00:00
commit 9bb77da74d
60 changed files with 1200 additions and 958 deletions

View file

@ -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,
);

View file

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

View file

@ -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,
);