1
Fork 0

the two hardest things in programming, names and...

This commit is contained in:
Bastian Kauschke 2020-09-24 22:01:46 +02:00
parent 0e84b61053
commit e5430e5306
2 changed files with 115 additions and 79 deletions

View file

@ -178,7 +178,7 @@ fn closure_return_type_suggestion(
); );
err.span_label( err.span_label(
span, span,
InferCtxt::missing_type_msg("type", &name, &descr, parent_name, parent_descr), InferCtxt::cannot_infer_msg("type", &name, &descr, parent_name, parent_descr),
); );
} }
@ -220,12 +220,23 @@ impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded {
} }
} }
/// Information about a constant or a type containing inference variables.
pub struct InferDiagnosticsData {
pub name: String,
pub span: Option<Span>,
pub description: Cow<'static, str>,
pub parent_name: Option<String>,
pub parent_description: Option<&'static str>,
}
impl<'a, 'tcx> InferCtxt<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn extract_type_name( /// Extracts data used by diagnostic for either types or constants
/// which were stuck during inference.
pub fn extract_infer_data(
&self, &self,
arg: GenericArg<'tcx>, arg: GenericArg<'tcx>,
highlight: Option<ty::print::RegionHighlightMode>, highlight: Option<ty::print::RegionHighlightMode>,
) -> (String, Option<Span>, Cow<'static, str>, Option<String>, Option<&'static str>) { ) -> InferDiagnosticsData {
match arg.unpack() { match arg.unpack() {
GenericArgKind::Type(ty) => { GenericArgKind::Type(ty) => {
if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
@ -236,32 +247,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
var_origin.kind var_origin.kind
{ {
let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id)); let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id));
let (parent_name, parent_desc) = if let Some(parent_def_id) = parent_def_id let (parent_name, parent_description) =
{ if let Some(parent_def_id) = parent_def_id {
let parent_name = self let parent_name = self
.tcx .tcx
.def_key(parent_def_id) .def_key(parent_def_id)
.disambiguated_data .disambiguated_data
.data .data
.get_opt_name() .get_opt_name()
.map(|parent_symbol| parent_symbol.to_string()); .map(|parent_symbol| parent_symbol.to_string());
( (
parent_name, parent_name,
Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)), Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
) )
} else { } else {
(None, None) (None, None)
}; };
if name != kw::SelfUpper { if name != kw::SelfUpper {
return ( return InferDiagnosticsData {
name.to_string(), name: name.to_string(),
Some(var_origin.span), span: Some(var_origin.span),
"type parameter".into(), description: "type parameter".into(),
parent_name, parent_name,
parent_desc, parent_description,
); };
} }
} }
} }
@ -272,56 +283,67 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
printer.region_highlight_mode = highlight; printer.region_highlight_mode = highlight;
} }
let _ = ty.print(printer); let _ = ty.print(printer);
(s, None, ty.prefix_string(), None, None) InferDiagnosticsData {
name: s,
span: None,
description: ty.prefix_string(),
parent_name: None,
parent_description: None,
}
} }
GenericArgKind::Const(ct) => { GenericArgKind::Const(ct) => {
let span = if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val { if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val {
let origin = let origin =
self.inner.borrow_mut().const_unification_table().probe_value(vid).origin; self.inner.borrow_mut().const_unification_table().probe_value(vid).origin;
if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) =
origin.kind origin.kind
{ {
let parent_def_id = self.tcx.parent(def_id); let parent_def_id = self.tcx.parent(def_id);
let (parent_name, parent_descr) = if let Some(parent_def_id) = parent_def_id let (parent_name, parent_description) =
{ if let Some(parent_def_id) = parent_def_id {
let parent_name = self let parent_name = self
.tcx .tcx
.def_key(parent_def_id) .def_key(parent_def_id)
.disambiguated_data .disambiguated_data
.data .data
.get_opt_name() .get_opt_name()
.map(|parent_symbol| parent_symbol.to_string()); .map(|parent_symbol| parent_symbol.to_string());
( (
parent_name, parent_name,
Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)), Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
) )
} else { } else {
(None, None) (None, None)
}; };
return ( return InferDiagnosticsData {
name.to_string(), name: name.to_string(),
Some(origin.span), span: Some(origin.span),
"const parameter".into(), description: "const parameter".into(),
parent_name, parent_name,
parent_descr, parent_description,
); };
} }
debug_assert!(!origin.span.is_dummy()); debug_assert!(!origin.span.is_dummy());
Some(origin.span) let mut s = String::new();
let mut printer =
ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS);
if let Some(highlight) = highlight {
printer.region_highlight_mode = highlight;
}
let _ = ct.print(printer);
InferDiagnosticsData {
name: s,
span: Some(origin.span),
description: "the constant".into(),
parent_name: None,
parent_description: None,
}
} else { } else {
bug!("unexpect const: {:?}", ct); bug!("unexpect const: {:?}", ct);
};
let mut s = String::new();
let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS);
if let Some(highlight) = highlight {
printer.region_highlight_mode = highlight;
} }
let _ = ct.print(printer);
(s, span, "the constant".into(), None, None)
} }
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
} }
@ -331,18 +353,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
&self, &self,
body_id: Option<hir::BodyId>, body_id: Option<hir::BodyId>,
span: Span, span: Span,
ty: GenericArg<'tcx>, arg: GenericArg<'tcx>,
error_code: TypeAnnotationNeeded, error_code: TypeAnnotationNeeded,
) -> DiagnosticBuilder<'tcx> { ) -> DiagnosticBuilder<'tcx> {
let ty = self.resolve_vars_if_possible(&ty); let ty = self.resolve_vars_if_possible(&arg);
let (name, name_sp, descr, parent_name, parent_descr) = self.extract_type_name(ty, None); let arg_data = self.extract_infer_data(arg, None);
let kind_str = match ty.unpack() { let kind_str = match ty.unpack() {
GenericArgKind::Type(_) => "type", GenericArgKind::Type(_) => "type",
GenericArgKind::Const(_) => "the value", GenericArgKind::Const(_) => "the value",
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
}; };
let mut local_visitor = FindHirNodeVisitor::new(&self, ty.into(), span); let mut local_visitor = FindHirNodeVisitor::new(&self, arg.into(), span);
let ty_to_string = |ty: Ty<'tcx>| -> String { let ty_to_string = |ty: Ty<'tcx>| -> String {
let mut s = String::new(); let mut s = String::new();
let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
@ -372,7 +394,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
} }
let err_span = if let Some(pattern) = local_visitor.found_arg_pattern { let err_span = if let Some(pattern) = local_visitor.found_arg_pattern {
pattern.span pattern.span
} else if let Some(span) = name_sp { } else if let Some(span) = arg_data.span {
// `span` here lets us point at `sum` instead of the entire right hand side expr: // `span` here lets us point at `sum` instead of the entire right hand side expr:
// error[E0282]: type annotations needed // error[E0282]: type annotations needed
// --> file2.rs:3:15 // --> file2.rs:3:15
@ -419,7 +441,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
_ => String::new(), _ => String::new(),
}; };
// When `name` corresponds to a type argument, show the path of the full type we're // When `arg_data.name` corresponds to a type argument, show the path of the full type we're
// trying to infer. In the following example, `ty_msg` contains // trying to infer. In the following example, `ty_msg` contains
// " in `std::result::Result<i32, E>`": // " in `std::result::Result<i32, E>`":
// ``` // ```
@ -458,11 +480,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
&mut err, &mut err,
&decl.output, &decl.output,
self.tcx.hir().body(body_id), self.tcx.hir().body(body_id),
&descr, &arg_data.description,
&name, &arg_data.name,
&ret, &ret,
parent_name, arg_data.parent_name,
parent_descr, arg_data.parent_description,
); );
// We don't want to give the other suggestions when the problem is the // We don't want to give the other suggestions when the problem is the
// closure return type. // closure return type.
@ -476,15 +498,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// nudge them in the right direction. // nudge them in the right direction.
format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret) format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret)
} }
Some(ty) if is_named_and_not_impl_trait(ty) && name == "_" => { Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => {
let ty = ty_to_string(ty); let ty = ty_to_string(ty);
format!("the explicit type `{}`, with the type parameters specified", ty) format!("the explicit type `{}`, with the type parameters specified", ty)
} }
Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != name => { Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
let ty = ty_to_string(ty); let ty = ty_to_string(ty);
format!( format!(
"the explicit type `{}`, where the type parameter `{}` is specified", "the explicit type `{}`, where the type parameter `{}` is specified",
ty, name, ty, arg_data.name,
) )
} }
_ => "a type".to_string(), _ => "a type".to_string(),
@ -601,7 +623,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// | ^^^ cannot infer type for `S` // | ^^^ cannot infer type for `S`
// | // |
// = note: type must be known at this point // = note: type must be known at this point
let span = name_sp.unwrap_or(err_span); let span = arg_data.span.unwrap_or(err_span);
if !err if !err
.span .span
.span_labels() .span_labels()
@ -612,7 +634,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// Avoid multiple labels pointing at `span`. // Avoid multiple labels pointing at `span`.
err.span_label( err.span_label(
span, span,
InferCtxt::missing_type_msg(kind_str, &name, &descr, parent_name, parent_descr), InferCtxt::cannot_infer_msg(
kind_str,
&arg_data.name,
&arg_data.description,
arg_data.parent_name,
arg_data.parent_description,
),
); );
} }
@ -672,7 +700,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> DiagnosticBuilder<'tcx> { ) -> DiagnosticBuilder<'tcx> {
let ty = self.resolve_vars_if_possible(&ty); let ty = self.resolve_vars_if_possible(&ty);
let (name, _, descr, parent_name, parent_descr) = self.extract_type_name(ty.into(), None); let data = self.extract_infer_data(ty.into(), None);
let mut err = struct_span_err!( let mut err = struct_span_err!(
self.tcx.sess, self.tcx.sess,
@ -683,12 +711,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
); );
err.span_label( err.span_label(
span, span,
InferCtxt::missing_type_msg("type", &name, &descr, parent_name, parent_descr), InferCtxt::cannot_infer_msg(
"type",
&data.name,
&data.description,
data.parent_name,
data.parent_description,
),
); );
err err
} }
fn missing_type_msg( fn cannot_infer_msg(
kind_str: &str, kind_str: &str,
type_name: &str, type_name: &str,
descr: &str, descr: &str,
@ -710,6 +744,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
"".to_string() "".to_string()
}; };
// FIXME: We really shouldn't be dealing with strings here
// but instead use a sensible enum for cases like this.
let preposition = if "the value" == kind_str { "of" } else { "for" }; let preposition = if "the value" == kind_str { "of" } else { "for" };
// For example: "cannot infer type for type parameter `T`" // For example: "cannot infer type for type parameter `T`"
format!( format!(

View file

@ -396,7 +396,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
) -> Option<RegionNameHighlight> { ) -> Option<RegionNameHighlight> {
let mut highlight = RegionHighlightMode::default(); let mut highlight = RegionHighlightMode::default();
highlight.highlighting_region_vid(needle_fr, counter); highlight.highlighting_region_vid(needle_fr, counter);
let type_name = self.infcx.extract_type_name(ty.into(), Some(highlight)).0; let type_name = self.infcx.extract_infer_data(ty.into(), Some(highlight)).name;
debug!( debug!(
"highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", "highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}",
@ -646,7 +646,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
let mut highlight = RegionHighlightMode::default(); let mut highlight = RegionHighlightMode::default();
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
let type_name = self.infcx.extract_type_name(return_ty.into(), Some(highlight)).0; let type_name = self.infcx.extract_infer_data(return_ty.into(), Some(highlight)).name;
let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id); let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id);
@ -698,7 +698,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
let mut highlight = RegionHighlightMode::default(); let mut highlight = RegionHighlightMode::default();
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
let type_name = self.infcx.extract_type_name(yield_ty.into(), Some(highlight)).0; let type_name = self.infcx.extract_infer_data(yield_ty.into(), Some(highlight)).name;
let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id); let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id);