diff --git a/README.md b/README.md index 83ff6daa9f5..63ecbf11fcf 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 98 lints included in this crate: +There are 99 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -20,6 +20,7 @@ name [cast_possible_wrap](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_wrap) | allow | casts that may cause wrapping around the value, e.g `x as i32` where `x: u32` and `x > i32::MAX` [cast_precision_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_precision_loss) | allow | casts that cause loss of precision, e.g `x as f32` where `x: u64` [cast_sign_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_sign_loss) | allow | casts from signed types to unsigned types, e.g `x as u32` where `x: i32` +[char_lit_as_u8](https://github.com/Manishearth/rust-clippy/wiki#char_lit_as_u8) | warn | Casting a character literal to u8 [chars_next_cmp](https://github.com/Manishearth/rust-clippy/wiki#chars_next_cmp) | warn | using `.chars().next()` to check if a string starts with a char [cmp_nan](https://github.com/Manishearth/rust-clippy/wiki#cmp_nan) | deny | comparisons to NAN (which will always return false, which is probably not intended) [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()` diff --git a/src/lib.rs b/src/lib.rs index c43c01268fe..b0cf4d46972 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,6 +140,7 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_late_lint_pass(box panic::PanicPass); reg.register_late_lint_pass(box strings::StringLitAsBytes); reg.register_late_lint_pass(box derive::Derive); + reg.register_late_lint_pass(box types::CharLitAsU8); reg.register_lint_group("clippy_pedantic", vec![ @@ -238,6 +239,7 @@ pub fn plugin_registrar(reg: &mut Registry) { temporary_assignment::TEMPORARY_ASSIGNMENT, transmute::USELESS_TRANSMUTE, types::BOX_VEC, + types::CHAR_LIT_AS_U8, types::LET_UNIT_VALUE, types::LINKEDLIST, types::TYPE_COMPLEXITY, diff --git a/src/types.rs b/src/types.rs index 427b695d385..d41896cd490 100644 --- a/src/types.rs +++ b/src/types.rs @@ -517,3 +517,43 @@ impl<'v> Visitor<'v> for TypeComplexityVisitor { self.nest -= sub_nest; } } + +/// **What it does:** This lint points out expressions where a character literal is casted to u8 and suggests using a byte literal instead. +/// +/// **Why is this bad?** In general, casting values to smaller types is error-prone and should be avoided where possible. In the particular case of converting a character literal to u8, it is easy to avoid by just using a byte literal instead. As an added bonus, `b'a'` is even slightly shorter than `'a' as u8`. +/// +/// **Known problems:** None +/// +/// **Example:** `'x' as u8` +declare_lint!(pub CHAR_LIT_AS_U8, Warn, + "Casting a character literal to u8"); + +pub struct CharLitAsU8; + +impl LintPass for CharLitAsU8 { + fn get_lints(&self) -> LintArray { + lint_array!(CHAR_LIT_AS_U8) + } +} + +impl LateLintPass for CharLitAsU8 { + fn check_expr(&mut self, cx: &LateContext, expr: &Expr) { + use syntax::ast::{Lit_, UintTy}; + + if let ExprCast(ref e, _) = expr.node { + if let ExprLit(ref l) = e.node { + if let Lit_::LitChar(_) = l.node { + if ty::TyUint(UintTy::TyU8) == cx.tcx.expr_ty(expr).sty && !in_macro(cx, expr.span) { + let msg = "casting character literal to u8. `char`s \ + are 4 bytes wide in rust, so casting to u8 \ + truncates them"; + let help = format!("Consider using a byte literal \ + instead:\nb{}", + snippet(cx, e.span, "'x'")); + span_help_and_lint(cx, CHAR_LIT_AS_U8, expr.span, msg, &help); + } + } + } + } + } +} diff --git a/tests/compile-fail/char_lit_as_u8.rs b/tests/compile-fail/char_lit_as_u8.rs new file mode 100644 index 00000000000..4fca878c4da --- /dev/null +++ b/tests/compile-fail/char_lit_as_u8.rs @@ -0,0 +1,8 @@ +#![feature(plugin)] +#![plugin(clippy)] + +#![deny(char_lit_as_u8)] +#![allow(unused_variables)] +fn main() { + let c = 'a' as u8; //~ERROR casting character literal +} diff --git a/util/update_lints.py b/util/update_lints.py index 94b2a3a57ba..6a59abcdc15 100755 --- a/util/update_lints.py +++ b/util/update_lints.py @@ -9,7 +9,7 @@ import sys declare_lint_re = re.compile(r''' declare_lint! \s* [{(] \s* - pub \s+ (?P[A-Z_]+) \s*,\s* + pub \s+ (?P[A-Z_][A-Z_0-9]*) \s*,\s* (?PForbid|Deny|Warn|Allow) \s*,\s* " (?P(?:[^"\\]+|\\.)*) " \s* [})] ''', re.X | re.S)