diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2024-02-10 15:04:50 +0100 |
|---|---|---|
| committer | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2024-02-23 13:22:21 +0100 |
| commit | d654acd554b679a61d9bde0247aa3ac8e691b964 (patch) | |
| tree | cf88ee74ef7b5e6b2c0334bc9ac9e837e9a34c68 | |
| parent | 6aa5f1ac6ff7628d02d9b21acb288ad3048e2f70 (diff) | |
| download | rust-d654acd554b679a61d9bde0247aa3ac8e691b964.tar.gz rust-d654acd554b679a61d9bde0247aa3ac8e691b964.zip | |
Add new `multiple_bound_locations` lint
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | clippy_lints/src/declared_lints.rs | 1 | ||||
| -rw-r--r-- | clippy_lints/src/lib.rs | 2 | ||||
| -rw-r--r-- | clippy_lints/src/multiple_bound_locations.rs | 84 |
4 files changed, 88 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index b35475c7340..d2afaa6ff9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5430,6 +5430,7 @@ Released 2018-09-13 [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic [`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one [`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments +[`multiple_bound_locations`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_bound_locations [`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions [`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl [`multiple_unsafe_ops_per_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_unsafe_ops_per_block diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 60744fee34d..455bf990721 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -498,6 +498,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::module_style::MOD_MODULE_FILES_INFO, crate::module_style::SELF_NAMED_MODULE_FILES_INFO, crate::multi_assignments::MULTI_ASSIGNMENTS_INFO, + crate::multiple_bound_locations::MULTIPLE_BOUND_LOCATIONS_INFO, crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, crate::mut_key::MUTABLE_KEY_TYPE_INFO, crate::mut_mut::MUT_MUT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5636f46b22f..99bae807e2c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -226,6 +226,7 @@ mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; +mod multiple_bound_locations; mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; @@ -1111,6 +1112,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); + store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs new file mode 100644 index 00000000000..191b32408ef --- /dev/null +++ b/clippy_lints/src/multiple_bound_locations.rs @@ -0,0 +1,84 @@ +use rustc_ast::visit::FnKind; +use rustc_ast::{NodeId, WherePredicate}; +use rustc_data_structures::fx::FxHashMap; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet_opt; + +declare_clippy_lint! { + /// ### What it does + /// Check if a generic is defined both in the bound predicate and in the `where` clause. + /// + /// ### Why is this bad? + /// It can be confusing for developers when seeing bounds for a generic in multiple places. + /// + /// ### Example + /// ```no_run + /// fn ty<F: std::fmt::Debug>(a: F) + /// where + /// F: Sized, + /// {} + /// ``` + /// Use instead: + /// ```no_run + /// fn ty<F>(a: F) + /// where + /// F: Sized + std::fmt::Debug, + /// {} + /// ``` + #[clippy::version = "1.77.0"] + pub MULTIPLE_BOUND_LOCATIONS, + suspicious, + "defining generic bounds in multiple locations" +} + +declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); + +impl EarlyLintPass for MultipleBoundLocations { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { + if let FnKind::Fn(_, _, _, _, generics, _) = kind + && !generics.params.is_empty() + && !generics.where_clause.predicates.is_empty() + { + let mut generic_params_with_bounds = FxHashMap::default(); + + for param in &generics.params { + if !param.bounds.is_empty() { + generic_params_with_bounds.insert(param.ident.name.as_str(), param.ident.span); + } + } + for clause in &generics.where_clause.predicates { + match clause { + WherePredicate::BoundPredicate(pred) => { + if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty()) + && let Some(name) = snippet_opt(cx, pred.bounded_ty.span) + && let Some(bound_span) = generic_params_with_bounds.get(name.as_str()) + { + emit_lint(cx, *bound_span, pred.bounded_ty.span); + } + }, + WherePredicate::RegionPredicate(pred) => { + if !pred.bounds.is_empty() + && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.name.as_str()) + { + emit_lint(cx, *bound_span, pred.lifetime.ident.span); + } + }, + WherePredicate::EqPredicate(_) => {}, + } + } + } + } +} + +fn emit_lint(cx: &EarlyContext<'_>, bound_span: Span, where_span: Span) { + span_lint( + cx, + MULTIPLE_BOUND_LOCATIONS, + vec![bound_span, where_span], + "bound is defined in more than one place", + ); +} |
