Skip to content

Commit b9d9776

Browse files
committed
Refactor cannot infer ... message rendering
1 parent 0496fde commit b9d9776

File tree

1 file changed

+92
-145
lines changed

1 file changed

+92
-145
lines changed

compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs

+92-145
Original file line numberDiff line numberDiff line change
@@ -217,16 +217,10 @@ impl UseDiagnostic<'_> {
217217

218218
/// Suggest giving an appropriate return type to a closure expression.
219219
fn closure_return_type_suggestion(
220-
span: Span,
221220
err: &mut DiagnosticBuilder<'_>,
222221
output: &FnRetTy<'_>,
223222
body: &Body<'_>,
224-
descr: &str,
225-
name: &str,
226223
ret: &str,
227-
use_diag: Option<&UseDiagnostic<'_>>,
228-
parent_name: Option<String>,
229-
parent_descr: Option<&str>,
230224
) {
231225
let (arrow, post) = match output {
232226
FnRetTy::DefaultReturn(_) => ("-> ", " "),
@@ -244,18 +238,6 @@ fn closure_return_type_suggestion(
244238
suggestion,
245239
Applicability::HasPlaceholders,
246240
);
247-
err.span_label(
248-
span,
249-
InferCtxt::cannot_infer_msg(
250-
span,
251-
"type",
252-
&name,
253-
&descr,
254-
use_diag,
255-
parent_name,
256-
parent_descr,
257-
),
258-
);
259241
}
260242

261243
/// Given a closure signature, return a `String` containing a list of all its argument types.
@@ -300,9 +282,52 @@ impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded {
300282
pub struct InferenceDiagnosticsData {
301283
pub name: String,
302284
pub span: Option<Span>,
303-
pub description: Cow<'static, str>,
304-
pub parent_name: Option<String>,
305-
pub parent_description: Option<&'static str>,
285+
pub kind: UnderspecifiedArgKind,
286+
pub parent: Option<InferenceDiagnosticsParentData>,
287+
}
288+
289+
pub struct InferenceDiagnosticsParentData {
290+
pub prefix: &'static str,
291+
pub name: String,
292+
}
293+
294+
pub enum UnderspecifiedArgKind {
295+
Type { prefix: Cow<'static, str> },
296+
Const { is_parameter: bool },
297+
}
298+
299+
impl InferenceDiagnosticsData {
300+
/// Generate a label for a generic argument which can't be inferred. When not
301+
/// much is known about the argument, `use_diag` may be used to describe the
302+
/// labeled value.
303+
fn cannot_infer_msg(&self, use_diag: Option<&UseDiagnostic<'_>>) -> String {
304+
if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
305+
if let Some(use_diag) = use_diag {
306+
return format!("cannot infer type of {}", use_diag.descr());
307+
}
308+
309+
return "cannot infer type".to_string();
310+
}
311+
312+
let suffix = match (&self.parent, use_diag) {
313+
(Some(parent), _) => format!(" declared on the {} `{}`", parent.prefix, parent.name),
314+
(None, Some(use_diag)) => format!(" in {}", use_diag.type_descr()),
315+
(None, None) => String::new(),
316+
};
317+
318+
// For example: "cannot infer type for type parameter `T`"
319+
format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
320+
}
321+
}
322+
323+
impl UnderspecifiedArgKind {
324+
fn prefix_string(&self) -> Cow<'static, str> {
325+
match self {
326+
Self::Type { prefix } => format!("type for {}", prefix).into(),
327+
Self::Const { is_parameter: true } => "the value of const parameter".into(),
328+
Self::Const { is_parameter: false } => "the value of the constant".into(),
329+
}
330+
}
306331
}
307332

308333
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -322,32 +347,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
322347
if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) =
323348
var_origin.kind
324349
{
325-
let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id));
326-
let (parent_name, parent_description) =
327-
if let Some(parent_def_id) = parent_def_id {
350+
let parent_data = def_id
351+
.and_then(|def_id| self.tcx.parent(def_id))
352+
.and_then(|parent_def_id| {
328353
let parent_name = self
329354
.tcx
330355
.def_key(parent_def_id)
331356
.disambiguated_data
332357
.data
333-
.get_opt_name()
334-
.map(|parent_symbol| parent_symbol.to_string());
335-
336-
(
337-
parent_name,
338-
Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
339-
)
340-
} else {
341-
(None, None)
342-
};
358+
.get_opt_name()?
359+
.to_string();
360+
361+
Some(InferenceDiagnosticsParentData {
362+
prefix: self.tcx.def_kind(parent_def_id).descr(parent_def_id),
363+
name: parent_name,
364+
})
365+
});
343366

344367
if name != kw::SelfUpper {
345368
return InferenceDiagnosticsData {
346369
name: name.to_string(),
347370
span: Some(var_origin.span),
348-
description: "type parameter".into(),
349-
parent_name,
350-
parent_description,
371+
kind: UnderspecifiedArgKind::Type {
372+
prefix: "type parameter".into(),
373+
},
374+
parent: parent_data,
351375
};
352376
}
353377
}
@@ -362,9 +386,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
362386
InferenceDiagnosticsData {
363387
name: s,
364388
span: None,
365-
description: ty.prefix_string(),
366-
parent_name: None,
367-
parent_description: None,
389+
kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string() },
390+
parent: None,
368391
}
369392
}
370393
GenericArgKind::Const(ct) => {
@@ -374,31 +397,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
374397
if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) =
375398
origin.kind
376399
{
377-
let parent_def_id = self.tcx.parent(def_id);
378-
let (parent_name, parent_description) =
379-
if let Some(parent_def_id) = parent_def_id {
380-
let parent_name = self
381-
.tcx
382-
.def_key(parent_def_id)
383-
.disambiguated_data
384-
.data
385-
.get_opt_name()
386-
.map(|parent_symbol| parent_symbol.to_string());
387-
388-
(
389-
parent_name,
390-
Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)),
391-
)
392-
} else {
393-
(None, None)
394-
};
400+
let parent_data = self.tcx.parent(def_id).and_then(|parent_def_id| {
401+
let parent_name = self
402+
.tcx
403+
.def_key(parent_def_id)
404+
.disambiguated_data
405+
.data
406+
.get_opt_name()?
407+
.to_string();
408+
409+
Some(InferenceDiagnosticsParentData {
410+
prefix: self.tcx.def_kind(parent_def_id).descr(parent_def_id),
411+
name: parent_name,
412+
})
413+
});
395414

396415
return InferenceDiagnosticsData {
397416
name: name.to_string(),
398417
span: Some(origin.span),
399-
description: "const parameter".into(),
400-
parent_name,
401-
parent_description,
418+
kind: UnderspecifiedArgKind::Const { is_parameter: true },
419+
parent: parent_data,
402420
};
403421
}
404422

@@ -413,9 +431,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
413431
InferenceDiagnosticsData {
414432
name: s,
415433
span: Some(origin.span),
416-
description: "the constant".into(),
417-
parent_name: None,
418-
parent_description: None,
434+
kind: UnderspecifiedArgKind::Const { is_parameter: false },
435+
parent: None,
419436
}
420437
} else {
421438
bug!("unexpect const: {:?}", ct);
@@ -554,19 +571,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
554571

555572
if let Some((decl, body_id)) = closure_decl_and_body_id {
556573
closure_return_type_suggestion(
557-
span,
558574
&mut err,
559575
&decl.output,
560576
self.tcx.hir().body(body_id),
561-
&arg_data.description,
562-
&arg_data.name,
563577
&ret,
564-
use_diag,
565-
arg_data.parent_name,
566-
arg_data.parent_description,
567578
);
568579
// We don't want to give the other suggestions when the problem is the
569580
// closure return type.
581+
err.span_label(
582+
span,
583+
arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
584+
);
570585
return err;
571586
}
572587

@@ -703,47 +718,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
703718
// |
704719
// = note: type must be known at this point
705720
let span = arg_data.span.unwrap_or(err_span);
721+
722+
// Avoid multiple labels pointing at `span`.
706723
if !err
707724
.span
708725
.span_labels()
709726
.iter()
710727
.any(|span_label| span_label.label.is_some() && span_label.span == span)
711728
&& local_visitor.found_arg_pattern.is_none()
712729
{
713-
let (kind_str, const_value) = match arg.unpack() {
714-
GenericArgKind::Type(_) => ("type", None),
715-
GenericArgKind::Const(_) => ("the value", Some(())),
716-
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
717-
};
718-
719730
// FIXME(const_generics): we would like to handle const arguments
720731
// as part of the normal diagnostics flow below, but there appear to
721732
// be subtleties in doing so, so for now we special-case const args
722733
// here.
723-
if let Some(suggestion) = const_value
724-
.and_then(|_| arg_data.parent_name.as_ref())
725-
.map(|parent| format!("{}::<{}>", parent, arg_data.name))
734+
if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) =
735+
(&arg_data.kind, &arg_data.parent)
726736
{
727737
err.span_suggestion_verbose(
728738
span,
729739
"consider specifying the const argument",
730-
suggestion,
740+
format!("{}::<{}>", parent_data.name, arg_data.name),
731741
Applicability::MaybeIncorrect,
732742
);
733743
}
734744

735-
// Avoid multiple labels pointing at `span`.
736745
err.span_label(
737746
span,
738-
InferCtxt::cannot_infer_msg(
739-
span,
740-
kind_str,
741-
&arg_data.name,
742-
&arg_data.description,
743-
use_diag,
744-
arg_data.parent_name,
745-
arg_data.parent_description,
746-
),
747+
arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))),
747748
);
748749
}
749750

@@ -826,61 +827,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
826827
"type inside {} must be known in this context",
827828
kind,
828829
);
829-
err.span_label(
830-
span,
831-
InferCtxt::cannot_infer_msg(
832-
span,
833-
"type",
834-
&data.name,
835-
&data.description,
836-
None,
837-
data.parent_name,
838-
data.parent_description,
839-
),
840-
);
830+
err.span_label(span, data.cannot_infer_msg(None));
841831
err
842832
}
843-
844-
fn cannot_infer_msg(
845-
span: Span,
846-
kind_str: &str,
847-
type_name: &str,
848-
descr: &str,
849-
use_diag: Option<&UseDiagnostic<'_>>,
850-
parent_name: Option<String>,
851-
parent_descr: Option<&str>,
852-
) -> String {
853-
let use_diag = use_diag.filter(|d| d.applies_to(span));
854-
855-
if type_name == "_" {
856-
if let Some(use_diag) = use_diag {
857-
format!("cannot infer {} of {}", kind_str, use_diag.descr())
858-
} else {
859-
format!("cannot infer {}", kind_str)
860-
}
861-
} else {
862-
let extra_descr = if let Some(parent_name) = parent_name {
863-
let parent_type_descr = if let Some(parent_descr) = parent_descr {
864-
format!(" the {}", parent_descr)
865-
} else {
866-
"".into()
867-
};
868-
869-
format!(" declared on{} `{}`", parent_type_descr, parent_name)
870-
} else if let Some(use_diag) = use_diag {
871-
format!(" in {}", use_diag.type_descr())
872-
} else {
873-
"".into()
874-
};
875-
876-
// FIXME: We really shouldn't be dealing with strings here
877-
// but instead use a sensible enum for cases like this.
878-
let preposition = if "the value" == kind_str { "of" } else { "for" };
879-
// For example: "cannot infer type for type parameter `T`"
880-
format!(
881-
"cannot infer {} {} {} `{}`{}",
882-
kind_str, preposition, descr, type_name, extra_descr
883-
)
884-
}
885-
}
886833
}

0 commit comments

Comments
 (0)