From 4c7fb9efb7804e489bcf4fe1d3cdf0f7df3b0eff Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 19:01:44 +0300 Subject: [PATCH 1/4] Add helper function to suggest multiple constraints Add `rustc_middle::ty::suggest_constraining_type_params` that suggests adding multiple constraints. `suggest_constraining_type_param` now just forwards params to this new function. --- compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/ty/diagnostics.rs | 398 ++++++++++++-------- 2 files changed, 236 insertions(+), 163 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 7ca564f29e659..f977b0fffebb6 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,6 +56,7 @@ #![feature(nonzero_ops)] #![feature(unwrap_infallible)] #![feature(decl_macro)] +#![feature(drain_filter)] #![recursion_limit = "512"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 58cf9fa7a8943..99a3d4c7fe4f7 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -7,6 +7,7 @@ use crate::ty::{ ProjectionTy, Term, Ty, TyCtxt, TypeAndMut, }; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -157,9 +158,17 @@ pub fn suggest_arbitrary_trait_bound( true } +#[derive(Debug)] +enum SuggestChangingConstraintsMessage<'a> { + RestrictBoundFurther, + RestrictType { ty: &'a str }, + RestrictTypeFurther { ty: &'a str }, + RemovingQSized, +} + fn suggest_removing_unsized_bound( generics: &hir::Generics<'_>, - err: &mut Diagnostic, + suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, param_name: &str, param: &hir::GenericParam<'_>, def_id: Option, @@ -221,13 +230,12 @@ fn suggest_removing_unsized_bound( // ^^^^^^^^^ (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()), }; - err.span_suggestion_verbose( + + suggestions.push(( sp, - "consider removing the `?Sized` bound to make the \ - type parameter `Sized`", String::new(), - Applicability::MaybeIncorrect, - ); + SuggestChangingConstraintsMessage::RemovingQSized, + )); } } _ => {} @@ -249,13 +257,12 @@ fn suggest_removing_unsized_bound( // ^^^^^^^^^ (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()), }; - err.span_suggestion_verbose( + + suggestions.push(( sp, - "consider removing the `?Sized` bound to make the type parameter \ - `Sized`", String::new(), - Applicability::MaybeIncorrect, - ); + SuggestChangingConstraintsMessage::RemovingQSized, + )); } _ => {} } @@ -271,184 +278,249 @@ pub fn suggest_constraining_type_param( constraint: &str, def_id: Option, ) -> bool { - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + suggest_constraining_type_params( + tcx, + generics, + err, + [(param_name, constraint, def_id)].into_iter(), + ) +} - let Some(param) = param else { - return false; - }; +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_params<'a>( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut Diagnostic, + param_names_and_constraints: impl Iterator)>, +) -> bool { + let mut grouped = FxHashMap::default(); + param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { + grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id)) + }); - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; - let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); - let msg_restrict_type_further = - format!("consider further restricting type parameter `{}`", param_name); + let mut applicability = Applicability::MachineApplicable; + let mut suggestions = Vec::new(); - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - suggest_removing_unsized_bound(generics, err, param_name, param, def_id); - return true; - } - let mut suggest_restrict = |span| { - err.span_suggestion_verbose( - span, - MSG_RESTRICT_BOUND_FURTHER, - format!(" + {}", constraint), - Applicability::MachineApplicable, - ); - }; + for (param_name, mut constraints) in grouped { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + let Some(param) = param else { return false }; - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - suggest_restrict(param.span.shrink_to_hi()); - return true; - } + { + let mut sized_constraints = + constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait()); + if let Some((constraint, def_id)) = sized_constraints.next() { + applicability = Applicability::MaybeIncorrect; - if generics.where_clause.predicates.is_empty() - // Given `trait Base: Super` where `T: Copy`, suggest restricting in the - // `where` clause instead of `trait Base: Super`. - && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - { - if let Some(span) = param.bounds_span_for_suggestions() { - // If user has provided some bounds, suggest restricting them: + err.span_label( + param.span, + &format!("this type parameter needs to be `{}`", constraint), + ); + suggest_removing_unsized_bound( + generics, + &mut suggestions, + param_name, + param, + def_id, + ); + } + } + + if constraints.is_empty() { + continue; + } + + let constraint = constraints.iter().map(|&(c, _)| c).collect::>().join(" + "); + let mut suggest_restrict = |span| { + suggestions.push(( + span, + format!(" + {}", constraint), + SuggestChangingConstraintsMessage::RestrictBoundFurther, + )) + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: // - // fn foo(t: T) { ... } - // --- + // fn foo(t: impl Foo) { ... } + // -------- // | // help: consider further restricting this bound with `+ Bar` // // Suggestion for tools in this case is: // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - suggest_restrict(span); - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - err.span_suggestion_verbose( - param.span.shrink_to_hi(), - &msg_restrict_type, - format!(": {}", constraint), - Applicability::MachineApplicable, - ); + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + continue; } - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - // - // Additionally, there may be no `where` clause whatsoever in the case that this was - // reached because the generic parameter has a default: - // - // Message: - // trait Foo {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // trait Foo where T: Zar {... } - // - insert: `where T: Zar` - - if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - && generics.where_clause.predicates.len() == 0 + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) { - // Suggest a bound, but there is no existing `where` clause *and* the type param has a - // default (``), so we suggest adding `where T: Bar`. - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(" where {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + if let Some(span) = param.bounds_span_for_suggestions() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(span); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + suggestions.push(( + param.span.shrink_to_hi(), + format!(": {}", constraint), + SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, + )); + } } else { - let mut param_spans = Vec::new(); + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + // + // Additionally, there may be no `where` clause whatsoever in the case that this was + // reached because the generic parameter has a default: + // + // Message: + // trait Foo {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // trait Foo where T: Zar {... } + // - insert: `where T: Zar` - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, - bounded_ty, - .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); + if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + && generics.where_clause.predicates.len() == 0 + { + // Suggest a bound, but there is no existing `where` clause *and* the type param has a + // default (``), so we suggest adding `where T: Bar`. + suggestions.push(( + generics.where_clause.tail_span_for_suggestion(), + format!(" where {}: {}", param_name, constraint), + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, + )); + } else { + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, + bounded_ty, + .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } } } } } - } - match param_spans[..] { - [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), - _ => { - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + match param_spans[..] { + [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + suggestions.push(( + generics.where_clause.tail_span_for_suggestion(), + constraints + .iter() + .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint)) + .collect::(), + SuggestChangingConstraintsMessage::RestrictTypeFurther { + ty: param_name, + }, + )); + } } } } + } - true + if suggestions.len() == 1 { + let (span, suggestion, msg) = suggestions.pop().unwrap(); + + let s; + let msg = match msg { + SuggestChangingConstraintsMessage::RestrictBoundFurther => { + "consider further restricting this bound" + } + SuggestChangingConstraintsMessage::RestrictType { ty } => { + s = format!("consider restricting type parameter `{}`", ty); + &s + } + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { + s = format!("consider further restricting type parameter `{}`", ty); + &s + } + SuggestChangingConstraintsMessage::RemovingQSized => { + "consider removing the `?Sized` bound to make the type parameter `Sized`" + } + }; + + err.span_suggestion_verbose(span, msg, suggestion, applicability); + } else { + err.multipart_suggestion_verbose( + "consider restricting type parameters", + suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(), + applicability, + ); } + + true } /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. From 400d343796065e57e78d92b4cf74c78bec38452a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:34:25 +0300 Subject: [PATCH 2/4] Suggest adding `Copy` bound when Adt is moved out Previously we've only suggested adding `Copy` bounds when the type being moved/copied is a type parameter (generic). With this commit we also suggest adding bounds when a type - Can be copy - All predicates that need to be satisfied for that are based on type params i.e. we will suggest `T: Copy` for `Option`, but won't suggest anything for `Option`. Future work: it would be nice to also suggest adding `.clone()` calls --- .../src/diagnostics/conflict_errors.rs | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index eb906d5fde7b4..e8ecb3cb6f3e7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -5,16 +5,21 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; +use rustc_middle::ty::{ + self, suggest_constraining_type_param, suggest_constraining_type_params, PredicateKind, Ty, +}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::TraitEngineExt as _; use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors; @@ -423,7 +428,63 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); } + } else { + // Try to find predicates on *generic params* that would allow copying `ty` + + let tcx = self.infcx.tcx; + let generics = tcx.generics_of(self.mir_def_id()); + if let Some(hir_generics) = tcx + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| tcx.hir().get_generics(def_id)) + { + let predicates: Result, _> = tcx.infer_ctxt().enter(|infcx| { + let mut fulfill_cx = + >::new(infcx.tcx); + + let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); + let cause = ObligationCause::new( + span, + self.mir_hir_id(), + rustc_infer::traits::ObligationCauseCode::MiscObligation, + ); + fulfill_cx.register_bound(&infcx, self.param_env, ty, copy_did, cause); + let errors = fulfill_cx.select_where_possible(&infcx); + + // Only emit suggestion if all required predicates are on generic + errors + .into_iter() + .map(|err| match err.obligation.predicate.kind().skip_binder() { + PredicateKind::Trait(predicate) => { + match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate + .trait_ref + .print_only_trait_path() + .to_string(), + )), + _ => Err(()), + } + } + _ => Err(()), + }) + .collect() + }); + + if let Ok(predicates) = predicates { + suggest_constraining_type_params( + tcx, + hir_generics, + &mut err, + predicates.iter().map(|(param, constraint)| { + (param.name.as_str(), &**constraint, None) + }), + ); + } + } } + let span = if let Some(local) = place.as_local() { let decl = &self.body.local_decls[local]; Some(decl.source_info.span) From 879efa84516da086e7461d65fff6797c8ae1cff9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:38:00 +0300 Subject: [PATCH 3/4] Add a test for Adt copy suggestions --- .../use_of_moved_value_copy_suggestions.rs | 65 ++++++++ ...use_of_moved_value_copy_suggestions.stderr | 151 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.rs create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs new file mode 100644 index 0000000000000..0930927c49d66 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -0,0 +1,65 @@ +fn duplicate_t(t: T) -> (T, T) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_opt(t: Option) -> (Option, Option) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom(t: S) -> (S, S) { + (t, t) //~ use of moved value: `t` +} + +struct S(T); +trait Trait {} +impl Clone for S { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Copy for S {} + +trait A {} +trait B {} + +// Test where bounds are added with different bound placements +fn duplicate_custom_1(t: S) -> (S, S) where { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_2(t: S) -> (S, S) +where + T: A, +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_3(t: S) -> (S, S) +where + T: A, + T: B, +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_4(t: S) -> (S, S) +where + T: B, +{ + (t, t) //~ use of moved value: `t` +} + +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint +fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr new file mode 100644 index 0000000000000..c32ac2cd78aa0 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -0,0 +1,151 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:2:9 + | +LL | fn duplicate_t(t: T) -> (T, T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_t(t: T) -> (T, T) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 + | +LL | fn duplicate_opt(t: Option) -> (Option, Option) { + | - move occurs because `t` has type `Option`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_opt(t: Option) -> (Option, Option) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:10:9 + | +LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:14:9 + | +LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameters + | +LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + | ++++++ ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:18:9 + | +LL | fn duplicate_custom(t: S) -> (S, S) { + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_custom(t: S) -> (S, S) { + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:35:9 + | +LL | fn duplicate_custom_1(t: S) -> (S, S) where { + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_custom_1(t: S) -> (S, S) where { + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:42:9 + | +LL | fn duplicate_custom_2(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting this bound + | +LL | T: A + Trait + Copy, + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:50:9 + | +LL | fn duplicate_custom_3(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting type parameter `T` + | +LL | T: B, T: Trait, T: Copy + | ~~~~~~~~~~~~~~~~~~~ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:57:9 + | +LL | fn duplicate_custom_4(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting this bound + | +LL | T: B + Trait + Copy, + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:62:9 + | +LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0382`. From f0a16b856011c8e63fac98fd6c127c2a0bbd532e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 1 Mar 2022 14:11:42 +0300 Subject: [PATCH 4/4] Use rustfix in copy suggestion test --- .../use_of_moved_value_clone_suggestions.rs | 6 ++ ...se_of_moved_value_clone_suggestions.stderr | 13 ++++ .../use_of_moved_value_copy_suggestions.fixed | 72 +++++++++++++++++++ .../use_of_moved_value_copy_suggestions.rs | 17 +++-- ...use_of_moved_value_copy_suggestions.stderr | 36 +++++----- 5 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/moves/use_of_moved_value_clone_suggestions.rs create mode 100644 src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs new file mode 100644 index 0000000000000..d5c8d4e6bdf2b --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs @@ -0,0 +1,6 @@ +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint +fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr new file mode 100644 index 0000000000000..c25981e6f8063 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions.rs:3:9 + | +LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed new file mode 100644 index 0000000000000..d31046c77006e --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed @@ -0,0 +1,72 @@ +// run-rustfix +#![allow(dead_code)] + +fn duplicate_t(t: T) -> (T, T) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_opt(t: Option) -> (Option, Option) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + //~^ HELP consider restricting type parameters + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom(t: S) -> (S, S) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +struct S(T); +trait Trait {} +impl Clone for S { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Copy for S {} + +trait A {} +trait B {} + +// Test where bounds are added with different bound placements +fn duplicate_custom_1(t: S) -> (S, S) where { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_2(t: S) -> (S, S) +where + T: A + Trait + Copy, + //~^ HELP consider further restricting this bound +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_3(t: S) -> (S, S) +where + T: A, + T: B, T: Trait, T: Copy + //~^ HELP consider further restricting type parameter `T` +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_4(t: S) -> (S, S) +where + T: B + Trait + Copy, + //~^ HELP consider further restricting this bound +{ + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs index 0930927c49d66..7cc5189fac017 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -1,20 +1,28 @@ +// run-rustfix +#![allow(dead_code)] + fn duplicate_t(t: T) -> (T, T) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_opt(t: Option) -> (Option, Option) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + //~^ HELP consider restricting type parameters (t, t) //~ use of moved value: `t` } fn duplicate_custom(t: S) -> (S, S) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } @@ -32,12 +40,14 @@ trait B {} // Test where bounds are added with different bound placements fn duplicate_custom_1(t: S) -> (S, S) where { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_custom_2(t: S) -> (S, S) where T: A, + //~^ HELP consider further restricting this bound { (t, t) //~ use of moved value: `t` } @@ -46,6 +56,7 @@ fn duplicate_custom_3(t: S) -> (S, S) where T: A, T: B, + //~^ HELP consider further restricting type parameter `T` { (t, t) //~ use of moved value: `t` } @@ -53,13 +64,9 @@ where fn duplicate_custom_4(t: S) -> (S, S) where T: B, + //~^ HELP consider further restricting this bound { (t, t) //~ use of moved value: `t` } -// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint -fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - (t, t) //~ use of moved value: `t` -} - fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr index c32ac2cd78aa0..8e72697ca30bb 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -1,8 +1,9 @@ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:2:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 | LL | fn duplicate_t(t: T) -> (T, T) { | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -14,10 +15,11 @@ LL | fn duplicate_t(t: T) -> (T, T) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:11:9 | LL | fn duplicate_opt(t: Option) -> (Option, Option) { | - move occurs because `t` has type `Option`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -29,10 +31,11 @@ LL | fn duplicate_opt(t: Option) -> (Option, Option) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:10:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:16:9 | LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -44,10 +47,11 @@ LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:14:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:21:9 | LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -59,10 +63,11 @@ LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { | ++++++ ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:18:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:26:9 | LL | fn duplicate_custom(t: S) -> (S, S) { | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -74,10 +79,11 @@ LL | fn duplicate_custom(t: S) -> (S, S) { | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:35:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:44:9 | LL | fn duplicate_custom_1(t: S) -> (S, S) where { | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -89,7 +95,7 @@ LL | fn duplicate_custom_1(t: S) -> (S, S) where { | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:42:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:52:9 | LL | fn duplicate_custom_2(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -105,7 +111,7 @@ LL | T: A + Trait + Copy, | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:50:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:61:9 | LL | fn duplicate_custom_3(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -121,7 +127,7 @@ LL | T: B, T: Trait, T: Copy | ~~~~~~~~~~~~~~~~~~~ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:57:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:69:9 | LL | fn duplicate_custom_4(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -136,16 +142,6 @@ help: consider further restricting this bound LL | T: B + Trait + Copy, | ++++++++++++++ -error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:62:9 - | -LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait -LL | (t, t) - | - ^ value used here after move - | | - | value moved here - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0382`.