about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-03-13 17:44:04 +0100
committerGitHub <noreply@github.com>2025-03-13 17:44:04 +0100
commitb5955e74e80d074f71625cd3340a31c14f7765c8 (patch)
treee287c6d376eac81bec14aa11929f8f9f6df6565b
parent93257e2d20809d82d1bc0fcc1942480d1a66d7cd (diff)
parent7a08d0368f067f46c05bd7075f098ac84a50d468 (diff)
downloadrust-b5955e74e80d074f71625cd3340a31c14f7765c8.tar.gz
rust-b5955e74e80d074f71625cd3340a31c14f7765c8.zip
Rollup merge of #138126 - compiler-errors:rtn-for-sugg, r=oli-obk
Add an opt-out in pretty printing for RTN rendering

Today, we render RPITIT types like `impl Sized { T::method(..) }` when RTN is enabled. This is very useful for diagnostics, since it's often not clear what the `impl Sized` type means by itself, and it makes it clear that that's an RPITIT that can be bounded using RTN syntax. See #115624.

However, since we don't distinguish types that are rendered for the purposes of printing messages vs suggestions, this representation leaks into suggestions and turns into code that can't be parsed. This PR adds a new `with_types_for_suggestion! {}` and `with_types_for_signature! {}` options to the pretty printing architecture to make it clear that we're rendering a type for code suggestions.

This can be applied later as we find that we need it.
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs5
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs117
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs7
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr4
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/display.stderr8
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/rendering.fixed15
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/rendering.rs14
-rw-r--r--tests/ui/associated-type-bounds/return-type-notation/rendering.stderr12
8 files changed, 152 insertions, 30 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 9c28fac809d..b4a16b2b805 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -84,6 +84,7 @@ use rustc_infer::infer::{self, TyCtxtInferExt as _};
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::print::with_types_for_signature;
 use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode};
 use rustc_middle::{bug, span_bug};
 use rustc_session::parse::feature_err;
@@ -240,11 +241,11 @@ fn missing_items_err(
         (Vec::new(), Vec::new(), Vec::new());
 
     for &trait_item in missing_items {
-        let snippet = suggestion_signature(
+        let snippet = with_types_for_signature!(suggestion_signature(
             tcx,
             trait_item,
             tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(),
-        );
+        ));
         let code = format!("{padding}{snippet}\n{padding}");
         if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
             missing_trait_item_label
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 2a3a7705b7b..72924c0dd4b 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -63,6 +63,18 @@ thread_local! {
     static FORCE_TRIMMED_PATH: Cell<bool> = const { Cell::new(false) };
     static REDUCED_QUERIES: Cell<bool> = const { Cell::new(false) };
     static NO_VISIBLE_PATH: Cell<bool> = const { Cell::new(false) };
+    static RTN_MODE: Cell<RtnMode> = const { Cell::new(RtnMode::ForDiagnostic) };
+}
+
+/// Rendering style for RTN types.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum RtnMode {
+    /// Print the RTN type as an impl trait with its path, i.e.e `impl Sized { T::method(..) }`.
+    ForDiagnostic,
+    /// Print the RTN type as an impl trait, i.e. `impl Sized`.
+    ForSignature,
+    /// Print the RTN type as a value path, i.e. `T::method(..): ...`.
+    ForSuggestion,
 }
 
 macro_rules! define_helper {
@@ -124,6 +136,38 @@ define_helper!(
     fn with_no_visible_paths(NoVisibleGuard, NO_VISIBLE_PATH);
 );
 
+#[must_use]
+pub struct RtnModeHelper(RtnMode);
+
+impl RtnModeHelper {
+    pub fn with(mode: RtnMode) -> RtnModeHelper {
+        RtnModeHelper(RTN_MODE.with(|c| c.replace(mode)))
+    }
+}
+
+impl Drop for RtnModeHelper {
+    fn drop(&mut self) {
+        RTN_MODE.with(|c| c.set(self.0))
+    }
+}
+
+/// Print types for the purposes of a suggestion.
+///
+/// Specifically, this will render RPITITs as `T::method(..)` which is suitable for
+/// things like where-clauses.
+pub macro with_types_for_suggestion($e:expr) {{
+    let _guard = $crate::ty::print::pretty::RtnModeHelper::with(RtnMode::ForSuggestion);
+    $e
+}}
+
+/// Print types for the purposes of a signature suggestion.
+///
+/// Specifically, this will render RPITITs as `impl Trait` rather than `T::method(..)`.
+pub macro with_types_for_signature($e:expr) {{
+    let _guard = $crate::ty::print::pretty::RtnModeHelper::with(RtnMode::ForSignature);
+    $e
+}}
+
 /// Avoids running any queries during prints.
 pub macro with_no_queries($e:expr) {{
     $crate::ty::print::with_reduced_queries!($crate::ty::print::with_forced_impl_filename_line!(
@@ -1223,22 +1267,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             }
         }
 
-        if self.tcx().features().return_type_notation()
-            && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) =
-                self.tcx().opt_rpitit_info(def_id)
-            && let ty::Alias(_, alias_ty) =
-                self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind()
-            && alias_ty.def_id == def_id
-            && let generics = self.tcx().generics_of(fn_def_id)
-            // FIXME(return_type_notation): We only support lifetime params for now.
-            && generics.own_params.iter().all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime))
-        {
-            let num_args = generics.count();
-            write!(self, " {{ ")?;
-            self.print_def_path(fn_def_id, &args[..num_args])?;
-            write!(self, "(..) }}")?;
-        }
-
         Ok(())
     }
 
@@ -1306,6 +1334,46 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         )
     }
 
+    fn pretty_print_rpitit(
+        &mut self,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Result<(), PrintError> {
+        let fn_args = if self.tcx().features().return_type_notation()
+            && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) =
+                self.tcx().opt_rpitit_info(def_id)
+            && let ty::Alias(_, alias_ty) =
+                self.tcx().fn_sig(fn_def_id).skip_binder().output().skip_binder().kind()
+            && alias_ty.def_id == def_id
+            && let generics = self.tcx().generics_of(fn_def_id)
+            // FIXME(return_type_notation): We only support lifetime params for now.
+            && generics.own_params.iter().all(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime))
+        {
+            let num_args = generics.count();
+            Some((fn_def_id, &args[..num_args]))
+        } else {
+            None
+        };
+
+        match (fn_args, RTN_MODE.with(|c| c.get())) {
+            (Some((fn_def_id, fn_args)), RtnMode::ForDiagnostic) => {
+                self.pretty_print_opaque_impl_type(def_id, args)?;
+                write!(self, " {{ ")?;
+                self.print_def_path(fn_def_id, fn_args)?;
+                write!(self, "(..) }}")?;
+            }
+            (Some((fn_def_id, fn_args)), RtnMode::ForSuggestion) => {
+                self.print_def_path(fn_def_id, fn_args)?;
+                write!(self, "(..)")?;
+            }
+            _ => {
+                self.pretty_print_opaque_impl_type(def_id, args)?;
+            }
+        }
+
+        Ok(())
+    }
+
     fn ty_infer_name(&self, _: ty::TyVid) -> Option<Symbol> {
         None
     }
@@ -3123,22 +3191,21 @@ define_print! {
     ty::AliasTerm<'tcx> {
         match self.kind(cx.tcx()) {
             ty::AliasTermKind::InherentTy => p!(pretty_print_inherent_projection(*self)),
-            ty::AliasTermKind::ProjectionTy
-            | ty::AliasTermKind::WeakTy
-            | ty::AliasTermKind::OpaqueTy
-            | ty::AliasTermKind::UnevaluatedConst
-            | ty::AliasTermKind::ProjectionConst => {
-                // If we're printing verbosely, or don't want to invoke queries
-                // (`is_impl_trait_in_trait`), then fall back to printing the def path.
-                // This is likely what you want if you're debugging the compiler anyways.
+            ty::AliasTermKind::ProjectionTy => {
                 if !(cx.should_print_verbose() || with_reduced_queries())
                     && cx.tcx().is_impl_trait_in_trait(self.def_id)
                 {
-                    return cx.pretty_print_opaque_impl_type(self.def_id, self.args);
+                    p!(pretty_print_rpitit(self.def_id, self.args))
                 } else {
                     p!(print_def_path(self.def_id, self.args));
                 }
             }
+            | ty::AliasTermKind::WeakTy
+            | ty::AliasTermKind::OpaqueTy
+            | ty::AliasTermKind::UnevaluatedConst
+            | ty::AliasTermKind::ProjectionConst => {
+                p!(print_def_path(self.def_id, self.args));
+            }
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 4f89f3c2b49..dce85b43df1 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -26,7 +26,7 @@ use rustc_middle::traits::IsConstable;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::print::{
     PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _,
-    with_forced_trimmed_paths, with_no_trimmed_paths,
+    with_forced_trimmed_paths, with_no_trimmed_paths, with_types_for_suggestion,
 };
 use rustc_middle::ty::{
     self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder,
@@ -110,7 +110,7 @@ impl<'a, 'tcx> CoroutineData<'a, 'tcx> {
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
     (
         generics.tail_span_for_predicate_suggestion(),
-        format!("{} {}", generics.add_where_or_trailing_comma(), pred),
+        with_types_for_suggestion!(format!("{} {}", generics.add_where_or_trailing_comma(), pred)),
     )
 }
 
@@ -136,7 +136,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
     if hir_generics.where_clause_span.from_expansion()
         || hir_generics.where_clause_span.desugaring_kind().is_some()
         || projection.is_some_and(|projection| {
-            tcx.is_impl_trait_in_trait(projection.def_id)
+            (tcx.is_impl_trait_in_trait(projection.def_id)
+                && !tcx.features().return_type_notation())
                 || tcx.lookup_stability(projection.def_id).is_some_and(|stab| stab.is_unstable())
         })
     {
diff --git a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr
index 0a31cc67533..459f3ea1642 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr
+++ b/tests/ui/associated-type-bounds/return-type-notation/basic.without.stderr
@@ -15,6 +15,10 @@ note: required by a bound in `is_send`
    |
 LL | fn is_send(_: impl Send) {}
    |                    ^^^^ required by this bound in `is_send`
+help: consider further restricting the associated type
+   |
+LL | >() where <T as Foo>::method(..): Send {
+   |     ++++++++++++++++++++++++++++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/associated-type-bounds/return-type-notation/display.stderr b/tests/ui/associated-type-bounds/return-type-notation/display.stderr
index b895d796952..a614089ce40 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/display.stderr
+++ b/tests/ui/associated-type-bounds/return-type-notation/display.stderr
@@ -11,6 +11,10 @@ note: required by a bound in `needs_trait`
    |
 LL | fn needs_trait(_: impl Trait) {}
    |                        ^^^^^ required by this bound in `needs_trait`
+help: consider further restricting the associated type
+   |
+LL | fn foo<T: Assoc>(t: T) where <T as Assoc>::method(..): Trait {
+   |                        +++++++++++++++++++++++++++++++++++++
 
 error[E0277]: the trait bound `impl Sized { <T as Assoc>::method_with_lt(..) }: Trait` is not satisfied
   --> $DIR/display.rs:16:17
@@ -25,6 +29,10 @@ note: required by a bound in `needs_trait`
    |
 LL | fn needs_trait(_: impl Trait) {}
    |                        ^^^^^ required by this bound in `needs_trait`
+help: consider further restricting the associated type
+   |
+LL | fn foo<T: Assoc>(t: T) where <T as Assoc>::method_with_lt(..): Trait {
+   |                        +++++++++++++++++++++++++++++++++++++++++++++
 
 error[E0277]: the trait bound `impl Sized: Trait` is not satisfied
   --> $DIR/display.rs:18:17
diff --git a/tests/ui/associated-type-bounds/return-type-notation/rendering.fixed b/tests/ui/associated-type-bounds/return-type-notation/rendering.fixed
new file mode 100644
index 00000000000..72c174a0ca0
--- /dev/null
+++ b/tests/ui/associated-type-bounds/return-type-notation/rendering.fixed
@@ -0,0 +1,15 @@
+//@ run-rustfix
+
+#![allow(unused)]
+#![feature(return_type_notation)]
+
+trait Foo {
+    fn missing() -> impl Sized;
+}
+
+impl Foo for () {
+    //~^ ERROR not all trait items implemented, missing: `missing`
+fn missing() -> impl Sized { todo!() }
+}
+
+fn main() {}
diff --git a/tests/ui/associated-type-bounds/return-type-notation/rendering.rs b/tests/ui/associated-type-bounds/return-type-notation/rendering.rs
new file mode 100644
index 00000000000..4c9948d4c06
--- /dev/null
+++ b/tests/ui/associated-type-bounds/return-type-notation/rendering.rs
@@ -0,0 +1,14 @@
+//@ run-rustfix
+
+#![allow(unused)]
+#![feature(return_type_notation)]
+
+trait Foo {
+    fn missing() -> impl Sized;
+}
+
+impl Foo for () {
+    //~^ ERROR not all trait items implemented, missing: `missing`
+}
+
+fn main() {}
diff --git a/tests/ui/associated-type-bounds/return-type-notation/rendering.stderr b/tests/ui/associated-type-bounds/return-type-notation/rendering.stderr
new file mode 100644
index 00000000000..62fdeb059dd
--- /dev/null
+++ b/tests/ui/associated-type-bounds/return-type-notation/rendering.stderr
@@ -0,0 +1,12 @@
+error[E0046]: not all trait items implemented, missing: `missing`
+  --> $DIR/rendering.rs:10:1
+   |
+LL |     fn missing() -> impl Sized;
+   |     --------------------------- `missing` from trait
+...
+LL | impl Foo for () {
+   | ^^^^^^^^^^^^^^^ missing `missing` in implementation
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0046`.