about summary refs log tree commit diff
diff options
context:
space:
mode:
authory21 <30553356+y21@users.noreply.github.com>2023-06-20 17:24:46 +0200
committery21 <30553356+y21@users.noreply.github.com>2023-06-20 17:24:46 +0200
commit87c28b94635b2ccf5518979b7ab7ee846365653b (patch)
treee7c728187c3e9faee4e4b12e88e80928f2cddc81
parent8fd021f50456b777bf44c044ea5381c341d772c1 (diff)
downloadrust-87c28b94635b2ccf5518979b7ab7ee846365653b.tar.gz
rust-87c28b94635b2ccf5518979b7ab7ee846365653b.zip
[`type_repetition_in_bounds`]: respect msrv for combining maybe bounds
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/trait_bounds.rs33
-rw-r--r--clippy_utils/src/msrvs.rs1
-rw-r--r--tests/ui/type_repetition_in_bounds.rs24
-rw-r--r--tests/ui/type_repetition_in_bounds.stderr10
5 files changed, 59 insertions, 11 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 3fb4e6c8fa5..0008f8f6d46 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -792,7 +792,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
     store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
     let max_trait_bounds = conf.max_trait_bounds;
-    store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
+    store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv())));
     store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
     let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
     store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs
index 3917d8f0225..2cf3529bd1b 100644
--- a/clippy_lints/src/trait_bounds.rs
+++ b/clippy_lints/src/trait_bounds.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
 use core::hash::{Hash, Hasher};
@@ -9,7 +10,7 @@ use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
+    GenericArg, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
     TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass};
@@ -86,15 +87,16 @@ declare_clippy_lint! {
     "check if the same trait bounds are specified more than once during a generic declaration"
 }
 
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 pub struct TraitBounds {
     max_trait_bounds: u64,
+    msrv: Msrv,
 }
 
 impl TraitBounds {
     #[must_use]
-    pub fn new(max_trait_bounds: u64) -> Self {
-        Self { max_trait_bounds }
+    pub fn new(max_trait_bounds: u64, msrv: Msrv) -> Self {
+        Self { max_trait_bounds, msrv }
     }
 }
 
@@ -222,10 +224,24 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 impl TraitBounds {
-    fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
+    /// Is the given bound a `?Sized` bound, and is combining it (i.e. `T: X + ?Sized`) an error on
+    /// this MSRV? See https://github.com/rust-lang/rust-clippy/issues/8772 for details.
+    fn cannot_combine_maybe_bound(&self, cx: &LateContext<'_>, bound: &GenericBound<'_>) -> bool {
+        if !self.msrv.meets(msrvs::COMBINED_MAYBE_BOUND)
+            && let GenericBound::Trait(tr, TraitBoundModifier::Maybe) = bound
+        {
+            cx.tcx.lang_items().get(LangItem::Sized) == tr.trait_ref.path.res.opt_def_id()
+        } else {
+            false
+        }
+    }
+
+    fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
         struct SpanlessTy<'cx, 'tcx> {
             ty: &'tcx Ty<'tcx>,
             cx: &'cx LateContext<'tcx>,
@@ -256,10 +272,9 @@ impl TraitBounds {
                 if p.origin != PredicateOrigin::ImplTrait;
                 if p.bounds.len() as u64 <= self.max_trait_bounds;
                 if !p.span.from_expansion();
-                if let Some(ref v) = map.insert(
-                    SpanlessTy { ty: p.bounded_ty, cx },
-                    p.bounds.iter().collect::<Vec<_>>()
-                );
+                let bounds = p.bounds.iter().filter(|b| !self.cannot_combine_maybe_bound(cx, b)).collect::<Vec<_>>();
+                if !bounds.is_empty();
+                if let Some(ref v) = map.insert(SpanlessTy { ty: p.bounded_ty, cx }, bounds);
                 if !is_from_proc_macro(cx, p.bounded_ty);
                 then {
                     let trait_bounds = v
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index e1b1a6f7184..b1f336461d1 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -47,6 +47,7 @@ msrv_aliases! {
     1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
     1,16,0 { STR_REPEAT }
+    1,15,0 { COMBINED_MAYBE_BOUND }
 }
 
 fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs
index 1d36f4b3c7b..874d97f7a46 100644
--- a/tests/ui/type_repetition_in_bounds.rs
+++ b/tests/ui/type_repetition_in_bounds.rs
@@ -110,4 +110,28 @@ where
 // This should not lint
 fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
 
+#[clippy::msrv = "1.14.0"]
+mod issue8772_fail {
+    pub trait Trait<X, Y, Z> {}
+
+    pub fn f<T: ?Sized, U>(arg: usize)
+    where
+        T: Trait<Option<usize>, Box<[String]>, bool> + 'static,
+        U: Clone + Sync + 'static,
+    {
+    }
+}
+
+#[clippy::msrv = "1.15.0"]
+mod issue8772_pass {
+    pub trait Trait<X, Y, Z> {}
+
+    pub fn f<T: ?Sized, U>(arg: usize)
+    where
+        T: Trait<Option<usize>, Box<[String]>, bool> + 'static,
+        U: Clone + Sync + 'static,
+    {
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr
index 56867f75b07..54973c5bda5 100644
--- a/tests/ui/type_repetition_in_bounds.stderr
+++ b/tests/ui/type_repetition_in_bounds.stderr
@@ -35,5 +35,13 @@ LL |     T: ?Sized,
    |
    = help: consider combining the bounds: `T: Clone + ?Sized`
 
-error: aborting due to 4 previous errors
+error: this type has already been used as a bound predicate
+  --> $DIR/type_repetition_in_bounds.rs:131:9
+   |
+LL |         T: Trait<Option<usize>, Box<[String]>, bool> + 'static,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider combining the bounds: `T: ?Sized + Trait<Option<usize>, Box<[String]>, bool>`
+
+error: aborting due to 5 previous errors