From af6b84aaab06aec34dff70284281b14e3f547008 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Thu, 26 Oct 2023 08:20:49 -0700 Subject: [PATCH 1/2] don't add redundant help for object safety violations --- .../src/traits/error_reporting/mod.rs | 12 ++- compiler/rustc_middle/src/traits/mod.rs | 95 +++++++++++++------ .../src/traits/object_safety.rs | 2 +- .../object-safety-err-ret.stderr | 1 - 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 32966011932d1..59aee542f9625 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -104,9 +104,15 @@ pub fn report_object_safety_error<'tcx>( if trait_span.is_some() { let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); reported_violations.sort(); - for violation in reported_violations { - // Only provide the help if its a local trait, otherwise it's not actionable. - violation.solution(&mut err); + + // Only provide the help if its a local trait, otherwise it's not actionable. + let mut potential_solutions: Vec<_> = + reported_violations.into_iter().map(|violation| violation.solution()).collect(); + potential_solutions.sort(); + // Allows us to skip suggesting that the same item should be moved to another trait multiple times. + potential_solutions.dedup(); + for solution in potential_solutions { + solution.add_to(&mut err); } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 2e367df2c4f1b..09543a2f0043c 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -843,50 +843,31 @@ impl ObjectSafetyViolation { } } - pub fn solution(&self, err: &mut Diagnostic) { + pub fn solution(&self) -> ObjectSafetyViolationSolution { match self { ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) - | ObjectSafetyViolation::SupertraitNonLifetimeBinder(..) => {} + | ObjectSafetyViolation::SupertraitNonLifetimeBinder(..) => { + ObjectSafetyViolationSolution::None + } ObjectSafetyViolation::Method( name, MethodViolationCode::StaticMethod(Some((add_self_sugg, make_sized_sugg))), _, - ) => { - err.span_suggestion( - add_self_sugg.1, - format!( - "consider turning `{name}` into a method by giving it a `&self` argument" - ), - add_self_sugg.0.to_string(), - Applicability::MaybeIncorrect, - ); - err.span_suggestion( - make_sized_sugg.1, - format!( - "alternatively, consider constraining `{name}` so it does not apply to \ - trait objects" - ), - make_sized_sugg.0.to_string(), - Applicability::MaybeIncorrect, - ); - } + ) => ObjectSafetyViolationSolution::AddSelfOrMakeSized { + name: *name, + add_self_sugg: add_self_sugg.clone(), + make_sized_sugg: make_sized_sugg.clone(), + }, ObjectSafetyViolation::Method( name, MethodViolationCode::UndispatchableReceiver(Some(span)), _, - ) => { - err.span_suggestion( - *span, - format!("consider changing method `{name}`'s `self` parameter to be `&self`"), - "&Self", - Applicability::MachineApplicable, - ); - } + ) => ObjectSafetyViolationSolution::ChangeToRefSelf(*name, *span), ObjectSafetyViolation::AssocConst(name, _) | ObjectSafetyViolation::GAT(name, _) | ObjectSafetyViolation::Method(name, ..) => { - err.help(format!("consider moving `{name}` to another trait")); + ObjectSafetyViolationSolution::MoveToAnotherTrait(*name) } } } @@ -910,6 +891,60 @@ impl ObjectSafetyViolation { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ObjectSafetyViolationSolution { + None, + AddSelfOrMakeSized { + name: Symbol, + add_self_sugg: (String, Span), + make_sized_sugg: (String, Span), + }, + ChangeToRefSelf(Symbol, Span), + MoveToAnotherTrait(Symbol), +} + +impl ObjectSafetyViolationSolution { + pub fn add_to(self, err: &mut Diagnostic) { + match self { + ObjectSafetyViolationSolution::None => {} + ObjectSafetyViolationSolution::AddSelfOrMakeSized { + name, + add_self_sugg, + make_sized_sugg, + } => { + err.span_suggestion( + add_self_sugg.1, + format!( + "consider turning `{name}` into a method by giving it a `&self` argument" + ), + add_self_sugg.0, + Applicability::MaybeIncorrect, + ); + err.span_suggestion( + make_sized_sugg.1, + format!( + "alternatively, consider constraining `{name}` so it does not apply to \ + trait objects" + ), + make_sized_sugg.0, + Applicability::MaybeIncorrect, + ); + } + ObjectSafetyViolationSolution::ChangeToRefSelf(name, span) => { + err.span_suggestion( + span, + format!("consider changing method `{name}`'s `self` parameter to be `&self`"), + "&Self", + Applicability::MachineApplicable, + ); + } + ObjectSafetyViolationSolution::MoveToAnotherTrait(name) => { + err.help(format!("consider moving `{name}` to another trait")); + } + } + } +} + /// Reasons a method might not be object-safe. #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] pub enum MethodViolationCode { diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 5d5440094fdda..8268273884d0e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -192,7 +192,7 @@ fn lint_object_unsafe_trait( ); if node.is_some() { // Only provide the help if its a local trait, otherwise it's not - violation.solution(err); + violation.solution().add_to(err); } err }, diff --git a/tests/ui/const-generics/generic_const_exprs/object-safety-err-ret.stderr b/tests/ui/const-generics/generic_const_exprs/object-safety-err-ret.stderr index b7ec657120c6d..15617a8a15755 100644 --- a/tests/ui/const-generics/generic_const_exprs/object-safety-err-ret.stderr +++ b/tests/ui/const-generics/generic_const_exprs/object-safety-err-ret.stderr @@ -14,7 +14,6 @@ LL | fn test(&self) -> [u8; bar::()]; | | | ...because method `test` references the `Self` type in its `where` clause = help: consider moving `test` to another trait - = help: consider moving `test` to another trait = help: only type `()` implements the trait, consider using it directly instead error: aborting due to previous error From ee96a7a28846aa269689c74dba210535f43e4588 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Sun, 26 Nov 2023 10:09:25 -0800 Subject: [PATCH 2/2] move comment about local trait --- compiler/rustc_infer/src/traits/error_reporting/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 59aee542f9625..b3cfd843ace3c 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -101,11 +101,12 @@ pub fn report_object_safety_error<'tcx>( to be resolvable dynamically; for more information visit \ ", ); + + // Only provide the help if its a local trait, otherwise it's not actionable. if trait_span.is_some() { let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); reported_violations.sort(); - // Only provide the help if its a local trait, otherwise it's not actionable. let mut potential_solutions: Vec<_> = reported_violations.into_iter().map(|violation| violation.solution()).collect(); potential_solutions.sort();