Skip to content

Commit 62a7989

Browse files
Wrap dyn type with parentheses in suggestion
1 parent c9a7db6 commit 62a7989

File tree

4 files changed

+84
-6
lines changed

4 files changed

+84
-6
lines changed

compiler/rustc_hir/src/hir.rs

+44
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,21 @@ impl GenericBound<'_> {
446446
}
447447
}
448448

449+
pub fn get_inner_ty(&self) -> Option<&Ty<'_>> {
450+
match self {
451+
GenericBound::Trait(data, _) => {
452+
let segment = data.trait_ref.path.segments.first()?;
453+
let binding = segment.args().bindings.first()?;
454+
if let TypeBindingKind::Equality { term: Term::Ty(ty) } = binding.kind {
455+
Some(ty)
456+
} else {
457+
None
458+
}
459+
}
460+
_ => None,
461+
}
462+
}
463+
449464
pub fn span(&self) -> Span {
450465
match self {
451466
GenericBound::Trait(t, ..) => t.span,
@@ -647,6 +662,35 @@ impl<'hir> Generics<'hir> {
647662
)
648663
}
649664

665+
/// Returns bounds span for suggestions.
666+
/// If the span including lifetime bound needs parentheses, it returns a tuple of a span to be surrounded by parentheses and true.
667+
/// e.g. `dyn Future<Output = ()> + 'static` needs parentheses `(dyn Future<Output = ()>) + 'static`
668+
pub fn bounds_span_for_suggestions_with_parentheses(
669+
&self,
670+
param_def_id: LocalDefId,
671+
) -> Option<(Span, bool)> {
672+
self.bounds_for_param(param_def_id).flat_map(|bp| bp.bounds.iter().rev()).find_map(
673+
|bound| {
674+
let span_for_parentheses = bound.get_inner_ty().and_then(|ty| {
675+
if let TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) = ty.kind {
676+
let span = ty.span;
677+
span.can_be_used_for_suggestions().then(|| span)
678+
} else {
679+
None
680+
}
681+
});
682+
683+
span_for_parentheses.map_or_else(
684+
|| {
685+
let bs = bound.span();
686+
bs.can_be_used_for_suggestions().then(|| (bs.shrink_to_hi(), false))
687+
},
688+
|span| Some((span, true)),
689+
)
690+
},
691+
)
692+
}
693+
650694
fn span_for_predicate_removal(&self, pos: usize) -> Span {
651695
let predicate = &self.predicates[pos];
652696
let span = predicate.span();

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -2386,7 +2386,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23862386
generic_param_scope = self.tcx.local_parent(generic_param_scope);
23872387
}
23882388

2389-
// type_param_sugg_span is (span, has_bounds)
2389+
// type_param_sugg_span is (span, has_bounds, needs_parentheses)
23902390
let (type_scope, type_param_sugg_span) = match bound_kind {
23912391
GenericKind::Param(ref param) => {
23922392
let generics = self.tcx.generics_of(generic_param_scope);
@@ -2396,11 +2396,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23962396
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
23972397
// instead we suggest `T: 'a + 'b` in that case.
23982398
let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
2399-
let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
2400-
Some(span) => Some((span, true)),
2399+
let sugg_span = match hir_generics
2400+
.bounds_span_for_suggestions_with_parentheses(def_id)
2401+
{
2402+
Some((span, needs_parentheses)) => Some((span, true, needs_parentheses)),
24012403
// If `param` corresponds to `Self`, no usable suggestion span.
24022404
None if generics.has_self && param.index == 0 => None,
2403-
None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false)),
2405+
None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false, false)),
24042406
};
24052407
(scope, sugg_span)
24062408
}
@@ -2423,12 +2425,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
24232425
let mut suggs = vec![];
24242426
let lt_name = self.suggest_name_region(sub, &mut suggs);
24252427

2426-
if let Some((sp, has_lifetimes)) = type_param_sugg_span
2428+
if let Some((sp, has_lifetimes, needs_parentheses)) = type_param_sugg_span
24272429
&& suggestion_scope == type_scope
24282430
{
24292431
let suggestion =
24302432
if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
2431-
suggs.push((sp, suggestion))
2433+
2434+
if needs_parentheses {
2435+
suggs.push((sp.shrink_to_lo(), "(".to_string()));
2436+
suggs.push((sp.shrink_to_hi(), format!("){suggestion}")));
2437+
} else {
2438+
suggs.push((sp, suggestion))
2439+
}
24322440
} else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
24332441
let pred = format!("{bound_kind}: {lt_name}");
24342442
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);

tests/ui/suggestions/issue-120223.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use std::{future::Future};
2+
3+
pub fn foo<T>(
4+
executor: impl FnOnce(T) -> dyn Future<Output = ()>,
5+
) -> Box<dyn FnOnce(T) -> dyn Future<Output = ()>> {
6+
Box::new(executor) //~ ERROR the parameter type
7+
}
8+
9+
fn main() {}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0310]: the parameter type `impl FnOnce(T) -> dyn Future<Output = ()>` may not live long enough
2+
--> $DIR/issue-120223.rs:6:5
3+
|
4+
LL | Box::new(executor)
5+
| ^^^^^^^^^^^^^^^^^^
6+
| |
7+
| the parameter type `impl FnOnce(T) -> dyn Future<Output = ()>` must be valid for the static lifetime...
8+
| ...so that the type `impl FnOnce(T) -> dyn Future<Output = ()>` will meet its required lifetime bounds
9+
|
10+
help: consider adding an explicit lifetime bound
11+
|
12+
LL | executor: impl FnOnce(T) -> (dyn Future<Output = ()>) + 'static,
13+
| + +++++++++++
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0310`.

0 commit comments

Comments
 (0)