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:
commit
8aa46e51df
19 changed files with 1165 additions and 77 deletions
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue