Add an opt-out in pretty printing for RTN rendering

This commit is contained in:
Michael Goulet 2025-03-12 19:38:09 +00:00
parent 6650252439
commit 7a08d0368f
8 changed files with 154 additions and 32 deletions

View File

@ -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

View File

@ -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,21 +3191,20 @@ define_print! {
ty::AliasTerm<'tcx> {
match self.kind(cx.tcx()) {
ty::AliasTermKind::InherentTy => p!(pretty_print_inherent_projection(*self)),
ty::AliasTermKind::ProjectionTy
ty::AliasTermKind::ProjectionTy => {
if !(cx.should_print_verbose() || with_reduced_queries())
&& cx.tcx().is_impl_trait_in_trait(self.def_id)
{
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 => {
// 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.
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);
} else {
p!(print_def_path(self.def_id, self.args));
}
p!(print_def_path(self.def_id, self.args));
}
}
}

View File

@ -27,7 +27,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,
@ -111,7 +111,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)),
)
}
@ -137,7 +137,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())
})
{

View File

@ -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

View File

@ -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

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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`.