Skip to content

Commit

Permalink
Refactor cannot infer ... message rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
wabain committed Jan 11, 2021
1 parent 0496fde commit b9d9776
Showing 1 changed file with 92 additions and 145 deletions.
237 changes: 92 additions & 145 deletions compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
Expand Up @@ -217,16 +217,10 @@ impl UseDiagnostic<'_> {

/// Suggest giving an appropriate return type to a closure expression.
fn closure_return_type_suggestion(
span: Span,
err: &mut DiagnosticBuilder<'_>,
output: &FnRetTy<'_>,
body: &Body<'_>,
descr: &str,
name: &str,
ret: &str,
use_diag: Option<&UseDiagnostic<'_>>,
parent_name: Option<String>,
parent_descr: Option<&str>,
) {
let (arrow, post) = match output {
FnRetTy::DefaultReturn(_) => ("-> ", " "),
Expand All @@ -244,18 +238,6 @@ fn closure_return_type_suggestion(
suggestion,
Applicability::HasPlaceholders,
);
err.span_label(
span,
InferCtxt::cannot_infer_msg(
span,
"type",
&name,
&descr,
use_diag,
parent_name,
parent_descr,
),
);
}

/// Given a closure signature, return a `String` containing a list of all its argument types.
Expand Down Expand Up @@ -300,9 +282,52 @@ impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded {
pub struct InferenceDiagnosticsData {
pub name: String,
pub span: Option<Span>,
pub description: Cow<'static, str>,
pub parent_name: Option<String>,
pub parent_description: Option<&'static str>,
pub kind: UnderspecifiedArgKind,
pub parent: Option<InferenceDiagnosticsParentData>,
}

pub struct InferenceDiagnosticsParentData {
pub prefix: &'static str,
pub name: String,
}

pub enum UnderspecifiedArgKind {
Type { prefix: Cow<'static, str> },
Const { is_parameter: bool },
}

impl InferenceDiagnosticsData {
/// Generate a label for a generic argument which can't be inferred. When not
/// much is known about the argument, `use_diag` may be used to describe the
/// labeled value.
fn cannot_infer_msg(&self, use_diag: Option<&UseDiagnostic<'_>>) -> String {
if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
if let Some(use_diag) = use_diag {
return format!("cannot infer type of {}", use_diag.descr());
}

return "cannot infer type".to_string();
}

let suffix = match (&self.parent, use_diag) {
(Some(parent), _) => format!(" declared on the {} `{}`", parent.prefix, parent.name),
(None, Some(use_diag)) => format!(" in {}", use_diag.type_descr()),
(None, None) => String::new(),
};

// For example: "cannot infer type for type parameter `T`"
format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
}
}

impl UnderspecifiedArgKind {
fn prefix_string(&self) -> Cow<'static, str> {
match self {
Self::Type { prefix } => format!("type for {}", prefix).into(),
Self::Const { is_parameter: true } => "the value of const parameter".into(),
Self::Const { is_parameter: false } => "the value of the constant".into(),
}
}
}

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
Expand All @@ -322,32 +347,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) =
var_origin.kind
{
let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id));
let (parent_name, parent_description) =
if let Some(parent_def_id) = parent_def_id {
let parent_data = def_id
.and_then(|def_id| self.tcx.parent(def_id))
.and_then(|parent_def_id| {
let parent_name = self
.tcx
.def_key(parent_def_id)
.disambiguated_data
.data
.get_opt_name()
.map(|parent_symbol| parent_symbol.to_string());

(
parent_name,
Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
)
} else {
(None, None)
};
.get_opt_name()?
.to_string();

Some(InferenceDiagnosticsParentData {
prefix: self.tcx.def_kind(parent_def_id).descr(parent_def_id),
name: parent_name,
})
});

if name != kw::SelfUpper {
return InferenceDiagnosticsData {
name: name.to_string(),
span: Some(var_origin.span),
description: "type parameter".into(),
parent_name,
parent_description,
kind: UnderspecifiedArgKind::Type {
prefix: "type parameter".into(),
},
parent: parent_data,
};
}
}
Expand All @@ -362,9 +386,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
InferenceDiagnosticsData {
name: s,
span: None,
description: ty.prefix_string(),
parent_name: None,
parent_description: None,
kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string() },
parent: None,
}
}
GenericArgKind::Const(ct) => {
Expand All @@ -374,31 +397,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) =
origin.kind
{
let parent_def_id = self.tcx.parent(def_id);
let (parent_name, parent_description) =
if let Some(parent_def_id) = parent_def_id {
let parent_name = self
.tcx
.def_key(parent_def_id)
.disambiguated_data
.data
.get_opt_name()
.map(|parent_symbol| parent_symbol.to_string());

(
parent_name,
Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
)
} else {
(None, None)
};
let parent_data = self.tcx.parent(def_id).and_then(|parent_def_id| {
let parent_name = self
.tcx
.def_key(parent_def_id)
.disambiguated_data
.data
.get_opt_name()?
.to_string();

Some(InferenceDiagnosticsParentData {
prefix: self.tcx.def_kind(parent_def_id).descr(parent_def_id),
name: parent_name,
})
});

return InferenceDiagnosticsData {
name: name.to_string(),
span: Some(origin.span),
description: "const parameter".into(),
parent_name,
parent_description,
kind: UnderspecifiedArgKind::Const { is_parameter: true },
parent: parent_data,
};
}

Expand All @@ -413,9 +431,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
InferenceDiagnosticsData {
name: s,
span: Some(origin.span),
description: "the constant".into(),
parent_name: None,
parent_description: None,
kind: UnderspecifiedArgKind::Const { is_parameter: false },
parent: None,
}
} else {
bug!("unexpect const: {:?}", ct);
Expand Down Expand Up @@ -554,19 +571,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

if let Some((decl, body_id)) = closure_decl_and_body_id {
closure_return_type_suggestion(
span,
&mut err,
&decl.output,
self.tcx.hir().body(body_id),
&arg_data.description,
&arg_data.name,
&ret,
use_diag,
arg_data.parent_name,
arg_data.parent_description,
);
// We don't want to give the other suggestions when the problem is the
// closure return type.
err.span_label(
span,
arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
);
return err;
}

Expand Down Expand Up @@ -703,47 +718,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// |
// = note: type must be known at this point
let span = arg_data.span.unwrap_or(err_span);

// Avoid multiple labels pointing at `span`.
if !err
.span
.span_labels()
.iter()
.any(|span_label| span_label.label.is_some() && span_label.span == span)
&& local_visitor.found_arg_pattern.is_none()
{
let (kind_str, const_value) = match arg.unpack() {
GenericArgKind::Type(_) => ("type", None),
GenericArgKind::Const(_) => ("the value", Some(())),
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
};

// FIXME(const_generics): we would like to handle const arguments
// as part of the normal diagnostics flow below, but there appear to
// be subtleties in doing so, so for now we special-case const args
// here.
if let Some(suggestion) = const_value
.and_then(|_| arg_data.parent_name.as_ref())
.map(|parent| format!("{}::<{}>", parent, arg_data.name))
if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) =
(&arg_data.kind, &arg_data.parent)
{
err.span_suggestion_verbose(
span,
"consider specifying the const argument",
suggestion,
format!("{}::<{}>", parent_data.name, arg_data.name),
Applicability::MaybeIncorrect,
);
}

// Avoid multiple labels pointing at `span`.
err.span_label(
span,
InferCtxt::cannot_infer_msg(
span,
kind_str,
&arg_data.name,
&arg_data.description,
use_diag,
arg_data.parent_name,
arg_data.parent_description,
),
arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
);
}

Expand Down Expand Up @@ -826,61 +827,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
"type inside {} must be known in this context",
kind,
);
err.span_label(
span,
InferCtxt::cannot_infer_msg(
span,
"type",
&data.name,
&data.description,
None,
data.parent_name,
data.parent_description,
),
);
err.span_label(span, data.cannot_infer_msg(None));
err
}

fn cannot_infer_msg(
span: Span,
kind_str: &str,
type_name: &str,
descr: &str,
use_diag: Option<&UseDiagnostic<'_>>,
parent_name: Option<String>,
parent_descr: Option<&str>,
) -> String {
let use_diag = use_diag.filter(|d| d.applies_to(span));

if type_name == "_" {
if let Some(use_diag) = use_diag {
format!("cannot infer {} of {}", kind_str, use_diag.descr())
} else {
format!("cannot infer {}", kind_str)
}
} else {
let extra_descr = if let Some(parent_name) = parent_name {
let parent_type_descr = if let Some(parent_descr) = parent_descr {
format!(" the {}", parent_descr)
} else {
"".into()
};

format!(" declared on{} `{}`", parent_type_descr, parent_name)
} else if let Some(use_diag) = use_diag {
format!(" in {}", use_diag.type_descr())
} else {
"".into()
};

// 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" };
// For example: "cannot infer type for type parameter `T`"
format!(
"cannot infer {} {} {} `{}`{}",
kind_str, preposition, descr, type_name, extra_descr
)
}
}
}

0 comments on commit b9d9776

Please sign in to comment.