Merge branch 'pr-152'

Conflicts:
	README.md
	src/lib.rs
	src/strings.rs
This commit is contained in:
Manish Goregaokar 2015-08-13 18:34:25 +05:30
commit 1d64553bc6
22 changed files with 238 additions and 97 deletions

View file

@ -6,36 +6,43 @@ A collection of lints that give helpful tips to newbies and catch oversights.
##Lints ##Lints
Lints included in this crate: 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. name | default | meaning
- `box_vec`: Warns on usage of `Box<Vec<T>>` ---------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- `linkedlist`: Warns on usage of `LinkedList` 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
- `str_to_string`: Warns on usage of `str::to_string()` 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)
- `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))`) box_vec | warn | usage of `Box<Vec<T>>`, vector elements are already on the heap
- `eq_op`: Warns on equal operands on both sides of a comparison or bitwise combination cmp_nan | deny | comparisons to NAN (which will always return false, which is probably not intended)
- `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) cmp_owned | warn | creating owned instances for comparing with others, e.g. `x == "foo".to_string()`
- `ineffective_bit_mask`: Warns on expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2` 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() }`
- `needless_bool` : Warns on if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }` eq_op | warn | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)
- `ptr_arg`: Warns on fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively 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)
- `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 identity_op | warn | using identity operations, e.g. `x + 0` or `y / 1`
- `cmp_nan`: Denies comparisons to NAN (which will always return false, which is probably not intended) ineffective_bit_mask | warn | expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`
- `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 inline_always | warn | `#[inline(always)]` is a bad idea in most cases
- `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)` len_without_is_empty | warn | traits and impls that have `.len()` but not `.is_empty()`
- `redundant_closure`: Warns on usage of eta-reducible closures like `|a| foo(a)` (which can be written as just `foo`) len_zero | warn | checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead
- `identity_op`: Warns on identity operations like `x + 0` or `y / 1` (which can be reduced to `x` and `y`, respectively) let_and_return | warn | creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a function
- `mut_mut`: Warns on `&mut &mut` which is either a copy'n'paste error, or shows a fundamental misunderstanding of references let_unit_value | warn | creating a let binding to a value of unit type, which usually can't be used afterwards
- `len_zero`: Warns on `_.len() == 0` and suggests using `_.is_empty()` (or similar comparisons with `>` or `!=`) linkedlist | warn | usage of LinkedList, usually a vector is faster, or a more specialized data structure like a RingBuf
- `len_without_is_empty`: Warns on traits or impls that have a `.len()` but no `.is_empty()` method modulo_one | warn | taking a number modulo 1, which always returns 0
- `cmp_owned`: Warns on creating owned instances for comparing with others, e.g. `x == "foo".to_string()` mut_mut | warn | usage of double-mut refs, e.g. `&mut &mut ...` (either copy'n'paste error, or shows a fundamental misunderstanding of references)
- `inline_always`: Warns on `#[inline(always)]`, because in most cases it is a bad idea needless_bool | warn | if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }`
- `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() }` needless_lifetimes | warn | using explicit lifetimes for references in function arguments when elision rules would allow omitting them
- `zero_width_space`: Warns on encountering a unicode zero-width space needless_range_loop | warn | for-looping over a range of indices where an iterator over items would do
- `string_add_assign`: Warns on `x = x + ..` where `x` is a `String` and suggests using `push_str(..)` instead. Allowed by default. needless_return | warn | using a return statement like `return expr;` where an expression would suffice
- `string_add`: Matches `x + ..` where `x` is a `String` and where `string_add_assign` doesn't warn. Allowed by default. non_ascii_literal | allow | using any literal non-ASCII chars in a string literal; suggests using the \\u escape instead
- `needless_return`: Warns on using `return expr;` when a simple `expr` would suffice. option_unwrap_used | warn | using `Option.unwrap()`, which should at least get a better message using `expect()`
- `let_and_return`: Warns on doing `let x = expr; x` at the end of a function. 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)`
- `option_unwrap_used`: Warns when `Option.unwrap()` is used, and suggests `.expect()`. ptr_arg | allow | fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively
- `result_unwrap_used`: Warns when `Result.unwrap()` is used (silent by default). redundant_closure | warn | using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)
- `modulo_one`: Warns on taking a number modulo 1, which always has a result of 0. 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 | allow | using `x = x + ..` where x is a `String`; suggests using `push_str()` instead
string_add_assign | allow | expressions of the form `x = x + ..` where x is a `String`
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: To use, add the following lines to your Cargo.toml:

View file

@ -12,7 +12,8 @@ use utils::span_lint;
declare_lint! { declare_lint! {
pub APPROX_CONSTANT, pub APPROX_CONSTANT,
Warn, 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"), const KNOWN_CONSTS : &'static [(f64, &'static str)] = &[(f64::E, "E"), (f64::FRAC_1_PI, "FRAC_1_PI"),

View file

@ -9,7 +9,7 @@ use syntax::parse::token::InternedString;
use utils::{in_macro, match_path, span_lint}; use utils::{in_macro, match_path, span_lint};
declare_lint! { pub INLINE_ALWAYS, Warn, 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)] #[derive(Copy,Clone)]

View file

@ -11,15 +11,14 @@ use utils::span_lint;
declare_lint! { declare_lint! {
pub BAD_BIT_MASK, pub BAD_BIT_MASK,
Deny, Deny,
"Deny the use of incompatible bit masks in comparisons, e.g. \ "expressions of the form `_ & mask == select` that will only ever return `true` or `false` \
'(a & 1) == 2'" (because in the example `select` containing bits that `mask` doesn't have)"
} }
declare_lint! { declare_lint! {
pub INEFFECTIVE_BIT_MASK, pub INEFFECTIVE_BIT_MASK,
Warn, Warn,
"Warn on the use of an ineffective bit mask in comparisons, e.g. \ "expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`"
'(a & 1) > 2'"
} }
/// Checks for incompatible bit masks in comparisons, e.g. `x & 1 == 2`. /// Checks for incompatible bit masks in comparisons, e.g. `x & 1 == 2`.

View file

@ -23,7 +23,8 @@ use utils::{in_macro, span_help_and_lint, snippet};
declare_lint! { declare_lint! {
pub COLLAPSIBLE_IF, pub COLLAPSIBLE_IF,
Warn, 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)] #[derive(Copy,Clone)]

View file

@ -8,7 +8,7 @@ use utils::span_lint;
declare_lint! { declare_lint! {
pub EQ_OP, pub EQ_OP,
Warn, 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)] #[derive(Copy,Clone)]

View file

@ -11,7 +11,7 @@ pub struct EtaPass;
declare_lint!(pub REDUNDANT_CLOSURE, Warn, 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 { impl LintPass for EtaPass {
fn get_lints(&self) -> LintArray { fn get_lints(&self) -> LintArray {

View file

@ -10,7 +10,7 @@ use syntax::codemap::Span;
use utils::{span_lint, snippet}; use utils::{span_lint, snippet};
declare_lint! { pub IDENTITY_OP, Warn, 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)] #[derive(Copy,Clone)]
pub struct IdentityOp; pub struct IdentityOp;

View file

@ -12,10 +12,11 @@ use syntax::ast::*;
use utils::{span_lint, walk_ptrs_ty, snippet}; use utils::{span_lint, walk_ptrs_ty, snippet};
declare_lint!(pub LEN_ZERO, Warn, 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, 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)] #[derive(Copy,Clone)]
pub struct LenZero; pub struct LenZero;

View file

@ -64,35 +64,41 @@ pub fn plugin_registrar(reg: &mut Registry) {
reg.register_lint_pass(box loops::LoopsPass as LintPassObject); reg.register_lint_pass(box loops::LoopsPass as LintPassObject);
reg.register_lint_pass(box lifetimes::LifetimePass 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![
misc::SINGLE_MATCH, approx_const::APPROX_CONSTANT,
misc::TOPLEVEL_REF_ARG, eq_op::EQ_OP, attrs::INLINE_ALWAYS,
bit_mask::BAD_BIT_MASK, bit_mask::BAD_BIT_MASK,
bit_mask::INEFFECTIVE_BIT_MASK, bit_mask::INEFFECTIVE_BIT_MASK,
ptr_arg::PTR_ARG, collapsible_if::COLLAPSIBLE_IF,
needless_bool::NEEDLESS_BOOL, eq_op::EQ_OP,
approx_const::APPROX_CONSTANT,
misc::CMP_NAN, misc::FLOAT_CMP,
misc::PRECEDENCE, misc::CMP_OWNED,
eta_reduction::REDUNDANT_CLOSURE, eta_reduction::REDUNDANT_CLOSURE,
identity_op::IDENTITY_OP, identity_op::IDENTITY_OP,
mut_mut::MUT_MUT,
len_zero::LEN_ZERO,
len_zero::LEN_WITHOUT_IS_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY,
attrs::INLINE_ALWAYS, len_zero::LEN_ZERO,
collapsible_if::COLLAPSIBLE_IF, lifetimes::NEEDLESS_LIFETIMES,
unicode::ZERO_WIDTH_SPACE, loops::NEEDLESS_RANGE_LOOP,
unicode::NON_ASCII_LITERAL,
strings::STRING_ADD_ASSIGN,
strings::STRING_ADD,
returns::NEEDLESS_RETURN,
misc::MODULO_ONE,
methods::OPTION_UNWRAP_USED, methods::OPTION_UNWRAP_USED,
methods::RESULT_UNWRAP_USED, methods::RESULT_UNWRAP_USED,
methods::STR_TO_STRING, methods::STR_TO_STRING,
methods::STRING_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,
strings::STRING_ADD_ASSIGN,
types::BOX_VEC,
types::LET_UNIT_VALUE, types::LET_UNIT_VALUE,
lifetimes::NEEDLESS_LIFETIMES, types::LINKEDLIST,
loops::NEEDLESS_RANGE_LOOP, unicode::NON_ASCII_LITERAL,
unicode::ZERO_WIDTH_SPACE,
]); ]);
} }

View file

@ -7,7 +7,8 @@ use std::collections::HashSet;
use std::iter::FromIterator; use std::iter::FromIterator;
declare_lint!(pub NEEDLESS_LIFETIMES, Warn, 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)] #[derive(Copy,Clone)]
pub struct LifetimePass; pub struct LifetimePass;

View file

@ -6,7 +6,7 @@ use std::collections::HashSet;
use utils::{span_lint, get_parent_expr}; use utils::{span_lint, get_parent_expr};
declare_lint!{ pub NEEDLESS_RANGE_LOOP, Warn, 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)] #[derive(Copy, Clone)]
pub struct LoopsPass; pub struct LoopsPass;

View file

@ -8,13 +8,13 @@ use utils::{span_lint, match_def_path, walk_ptrs_ty};
pub struct MethodsPass; pub struct MethodsPass;
declare_lint!(pub OPTION_UNWRAP_USED, Warn, 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, 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, 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, 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 { impl LintPass for MethodsPass {
fn get_lints(&self) -> LintArray { fn get_lints(&self) -> LintArray {

View file

@ -16,7 +16,8 @@ pub struct MiscPass;
declare_lint!(pub SINGLE_MATCH, Warn, 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 { impl LintPass for MiscPass {
fn get_lints(&self) -> LintArray { 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)] #[allow(missing_copy_implementations)]
pub struct TopLevelRefPass; 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)] #[derive(Copy,Clone)]
pub struct CmpNan; pub struct CmpNan;
@ -114,7 +118,9 @@ fn check_nan(cx: &Context, path: &Path, span: Span) {
} }
declare_lint!(pub FLOAT_CMP, Warn, 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)] #[derive(Copy,Clone)]
pub struct FloatCmp; pub struct FloatCmp;
@ -147,7 +153,8 @@ fn is_float(cx: &Context, expr: &Expr) -> bool {
} }
declare_lint!(pub PRECEDENCE, Warn, 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)] #[derive(Copy,Clone)]
pub struct Precedence; pub struct Precedence;
@ -190,7 +197,7 @@ fn is_arith_op(op : BinOp_) -> bool {
} }
declare_lint!(pub CMP_OWNED, Warn, 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)] #[derive(Copy,Clone)]
pub struct CmpOwned; pub struct CmpOwned;
@ -242,7 +249,7 @@ fn is_str_arg(cx: &Context, args: &[P<Expr>]) -> bool {
walk_ptrs_ty(cx.tcx.expr_ty(&*args[0])).sty { true } else { false } 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)] #[derive(Copy,Clone)]
pub struct ModuloOne; pub struct ModuloOne;

View file

@ -6,7 +6,8 @@ use syntax::codemap::{BytePos, ExpnInfo, Span};
use utils::{in_macro, span_lint}; use utils::{in_macro, span_lint};
declare_lint!(pub MUT_MUT, Warn, 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)] #[derive(Copy,Clone)]
pub struct MutMut; pub struct MutMut;

View file

@ -15,7 +15,8 @@ use utils::{de_p, span_lint};
declare_lint! { declare_lint! {
pub NEEDLESS_BOOL, pub NEEDLESS_BOOL,
Warn, 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)] #[derive(Copy,Clone)]

View file

@ -16,7 +16,8 @@ use utils::span_lint;
declare_lint! { declare_lint! {
pub PTR_ARG, pub PTR_ARG,
Allow, 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)] #[derive(Copy,Clone)]

View file

@ -7,9 +7,10 @@ use rustc::lint::{Context, LintPass, LintArray, Level};
use utils::{span_lint, snippet, match_path}; use utils::{span_lint, snippet, match_path};
declare_lint!(pub NEEDLESS_RETURN, Warn, 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, 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)] #[derive(Copy,Clone)]
pub struct ReturnPass; pub struct ReturnPass;

View file

@ -20,7 +20,7 @@ declare_lint! {
declare_lint! { declare_lint! {
pub STRING_ADD, pub STRING_ADD,
Allow, Allow,
"expressions of the form `x + ..` where x is a `String" "using `x = x + ..` where x is a `String`; suggests using `push_str()` instead"
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]

View file

@ -12,9 +12,10 @@ use utils::{in_macro, snippet, span_lint, span_help_and_lint};
pub struct TypePass; pub struct TypePass;
declare_lint!(pub BOX_VEC, Warn, declare_lint!(pub BOX_VEC, Warn,
"Warn on usage of Box<Vec<T>>"); "usage of `Box<Vec<T>>`, vector elements are already on the heap");
declare_lint!(pub LINKEDLIST, Warn, 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 /// 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<Ty>]> { pub fn match_ty_unwrap<'a>(ty: &'a Ty, segments: &[&str]) -> Option<&'a [P<Ty>]> {
@ -81,7 +82,7 @@ impl LintPass for TypePass {
pub struct LetPass; pub struct LetPass;
declare_lint!(pub LET_UNIT_VALUE, Warn, 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>) { fn check_let_unit(cx: &Context, decl: &Decl, info: Option<&ExpnInfo>) {

View file

@ -3,8 +3,11 @@ use syntax::ast::*;
use syntax::codemap::{BytePos, Span}; use syntax::codemap::{BytePos, Span};
use utils::span_lint; use utils::span_lint;
declare_lint!{ pub ZERO_WIDTH_SPACE, Deny, "Zero-width space is confusing" } declare_lint!{ pub ZERO_WIDTH_SPACE, Deny,
declare_lint!{ pub NON_ASCII_LITERAL, Allow, "Lint literal non-ASCII chars in literals" } "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)] #[derive(Copy, Clone)]
pub struct Unicode; pub struct Unicode;

110
util/update_lints.py Normal file
View file

@ -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<name>[A-Z_]+) \s*,\s*
(?P<level>Forbid|Deny|Warn|Allow) \s*,\s*
" (?P<desc>(?:[^"\\]+|\\.)*) " \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)