1
Fork 0

Rollup merge of #88123 - camelid:tup-pat-precise-spans, r=estebank

Make spans for tuple patterns in E0023 more precise

As suggested in #86307. Closes #86307.

r? ````@estebank````
This commit is contained in:
Manish Goregaokar 2021-08-26 12:38:06 -07:00 committed by GitHub
commit 8aa46e51df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1165 additions and 77 deletions

View file

@ -3183,6 +3183,20 @@ pub enum Node<'hir> {
}
impl<'hir> Node<'hir> {
/// Get the identifier of this `Node`, if applicable.
///
/// # Edge cases
///
/// Calling `.ident()` on a [`Node::Ctor`] will return `None`
/// because `Ctor`s do not have identifiers themselves.
/// Instead, call `.ident()` on the parent struct/variant, like so:
///
/// ```ignore (illustrative)
/// ctor
/// .ctor_hir_id()
/// .and_then(|ctor_id| tcx.hir().find(tcx.hir().get_parent_node(ctor_id)))
/// .and_then(|parent| parent.ident())
/// ```
pub fn ident(&self) -> Option<Ident> {
match self {
Node::TraitItem(TraitItem { ident, .. })
@ -3191,8 +3205,25 @@ impl<'hir> Node<'hir> {
| Node::Field(FieldDef { ident, .. })
| Node::Variant(Variant { ident, .. })
| Node::MacroDef(MacroDef { ident, .. })
| Node::Item(Item { ident, .. }) => Some(*ident),
_ => None,
| Node::Item(Item { ident, .. })
| Node::PathSegment(PathSegment { ident, .. }) => Some(*ident),
Node::Lifetime(lt) => Some(lt.name.ident()),
Node::GenericParam(p) => Some(p.name.ident()),
Node::Param(..)
| Node::AnonConst(..)
| Node::Expr(..)
| Node::Stmt(..)
| Node::Block(..)
| Node::Ctor(..)
| Node::Pat(..)
| Node::Binding(..)
| Node::Arm(..)
| Node::Local(..)
| Node::Visibility(..)
| Node::Crate(..)
| Node::Ty(..)
| Node::TraitRef(..)
| Node::Infer(..) => None,
}
}

View file

@ -223,7 +223,18 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> {
}
fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
tcx.hir().get_if_local(def_id).and_then(|node| node.ident()).map(|ident| ident.span)
tcx.hir()
.get_if_local(def_id)
.and_then(|node| match node {
// A `Ctor` doesn't have an identifier itself, but its parent
// struct/variant does. Compare with `hir::Map::opt_span`.
hir::Node::Ctor(ctor) => ctor
.ctor_hir_id()
.and_then(|ctor_id| tcx.hir().find(tcx.hir().get_parent_node(ctor_id)))
.and_then(|parent| parent.ident()),
_ => node.ident(),
})
.map(|ident| ident.span)
}
/// If the given `DefId` describes an item belonging to a trait,

View file

@ -15,7 +15,7 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::Ident;
use rustc_span::{BytePos, DUMMY_SP};
use rustc_span::{BytePos, MultiSpan, DUMMY_SP};
use rustc_trait_selection::autoderef::Autoderef;
use rustc_trait_selection::traits::{ObligationCause, Pattern};
use ty::VariantDef;
@ -990,10 +990,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let subpats_ending = pluralize!(subpats.len());
let fields_ending = pluralize!(fields.len());
let subpat_spans = if subpats.is_empty() {
vec![pat_span]
} else {
subpats.iter().map(|p| p.span).collect()
};
let last_subpat_span = *subpat_spans.last().unwrap();
let res_span = self.tcx.def_span(res.def_id());
let def_ident_span = self.tcx.def_ident_span(res.def_id()).unwrap_or(res_span);
let field_def_spans = if fields.is_empty() {
vec![res_span]
} else {
fields.iter().map(|f| f.ident.span).collect()
};
let last_field_def_span = *field_def_spans.last().unwrap();
let mut err = struct_span_err!(
self.tcx.sess,
pat_span,
MultiSpan::from_spans(subpat_spans.clone()),
E0023,
"this pattern has {} field{}, but the corresponding {} has {} field{}",
subpats.len(),
@ -1003,10 +1018,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fields_ending,
);
err.span_label(
pat_span,
format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len(),),
)
.span_label(res_span, format!("{} defined here", res.descr()));
last_subpat_span,
&format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len()),
);
if self.tcx.sess.source_map().is_multiline(qpath.span().between(last_subpat_span)) {
err.span_label(qpath.span(), "");
}
if self.tcx.sess.source_map().is_multiline(def_ident_span.between(last_field_def_span)) {
err.span_label(def_ident_span, format!("{} defined here", res.descr()));
}
for span in &field_def_spans[..field_def_spans.len() - 1] {
err.span_label(*span, "");
}
err.span_label(
last_field_def_span,
&format!("{} has {} field{}", res.descr(), fields.len(), fields_ending),
);
// Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`.
// More generally, the expected type wants a tuple variant with one field of an