diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e495961b0..9e198b57073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Change Log All notable changes to this project will be documented in this file. +## 0.0.86 — ? +* New lint: [`zero_prefixed_literal`] + ## 0.0.85 — 2016-08-19 * Fix ICE with [`useless_attribute`] * [`useless_attribute`] ignores [`unused_imports`] on `use` statements @@ -329,5 +332,6 @@ All notable changes to this project will be documented in this file. [`wrong_self_convention`]: https://github.com/Manishearth/rust-clippy/wiki#wrong_self_convention [`wrong_transmute`]: https://github.com/Manishearth/rust-clippy/wiki#wrong_transmute [`zero_divided_by_zero`]: https://github.com/Manishearth/rust-clippy/wiki#zero_divided_by_zero +[`zero_prefixed_literal`]: https://github.com/Manishearth/rust-clippy/wiki#zero_prefixed_literal [`zero_width_space`]: https://github.com/Manishearth/rust-clippy/wiki#zero_width_space diff --git a/README.md b/README.md index 40112616532..b294c75a756 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ name [wrong_self_convention](https://github.com/Manishearth/rust-clippy/wiki#wrong_self_convention) | warn | defining a method named with an established prefix (like "into_") that takes `self` with the wrong convention [wrong_transmute](https://github.com/Manishearth/rust-clippy/wiki#wrong_transmute) | warn | transmutes that are confusing at best, undefined behaviour at worst and always useless [zero_divided_by_zero](https://github.com/Manishearth/rust-clippy/wiki#zero_divided_by_zero) | warn | usage of `0.0 / 0.0` to obtain NaN instead of std::f32::NaN or std::f64::NaN +[zero_prefixed_literal](https://github.com/Manishearth/rust-clippy/wiki#zero_prefixed_literal) | warn | integer literals starting with `0` [zero_width_space](https://github.com/Manishearth/rust-clippy/wiki#zero_width_space) | deny | using a zero-width space in a string literal, which is confusing More to come, please [file an issue](https://github.com/Manishearth/rust-clippy/issues) if you have ideas! diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 07d504f75ad..df0b0d9efff 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -387,6 +387,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { misc_early::MIXED_CASE_HEX_LITERALS, misc_early::REDUNDANT_CLOSURE_CALL, misc_early::UNNEEDED_FIELD_PATTERN, + misc_early::ZERO_PREFIXED_LITERAL, module_inception::MODULE_INCEPTION, mut_reference::UNNECESSARY_MUT_PASSED, mutex_atomic::MUTEX_ATOMIC, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 63b27f48f0d..47b6854cfa5 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -105,6 +105,42 @@ declare_lint! { "literals whose suffix is not separated by an underscore" } +/// **What it does:** Warns if a integral constant literal starts with `0`. +/// +/// **Why is this bad?** In some languages (including the infamous C language and most of its +/// familly), this marks an octal constant. In Rust however, this is a decimal constant. This could +/// be confusing for both the writer and a reader of the constant. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// +/// In Rust: +/// ```rust +/// fn main() { +/// let a = 0123; +/// println!("{}", a); +/// } +/// ``` +/// +/// prints `123`, while in C: +/// +/// ```c +/// #include +/// +/// int main() { +/// int a = 0123; +/// printf("%d\n", a); +/// } +/// ``` +/// +/// prints `83` (as `89 == 0o123` while `123 == 0o173`). +declare_lint! { + pub ZERO_PREFIXED_LITERAL, + Warn, + "integer literals starting with `0`" +} + #[derive(Copy, Clone)] pub struct MiscEarly; @@ -112,7 +148,8 @@ pub struct MiscEarly; impl LintPass for MiscEarly { fn get_lints(&self) -> LintArray { lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL, - DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX) + DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX, + ZERO_PREFIXED_LITERAL) } } @@ -220,7 +257,7 @@ impl EarlyLintPass for MiscEarly { } ExprKind::Lit(ref lit) => { if_let_chain! {[ - let LitKind::Int(..) = lit.node, + let LitKind::Int(value, ..) = lit.node, let Some(src) = snippet_opt(cx, lit.span), let Some(firstch) = src.chars().next(), char::to_digit(firstch, 10).is_some() @@ -250,6 +287,15 @@ impl EarlyLintPass for MiscEarly { span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span, "inconsistent casing in hexadecimal literal"); } + } else if value != 0 && src.starts_with('0') { + span_lint_and_then(cx, + ZERO_PREFIXED_LITERAL, + lit.span, + "this is a decimal constant", + |db| { + db.span_suggestion(lit.span, "if you mean to use a decimal constant, remove the `0` to remove confusion:", src[1..].to_string()); + db.span_suggestion(lit.span, "if you mean to use an octal constant, use `0o`:", format!("0o{}", &src[1..])); + }); } }} if_let_chain! {[ diff --git a/tests/compile-fail/literals.rs b/tests/compile-fail/literals.rs index 7645fb56e20..91a63646998 100644 --- a/tests/compile-fail/literals.rs +++ b/tests/compile-fail/literals.rs @@ -2,6 +2,7 @@ #![plugin(clippy)] #![deny(mixed_case_hex_literals)] #![deny(unseparated_literal_suffix)] +#![deny(zero_prefixed_literal)] #![allow(dead_code)] fn main() { @@ -22,4 +23,13 @@ fn main() { let fail5 = 1234isize; //~ERROR integer type suffix should be separated let fail6 = 1234usize; //~ERROR integer type suffix should be separated let fail7 = 1.5f32; //~ERROR float type suffix should be separated + + let ok9 = 0; + let ok10 = 0_i64; + let fail8 = 0123; + //~^ERROR decimal constant + //~|HELP remove the `0` + //~|SUGGESTION = 123; + //~|HELP use `0o` + //~|SUGGESTION = 0o123; }