mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
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.
This commit is contained in:
commit
b5955e74e8
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
})
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() {}
|
@ -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() {}
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user