mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-15 01:17:16 +00:00
Add new multiple_bound_locations
lint
This commit is contained in:
parent
6aa5f1ac6f
commit
d654acd554
4 changed files with 88 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`
|
||||
}
|
||||
|
||||
|
|
84
clippy_lints/src/multiple_bound_locations.rs
Normal file
84
clippy_lints/src/multiple_bound_locations.rs
Normal file
|
@ -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",
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue