diff --git a/README.md b/README.md index 0e0a60537..2cd932749 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code. [Jump to usage instructions](#usage) ##Lints -There are 88 lints included in this crate: +There are 89 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -24,6 +24,7 @@ name [cmp_owned](https://github.com/Manishearth/rust-clippy/wiki#cmp_owned) | warn | creating owned instances for comparing with others, e.g. `x == "foo".to_string()` [collapsible_if](https://github.com/Manishearth/rust-clippy/wiki#collapsible_if) | warn | two nested `if`-expressions can be collapsed into one, e.g. `if x { if y { foo() } }` can be written as `if x && y { foo() }` [cyclomatic_complexity](https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity) | warn | finds functions that should be split up into multiple functions +[duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument)| warn | Function arguments having names which only differ by an underscore [empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop) | warn | empty `loop {}` detected [eq_op](https://github.com/Manishearth/rust-clippy/wiki#eq_op) | warn | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`) [explicit_counter_loop](https://github.com/Manishearth/rust-clippy/wiki#explicit_counter_loop) | warn | for-looping with an explicit counter when `_.enumerate()` would do diff --git a/src/lib.rs b/src/lib.rs index 6d39cad20..6610173f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,6 +193,7 @@ pub fn plugin_registrar(reg: &mut Registry) { misc::REDUNDANT_PATTERN, misc::TOPLEVEL_REF_ARG, misc::USED_UNDERSCORE_BINDING, + misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, misc_early::UNNEEDED_FIELD_PATTERN, mut_reference::UNNECESSARY_MUT_PASSED, mutex_atomic::MUTEX_ATOMIC, diff --git a/src/misc_early.rs b/src/misc_early.rs index dafe7816f..77114ccb4 100644 --- a/src/misc_early.rs +++ b/src/misc_early.rs @@ -1,8 +1,11 @@ -//use rustc_front::hir::*; - use rustc::lint::*; +use std::collections::HashMap; + use syntax::ast::*; +use syntax::codemap::Span; +use syntax::print::pprust; +use syntax::visit::FnKind; use utils::{span_lint, span_help_and_lint}; @@ -16,12 +19,22 @@ use utils::{span_lint, span_help_and_lint}; declare_lint!(pub UNNEEDED_FIELD_PATTERN, Warn, "Struct fields are bound to a wildcard instead of using `..`"); +/// **What it does:** This lint `Warn`s on function arguments having the same name except one starts with '_' +/// +/// **Why is this bad?** It makes source code documentation more difficult +/// +/// **Known problems:** None. +/// +/// **Example:** `fn foo(a: i32, _a: i32) {}` +declare_lint!(pub DUPLICATE_UNDERSCORE_ARGUMENT, Warn, + "Function arguments having names which only differ by an underscore"); + #[derive(Copy, Clone)] pub struct MiscEarly; impl LintPass for MiscEarly { fn get_lints(&self) -> LintArray { - lint_array!(UNNEEDED_FIELD_PATTERN) + lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT) } } @@ -77,4 +90,23 @@ impl EarlyLintPass for MiscEarly { } } } + + fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) { + let mut registered_names : HashMap = HashMap::new(); + + for ref arg in &decl.inputs { + let arg_name = pprust::pat_to_string(&arg.pat); + + if arg_name.starts_with("_") { + if let Some(correspondance) = registered_names.get(&arg_name[1..].to_owned()) { + span_lint(cx, DUPLICATE_UNDERSCORE_ARGUMENT, *correspondance, + &format!("`{}` already exists, having another argument having almost \ + the same name makes code comprehension and documentation \ + more difficult", arg_name[1..].to_owned())); + } + } else { + registered_names.insert(arg_name.to_owned(), arg.pat.span.clone()); + } + } + } } diff --git a/tests/compile-fail/duplicate_underscore_argument.rs b/tests/compile-fail/duplicate_underscore_argument.rs new file mode 100644 index 000000000..4d908e7f0 --- /dev/null +++ b/tests/compile-fail/duplicate_underscore_argument.rs @@ -0,0 +1,13 @@ +#![feature(plugin)] +#![plugin(clippy)] + +#![deny(duplicate_underscore_argument)] +#[allow(dead_code, unused)] + +fn join_the_dark_side(darth: i32, _darth: i32) {} //~ERROR `darth` already exists +fn join_the_light_side(knight: i32, _master: i32) {} // the Force is strong with this one + +fn main() { + join_the_dark_side(0, 0); + join_the_light_side(0, 0); +} \ No newline at end of file diff --git a/tests/compile-fail/unneeded_field_pattern.rs b/tests/compile-fail/unneeded_field_pattern.rs index bbe72a713..9c7623d85 100644 --- a/tests/compile-fail/unneeded_field_pattern.rs +++ b/tests/compile-fail/unneeded_field_pattern.rs @@ -23,4 +23,4 @@ fn main() { Foo { b: 0, .. } => {} // should be OK Foo { .. } => {} // and the Force might be with this one } -} \ No newline at end of file +}