-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[implied_bounds_in_impls]: include (previously omitted) associated types in suggestion
#11459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,93 @@ declare_clippy_lint! { | |
| } | ||
| declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]); | ||
|
|
||
| #[allow(clippy::too_many_arguments)] | ||
| fn emit_lint( | ||
| cx: &LateContext<'_>, | ||
| poly_trait: &rustc_hir::PolyTraitRef<'_>, | ||
| opaque_ty: &rustc_hir::OpaqueTy<'_>, | ||
| index: usize, | ||
| implied_bindings: &[rustc_hir::TypeBinding<'_>], | ||
| implied_by_bindings: &[rustc_hir::TypeBinding<'_>], | ||
| implied_by_args: &[GenericArg<'_>], | ||
| implied_by_span: Span, | ||
| ) { | ||
| let implied_by = snippet(cx, implied_by_span, ".."); | ||
|
|
||
| span_lint_and_then( | ||
| cx, | ||
| IMPLIED_BOUNDS_IN_IMPLS, | ||
| poly_trait.span, | ||
| &format!("this bound is already specified as the supertrait of `{implied_by}`"), | ||
| |diag| { | ||
| // If we suggest removing a bound, we may also need extend the span | ||
y21 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // to include the `+` token that is ahead or behind, | ||
| // so we don't end up with something like `impl + B` or `impl A + ` | ||
|
|
||
| let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { | ||
| poly_trait.span.to(next_bound.span().shrink_to_lo()) | ||
| } else if index > 0 | ||
| && let Some(prev_bound) = opaque_ty.bounds.get(index - 1) | ||
| { | ||
| prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi()) | ||
| } else { | ||
| poly_trait.span | ||
| }; | ||
|
Comment on lines
+78
to
+86
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm kinda worried about this check, it's kinda inconsistent (one checking index and the other not), but it may not matter if we make sure it doesn't fail. Could you add a test with 3 bounds? Maybe some with generics and some without them?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason why index - 1 has a check and the other doesn't is mainly that Only if there's neither an element before or after it (i.e. only one bound) will it not extend the span |
||
|
|
||
| let mut sugg = vec![(implied_span_extended, String::new())]; | ||
|
|
||
| // We also might need to include associated type binding that were specified in the implied bound, | ||
| // but omitted in the implied-by bound: | ||
| // `fn f() -> impl Deref<Target = u8> + DerefMut` | ||
| // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut` | ||
| let omitted_assoc_tys: Vec<_> = implied_bindings | ||
| .iter() | ||
| .filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident)) | ||
| .collect(); | ||
|
|
||
| if !omitted_assoc_tys.is_empty() { | ||
| // `<>` needs to be added if there aren't yet any generic arguments or bindings | ||
| let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty(); | ||
| let insert_span = match (implied_by_args, implied_by_bindings) { | ||
| ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(), | ||
| ([.., arg], []) => arg.span().shrink_to_hi(), | ||
| ([], [.., binding]) => binding.span.shrink_to_hi(), | ||
| ([], []) => implied_by_span.shrink_to_hi(), | ||
| }; | ||
|
|
||
| let mut associated_tys_sugg = if needs_angle_brackets { | ||
| "<".to_owned() | ||
| } else { | ||
| // If angle brackets aren't needed (i.e., there are already generic arguments or bindings), | ||
| // we need to add a comma: | ||
| // `impl A<B, C >` | ||
| // ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax: | ||
| // `impl A<B, C Assoc=i32>` | ||
| ", ".to_owned() | ||
| }; | ||
|
|
||
| for (index, binding) in omitted_assoc_tys.into_iter().enumerate() { | ||
| if index > 0 { | ||
| associated_tys_sugg += ", "; | ||
| } | ||
| associated_tys_sugg += &snippet(cx, binding.span, ".."); | ||
| } | ||
| if needs_angle_brackets { | ||
| associated_tys_sugg += ">"; | ||
| } | ||
| sugg.push((insert_span, associated_tys_sugg)); | ||
| } | ||
|
|
||
| diag.multipart_suggestion_with_style( | ||
| "try removing this bound", | ||
| sugg, | ||
| Applicability::MachineApplicable, | ||
| SuggestionStyle::ShowAlways, | ||
| ); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| /// Tries to "resolve" a type. | ||
| /// The index passed to this function must start with `Self=0`, i.e. it must be a valid | ||
| /// type parameter index. | ||
|
|
@@ -149,7 +236,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { | |
| && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates | ||
| && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add. | ||
| { | ||
| Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates, trait_def_id)) | ||
| Some((bound.span(), path, predicates, trait_def_id)) | ||
| } else { | ||
| None | ||
| } | ||
|
|
@@ -162,10 +249,14 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { | |
| if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound | ||
| && let [.., path] = poly_trait.trait_ref.path.segments | ||
| && let implied_args = path.args.map_or([].as_slice(), |a| a.args) | ||
| && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings) | ||
| && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() | ||
| && let Some(implied_by_span) = implied_bounds | ||
| && let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds | ||
| .iter() | ||
| .find_map(|&(span, implied_by_args, preds, implied_by_def_id)| { | ||
| .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| { | ||
| let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args); | ||
| let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings); | ||
y21 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| preds.iter().find_map(|(clause, _)| { | ||
| if let ClauseKind::Trait(tr) = clause.kind().skip_binder() | ||
| && tr.def_id() == def_id | ||
|
|
@@ -178,36 +269,22 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { | |
| def_id, | ||
| ) | ||
| { | ||
| Some(span) | ||
| Some((span, implied_by_args, implied_by_bindings)) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| }) | ||
| { | ||
| let implied_by = snippet(cx, implied_by_span, ".."); | ||
| span_lint_and_then( | ||
| cx, IMPLIED_BOUNDS_IN_IMPLS, | ||
| poly_trait.span, | ||
| &format!("this bound is already specified as the supertrait of `{implied_by}`"), | ||
| |diag| { | ||
| // If we suggest removing a bound, we may also need extend the span | ||
| // to include the `+` token, so we don't end up with something like `impl + B` | ||
|
|
||
| let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { | ||
| poly_trait.span.to(next_bound.span().shrink_to_lo()) | ||
| } else { | ||
| poly_trait.span | ||
| }; | ||
|
|
||
| diag.span_suggestion_with_style( | ||
| implied_span_extended, | ||
| "try removing this bound", | ||
| "", | ||
| Applicability::MachineApplicable, | ||
| SuggestionStyle::ShowAlways | ||
| ); | ||
| } | ||
| emit_lint( | ||
| cx, | ||
| poly_trait, | ||
| opaque_ty, | ||
| index, | ||
| implied_bindings, | ||
| implied_by_bindings, | ||
| implied_by_args, | ||
| implied_by_span | ||
| ); | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.