From 62a7989615d3cde041d9dc1952c3044fbacbed22 Mon Sep 17 00:00:00 2001 From: long-long-float Date: Thu, 25 Jan 2024 23:15:54 +0900 Subject: [PATCH] Wrap dyn type with parentheses in suggestion --- compiler/rustc_hir/src/hir.rs | 44 +++++++++++++++++++ .../src/infer/error_reporting/mod.rs | 20 ++++++--- tests/ui/suggestions/issue-120223.rs | 9 ++++ tests/ui/suggestions/issue-120223.stderr | 17 +++++++ 4 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 tests/ui/suggestions/issue-120223.rs create mode 100644 tests/ui/suggestions/issue-120223.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 77044df9a4092..8556e4368eee2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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, @@ -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 + 'static` needs parentheses `(dyn Future) + '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(); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 104bf4a5be873..d0f5f3a4b8a20 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -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); @@ -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) } @@ -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); diff --git a/tests/ui/suggestions/issue-120223.rs b/tests/ui/suggestions/issue-120223.rs new file mode 100644 index 0000000000000..927bc546fd055 --- /dev/null +++ b/tests/ui/suggestions/issue-120223.rs @@ -0,0 +1,9 @@ +use std::{future::Future}; + +pub fn foo( + executor: impl FnOnce(T) -> dyn Future, +) -> Box dyn Future> { + Box::new(executor) //~ ERROR the parameter type +} + +fn main() {} diff --git a/tests/ui/suggestions/issue-120223.stderr b/tests/ui/suggestions/issue-120223.stderr new file mode 100644 index 0000000000000..83a760715b2fc --- /dev/null +++ b/tests/ui/suggestions/issue-120223.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `impl FnOnce(T) -> dyn Future` may not live long enough + --> $DIR/issue-120223.rs:6:5 + | +LL | Box::new(executor) + | ^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `impl FnOnce(T) -> dyn Future` must be valid for the static lifetime... + | ...so that the type `impl FnOnce(T) -> dyn Future` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | executor: impl FnOnce(T) -> (dyn Future) + 'static, + | + +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`.