From 2c2716f04587eb379bf7715f1dd7d7a46c8f7eab Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 13 Aug 2015 10:32:35 +0200 Subject: [PATCH 1/2] all: DRY for lint descriptions * use the rustc style for lint descriptions * add a script to parse all lint descriptions and put the generated table into README --- README.md | 65 +++++++++++++++++++---------------- src/approx_const.rs | 3 +- src/attrs.rs | 2 +- src/bit_mask.rs | 7 ++-- src/collapsible_if.rs | 3 +- src/eq_op.rs | 2 +- src/eta_reduction.rs | 2 +- src/identity_op.rs | 2 +- src/len_zero.rs | 5 +-- src/lib.rs | 15 +++++--- src/lifetimes.rs | 3 +- src/loops.rs | 2 +- src/methods.rs | 8 ++--- src/misc.rs | 21 ++++++++---- src/mut_mut.rs | 3 +- src/needless_bool.rs | 3 +- src/ptr_arg.rs | 3 +- src/returns.rs | 5 +-- src/strings.rs | 2 +- src/types.rs | 7 ++-- src/unicode.rs | 7 ++-- util/update_readme.py | 80 +++++++++++++++++++++++++++++++++++++++++++ 22 files changed, 180 insertions(+), 70 deletions(-) create mode 100644 util/update_readme.py diff --git a/README.md b/README.md index b61f35613..193d759ff 100644 --- a/README.md +++ b/README.md @@ -6,35 +6,42 @@ A collection of lints that give helpful tips to newbies and catch oversights. ##Lints Lints included in this crate: - - `single_match`: Warns when a match statement with a single nontrivial arm (i.e, where the other arm is `_ => {}`) is used, and recommends `if let` instead. - - `box_vec`: Warns on usage of `Box>` - - `linkedlist`: Warns on usage of `LinkedList` - - `str_to_string`: Warns on usage of `str::to_string()` - - `toplevel_ref_arg`: Warns when a function argument is declared `ref` (i.e. `fn foo(ref x: u8)`, but not `fn foo((ref x, ref y): (u8, u8))`) - - `eq_op`: Warns on equal operands on both sides of a comparison or bitwise combination - - `bad_bit_mask`: Denies expressions of the form `_ & mask == select` that will only ever return `true` or `false` (because in the example `select` containing bits that `mask` doesn't have) - - `ineffective_bit_mask`: Warns on expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` - - `needless_bool` : Warns on if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }` - - `ptr_arg`: Warns on fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively - - `approx_constant`: Warns if the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) is found and suggests to use the constant - - `cmp_nan`: Denies comparisons to NAN (which will always return false, which is probably not intended) - - `float_cmp`: Warns on `==` or `!=` comparisons of floaty typed values. As floating-point operations usually involve rounding errors, it is always better to check for approximate equality within some small bounds - - `precedence`: Warns on expressions where precedence may trip up the unwary reader of the source and suggests adding parenthesis, e.g. `x << 2 + y` will be parsed as `x << (2 + y)` - - `redundant_closure`: Warns on usage of eta-reducible closures like `|a| foo(a)` (which can be written as just `foo`) - - `identity_op`: Warns on identity operations like `x + 0` or `y / 1` (which can be reduced to `x` and `y`, respectively) - - `mut_mut`: Warns on `&mut &mut` which is either a copy'n'paste error, or shows a fundamental misunderstanding of references - - `len_zero`: Warns on `_.len() == 0` and suggests using `_.is_empty()` (or similar comparisons with `>` or `!=`) - - `len_without_is_empty`: Warns on traits or impls that have a `.len()` but no `.is_empty()` method - - `cmp_owned`: Warns on creating owned instances for comparing with others, e.g. `x == "foo".to_string()` - - `inline_always`: Warns on `#[inline(always)]`, because in most cases it is a bad idea - - `collapsible_if`: Warns on cases where two nested `if`-expressions can be collapsed into one, e.g. `if x { if y { foo() } }` can be written as `if x && y { foo() }` - - `zero_width_space`: Warns on encountering a unicode zero-width space - - `string_add_assign`: Warns on `x = x + ..` where `x` is a `String` and suggests using `push_str(..)` instead. - - `needless_return`: Warns on using `return expr;` when a simple `expr` would suffice. - - `let_and_return`: Warns on doing `let x = expr; x` at the end of a function. - - `option_unwrap_used`: Warns when `Option.unwrap()` is used, and suggests `.expect()`. - - `result_unwrap_used`: Warns when `Result.unwrap()` is used (silent by default). - - `modulo_one`: Warns on taking a number modulo 1, which always has a result of 0. +name | default | meaning +---------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +approx_constant | warn | the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) is found; suggests to use the constant +bad_bit_mask | deny | expressions of the form `_ & mask == select` that will only ever return `true` or `false` (because in the example `select` containing bits that `mask` doesn't have) +box_vec | warn | usage of `Box>`, vector elements are already on the heap +cmp_nan | deny | comparisons to NAN (which will always return false, which is probably not intended) +cmp_owned | warn | creating owned instances for comparing with others, e.g. `x == "foo".to_string()` +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() }` +eq_op | warn | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`) +float_cmp | warn | using `==` or `!=` on float values (as floating-point operations usually involve rounding errors, it is always better to check for approximate equality within small bounds) +identity_op | warn | using identity operations, e.g. `x + 0` or `y / 1` +ineffective_bit_mask | warn | expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` +inline_always | warn | `#[inline(always)]` is a bad idea in most cases +len_without_is_empty | warn | traits and impls that have `.len()` but not `.is_empty()` +len_zero | warn | checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead +let_and_return | warn | creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a function +let_unit_value | warn | creating a let binding to a value of unit type, which usually can't be used afterwards +linkedlist | warn | usage of LinkedList, usually a vector is faster, or a more specialized data structure like a RingBuf +modulo_one | warn | taking a number modulo 1, which always returns 0 +mut_mut | warn | usage of double-mut refs, e.g. `&mut &mut ...` (either copy'n'paste error, or shows a fundamental misunderstanding of references) +needless_bool | warn | if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }` +needless_lifetimes | warn | using explicit lifetimes for references in function arguments when elision rules would allow omitting them +needless_range_loop | warn | for-looping over a range of indices where an iterator over items would do +needless_return | warn | using a return statement like `return expr;` where an expression would suffice +non_ascii_literal | allow | using any literal non-ASCII chars in a string literal; suggests using the \\u escape instead +option_unwrap_used | warn | using `Option.unwrap()`, which should at least get a better message using `expect()` +precedence | warn | expressions where precedence may trip up the unwary reader of the source; suggests adding parentheses, e.g. `x << 2 + y` will be parsed as `x << (2 + y)` +ptr_arg | allow | fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively +redundant_closure | warn | using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`) +result_unwrap_used | allow | using `Result.unwrap()`, which might be better handled +single_match | warn | a match statement with a single nontrivial arm (i.e, where the other arm is `_ => {}`) is used; recommends `if let` instead +str_to_string | warn | using `to_string()` on a str, which should be `to_owned()` +string_add_assign | warn | using `x = x + ..` where x is a `String`; suggests using `push_str()` instead +string_to_string | warn | calling `String.to_string()` which is a no-op +toplevel_ref_arg | warn | a function argument is declared `ref` (i.e. `fn foo(ref x: u8)`, but not `fn foo((ref x, ref y): (u8, u8))`) +zero_width_space | deny | using a zero-width space in a string literal, which is confusing To use, add the following lines to your Cargo.toml: diff --git a/src/approx_const.rs b/src/approx_const.rs index 377c7e66e..3c39b7988 100644 --- a/src/approx_const.rs +++ b/src/approx_const.rs @@ -12,7 +12,8 @@ use utils::span_lint; declare_lint! { pub APPROX_CONSTANT, Warn, - "Warn if a user writes an approximate known constant in their code" + "the approximate of a known float constant (in `std::f64::consts` or `std::f32::consts`) \ + is found; suggests to use the constant" } const KNOWN_CONSTS : &'static [(f64, &'static str)] = &[(f64::E, "E"), (f64::FRAC_1_PI, "FRAC_1_PI"), diff --git a/src/attrs.rs b/src/attrs.rs index 789a992e3..ca5b6c55c 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -9,7 +9,7 @@ use syntax::parse::token::InternedString; use utils::{in_macro, match_path, span_lint}; declare_lint! { pub INLINE_ALWAYS, Warn, - "#[inline(always)] is usually a bad idea."} + "`#[inline(always)]` is a bad idea in most cases" } #[derive(Copy,Clone)] diff --git a/src/bit_mask.rs b/src/bit_mask.rs index b5f088c23..169975001 100644 --- a/src/bit_mask.rs +++ b/src/bit_mask.rs @@ -11,15 +11,14 @@ use utils::span_lint; declare_lint! { pub BAD_BIT_MASK, Deny, - "Deny the use of incompatible bit masks in comparisons, e.g. \ - '(a & 1) == 2'" + "expressions of the form `_ & mask == select` that will only ever return `true` or `false` \ + (because in the example `select` containing bits that `mask` doesn't have)" } declare_lint! { pub INEFFECTIVE_BIT_MASK, Warn, - "Warn on the use of an ineffective bit mask in comparisons, e.g. \ - '(a & 1) > 2'" + "expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`" } /// Checks for incompatible bit masks in comparisons, e.g. `x & 1 == 2`. diff --git a/src/collapsible_if.rs b/src/collapsible_if.rs index 0350db816..f0f53622c 100644 --- a/src/collapsible_if.rs +++ b/src/collapsible_if.rs @@ -23,7 +23,8 @@ use utils::{in_macro, span_help_and_lint, snippet}; declare_lint! { pub COLLAPSIBLE_IF, Warn, - "Warn on if expressions that can be collapsed" + "two nested `if`-expressions can be collapsed into one, e.g. `if x { if y { foo() } }` \ + can be written as `if x && y { foo() }`" } #[derive(Copy,Clone)] diff --git a/src/eq_op.rs b/src/eq_op.rs index 6ad0e0658..495696b81 100644 --- a/src/eq_op.rs +++ b/src/eq_op.rs @@ -8,7 +8,7 @@ use utils::span_lint; declare_lint! { pub EQ_OP, Warn, - "warn about comparing equal expressions (e.g. x == x)" + "equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)" } #[derive(Copy,Clone)] diff --git a/src/eta_reduction.rs b/src/eta_reduction.rs index eda38419d..484d46ddc 100644 --- a/src/eta_reduction.rs +++ b/src/eta_reduction.rs @@ -11,7 +11,7 @@ pub struct EtaPass; declare_lint!(pub REDUNDANT_CLOSURE, Warn, - "Warn on usage of redundant closures, i.e. `|a| foo(a)`"); + "using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)"); impl LintPass for EtaPass { fn get_lints(&self) -> LintArray { diff --git a/src/identity_op.rs b/src/identity_op.rs index 8c6940e3d..964675b76 100644 --- a/src/identity_op.rs +++ b/src/identity_op.rs @@ -10,7 +10,7 @@ use syntax::codemap::Span; use utils::{span_lint, snippet}; declare_lint! { pub IDENTITY_OP, Warn, - "Warn on identity operations, e.g. '_ + 0'"} + "using identity operations, e.g. `x + 0` or `y / 1`" } #[derive(Copy,Clone)] pub struct IdentityOp; diff --git a/src/len_zero.rs b/src/len_zero.rs index da230c1d2..d5f3d1ad8 100644 --- a/src/len_zero.rs +++ b/src/len_zero.rs @@ -12,10 +12,11 @@ use syntax::ast::*; use utils::{span_lint, walk_ptrs_ty, snippet}; declare_lint!(pub LEN_ZERO, Warn, - "Warn when .is_empty() could be used instead of checking .len()"); + "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` \ + could be used instead"); declare_lint!(pub LEN_WITHOUT_IS_EMPTY, Warn, - "Warn on traits and impls that have .len() but not .is_empty()"); + "traits and impls that have `.len()` but not `.is_empty()`"); #[derive(Copy,Clone)] pub struct LenZero; diff --git a/src/lib.rs b/src/lib.rs index f788c72db..eb2704cd4 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,16 +64,21 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box loops::LoopsPass as LintPassObject); reg.register_lint_pass(box lifetimes::LifetimePass as LintPassObject); - reg.register_lint_group("clippy", vec![types::BOX_VEC, types::LINKEDLIST, + reg.register_lint_group("clippy", vec![types::BOX_VEC, + types::LINKEDLIST, + types::LET_UNIT_VALUE, misc::SINGLE_MATCH, - misc::TOPLEVEL_REF_ARG, eq_op::EQ_OP, + misc::TOPLEVEL_REF_ARG, + misc::CMP_NAN, + misc::FLOAT_CMP, + misc::PRECEDENCE, + misc::CMP_OWNED, + eq_op::EQ_OP, bit_mask::BAD_BIT_MASK, bit_mask::INEFFECTIVE_BIT_MASK, ptr_arg::PTR_ARG, needless_bool::NEEDLESS_BOOL, approx_const::APPROX_CONSTANT, - misc::CMP_NAN, misc::FLOAT_CMP, - misc::PRECEDENCE, misc::CMP_OWNED, eta_reduction::REDUNDANT_CLOSURE, identity_op::IDENTITY_OP, mut_mut::MUT_MUT, @@ -85,12 +90,12 @@ pub fn plugin_registrar(reg: &mut Registry) { unicode::NON_ASCII_LITERAL, strings::STRING_ADD_ASSIGN, returns::NEEDLESS_RETURN, + returns::LET_AND_RETURN, misc::MODULO_ONE, methods::OPTION_UNWRAP_USED, methods::RESULT_UNWRAP_USED, methods::STR_TO_STRING, methods::STRING_TO_STRING, - types::LET_UNIT_VALUE, lifetimes::NEEDLESS_LIFETIMES, loops::NEEDLESS_RANGE_LOOP, ]); diff --git a/src/lifetimes.rs b/src/lifetimes.rs index b510173e7..644537f9b 100644 --- a/src/lifetimes.rs +++ b/src/lifetimes.rs @@ -7,7 +7,8 @@ use std::collections::HashSet; use std::iter::FromIterator; declare_lint!(pub NEEDLESS_LIFETIMES, Warn, - "Warn on explicit lifetimes when elision rules would apply"); + "using explicit lifetimes for references in function arguments when elision rules \ + would allow omitting them"); #[derive(Copy,Clone)] pub struct LifetimePass; diff --git a/src/loops.rs b/src/loops.rs index 83d7ca4ec..b406bc679 100644 --- a/src/loops.rs +++ b/src/loops.rs @@ -6,7 +6,7 @@ use std::collections::HashSet; use utils::{span_lint, get_parent_expr}; declare_lint!{ pub NEEDLESS_RANGE_LOOP, Warn, - "Warn about looping over a range of indices if a normal iterator would do" } + "for-looping over a range of indices where an iterator over items would do" } #[derive(Copy, Clone)] pub struct LoopsPass; diff --git a/src/methods.rs b/src/methods.rs index 403845771..07b0fdf70 100644 --- a/src/methods.rs +++ b/src/methods.rs @@ -8,13 +8,13 @@ use utils::{span_lint, match_def_path, walk_ptrs_ty}; pub struct MethodsPass; declare_lint!(pub OPTION_UNWRAP_USED, Warn, - "Warn on using unwrap() on an Option value"); + "using `Option.unwrap()`, which should at least get a better message using `expect()`"); declare_lint!(pub RESULT_UNWRAP_USED, Allow, - "Warn on using unwrap() on a Result value"); + "using `Result.unwrap()`, which might be better handled"); declare_lint!(pub STR_TO_STRING, Warn, - "Warn when a String could use to_owned() instead of to_string()"); + "using `to_string()` on a str, which should be `to_owned()`"); declare_lint!(pub STRING_TO_STRING, Warn, - "Warn when calling String.to_string()"); + "calling `String.to_string()` which is a no-op"); impl LintPass for MethodsPass { fn get_lints(&self) -> LintArray { diff --git a/src/misc.rs b/src/misc.rs index 925843a70..6c9a7d92c 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -16,7 +16,8 @@ pub struct MiscPass; declare_lint!(pub SINGLE_MATCH, Warn, - "Warn on usage of matches with a single nontrivial arm"); + "a match statement with a single nontrivial arm (i.e, where the other arm \ + is `_ => {}`) is used; recommends `if let` instead"); impl LintPass for MiscPass { fn get_lints(&self) -> LintArray { @@ -59,7 +60,9 @@ impl LintPass for MiscPass { } -declare_lint!(pub TOPLEVEL_REF_ARG, Warn, "Warn about pattern matches with top-level `ref` bindings"); +declare_lint!(pub TOPLEVEL_REF_ARG, Warn, + "a function argument is declared `ref` (i.e. `fn foo(ref x: u8)`, but not \ + `fn foo((ref x, ref y): (u8, u8))`)"); #[allow(missing_copy_implementations)] pub struct TopLevelRefPass; @@ -82,7 +85,8 @@ impl LintPass for TopLevelRefPass { } } -declare_lint!(pub CMP_NAN, Deny, "Deny comparisons to std::f32::NAN or std::f64::NAN"); +declare_lint!(pub CMP_NAN, Deny, + "comparisons to NAN (which will always return false, which is probably not intended)"); #[derive(Copy,Clone)] pub struct CmpNan; @@ -114,7 +118,9 @@ fn check_nan(cx: &Context, path: &Path, span: Span) { } declare_lint!(pub FLOAT_CMP, Warn, - "Warn on ==/!= comparison of floaty values"); + "using `==` or `!=` on float values (as floating-point operations \ + usually involve rounding errors, it is always better to check for approximate \ + equality within small bounds)"); #[derive(Copy,Clone)] pub struct FloatCmp; @@ -147,7 +153,8 @@ fn is_float(cx: &Context, expr: &Expr) -> bool { } declare_lint!(pub PRECEDENCE, Warn, - "Warn on mixing bit ops with integer arithmetic without parentheses"); + "expressions where precedence may trip up the unwary reader of the source; \ + suggests adding parentheses, e.g. `x << 2 + y` will be parsed as `x << (2 + y)`"); #[derive(Copy,Clone)] pub struct Precedence; @@ -190,7 +197,7 @@ fn is_arith_op(op : BinOp_) -> bool { } declare_lint!(pub CMP_OWNED, Warn, - "Warn on creating an owned string just for comparison"); + "creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`"); #[derive(Copy,Clone)] pub struct CmpOwned; @@ -242,7 +249,7 @@ fn is_str_arg(cx: &Context, args: &[P]) -> bool { walk_ptrs_ty(cx.tcx.expr_ty(&*args[0])).sty { true } else { false } } -declare_lint!(pub MODULO_ONE, Warn, "Warn on expressions that include % 1, which is always 0"); +declare_lint!(pub MODULO_ONE, Warn, "taking a number modulo 1, which always returns 0"); #[derive(Copy,Clone)] pub struct ModuloOne; diff --git a/src/mut_mut.rs b/src/mut_mut.rs index a2055bb65..a3c40d06f 100644 --- a/src/mut_mut.rs +++ b/src/mut_mut.rs @@ -6,7 +6,8 @@ use syntax::codemap::{BytePos, ExpnInfo, Span}; use utils::{in_macro, span_lint}; declare_lint!(pub MUT_MUT, Warn, - "Warn on usage of double-mut refs, e.g. '&mut &mut ...'"); + "usage of double-mut refs, e.g. `&mut &mut ...` (either copy'n'paste error, \ + or shows a fundamental misunderstanding of references)"); #[derive(Copy,Clone)] pub struct MutMut; diff --git a/src/needless_bool.rs b/src/needless_bool.rs index d97e81907..6a4a55aed 100644 --- a/src/needless_bool.rs +++ b/src/needless_bool.rs @@ -15,7 +15,8 @@ use utils::{de_p, span_lint}; declare_lint! { pub NEEDLESS_BOOL, Warn, - "Warn on needless use of if x { true } else { false } (or vice versa)" + "if-statements with plain booleans in the then- and else-clause, e.g. \ + `if p { true } else { false }`" } #[derive(Copy,Clone)] diff --git a/src/ptr_arg.rs b/src/ptr_arg.rs index 3868854c7..85db4aa7b 100644 --- a/src/ptr_arg.rs +++ b/src/ptr_arg.rs @@ -16,7 +16,8 @@ use utils::span_lint; declare_lint! { pub PTR_ARG, Allow, - "Warn on declaration of a &Vec- or &String-typed method argument" + "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` \ + instead, respectively" } #[derive(Copy,Clone)] diff --git a/src/returns.rs b/src/returns.rs index be28e1400..94b9ec965 100644 --- a/src/returns.rs +++ b/src/returns.rs @@ -7,9 +7,10 @@ use rustc::lint::{Context, LintPass, LintArray, Level}; use utils::{span_lint, snippet, match_path}; declare_lint!(pub NEEDLESS_RETURN, Warn, - "Warn on using a return statement where an expression would be enough"); + "using a return statement like `return expr;` where an expression would suffice"); declare_lint!(pub LET_AND_RETURN, Warn, - "Warn on creating a let-binding and then immediately returning it"); + "creating a let-binding and then immediately returning it like `let x = expr; x` at \ + the end of a function"); #[derive(Copy,Clone)] pub struct ReturnPass; diff --git a/src/strings.rs b/src/strings.rs index afc444dd6..20844be31 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -14,7 +14,7 @@ use utils::{match_def_path, span_lint, walk_ptrs_ty}; declare_lint! { pub STRING_ADD_ASSIGN, Warn, - "Warn on `x = x + ..` where x is a `String`" + "using `x = x + ..` where x is a `String`; suggests using `push_str()` instead" } #[derive(Copy,Clone)] diff --git a/src/types.rs b/src/types.rs index c22d088ce..ad7b767d9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -12,9 +12,10 @@ use utils::{in_macro, snippet, span_lint, span_help_and_lint}; pub struct TypePass; declare_lint!(pub BOX_VEC, Warn, - "Warn on usage of Box>"); + "usage of `Box>`, vector elements are already on the heap"); declare_lint!(pub LINKEDLIST, Warn, - "Warn on usage of LinkedList"); + "usage of LinkedList, usually a vector is faster, or a more specialized data \ + structure like a RingBuf"); /// Matches a type with a provided string, and returns its type parameters if successful pub fn match_ty_unwrap<'a>(ty: &'a Ty, segments: &[&str]) -> Option<&'a [P]> { @@ -81,7 +82,7 @@ impl LintPass for TypePass { pub struct LetPass; declare_lint!(pub LET_UNIT_VALUE, Warn, - "Warn on let-binding a value of unit type"); + "creating a let binding to a value of unit type, which usually can't be used afterwards"); fn check_let_unit(cx: &Context, decl: &Decl, info: Option<&ExpnInfo>) { diff --git a/src/unicode.rs b/src/unicode.rs index 161e90d0f..62b4a9dad 100644 --- a/src/unicode.rs +++ b/src/unicode.rs @@ -3,8 +3,11 @@ use syntax::ast::*; use syntax::codemap::{BytePos, Span}; use utils::span_lint; -declare_lint!{ pub ZERO_WIDTH_SPACE, Deny, "Zero-width space is confusing" } -declare_lint!{ pub NON_ASCII_LITERAL, Allow, "Lint literal non-ASCII chars in literals" } +declare_lint!{ pub ZERO_WIDTH_SPACE, Deny, + "using a zero-width space in a string literal, which is confusing" } +declare_lint!{ pub NON_ASCII_LITERAL, Allow, + "using any literal non-ASCII chars in a string literal; suggests \ + using the \\u escape instead" } #[derive(Copy, Clone)] pub struct Unicode; diff --git a/util/update_readme.py b/util/update_readme.py new file mode 100644 index 000000000..0b54afa83 --- /dev/null +++ b/util/update_readme.py @@ -0,0 +1,80 @@ +# Generate a Markdown table of all lints, and put it in README.md. +# With -n option, only print the new table to stdout. + +import os +import re +import sys + +declare_lint_re = re.compile(r''' + declare_lint! \s* [{(] \s* + pub \s+ (?P[A-Z_]+) \s*,\s* + (?PForbid|Deny|Warn|Allow) \s*,\s* + " (?P(?:[^"\\]+|\\.)*) " \s* [})] +''', re.X | re.S) + +nl_escape_re = re.compile(r'\\\n\s*') + + +def collect(lints, fp): + code = fp.read() + for match in declare_lint_re.finditer(code): + # remove \-newline escapes from description string + desc = nl_escape_re.sub('', match.group('desc')) + lints.append((match.group('name').lower(), + match.group('level').lower(), + desc.replace('\\"', '"'))) + + +def write_tbl(lints, fp): + # first and third column widths + w_name = max(len(l[0]) for l in lints) + w_desc = max(len(l[2]) for l in lints) + # header and underline + fp.write('%-*s | default | meaning\n' % (w_name, 'name')) + fp.write('%s-|-%s-|-%s\n' % ('-' * w_name, '-' * 7, '-' * w_desc)) + # one table row per lint + for (name, default, meaning) in sorted(lints): + fp.write('%-*s | %-7s | %s\n' % (w_name, name, default, meaning)) + + +def main(print_only=False): + lints = [] + + # check directory + if not os.path.isfile('src/lib.rs'): + print('Error: call this script from clippy checkout directory!') + return + + # collect all lints from source files + for root, dirs, files in os.walk('src'): + for fn in files: + if fn.endswith('.rs'): + with open(os.path.join(root, fn)) as fp: + collect(lints, fp) + + if print_only: + write_tbl(lints, sys.stdout) + return + + # read current README.md content + with open('README.md') as fp: + lines = list(fp) + + # replace old table with new table + with open('README.md', 'w') as fp: + in_old_tbl = False + for line in lines: + if line.replace(' ', '').strip() == 'name|default|meaning': + # old table starts here + write_tbl(lints, fp) + in_old_tbl = True + if in_old_tbl: + # the old table is finished by an empty line + if line.strip(): + continue + in_old_tbl = False + fp.write(line) + + +if __name__ == '__main__': + main(print_only='-n' in sys.argv) From ea0cf2a29685a0939c4ad0a04ea1aada608b8731 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 13 Aug 2015 11:31:09 +0200 Subject: [PATCH 2/2] update script: also generate lint list in lib.rs --- src/lib.rs | 71 +++++++++++++-------------- util/update_lints.py | 110 ++++++++++++++++++++++++++++++++++++++++++ util/update_readme.py | 80 ------------------------------ 3 files changed, 146 insertions(+), 115 deletions(-) create mode 100644 util/update_lints.py delete mode 100644 util/update_readme.py diff --git a/src/lib.rs b/src/lib.rs index eb2704cd4..9fc5def88 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,39 +64,40 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box loops::LoopsPass as LintPassObject); reg.register_lint_pass(box lifetimes::LifetimePass as LintPassObject); - reg.register_lint_group("clippy", vec![types::BOX_VEC, - types::LINKEDLIST, - types::LET_UNIT_VALUE, - misc::SINGLE_MATCH, - misc::TOPLEVEL_REF_ARG, - misc::CMP_NAN, - misc::FLOAT_CMP, - misc::PRECEDENCE, - misc::CMP_OWNED, - eq_op::EQ_OP, - bit_mask::BAD_BIT_MASK, - bit_mask::INEFFECTIVE_BIT_MASK, - ptr_arg::PTR_ARG, - needless_bool::NEEDLESS_BOOL, - approx_const::APPROX_CONSTANT, - eta_reduction::REDUNDANT_CLOSURE, - identity_op::IDENTITY_OP, - mut_mut::MUT_MUT, - len_zero::LEN_ZERO, - len_zero::LEN_WITHOUT_IS_EMPTY, - attrs::INLINE_ALWAYS, - collapsible_if::COLLAPSIBLE_IF, - unicode::ZERO_WIDTH_SPACE, - unicode::NON_ASCII_LITERAL, - strings::STRING_ADD_ASSIGN, - returns::NEEDLESS_RETURN, - returns::LET_AND_RETURN, - misc::MODULO_ONE, - methods::OPTION_UNWRAP_USED, - methods::RESULT_UNWRAP_USED, - methods::STR_TO_STRING, - methods::STRING_TO_STRING, - lifetimes::NEEDLESS_LIFETIMES, - loops::NEEDLESS_RANGE_LOOP, - ]); + reg.register_lint_group("clippy", vec![ + approx_const::APPROX_CONSTANT, + attrs::INLINE_ALWAYS, + bit_mask::BAD_BIT_MASK, + bit_mask::INEFFECTIVE_BIT_MASK, + collapsible_if::COLLAPSIBLE_IF, + eq_op::EQ_OP, + eta_reduction::REDUNDANT_CLOSURE, + identity_op::IDENTITY_OP, + len_zero::LEN_WITHOUT_IS_EMPTY, + len_zero::LEN_ZERO, + lifetimes::NEEDLESS_LIFETIMES, + loops::NEEDLESS_RANGE_LOOP, + methods::OPTION_UNWRAP_USED, + methods::RESULT_UNWRAP_USED, + methods::STR_TO_STRING, + methods::STRING_TO_STRING, + misc::CMP_NAN, + misc::CMP_OWNED, + misc::FLOAT_CMP, + misc::MODULO_ONE, + misc::PRECEDENCE, + misc::SINGLE_MATCH, + misc::TOPLEVEL_REF_ARG, + mut_mut::MUT_MUT, + needless_bool::NEEDLESS_BOOL, + ptr_arg::PTR_ARG, + returns::LET_AND_RETURN, + returns::NEEDLESS_RETURN, + strings::STRING_ADD_ASSIGN, + types::BOX_VEC, + types::LET_UNIT_VALUE, + types::LINKEDLIST, + unicode::NON_ASCII_LITERAL, + unicode::ZERO_WIDTH_SPACE, + ]); } diff --git a/util/update_lints.py b/util/update_lints.py new file mode 100644 index 000000000..c2a9e08c9 --- /dev/null +++ b/util/update_lints.py @@ -0,0 +1,110 @@ +# Generate a Markdown table of all lints, and put it in README.md. +# With -n option, only print the new table to stdout. + +import os +import re +import sys + +declare_lint_re = re.compile(r''' + declare_lint! \s* [{(] \s* + pub \s+ (?P[A-Z_]+) \s*,\s* + (?PForbid|Deny|Warn|Allow) \s*,\s* + " (?P(?:[^"\\]+|\\.)*) " \s* [})] +''', re.X | re.S) + +nl_escape_re = re.compile(r'\\\n\s*') + + +def collect(lints, fn): + """Collect all lints from a file. + + Adds entries to the lints list as `(module, name, level, desc)`. + """ + with open(fn) as fp: + code = fp.read() + for match in declare_lint_re.finditer(code): + # remove \-newline escapes from description string + desc = nl_escape_re.sub('', match.group('desc')) + lints.append((os.path.splitext(os.path.basename(fn))[0], + match.group('name').lower(), + match.group('level').lower(), + desc.replace('\\"', '"'))) + + +def write_tbl(lints, fp): + """Write lint table in Markdown format.""" + # first and third column widths + w_name = max(len(l[1]) for l in lints) + w_desc = max(len(l[3]) for l in lints) + # header and underline + fp.write('%-*s | default | meaning\n' % (w_name, 'name')) + fp.write('%s-|-%s-|-%s\n' % ('-' * w_name, '-' * 7, '-' * w_desc)) + # one table row per lint + for (_, name, default, meaning) in sorted(lints, key=lambda l: l[1]): + fp.write('%-*s | %-7s | %s\n' % (w_name, name, default, meaning)) + + +def write_group(lints, fp): + """Write lint group (list of all lints in the form module::NAME).""" + for (module, name, _, _) in sorted(lints): + fp.write(' %s::%s,\n' % (module, name.upper())) + + +def replace_region(fn, region_start, region_end, callback, + replace_start=True): + """Replace a region in a file delimited by two lines matching regexes. + + A callback is called to write the new region. If `replace_start` is true, + the start delimiter line is replaced as well. The end delimiter line is + never replaced. + """ + # read current content + with open(fn) as fp: + lines = list(fp) + + # replace old region with new region + with open(fn, 'w') as fp: + in_old_region = False + for line in lines: + if in_old_region: + if re.search(region_end, line): + in_old_region = False + fp.write(line) + elif re.search(region_start, line): + if not replace_start: + fp.write(line) + # old region starts here + in_old_region = True + callback(fp) + else: + fp.write(line) + + +def main(print_only=False): + lints = [] + + # check directory + if not os.path.isfile('src/lib.rs'): + print('Error: call this script from clippy checkout directory!') + return + + # collect all lints from source files + for root, dirs, files in os.walk('src'): + for fn in files: + if fn.endswith('.rs'): + collect(lints, os.path.join(root, fn)) + + if print_only: + write_tbl(lints, sys.stdout) + return + + # replace table in README.md + replace_region('README.md', r'^name +\|', '^$', lambda fp: write_tbl(lints, fp)) + + # same for "clippy" lint collection + replace_region('src/lib.rs', r'reg.register_lint_group\("clippy"', r'\]\);', + lambda fp: write_group(lints, fp), replace_start=False) + + +if __name__ == '__main__': + main(print_only='-n' in sys.argv) diff --git a/util/update_readme.py b/util/update_readme.py deleted file mode 100644 index 0b54afa83..000000000 --- a/util/update_readme.py +++ /dev/null @@ -1,80 +0,0 @@ -# Generate a Markdown table of all lints, and put it in README.md. -# With -n option, only print the new table to stdout. - -import os -import re -import sys - -declare_lint_re = re.compile(r''' - declare_lint! \s* [{(] \s* - pub \s+ (?P[A-Z_]+) \s*,\s* - (?PForbid|Deny|Warn|Allow) \s*,\s* - " (?P(?:[^"\\]+|\\.)*) " \s* [})] -''', re.X | re.S) - -nl_escape_re = re.compile(r'\\\n\s*') - - -def collect(lints, fp): - code = fp.read() - for match in declare_lint_re.finditer(code): - # remove \-newline escapes from description string - desc = nl_escape_re.sub('', match.group('desc')) - lints.append((match.group('name').lower(), - match.group('level').lower(), - desc.replace('\\"', '"'))) - - -def write_tbl(lints, fp): - # first and third column widths - w_name = max(len(l[0]) for l in lints) - w_desc = max(len(l[2]) for l in lints) - # header and underline - fp.write('%-*s | default | meaning\n' % (w_name, 'name')) - fp.write('%s-|-%s-|-%s\n' % ('-' * w_name, '-' * 7, '-' * w_desc)) - # one table row per lint - for (name, default, meaning) in sorted(lints): - fp.write('%-*s | %-7s | %s\n' % (w_name, name, default, meaning)) - - -def main(print_only=False): - lints = [] - - # check directory - if not os.path.isfile('src/lib.rs'): - print('Error: call this script from clippy checkout directory!') - return - - # collect all lints from source files - for root, dirs, files in os.walk('src'): - for fn in files: - if fn.endswith('.rs'): - with open(os.path.join(root, fn)) as fp: - collect(lints, fp) - - if print_only: - write_tbl(lints, sys.stdout) - return - - # read current README.md content - with open('README.md') as fp: - lines = list(fp) - - # replace old table with new table - with open('README.md', 'w') as fp: - in_old_tbl = False - for line in lines: - if line.replace(' ', '').strip() == 'name|default|meaning': - # old table starts here - write_tbl(lints, fp) - in_old_tbl = True - if in_old_tbl: - # the old table is finished by an empty line - if line.strip(): - continue - in_old_tbl = False - fp.write(line) - - -if __name__ == '__main__': - main(print_only='-n' in sys.argv)