From 24ac777a641c7efc6d2f6e9a69617800314119f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 25 Oct 2024 17:01:20 +0000 Subject: [PATCH 1/3] Add test for #132013 --- ...-between-expected-trait-and-found-trait.rs | 20 ++++++ ...between-expected-trait-and-found-trait.svg | 62 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs create mode 100644 tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg diff --git a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs new file mode 100644 index 0000000000000..f1c196a154d52 --- /dev/null +++ b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.rs @@ -0,0 +1,20 @@ +//@ only-linux +//@ compile-flags: --error-format=human --color=always +//@ error-pattern: the trait bound + +trait Foo: Bar {} + +trait Bar {} + +struct Struct; + +impl Foo for T where T: Bar +{} + +impl<'a> Bar<()> for Struct {} + +fn foo() -> impl Foo { + Struct +} + +fn main() {} diff --git a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg new file mode 100644 index 0000000000000..aaf08b9a246c3 --- /dev/null +++ b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg @@ -0,0 +1,62 @@ + + + + + + + error[E0277]: the trait bound `Struct: Foo<i32>` is not satisfied + + --> $DIR/highlight-difference-between-expected-trait-and-found-trait.rs:16:13 + + | + + LL | fn foo() -> impl Foo<i32> { + + | ^^^^^^^^^^^^^ the trait `Bar<i32>` is not implemented for `Struct`, which is required by `Struct: Foo<i32>` + + | + + = help: the trait `Bar<()>` is implemented for `Struct` + + = help: for that trait implementation, expected `()`, found `i32` + + note: required for `Struct` to implement `Foo<i32>` + + --> $DIR/highlight-difference-between-expected-trait-and-found-trait.rs:11:12 + + | + + LL | impl<T, K> Foo<K> for T where T: Bar<K> + + | ^^^^^^ ^ ------ unsatisfied trait bound introduced here + + + + error: aborting due to 1 previous error + + + + For more information about this error, try `rustc --explain E0277`. + + + + + + From aa82fd6d1dd2f2ac6a44dbfa99e1de779b58428a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 24 Oct 2024 00:20:04 +0000 Subject: [PATCH 2/3] Tweak highlighting when trait is available for different type When printing ``` = help: the trait `chumsky::private::ParserSealed<'_, &'a str, ((), ()), chumsky::extra::Full>` is implemented for `Then>, {closure@src/main.rs:9:17: 9:27}>, char>, chumsky::combinator::Map, O, {closure@src/main.rs:11:24: 11:27}>, (), (), chumsky::extra::Full>` = help: for that trait implementation, expected `((), ())`, found `()` ``` Highlight only the `expected` and `found` types, instead of the full type in the first `help`. --- .../traits/fulfillment_errors.rs | 19 ++++++++++++++----- ...between-expected-trait-and-found-trait.svg | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index b7e2ed391cd4b..1852837d5c729 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1835,6 +1835,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if impl_trait_ref.references_error() { return false; } + let self_ty = impl_trait_ref.self_ty().to_string(); err.highlighted_help(vec![ StringPart::normal(format!( "the trait `{}` ", @@ -1842,16 +1843,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { )), StringPart::highlighted("is"), StringPart::normal(" implemented for `"), - StringPart::highlighted(impl_trait_ref.self_ty().to_string()), + if let [TypeError::Sorts(_)] = &terrs[..] { + StringPart::normal(self_ty) + } else { + StringPart::highlighted(self_ty) + }, StringPart::normal("`"), ]); if let [TypeError::Sorts(exp_found)] = &terrs[..] { let exp_found = self.resolve_vars_if_possible(*exp_found); - err.help(format!( - "for that trait implementation, expected `{}`, found `{}`", - exp_found.expected, exp_found.found - )); + err.highlighted_help(vec![ + StringPart::normal("for that trait implementation, "), + StringPart::normal("expected `"), + StringPart::highlighted(exp_found.expected.to_string()), + StringPart::normal("`, found `"), + StringPart::highlighted(exp_found.found.to_string()), + StringPart::normal("`"), + ]); } true diff --git a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg index aaf08b9a246c3..e09b96e5ff2c1 100644 --- a/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg +++ b/tests/ui/impl-trait/diagnostics/highlight-difference-between-expected-trait-and-found-trait.svg @@ -33,9 +33,9 @@ | - = help: the trait `Bar<()>` is implemented for `Struct` + = help: the trait `Bar<()>` is implemented for `Struct` - = help: for that trait implementation, expected `()`, found `i32` + = help: for that trait implementation, expected `()`, found `i32` note: required for `Struct` to implement `Foo<i32>` From 5980a32ef1b8a542bbf64e685af09d5a87d9b501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 24 Oct 2024 00:22:37 +0000 Subject: [PATCH 3/3] Pass long type path into `note_obligation_cause_code` to avoid printing same path multiple times Because `note_obligation_cause_code` is recursive, if multiple types are too long to print to the terminal, a `long_ty_file` will be created. Before, one was created *per recursion*. Now, it is passed in so it gets printed only once. Part of #132013. --- .../traits/fulfillment_errors.rs | 9 +++++ .../src/error_reporting/traits/mod.rs | 11 ++++++ .../src/error_reporting/traits/overflow.rs | 11 ++++++ .../src/error_reporting/traits/suggestions.rs | 38 +++++++++---------- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1852837d5c729..6014ed555b64d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2169,6 +2169,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { + let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -2177,7 +2178,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.code(), &mut vec![], &mut Default::default(), + &mut long_ty_file, ); + if let Some(file) = long_ty_file { + err.note(format!( + "the full name for the type has been written to '{}'", + file.display(), + )); + err.note("consider using `--verbose` to print the full type name to the console"); + } self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index ca23f7765819f..b108a9352a53d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -305,6 +305,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let ObligationCauseCode::WhereClause(..) | ObligationCauseCode::WhereClauseInExpr(..) = code { + let mut long_ty_file = None; self.note_obligation_cause_code( error.obligation.cause.body_id, &mut diag, @@ -313,7 +314,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { code, &mut vec![], &mut Default::default(), + &mut long_ty_file, ); + if let Some(file) = long_ty_file { + diag.note(format!( + "the full name for the type has been written to '{}'", + file.display(), + )); + diag.note( + "consider using `--verbose` to print the full type name to the console", + ); + } } diag.emit() } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index f4c5733d4a6d9..c47c21696911b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -144,6 +144,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.span, suggest_increasing_limit, |err| { + let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -152,7 +153,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.code(), &mut vec![], &mut Default::default(), + &mut long_ty_file, ); + if let Some(file) = long_ty_file { + err.note(format!( + "the full name for the type has been written to '{}'", + file.display(), + )); + err.note( + "consider using `--verbose` to print the full type name to the console", + ); + } }, ); } 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 1fe93cb017ab9..a7d1f1bd8d042 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3,6 +3,7 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; +use std::path::PathBuf; use itertools::{EitherOrBoth, Itertools}; use rustc_data_structures::fx::FxHashSet; @@ -2703,6 +2704,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Add a note for the item obligation that remains - normally a note pointing to the // bound that introduced the obligation (e.g. `T: Send`). debug!(?next_code); + let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -2711,6 +2713,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { next_code.unwrap(), &mut Vec::new(), &mut Default::default(), + &mut long_ty_file, ); } @@ -2723,11 +2726,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause_code: &ObligationCauseCode<'tcx>, obligated_types: &mut Vec>, seen_requirements: &mut FxHashSet, + long_ty_file: &mut Option, ) where T: Upcast, ty::Predicate<'tcx>>, { - let mut long_ty_file = None; - let tcx = self.tcx; let predicate = predicate.upcast(tcx); let suggest_remove_deref = |err: &mut Diag<'_, G>, expr: &hir::Expr<'_>| { @@ -2957,9 +2959,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::Coercion { source, target } => { let source = - tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut long_ty_file); + tcx.short_ty_string(self.resolve_vars_if_possible(source), long_ty_file); let target = - tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut long_ty_file); + tcx.short_ty_string(self.resolve_vars_if_possible(target), long_ty_file); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); @@ -3249,7 +3251,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if !is_upvar_tys_infer_tuple { - let ty_str = tcx.short_ty_string(ty, &mut long_ty_file); + let ty_str = tcx.short_ty_string(ty, long_ty_file); let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { @@ -3327,6 +3329,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, + long_ty_file, ) }); } else { @@ -3339,6 +3342,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause_code.peel_derives(), obligated_types, seen_requirements, + long_ty_file, ) }); } @@ -3347,8 +3351,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut parent_trait_pred = self.resolve_vars_if_possible(data.derived.parent_trait_pred); let parent_def_id = parent_trait_pred.def_id(); - let self_ty_str = tcx - .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut long_ty_file); + let self_ty_str = + tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file); let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string(); let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`"); let mut is_auto_trait = false; @@ -3444,10 +3448,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { count, pluralize!(count) )); - let self_ty = tcx.short_ty_string( - parent_trait_pred.skip_binder().self_ty(), - &mut long_ty_file, - ); + let self_ty = tcx + .short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3463,6 +3465,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, + long_ty_file, ) }); } @@ -3479,6 +3482,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, + long_ty_file, ) }); } @@ -3493,6 +3497,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { nested, obligated_types, seen_requirements, + long_ty_file, ) }); let mut multispan = MultiSpan::from(span); @@ -3523,6 +3528,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent_code, obligated_types, seen_requirements, + long_ty_file, ) }); } @@ -3562,7 +3568,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::OpaqueReturnType(expr_info) => { if let Some((expr_ty, hir_id)) = expr_info { - let expr_ty = self.tcx.short_ty_string(expr_ty, &mut long_ty_file); + let expr_ty = self.tcx.short_ty_string(expr_ty, long_ty_file); let expr = self.infcx.tcx.hir().expect_expr(hir_id); err.span_label( expr.span, @@ -3574,14 +3580,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } } - - if let Some(file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - err.note("consider using `--verbose` to print the full type name to the console"); - } } #[instrument(