diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index f26442447..9da20a28f 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -15,12 +15,13 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does /// Suggests to use dedicated built-in methods, - /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// `is_ascii_(lowercase|uppercase|digit|hexdigit)` for checking on corresponding + /// ascii range /// /// ### Why is this bad? /// Using the built-in functions is more readable and makes it /// clear that it's not a specific subset of characters, but all - /// ASCII (lowercase|uppercase|digit) characters. + /// ASCII (lowercase|uppercase|digit|hexdigit) characters. /// ### Example /// ```rust /// fn main() { @@ -28,6 +29,7 @@ declare_clippy_lint! { /// assert!(matches!(b'X', b'A'..=b'Z')); /// assert!(matches!('2', '0'..='9')); /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// assert!(matches!('C', '0'..='9' | 'a'..='f' | 'A'..='F')); /// /// ('0'..='9').contains(&'0'); /// ('a'..='z').contains(&'a'); @@ -41,6 +43,7 @@ declare_clippy_lint! { /// assert!(b'X'.is_ascii_uppercase()); /// assert!('2'.is_ascii_digit()); /// assert!('x'.is_ascii_alphabetic()); + /// assert!('C'.is_ascii_hexdigit()); /// /// '0'.is_ascii_digit(); /// 'a'.is_ascii_lowercase(); @@ -75,6 +78,12 @@ enum CharRange { FullChar, /// '0..=9' Digit, + /// 'a..=f' + LowerHexLetter, + /// 'A..=F' + UpperHexLetter, + /// '0..=9' | 'a..=f' | 'A..=F' + HexDigit, Otherwise, } @@ -116,7 +125,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha CharRange::LowerChar => Some("is_ascii_lowercase"), CharRange::FullChar => Some("is_ascii_alphabetic"), CharRange::Digit => Some("is_ascii_digit"), - CharRange::Otherwise => None, + CharRange::HexDigit => Some("is_ascii_hexdigit"), + CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None, } { let default_snip = ".."; let mut app = Applicability::MachineApplicable; @@ -141,6 +151,12 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { CharRange::FullChar + } else if ranges.len() == 3 + && ranges.contains(&CharRange::Digit) + && ranges.contains(&CharRange::LowerHexLetter) + && ranges.contains(&CharRange::UpperHexLetter) + { + CharRange::HexDigit } else { CharRange::Otherwise } @@ -156,6 +172,8 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { match (&start_lit.node, &end_lit.node) { (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, + (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, _ => CharRange::Otherwise, } diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 5be2dd280..9c4bd335a 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -33,6 +33,7 @@ fn msrv_1_23() { assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); } #[clippy::msrv = "1.24"] @@ -40,14 +41,17 @@ fn msrv_1_24() { assert!(b'1'.is_ascii_digit()); assert!('X'.is_ascii_uppercase()); assert!('x'.is_ascii_alphabetic()); + assert!('x'.is_ascii_hexdigit()); } #[clippy::msrv = "1.46"] fn msrv_1_46() { const FOO: bool = matches!('x', '0'..='9'); + const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } #[clippy::msrv = "1.47"] fn msrv_1_47() { const FOO: bool = 'x'.is_ascii_digit(); + const BAR: bool = 'x'.is_ascii_hexdigit(); } diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index f9249e22a..785943cd2 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -33,6 +33,7 @@ fn msrv_1_23() { assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); } #[clippy::msrv = "1.24"] @@ -40,14 +41,17 @@ fn msrv_1_24() { assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); } #[clippy::msrv = "1.46"] fn msrv_1_46() { const FOO: bool = matches!('x', '0'..='9'); + const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } #[clippy::msrv = "1.47"] fn msrv_1_47() { const FOO: bool = matches!('x', '0'..='9'); + const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); } diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index e0fb46e59..f69522c5f 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -98,28 +98,40 @@ LL | ('A'..='Z').contains(cool_letter); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:40:13 + --> $DIR/manual_is_ascii_check.rs:41:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:41:13 + --> $DIR/manual_is_ascii_check.rs:42:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:42:13 + --> $DIR/manual_is_ascii_check.rs:43:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:52:23 + --> $DIR/manual_is_ascii_check.rs:44:13 + | +LL | assert!(matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:55:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` -error: aborting due to 20 previous errors +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:56:23 + | +LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()` + +error: aborting due to 22 previous errors