Skip to content

Commit

Permalink
Wrap dyn type with parentheses in suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
long-long-float committed Feb 16, 2024
1 parent c9a7db6 commit 62a7989
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 6 deletions.
44 changes: 44 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,21 @@ impl GenericBound<'_> {
}
}

pub fn get_inner_ty(&self) -> Option<&Ty<'_>> {
match self {
GenericBound::Trait(data, _) => {
let segment = data.trait_ref.path.segments.first()?;
let binding = segment.args().bindings.first()?;
if let TypeBindingKind::Equality { term: Term::Ty(ty) } = binding.kind {
Some(ty)
} else {
None
}
}
_ => None,
}
}

pub fn span(&self) -> Span {
match self {
GenericBound::Trait(t, ..) => t.span,
Expand Down Expand Up @@ -647,6 +662,35 @@ impl<'hir> Generics<'hir> {
)
}

/// Returns bounds span for suggestions.
/// If the span including lifetime bound needs parentheses, it returns a tuple of a span to be surrounded by parentheses and true.
/// e.g. `dyn Future<Output = ()> + 'static` needs parentheses `(dyn Future<Output = ()>) + 'static`
pub fn bounds_span_for_suggestions_with_parentheses(
&self,
param_def_id: LocalDefId,
) -> Option<(Span, bool)> {
self.bounds_for_param(param_def_id).flat_map(|bp| bp.bounds.iter().rev()).find_map(
|bound| {
let span_for_parentheses = bound.get_inner_ty().and_then(|ty| {
if let TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) = ty.kind {
let span = ty.span;
span.can_be_used_for_suggestions().then(|| span)
} else {
None
}
});

span_for_parentheses.map_or_else(
|| {
let bs = bound.span();
bs.can_be_used_for_suggestions().then(|| (bs.shrink_to_hi(), false))
},
|span| Some((span, true)),
)
},
)
}

fn span_for_predicate_removal(&self, pos: usize) -> Span {
let predicate = &self.predicates[pos];
let span = predicate.span();
Expand Down
20 changes: 14 additions & 6 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2386,7 +2386,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
generic_param_scope = self.tcx.local_parent(generic_param_scope);
}

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

if let Some((sp, has_lifetimes)) = type_param_sugg_span
if let Some((sp, has_lifetimes, needs_parentheses)) = type_param_sugg_span
&& suggestion_scope == type_scope
{
let suggestion =
if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
suggs.push((sp, suggestion))

if needs_parentheses {
suggs.push((sp.shrink_to_lo(), "(".to_string()));
suggs.push((sp.shrink_to_hi(), format!("){suggestion}")));
} else {
suggs.push((sp, suggestion))
}
} else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
let pred = format!("{bound_kind}: {lt_name}");
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/suggestions/issue-120223.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use std::{future::Future};

pub fn foo<T>(
executor: impl FnOnce(T) -> dyn Future<Output = ()>,
) -> Box<dyn FnOnce(T) -> dyn Future<Output = ()>> {
Box::new(executor) //~ ERROR the parameter type
}

fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/suggestions/issue-120223.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0310]: the parameter type `impl FnOnce(T) -> dyn Future<Output = ()>` may not live long enough
--> $DIR/issue-120223.rs:6:5
|
LL | Box::new(executor)
| ^^^^^^^^^^^^^^^^^^
| |
| the parameter type `impl FnOnce(T) -> dyn Future<Output = ()>` must be valid for the static lifetime...
| ...so that the type `impl FnOnce(T) -> dyn Future<Output = ()>` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
LL | executor: impl FnOnce(T) -> (dyn Future<Output = ()>) + 'static,
| + +++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0310`.

0 comments on commit 62a7989

Please sign in to comment.