From 7dcda5f2e1c6eab4ff6a6f1f5c55ae7667b7d0bd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 9 Jul 2026 19:37:20 -0400 Subject: [PATCH] Rewrite and merge `LiteralDigitGrouping` and `LiteralRepresentation` passes. --- clippy_lints/src/declared_lints.rs | 12 +- clippy_lints/src/lib.rs | 5 +- clippy_lints/src/literal_representation.rs | 137 +----- .../decimal_literal_representation.rs | 74 +++ clippy_lints/src/literals/digit_grouping.rs | 155 +++++++ .../src/literals/mistyped_literal_suffix.rs | 94 ++++ clippy_lints/src/literals/mod.rs | 191 ++++++++ clippy_utils/src/lib.rs | 2 + clippy_utils/src/numeric_literal.rs | 421 +++++++++++++++++- .../decimal_literal_representation.fixed | 2 +- .../decimal_literal_representation.stderr | 7 +- .../lint_decimal_readability/test.fixed | 4 +- .../ui-toml/lint_decimal_readability/test.rs | 4 +- .../lint_decimal_readability/test.stderr | 20 +- tests/ui/decimal_literal_representation.fixed | 12 +- .../ui/decimal_literal_representation.stderr | 55 ++- tests/ui/inconsistent_digit_grouping.fixed | 38 +- tests/ui/inconsistent_digit_grouping.rs | 32 +- tests/ui/inconsistent_digit_grouping.stderr | 121 +++-- tests/ui/large_digit_groups.stderr | 48 +- tests/ui/literals.stderr | 38 +- ....fixed => mistyped_literal_suffix.1.fixed} | 12 +- tests/ui/mistyped_literal_suffix.2.fixed | 69 +++ tests/ui/mistyped_literal_suffix.3.fixed | 69 +++ tests/ui/mistyped_literal_suffix.stderr | 285 ++++++++++-- tests/ui/unreadable_literal.fixed | 41 +- tests/ui/unreadable_literal.rs | 35 +- tests/ui/unreadable_literal.stderr | 125 +++++- 28 files changed, 1714 insertions(+), 394 deletions(-) create mode 100644 clippy_lints/src/literals/decimal_literal_representation.rs create mode 100644 clippy_lints/src/literals/digit_grouping.rs create mode 100644 clippy_lints/src/literals/mistyped_literal_suffix.rs create mode 100644 clippy_lints/src/literals/mod.rs rename tests/ui/{mistyped_literal_suffix.fixed => mistyped_literal_suffix.1.fixed} (88%) create mode 100644 tests/ui/mistyped_literal_suffix.2.fixed create mode 100644 tests/ui/mistyped_literal_suffix.3.fixed diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 51a848d022d8..055c7f4f832c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -263,13 +263,13 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, - crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, - crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, - crate::literal_representation::LARGE_DIGIT_GROUPS_INFO, - crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, - crate::literal_representation::UNREADABLE_LITERAL_INFO, - crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, + crate::literals::DECIMAL_LITERAL_REPRESENTATION_INFO, + crate::literals::INCONSISTENT_DIGIT_GROUPING_INFO, + crate::literals::LARGE_DIGIT_GROUPS_INFO, + crate::literals::MISTYPED_LITERAL_SUFFIXES_INFO, + crate::literals::UNREADABLE_LITERAL_INFO, + crate::literals::UNUSUAL_BYTE_GROUPINGS_INFO, crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b325d550a7fe..838ec8ee6bd4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -192,8 +192,8 @@ mod let_if_seq; mod let_underscore; mod let_with_type_underscore; mod lifetimes; -mod literal_representation; mod literal_string_with_formatting_args; +mod literals; mod loops; mod macro_metavars_in_unsafe; mod macro_use; @@ -510,8 +510,7 @@ rustc_lint::early_lint_methods!( UnusedUnit: unused_unit::UnusedUnit = unused_unit::UnusedUnit, Precedence: precedence::Precedence = precedence::Precedence, NeedlessArbitrarySelfType: needless_arbitrary_self_type::NeedlessArbitrarySelfType = needless_arbitrary_self_type::NeedlessArbitrarySelfType, - LiteralDigitGrouping: literal_representation::LiteralDigitGrouping = literal_representation::LiteralDigitGrouping::new(conf), - DecimalLiteralRepresentation: literal_representation::DecimalLiteralRepresentation = literal_representation::DecimalLiteralRepresentation::new(conf), + EarlyLiterals: literals::EarlyLiterals = literals::EarlyLiterals::new(conf), TabsInDocComments: tabs_in_doc_comments::TabsInDocComments = tabs_in_doc_comments::TabsInDocComments, SingleComponentPathImports: single_component_path_imports::SingleComponentPathImports = single_component_path_imports::SingleComponentPathImports::default(), OptionEnvUnwrap: option_env_unwrap::OptionEnvUnwrap = option_env_unwrap::OptionEnvUnwrap, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 4e7ff879ae59..38608706239e 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -10,141 +10,6 @@ use rustc_session::impl_lint_pass; use rustc_span::Span; use std::iter; -declare_clippy_lint! { - /// ### What it does - /// Warns if there is a better representation for a numeric literal. - /// - /// ### Why restrict this? - /// Especially for big powers of 2, a hexadecimal representation is usually more - /// readable than a decimal representation. - /// - /// ### Example - /// ```text - /// `255` => `0xFF` - /// `65_535` => `0xFFFF` - /// `4_042_322_160` => `0xF0F0_F0F0` - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DECIMAL_LITERAL_REPRESENTATION, - restriction, - "using decimal representation when hexadecimal would be better" -} - -declare_clippy_lint! { - /// ### What it does - /// Warns if an integral or floating-point constant is - /// grouped inconsistently with underscores. - /// - /// ### Why is this bad? - /// Readers may incorrectly interpret inconsistently - /// grouped digits. - /// - /// ### Example - /// ```no_run - /// # let _: u64 = - /// 618_64_9189_73_511 - /// # ; - /// ``` - /// - /// Use instead: - /// ```no_run - /// # let _: u64 = - /// 61_864_918_973_511 - /// # ; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INCONSISTENT_DIGIT_GROUPING, - style, - "integer literals with digits grouped inconsistently" -} - -declare_clippy_lint! { - /// ### What it does - /// Warns if the digits of an integral or floating-point - /// constant are grouped into groups that - /// are too large. - /// - /// ### Why is this bad? - /// Negatively impacts readability. - /// - /// ### Example - /// ```no_run - /// let x: u64 = 6186491_8973511; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub LARGE_DIGIT_GROUPS, - pedantic, - "grouping digits into groups that are too large" -} - -declare_clippy_lint! { - /// ### What it does - /// Warns for mistyped suffix in literals - /// - /// ### Why is this bad? - /// This is most probably a typo - /// - /// ### Known problems - /// - Does not match on integers too large to fit in the corresponding unsigned type - /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers - /// - /// ### Example - /// ```ignore - /// `2_32` => `2_i32` - /// `250_8 => `250_u8` - /// ``` - #[clippy::version = "1.30.0"] - pub MISTYPED_LITERAL_SUFFIXES, - correctness, - "mistyped literal suffix" -} - -declare_clippy_lint! { - /// ### What it does - /// Warns if a long integral or floating-point constant does - /// not contain underscores. - /// - /// ### Why is this bad? - /// Reading long numbers is difficult without separators. - /// - /// ### Example - /// ```no_run - /// # let _: u64 = - /// 61864918973511 - /// # ; - /// ``` - /// - /// Use instead: - /// ```no_run - /// # let _: u64 = - /// 61_864_918_973_511 - /// # ; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub UNREADABLE_LITERAL, - pedantic, - "long literal without underscores" -} - -declare_clippy_lint! { - /// ### What it does - /// Warns if hexadecimal or binary literals are not grouped - /// by nibble or byte. - /// - /// ### Why is this bad? - /// Negatively impacts readability. - /// - /// ### Example - /// ```no_run - /// let x: u32 = 0xFFF_FFF; - /// let y: u8 = 0b01_011_101; - /// ``` - #[clippy::version = "1.49.0"] - pub UNUSUAL_BYTE_GROUPINGS, - style, - "binary or hex literals that aren't grouped by four" -} - impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]); impl_lint_pass!(LiteralDigitGrouping => [ @@ -182,7 +47,7 @@ impl WarningType { Self::DecimalRepresentation => ( DECIMAL_LITERAL_REPRESENTATION, "integer literal has a better hexadecimal representation", - "consider", + "use a hex literal", ), Self::UnusualByteGroupings => ( UNUSUAL_BYTE_GROUPINGS, diff --git a/clippy_lints/src/literals/decimal_literal_representation.rs b/clippy_lints/src/literals/decimal_literal_representation.rs new file mode 100644 index 000000000000..b5c4a3f48322 --- /dev/null +++ b/clippy_lints/src/literals/decimal_literal_representation.rs @@ -0,0 +1,74 @@ +use arrayvec::ArrayVec; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::numeric_literal::{IntStr, Radix}; +use clippy_utils::source::SpanExt; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, LintContext}; +use rustc_span::Span; + +use super::DECIMAL_LITERAL_REPRESENTATION; + +pub(super) fn check(cx: &EarlyContext<'_>, threshold: u64, num: IntStr<'_>, sp: Span) { + if let Radix::Dec = num.radix + && num.suffix.is_none_or(|s| !s.is_float()) + && let Some(val @ 1..) = num.parse_as_u128() + && val >= u128::from(threshold) + && check_val(val) + && let sp_data = sp.data() + && !sp_data.ctxt.in_external_macro(cx.sess().source_map()) + && sp_data.check_text(cx, |src| num.eq_str(src)) + { + span_lint_and_then( + cx, + DECIMAL_LITERAL_REPRESENTATION, + sp, + "integer literal has a better hexadecimal representation", + |diag| { + diag.span_suggestion_verbose( + num.trim_sp_to_digits(&sp_data), + "use a hex literal", + fmt(val), + Applicability::MachineApplicable, + ); + }, + ); + } +} + +fn check_val(mut val: u128) -> bool { + // Power-of-two or power-of-two minus one. + // Ignore the lowest digit for larger numbers. + let pval = if val > 0xfff { val >> 4 } else { val }; + if pval.count_ones() == 1 || pval.wrapping_add(1).count_ones() <= 1 { + return true; + } + // All hex digits are either `0` or `f`. + for _ in 0..15 { + if !matches!(val & 0xff, 0 | 0xf | 0xf0 | 0xff) { + break; + } + val >>= 8; + } + matches!(val, 0 | 0x7 | 0xf | 0x70 | 0x7f | 0xf0 | 0xff) +} + +fn fmt(mut num: u128) -> String { + let mut buf = ArrayVec::::new(); + let mut i = 4u8; + while num != 0 { + if i == 0 { + let _ = buf.try_push(b'_'); + i = 3; + } else { + i -= 1; + } + let _ = buf.try_push(match num % 16 { + c @ 0..10 => c as u8 + b'0', + c => c as u8 - 10 + b'a', + }); + num /= 16; + } + let _ = buf.try_extend_from_slice(b"x0"); + buf.reverse(); + String::from_utf8(buf.to_vec()).unwrap_or(String::new()) +} diff --git a/clippy_lints/src/literals/digit_grouping.rs b/clippy_lints/src/literals/digit_grouping.rs new file mode 100644 index 000000000000..75a34d4549b3 --- /dev/null +++ b/clippy_lints/src/literals/digit_grouping.rs @@ -0,0 +1,155 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::numeric_literal::{FloatStr, IntStr, Radix, reformat_whole_opt_fract}; +use clippy_utils::source::SpanExt; +use core::ptr; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, Lint, LintContext}; +use rustc_span::Span; + +use super::{INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, UNREADABLE_LITERAL, UNUSUAL_BYTE_GROUPINGS}; + +struct LintInfo { + lint: &'static Lint, + msg: &'static str, + sugg_msg: &'static str, +} +impl PartialEq for LintInfo { + fn eq(&self, other: &Self) -> bool { + ptr::addr_eq(self, other) + } +} +static INCONSISTENT_DIGIT_GROUPING_INFO: LintInfo = LintInfo { + lint: INCONSISTENT_DIGIT_GROUPING, + msg: "digits in groups of unequal sizes", + sugg_msg: "group the digits with a consistent size", +}; +static LARGE_DIGIT_GROUPS_INFO: LintInfo = LintInfo { + lint: LARGE_DIGIT_GROUPS, + msg: "digits separated into large groups", + sugg_msg: "split the digits into smaller groups", +}; +static UNREADABLE_LITERAL_INFO: LintInfo = LintInfo { + lint: UNREADABLE_LITERAL, + msg: "long literal lacking separators", + sugg_msg: "separate the digits", +}; +static UNUSUAL_BYTE_GROUPINGS_INFO: LintInfo = LintInfo { + lint: UNUSUAL_BYTE_GROUPINGS, + msg: "digits in groups of unequal sizes", + sugg_msg: "group the digits with a consistent size", +}; + +pub(crate) fn check_int(cx: &EarlyContext<'_>, num: IntStr<'_>, sp: Span) { + let l = 'check: { + let mut groups = num.text.split('_').map(str::len); + let first = groups.next().unwrap(); + let Some(second) = groups.next() else { + if num.text.len() > 5 { + break 'check &UNREADABLE_LITERAL_INFO; + } + return; + }; + + let inconsistent_groups = if first > second { + // Allow uuid formatted numbers + !(num.radix == Radix::Hex + && first == 8 + && second == 4 + && groups.next() == Some(4) + && groups.next() == Some(4) + && groups.next() == Some(12) + && groups.next().is_none()) + } else { + groups.any(|x| x != second) + }; + + if inconsistent_groups { + break 'check if let Radix::Dec = num.radix { + &INCONSISTENT_DIGIT_GROUPING_INFO + } else { + &UNUSUAL_BYTE_GROUPINGS_INFO + }; + } else if second > 4 && num.radix == Radix::Dec { + break 'check &LARGE_DIGIT_GROUPS_INFO; + }; + return; + }; + + let sp_data = sp.data(); + if !sp_data.ctxt.in_external_macro(cx.sess().source_map()) && sp_data.check_text(cx, |src| num.eq_str(src)) { + span_lint_and_then(cx, l.lint, sp, l.msg, |diag| { + diag.span_suggestion_verbose( + num.trim_sp_to_digits(&sp_data), + l.sugg_msg, + num.reformat_digits(), + Applicability::MachineApplicable, + ); + }); + } +} + +pub(super) fn check_float(cx: &EarlyContext<'_>, num: &FloatStr<'_>, lint_unreadable_fract: bool, sp: Span) { + let (whole, fract) = num.whole_fract_digits_str(); + let (whole_lint, whole_group_size) = 'check_whole: { + let mut groups = whole.split('_').map(str::len); + let first = groups.next().unwrap(); + let Some(second) = groups.next() else { + break 'check_whole ((first > 5).then_some(&UNREADABLE_LITERAL_INFO), None); + }; + let l = if first > second || groups.any(|x| x != second) { + Some(&INCONSISTENT_DIGIT_GROUPING_INFO) + } else if second > 4 { + Some(&LARGE_DIGIT_GROUPS_INFO) + } else { + None + }; + (l, Some(second)) + }; + + let (fract_lint, fract_group_size) = 'check_fract: { + let Some(fract) = fract else { + break 'check_fract (None, None); + }; + let mut groups = fract.rsplit('_').map(str::len); + let first = groups.next().unwrap(); + let Some(second) = groups.next() else { + break 'check_fract ( + (lint_unreadable_fract && first > 5).then_some(&UNREADABLE_LITERAL_INFO), + None, + ); + }; + let l = if first > second || groups.any(|x| x != second) { + Some(&INCONSISTENT_DIGIT_GROUPING_INFO) + } else if second > 4 { + Some(&LARGE_DIGIT_GROUPS_INFO) + } else { + None + }; + (l, Some(second)) + }; + + let whole_fract_inconsistent = whole_group_size.zip(fract_group_size).is_some_and(|(x, y)| x != y); + if (whole_lint.is_some() || fract_lint.is_some() || whole_fract_inconsistent) + && let sp_data = sp.data() + && sp_data.check_text(cx, |src| num.eq_str(src)) + { + let l = if whole_fract_inconsistent + || whole_lint == Some(&INCONSISTENT_DIGIT_GROUPING_INFO) + || fract_lint == Some(&INCONSISTENT_DIGIT_GROUPING_INFO) + { + &INCONSISTENT_DIGIT_GROUPING_INFO + } else if whole_lint == Some(&LARGE_DIGIT_GROUPS_INFO) || fract_lint == Some(&LARGE_DIGIT_GROUPS_INFO) { + &LARGE_DIGIT_GROUPS_INFO + } else { + &UNREADABLE_LITERAL_INFO + }; + span_lint_and_then(cx, l.lint, sp, l.msg, |diag| { + diag.span_suggestion_verbose( + num.strip_exp_from_sp(&sp_data), + l.sugg_msg, + reformat_whole_opt_fract(whole, fract), + Applicability::MachineApplicable, + ); + }); + } +} diff --git a/clippy_lints/src/literals/mistyped_literal_suffix.rs b/clippy_lints/src/literals/mistyped_literal_suffix.rs new file mode 100644 index 000000000000..3237c82fc9c2 --- /dev/null +++ b/clippy_lints/src/literals/mistyped_literal_suffix.rs @@ -0,0 +1,94 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::numeric_literal::{FloatStr, IntStr}; +use clippy_utils::source::SpanExt; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, LintContext}; +use rustc_span::{BytePos, Span}; + +use super::MISTYPED_LITERAL_SUFFIXES; + +/// Returns `true` if the lint is emitted. +pub(super) fn check_int(cx: &EarlyContext<'_>, num: IntStr<'_>, sp: Span) -> bool { + let (text, max) = match num.text.as_bytes() { + [.., b'_', b'8'] => (&num.text[..num.text.len() - 2], u64::from(u8::MAX)), + [.., b'_', b'1', b'6'] => (&num.text[..num.text.len() - 3], u64::from(u16::MAX)), + [.., b'_', b'3', b'2'] => (&num.text[..num.text.len() - 3], u64::from(u32::MAX)), + [.., b'_', b'6', b'4'] => (&num.text[..num.text.len() - 3], u64::MAX), + _ => return false, + }; + if let Some(val) = (IntStr { text, ..num }).parse_as_u64() + && val <= max + { + let sp_data = sp.data(); + if !sp_data.ctxt.in_external_macro(cx.sess().source_map()) && sp_data.check_text(cx, |src| num.eq_str(src)) { + let insert_pos = BytePos(sp_data.hi.0 - u32::from(max != 0xff) - 1); + span_lint_and_then( + cx, + MISTYPED_LITERAL_SUFFIXES, + Span::new(BytePos(insert_pos.0 - 1), sp_data.hi, sp_data.ctxt, sp_data.parent), + "the final digit group looks like a type suffix", + |diag| { + let insert_sp = Span::new(insert_pos, insert_pos, sp_data.ctxt, sp_data.parent); + if val <= (max >> 1) { + diag.span_suggestion_verbose( + insert_sp, + "change the final digit group to a signed type", + "i", + Applicability::MaybeIncorrect, + ); + } + diag.span_suggestion_verbose( + insert_sp, + "change the final digit group to an unsigned type", + "u", + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + num.trim_sp_to_digits(&sp_data), + "or adjust the digit groupings", + num.reformat_digits(), + Applicability::MaybeIncorrect, + ); + }, + ); + } + true + } else { + false + } +} + +/// Returns `true` if the lint is emitted. +pub(super) fn check_float(cx: &EarlyContext<'_>, num: &FloatStr<'_>, sp: Span) -> bool { + if let Some(exp) = num.exp_digits_str() + && let [.., b'_', b'3', b'2'] | [.., b'_', b'6', b'4'] = exp.as_bytes() + { + let sp_data = sp.data(); + if !sp_data.ctxt.in_external_macro(cx.sess().source_map()) && sp_data.check_text(cx, |src| num.text == src) { + span_lint_and_then( + cx, + MISTYPED_LITERAL_SUFFIXES, + sp, + "the final digit group looks like a type suffix", + |diag| { + let insert_pos = BytePos(sp_data.hi.0 - 2); + diag.span_suggestion_verbose( + Span::new(insert_pos, insert_pos, sp_data.ctxt, sp_data.parent), + "change the final digit group to a float type", + "f", + Applicability::MaybeIncorrect, + ); + diag.span_suggestion_verbose( + num.trim_sp_to_exp_digits(&sp_data), + "or remove the digit groups from the exponent", + exp.replace('_', ""), + Applicability::MaybeIncorrect, + ); + }, + ); + } + true + } else { + false + } +} diff --git a/clippy_lints/src/literals/mod.rs b/clippy_lints/src/literals/mod.rs new file mode 100644 index 000000000000..1a2e179db987 --- /dev/null +++ b/clippy_lints/src/literals/mod.rs @@ -0,0 +1,191 @@ +mod decimal_literal_representation; +mod digit_grouping; +mod mistyped_literal_suffix; + +use clippy_config::Conf; +use clippy_utils::numeric_literal::{FloatStr, IntStr, Radix}; +use rustc_ast::{ast, token}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Warns if there is a better representation for a numeric literal. + /// + /// ### Why restrict this? + /// Especially for big powers of 2, a hexadecimal representation is usually more + /// readable than a decimal representation. + /// + /// ### Example + /// ```text + /// `255` => `0xFF` + /// `65_535` => `0xFFFF` + /// `4_042_322_160` => `0xF0F0_F0F0` + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DECIMAL_LITERAL_REPRESENTATION, + restriction, + "using decimal representation when hexadecimal would be better" +} + +declare_clippy_lint! { + /// ### What it does + /// Warns if an integral or floating-point constant is + /// grouped inconsistently with underscores. + /// + /// ### Why is this bad? + /// Readers may incorrectly interpret inconsistently + /// grouped digits. + /// + /// ### Example + /// ```no_run + /// # let _: u64 = + /// 618_64_9189_73_511 + /// # ; + /// ``` + /// + /// Use instead: + /// ```no_run + /// # let _: u64 = + /// 61_864_918_973_511 + /// # ; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub INCONSISTENT_DIGIT_GROUPING, + style, + "integer literals with digits grouped inconsistently" +} + +declare_clippy_lint! { + /// ### What it does + /// Warns if the digits of an integral or floating-point + /// constant are grouped into groups that + /// are too large. + /// + /// ### Why is this bad? + /// Negatively impacts readability. + /// + /// ### Example + /// ```no_run + /// let x: u64 = 6186491_8973511; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub LARGE_DIGIT_GROUPS, + pedantic, + "grouping digits into groups that are too large" +} + +declare_clippy_lint! { + /// ### What it does + /// Warns for mistyped suffix in literals + /// + /// ### Why is this bad? + /// This is most probably a typo + /// + /// ### Known problems + /// - Does not match on integers too large to fit in the corresponding unsigned type + /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers + /// + /// ### Example + /// ```ignore + /// `2_32` => `2_i32` + /// `250_8 => `250_u8` + /// ``` + #[clippy::version = "1.30.0"] + pub MISTYPED_LITERAL_SUFFIXES, + correctness, + "mistyped literal suffix" +} + +declare_clippy_lint! { + /// ### What it does + /// Warns if a long integral or floating-point constant does + /// not contain underscores. + /// + /// ### Why is this bad? + /// Reading long numbers is difficult without separators. + /// + /// ### Example + /// ```no_run + /// # let _: u64 = + /// 61864918973511 + /// # ; + /// ``` + /// + /// Use instead: + /// ```no_run + /// # let _: u64 = + /// 61_864_918_973_511 + /// # ; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub UNREADABLE_LITERAL, + pedantic, + "long literal without underscores" +} + +declare_clippy_lint! { + /// ### What it does + /// Warns if hexadecimal or binary literals are not grouped + /// by nibble or byte. + /// + /// ### Why is this bad? + /// Negatively impacts readability. + /// + /// ### Example + /// ```no_run + /// let x: u32 = 0xFFF_FFF; + /// let y: u8 = 0b01_011_101; + /// ``` + #[clippy::version = "1.49.0"] + pub UNUSUAL_BYTE_GROUPINGS, + style, + "binary or hex literals that aren't grouped by four" +} + +impl_lint_pass!(EarlyLiterals => [ + DECIMAL_LITERAL_REPRESENTATION, + INCONSISTENT_DIGIT_GROUPING, + LARGE_DIGIT_GROUPS, + MISTYPED_LITERAL_SUFFIXES, + UNREADABLE_LITERAL, + UNUSUAL_BYTE_GROUPINGS, +]); + +pub struct EarlyLiterals { + literal_representation_threshold: u64, + unreadable_literal_lint_fractions: bool, +} +impl EarlyLiterals { + pub fn new(conf: &'static Conf) -> Self { + Self { + literal_representation_threshold: conf.literal_representation_threshold, + unreadable_literal_lint_fractions: conf.unreadable_literal_lint_fractions, + } + } +} + +impl EarlyLintPass for EarlyLiterals { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::Lit(ref lit) = e.kind { + match lit.kind { + token::LitKind::Integer + if let Some(num) = IntStr::from_token_lit(lit) + && (num.suffix.is_some() + || num.radix == Radix::Bin + || !mistyped_literal_suffix::check_int(cx, num, e.span)) => + { + decimal_literal_representation::check(cx, self.literal_representation_threshold, num, e.span); + digit_grouping::check_int(cx, num, e.span); + }, + token::LitKind::Float + if let Some(num) = FloatStr::from_token_lit(lit) + && (num.suffix.is_some() || !mistyped_literal_suffix::check_float(cx, &num, e.span)) => + { + digit_grouping::check_float(cx, &num, self.unreadable_literal_lint_fractions, e.span); + }, + _ => {}, + } + } + } +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 93bb20aac309..a518219daaec 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,7 +1,9 @@ #![feature(box_patterns)] #![feature(macro_metavar_expr)] +#![feature(macro_metavar_expr_concat)] #![feature(rustc_private)] #![feature(unwrap_infallible)] +#![feature(vec_push_within_capacity)] #![recursion_limit = "512"] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] #![warn( diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index b5fffab13b1d..aa18c39f42fc 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,17 +1,22 @@ -use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; -use std::iter; +use core::iter; +use core::num::NonZero; +use rustc_ast::ast::{FloatTy, LitFloatType, LitIntType, LitKind}; +use rustc_ast::token; +use rustc_span::{BytePos, Span, SpanData, Symbol}; + +use crate::sym; /// Represents the base of a numeric literal, used for parsing and formatting. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Radix { + /// A decimal literal (e.g., `123`) + Dec, /// A binary literal (e.g., `0b1010`) - Binary, + Bin, /// An octal literal (e.g., `0o670`) - Octal, - /// A decimal literal (e.g., `123`) - Decimal, + Oct, /// A hexadecimal literal (e.g., `0xFF`) - Hexadecimal, + Hex, } impl Radix { @@ -19,8 +24,17 @@ impl Radix { #[must_use] fn suggest_grouping(self) -> usize { match self { - Self::Binary | Self::Hexadecimal => 4, - Self::Octal | Self::Decimal => 3, + Self::Bin | Self::Hex => 4, + Self::Oct | Self::Dec => 3, + } + } + + pub fn prefix(self) -> &'static str { + match self { + Self::Dec => "", + Self::Bin => "0b", + Self::Oct => "0o", + Self::Hex => "0x", } } } @@ -75,17 +89,17 @@ impl<'a> NumericLiteral<'a> { let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. let radix = if unsigned_lit.starts_with("0x") { - Radix::Hexadecimal + Radix::Hex } else if unsigned_lit.starts_with("0b") { - Radix::Binary + Radix::Bin } else if unsigned_lit.starts_with("0o") { - Radix::Octal + Radix::Oct } else { - Radix::Decimal + Radix::Dec }; // Grab part of the literal after prefix, if present. - let (prefix, mut sans_prefix) = if radix == Radix::Decimal { + let (prefix, mut sans_prefix) = if radix == Radix::Dec { (None, lit) } else { let (p, s) = lit.split_at(2); @@ -111,7 +125,7 @@ impl<'a> NumericLiteral<'a> { /// Checks if the literal's radix is `Radix::Decimal` pub fn is_decimal(&self) -> bool { - self.radix == Radix::Decimal + self.radix == Radix::Dec } fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { @@ -155,13 +169,7 @@ impl<'a> NumericLiteral<'a> { let group_size = self.radix.suggest_grouping(); - Self::group_digits( - &mut output, - self.integer, - group_size, - true, - self.radix == Radix::Hexadecimal, - ); + Self::group_digits(&mut output, self.integer, group_size, true, self.radix == Radix::Hex); if let Some(fraction) = self.fraction { output.push('.'); @@ -255,3 +263,372 @@ fn lit_suffix_length(lit_kind: &LitKind) -> Option { suffix.map(str::len) } + +#[derive(Clone, Copy)] +pub enum NumTy { + U8, + U16, + U32, + U64, + U128, + Usize, + I8, + I16, + I32, + I64, + I128, + Isize, + F16, + F32, + F64, + F128, +} +impl NumTy { + pub fn from_sym(s: Symbol) -> Option { + match s { + sym::u8 => Some(Self::U8), + sym::u16 => Some(Self::U16), + sym::u32 => Some(Self::U32), + sym::u64 => Some(Self::U64), + sym::u128 => Some(Self::U128), + sym::usize => Some(Self::Usize), + sym::i8 => Some(Self::I8), + sym::i16 => Some(Self::I16), + sym::i32 => Some(Self::I32), + sym::i64 => Some(Self::I64), + sym::i128 => Some(Self::I128), + sym::isize => Some(Self::Isize), + sym::f16 => Some(Self::F16), + sym::f32 => Some(Self::F32), + sym::f64 => Some(Self::F64), + sym::f128 => Some(Self::F128), + _ => None, + } + } + + pub fn is_float(self) -> bool { + matches!(self, Self::F16 | Self::F32 | Self::F64 | Self::F128) + } + + pub fn name_str(self) -> &'static str { + match self { + Self::U8 => "u8", + Self::U16 => "u16", + Self::U32 => "u32", + Self::U64 => "u64", + Self::U128 => "u128", + Self::Usize => "usize", + Self::I8 => "i8", + Self::I16 => "i16", + Self::I32 => "i32", + Self::I64 => "i64", + Self::I128 => "i128", + Self::Isize => "isize", + Self::F16 => "f16", + Self::F32 => "f32", + Self::F64 => "f64", + Self::F128 => "f128", + } + } +} + +pub fn float_ty_from_sym(sym: Symbol) -> Option { + match sym { + sym::f16 => Some(FloatTy::F16), + sym::f32 => Some(FloatTy::F32), + sym::f64 => Some(FloatTy::F64), + sym::f128 => Some(FloatTy::F128), + _ => None, + } +} + +/// The text of a floating point literal. +#[derive(Clone, Copy)] +pub struct FloatStr<'a> { + /// The literal without the suffix separator. + pub text: &'a str, + pub dot: Option>, + pub exp: Option>, + has_exp_sign: bool, + has_suffix_sep: bool, + pub suffix: Option, +} +impl<'a> FloatStr<'a> { + #[expect(clippy::cast_possible_truncation)] + pub fn from_token_lit(lit: &'a token::Lit) -> Option { + let text = lit.symbol.as_str(); + let (mut dot, mut exp) = (0u32, 0u32); + let mut has_exp_sign = false; + let mut iter = text.bytes().enumerate(); + while let Some((i, c)) = iter.next() { + match c { + b'.' => dot = i as u32, + b'e' | b'E' => { + exp = i as u32; + if let Some((_, b'+' | b'-')) = iter.clone().next() { + iter.next(); + has_exp_sign = true; + } + }, + _ => {}, + } + } + let (text, has_suffix_sep, suffix) = match lit.suffix { + Some(suffix) => match float_ty_from_sym(suffix) { + Some(suffix) => match text.strip_suffix('_') { + Some(text) => (text, true, Some(suffix)), + None => (text, false, Some(suffix)), + }, + None => return None, + }, + None => (text, false, None), + }; + Some(Self { + text, + dot: NonZero::new(dot), + exp: NonZero::new(exp), + has_exp_sign, + has_suffix_sep, + suffix, + }) + } + + pub fn eq_str(&self, s: &str) -> bool { + let Some(s) = s.strip_prefix(self.text) else { + return false; + }; + let s = match (self.has_suffix_sep, s.as_bytes()) { + (true, [b'_', s @ ..]) => s, + (true, _) => return false, + (false, s) => s, + }; + s == self.suffix.map_or("", FloatTy::name_str).as_bytes() + } + + pub fn exp_digits_str(&self) -> Option<&str> { + self.exp + .and_then(|pos| self.text.get(pos.get() as usize + 1 + usize::from(self.has_exp_sign)..)) + } + + /// Trims the span to just the digits of the exponent. + /// + /// This assumes the text of the span matches the literal. + pub fn trim_sp_to_exp_digits(&self, sp: &SpanData) -> Span { + Span::new( + BytePos(sp.lo.0 + self.exp.map_or(0, NonZero::get) + 1 + u32::from(self.has_exp_sign)), + BytePos(sp.lo.0 + self.text.len() as u32), + sp.ctxt, + sp.parent, + ) + } + + /// Removes both the suffix the exponent from the span. + /// + /// This assumes the text of the span matches the literal. + pub fn strip_exp_from_sp(&self, sp: &SpanData) -> Span { + Span::new( + sp.lo, + BytePos(match self.exp { + Some(exp) => { + sp.lo.0 + exp.get() + - u32::from(matches!(self.text.as_bytes().get(exp.get() as usize - 1), Some(b'_'))) + }, + None => sp.hi.0 - u32::from(self.has_suffix_sep) - self.suffix.map_or(0, |x| x.name_str().len() as u32), + }), + sp.ctxt, + sp.parent, + ) + } + + /// Gets the text for the whole part and the fractional part of the literal. + pub fn whole_fract_digits_str(&self) -> (&'a str, Option<&'a str>) { + let end = match self.exp { + Some(exp) => { + exp.get() as usize - usize::from(matches!(self.text.as_bytes().get(exp.get() as usize - 1), Some(b'_'))) + }, + None => self.text.len(), + }; + if let Some(dot) = self.dot { + ( + self.text.get(..dot.get() as usize).unwrap_or(self.text), + self.text.get(dot.get() as usize + 1..end), + ) + } else { + (self.text.get(..end).unwrap_or(self.text), None) + } + } +} + +#[derive(Clone, Copy)] +pub struct IntStr<'a> { + /// The literal text without the prefix or suffix separator. + pub text: &'a str, + pub radix: Radix, + pub has_suffix_sep: bool, + pub suffix: Option, +} +impl<'a> IntStr<'a> { + pub fn from_token_lit(lit: &'a token::Lit) -> Option { + let text = lit.symbol.as_str(); + let (radix, text) = match text.as_bytes() { + [b'0', b'x', ..] => (Radix::Hex, &text[2..]), + [b'0', b'o', ..] => (Radix::Oct, &text[2..]), + [b'0', b'b', ..] => (Radix::Bin, &text[2..]), + _ => (Radix::Dec, text), + }; + let (text, has_suffix_sep, suffix) = match lit.suffix { + Some(suffix) => match NumTy::from_sym(suffix) { + Some(suffix) => match text.strip_suffix('_') { + Some(text) => (text, true, Some(suffix)), + None => (text, false, Some(suffix)), + }, + None => return None, + }, + None => (text, false, None), + }; + Some(Self { + text, + radix, + has_suffix_sep, + suffix, + }) + } + + pub fn trim_sp_to_digits(&self, sp: &SpanData) -> Span { + Span::new( + BytePos(sp.lo.0 + self.radix.prefix().len() as u32), + BytePos(sp.hi.0 - u32::from(self.has_suffix_sep) - self.suffix.map_or(0, |s| s.name_str().len() as u32)), + sp.ctxt, + sp.parent, + ) + } + + /// Checks whether this literal's text exactly equals the unparsed text. + pub fn eq_str(&self, s: &str) -> bool { + if let Some(s) = s.strip_prefix(self.radix.prefix()) + && let Some(s) = s.strip_prefix(self.text) + { + let s = match (self.has_suffix_sep, s.as_bytes()) { + (true, [b'_', s @ ..]) => s, + (true, _) => return false, + (false, s) => s, + }; + s == self.suffix.map_or("", NumTy::name_str).as_bytes() + } else { + false + } + } + + /// Reformats the digits of this literal with the default digit grouping. + pub fn reformat_digits(&self) -> String { + // Allocate enough room to hold the formatted literal. + let mut sugg = Vec::with_capacity(self.text.len() + self.text.len() / 3); + let size = match self.radix { + Radix::Bin | Radix::Hex => 4u8, + Radix::Oct | Radix::Dec => 3u8, + }; + reformat_int_rev_into(size, self.text, &mut sugg); + sugg.reverse(); + String::from_utf8(sugg).unwrap_or_default() + } +} + +macro_rules! impl_parse_int_as { + ($($ty:ident)*) => { + impl IntStr<'_> {$( + /// Parses the integer's digits. + pub fn ${concat(parse_as_, $ty)}(&self) -> Option<$ty> { + let mut acc: $ty = 0; + let base_lg2 = match self.radix { + Radix::Dec => { + for c in self.text.bytes() { + let next = acc.checked_mul(10); + let c = match c { + b'_' => continue, + c => c - b'0', + }; + acc = next?.checked_add($ty::from(c))?; + } + return Some(acc); + }, + Radix::Bin => 1, + Radix::Oct => 2, + Radix::Hex => 4, + }; + + let limit = $ty::MAX >> base_lg2; + for c in self.text.bytes() { + if acc > limit { + return None; + } + let c = match c { + b'0'..=b'9' => c - b'0', + b'A'..=b'F' => c - b'A', + b'_' => continue, + c => c - b'a', + }; + acc = (acc << base_lg2) | $ty::from(c); + } + Some(acc) + } + )*} + } +} +impl_parse_int_as!(u64 u128); + +/// Reformats a float literal using the default digit grouping. +pub fn reformat_whole_opt_fract(whole: &str, fract: Option<&str>) -> String { + // Allocate with enough room to hold the formatted literal. + let cap = if let Some(fract) = fract { + let digits = whole.len() + fract.len(); + digits + digits / 3 + 1 + } else { + whole.len() + whole.len() / 3 + }; + let mut sugg = Vec::with_capacity(cap); + reformat_int_rev_into(3, whole, &mut sugg); + sugg.reverse(); + if let Some(fract) = fract { + let _ = sugg.push_within_capacity(b'.'); + reformat_fract_into(fract, &mut sugg); + } + String::from_utf8(sugg).unwrap_or_default() +} + +pub fn reformat_fract(fract: &str) -> String { + // Allocate with enough room to hold the formatted literal. + let mut sugg = Vec::with_capacity(fract.len() + fract.len() / 3); + reformat_fract_into(fract, &mut sugg); + String::from_utf8(sugg).unwrap_or_default() +} + +fn reformat_int_rev_into(mut group_size: u8, num: &str, dst: &mut Vec) { + let mut i = group_size; + group_size -= 1; + for c in num.bytes().rev() { + if c != b'_' { + if let Some(x) = i.checked_sub(1) { + i = x; + } else { + let _ = dst.push_within_capacity(b'_'); + i = group_size; + } + let _ = dst.push_within_capacity(c); + } + } +} + +fn reformat_fract_into(num: &str, dst: &mut Vec) { + let mut i = 3u8; + for c in num.bytes() { + if c != b'_' { + if let Some(x) = i.checked_sub(1) { + i = x; + } else { + let _ = dst.push_within_capacity(b'_'); + i = 2; + } + let _ = dst.push_within_capacity(c); + } + } +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed index 750f3be84c04..d9353501441d 100644 --- a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed @@ -1,6 +1,6 @@ #![warn(clippy::decimal_literal_representation)] fn main() { let _ = 8388608; - let _ = 0x00FF_FFFF; + let _ = 0xff_ffff; //~^ ERROR: integer literal has a better hexadecimal representation } diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr index 861ba9e79a18..ffce1001b28a 100644 --- a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr @@ -2,10 +2,15 @@ error: integer literal has a better hexadecimal representation --> tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs:4:13 | LL | let _ = 16777215; - | ^^^^^^^^ help: consider: `0x00FF_FFFF` + | ^^^^^^^^ | = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::decimal_literal_representation)]` +help: use a hex literal + | +LL - let _ = 16777215; +LL + let _ = 0xff_ffff; + | error: aborting due to 1 previous error diff --git a/tests/ui-toml/lint_decimal_readability/test.fixed b/tests/ui-toml/lint_decimal_readability/test.fixed index 47a61122c9bc..0122d1a6c5bc 100644 --- a/tests/ui-toml/lint_decimal_readability/test.fixed +++ b/tests/ui-toml/lint_decimal_readability/test.fixed @@ -13,9 +13,7 @@ fn main() { let _pass4 = 10000.00001; let _pass5 = 1.123456789e1; - // due to clippy::inconsistent-digit-grouping - let _fail1 = 100_200_300.123_456_789; - //~^ inconsistent_digit_grouping + let _ = 100_200_300.123456789; // fail due to the integer part let _fail2 = 100_200_300.300_200_100; diff --git a/tests/ui-toml/lint_decimal_readability/test.rs b/tests/ui-toml/lint_decimal_readability/test.rs index eca273274d0d..bc5003ec5ab9 100644 --- a/tests/ui-toml/lint_decimal_readability/test.rs +++ b/tests/ui-toml/lint_decimal_readability/test.rs @@ -13,9 +13,7 @@ fn main() { let _pass4 = 10000.00001; let _pass5 = 1.123456789e1; - // due to clippy::inconsistent-digit-grouping - let _fail1 = 100_200_300.123456789; - //~^ inconsistent_digit_grouping + let _ = 100_200_300.123456789; // fail due to the integer part let _fail2 = 100200300.300200100; diff --git a/tests/ui-toml/lint_decimal_readability/test.stderr b/tests/ui-toml/lint_decimal_readability/test.stderr index e010e1e4d2fd..c171d591d9f1 100644 --- a/tests/ui-toml/lint_decimal_readability/test.stderr +++ b/tests/ui-toml/lint_decimal_readability/test.stderr @@ -1,20 +1,16 @@ -error: digits grouped inconsistently by underscores - --> tests/ui-toml/lint_decimal_readability/test.rs:17:18 - | -LL | let _fail1 = 100_200_300.123456789; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789` - | - = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` - error: long literal lacking separators - --> tests/ui-toml/lint_decimal_readability/test.rs:21:18 + --> tests/ui-toml/lint_decimal_readability/test.rs:19:18 | LL | let _fail2 = 100200300.300200100; - | ^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.300_200_100` + | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unreadable-literal` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unreadable_literal)]` +help: separate the digits + | +LL - let _fail2 = 100200300.300200100; +LL + let _fail2 = 100_200_300.300_200_100; + | -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/decimal_literal_representation.fixed b/tests/ui/decimal_literal_representation.fixed index e0477578488c..9bc4e4585a1d 100644 --- a/tests/ui/decimal_literal_representation.fixed +++ b/tests/ui/decimal_literal_representation.fixed @@ -15,18 +15,18 @@ fn main() { let bad = ( // Hex: 0x8005, // 0x8005 //~^ decimal_literal_representation - 0xFF00, // 0xFF00 + 0xff00, // 0xFF00 //~^ decimal_literal_representation - 0x7F0F_F00F, // 0x7F0F_F00F + 0x7f0f_f00f, // 0x7F0F_F00F //~^ decimal_literal_representation - 0x7FFF_FFFF, // 0x7FFF_FFFF + 0x7fff_ffff, // 0x7FFF_FFFF //~^ decimal_literal_representation #[allow(overflowing_literals)] - 0xF0F0_F0F0, // 0xF0F0_F0F0 + 0xf0f0_f0f0, // 0xF0F0_F0F0 //~^ decimal_literal_representation - 0x8005_usize, // 0x8005_usize + 0x8005usize, // 0x8005_usize //~^ decimal_literal_representation - 0x7F0F_F00F_isize, // 0x7F0F_F00F_isize + 0x7f0f_f00fisize, // 0x7F0F_F00F_isize //~^ decimal_literal_representation ); } diff --git a/tests/ui/decimal_literal_representation.stderr b/tests/ui/decimal_literal_representation.stderr index 0196350ce402..29e6a0d7412e 100644 --- a/tests/ui/decimal_literal_representation.stderr +++ b/tests/ui/decimal_literal_representation.stderr @@ -2,46 +2,87 @@ error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:16:9 | LL | 32_773, // 0x8005 - | ^^^^^^ help: consider: `0x8005` + | ^^^^^^ | = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::decimal_literal_representation)]` +help: use a hex literal + | +LL - 32_773, // 0x8005 +LL + 0x8005, // 0x8005 + | error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:18:9 | LL | 65_280, // 0xFF00 - | ^^^^^^ help: consider: `0xFF00` + | ^^^^^^ + | +help: use a hex literal + | +LL - 65_280, // 0xFF00 +LL + 0xff00, // 0xFF00 + | error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:20:9 | LL | 2_131_750_927, // 0x7F0F_F00F - | ^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F` + | ^^^^^^^^^^^^^ + | +help: use a hex literal + | +LL - 2_131_750_927, // 0x7F0F_F00F +LL + 0x7f0f_f00f, // 0x7F0F_F00F + | error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:22:9 | LL | 2_147_483_647, // 0x7FFF_FFFF - | ^^^^^^^^^^^^^ help: consider: `0x7FFF_FFFF` + | ^^^^^^^^^^^^^ + | +help: use a hex literal + | +LL - 2_147_483_647, // 0x7FFF_FFFF +LL + 0x7fff_ffff, // 0x7FFF_FFFF + | error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:25:9 | LL | 4_042_322_160, // 0xF0F0_F0F0 - | ^^^^^^^^^^^^^ help: consider: `0xF0F0_F0F0` + | ^^^^^^^^^^^^^ + | +help: use a hex literal + | +LL - 4_042_322_160, // 0xF0F0_F0F0 +LL + 0xf0f0_f0f0, // 0xF0F0_F0F0 + | error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:27:9 | LL | 32_773usize, // 0x8005_usize - | ^^^^^^^^^^^ help: consider: `0x8005_usize` + | ^^^^^^^^^^^ + | +help: use a hex literal + | +LL - 32_773usize, // 0x8005_usize +LL + 0x8005usize, // 0x8005_usize + | error: integer literal has a better hexadecimal representation --> tests/ui/decimal_literal_representation.rs:29:9 | LL | 2_131_750_927isize, // 0x7F0F_F00F_isize - | ^^^^^^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F_isize` + | ^^^^^^^^^^^^^^^^^^ + | +help: use a hex literal + | +LL - 2_131_750_927isize, // 0x7F0F_F00F_isize +LL + 0x7f0f_f00fisize, // 0x7F0F_F00F_isize + | error: aborting due to 7 previous errors diff --git a/tests/ui/inconsistent_digit_grouping.fixed b/tests/ui/inconsistent_digit_grouping.fixed index 23d2d7cc0aa4..8c44f3eea2ae 100644 --- a/tests/ui/inconsistent_digit_grouping.fixed +++ b/tests/ui/inconsistent_digit_grouping.fixed @@ -2,17 +2,6 @@ #[deny(clippy::unreadable_literal)] #[allow(unused_variables, clippy::excessive_precision)] fn main() { - macro_rules! mac1 { - () => { - 1_23_456 - }; - } - macro_rules! mac2 { - () => { - 1_234.5678_f32 - }; - } - let good = ( 123, 1_234, @@ -22,21 +11,20 @@ fn main() { 1_234.123_4_f32, 1.123_456_7_f32, ); - let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32); + let bad = (123_456, 12_345_678, 1_234_567, 1.234_567_8_f32); //~^ inconsistent_digit_grouping //~| inconsistent_digit_grouping //~| inconsistent_digit_grouping //~| inconsistent_digit_grouping - //~| inconsistent_digit_grouping // Test padding - let _ = 0x0010_0000; + let _ = 0x10_0000; //~^ unreadable_literal - let _ = 0x0100_0000; + let _ = 0x100_0000; //~^ unreadable_literal let _ = 0x1000_0000; //~^ unreadable_literal - let _ = 0x0001_0000_0000_u64; + let _ = 0x1_0000_0000_u64; //~^ unreadable_literal // Test suggestion when fraction has no digits @@ -46,9 +34,21 @@ fn main() { // Test UUID formatted literal let _: u128 = 0x12345678_1234_1234_1234_123456789012; - // Ignore literals in macros - let _ = mac1!(); - let _ = mac2!(); + { + macro_rules! mac1 { + () => { + 123_456 //~ inconsistent_digit_grouping + }; + } + macro_rules! mac2 { + () => { + 1_234.5678_f32 + }; + } + + let _ = mac1!(); + let _ = mac2!(); + } // Issue #6096 // Allow separating exponent with '_' diff --git a/tests/ui/inconsistent_digit_grouping.rs b/tests/ui/inconsistent_digit_grouping.rs index a172c5450ef8..ec5f14e9a496 100644 --- a/tests/ui/inconsistent_digit_grouping.rs +++ b/tests/ui/inconsistent_digit_grouping.rs @@ -2,17 +2,6 @@ #[deny(clippy::unreadable_literal)] #[allow(unused_variables, clippy::excessive_precision)] fn main() { - macro_rules! mac1 { - () => { - 1_23_456 - }; - } - macro_rules! mac2 { - () => { - 1_234.5678_f32 - }; - } - let good = ( 123, 1_234, @@ -22,12 +11,11 @@ fn main() { 1_234.123_4_f32, 1.123_456_7_f32, ); - let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); //~^ inconsistent_digit_grouping //~| inconsistent_digit_grouping //~| inconsistent_digit_grouping //~| inconsistent_digit_grouping - //~| inconsistent_digit_grouping // Test padding let _ = 0x100000; @@ -46,9 +34,21 @@ fn main() { // Test UUID formatted literal let _: u128 = 0x12345678_1234_1234_1234_123456789012; - // Ignore literals in macros - let _ = mac1!(); - let _ = mac2!(); + { + macro_rules! mac1 { + () => { + 1_23_456 //~ inconsistent_digit_grouping + }; + } + macro_rules! mac2 { + () => { + 1_234.5678_f32 + }; + } + + let _ = mac1!(); + let _ = mac2!(); + } // Issue #6096 // Allow separating exponent with '_' diff --git a/tests/ui/inconsistent_digit_grouping.stderr b/tests/ui/inconsistent_digit_grouping.stderr index be54de5e7c16..77562266e1a9 100644 --- a/tests/ui/inconsistent_digit_grouping.stderr +++ b/tests/ui/inconsistent_digit_grouping.stderr @@ -1,71 +1,128 @@ -error: digits grouped inconsistently by underscores - --> tests/ui/inconsistent_digit_grouping.rs:25:16 +error: digits in groups of unequal sizes + --> tests/ui/inconsistent_digit_grouping.rs:14:16 | -LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); - | ^^^^^^^^ help: consider: `123_456` +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); + | ^^^^^^^^ | = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` - -error: digits grouped inconsistently by underscores - --> tests/ui/inconsistent_digit_grouping.rs:25:26 +help: group the digits with a consistent size + | +LL - let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); +LL + let bad = (123_456, 1_234_5678, 1234_567, 1.234_5678_f32); | -LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); - | ^^^^^^^^^^ help: consider: `12_345_678` -error: digits grouped inconsistently by underscores - --> tests/ui/inconsistent_digit_grouping.rs:25:38 +error: digits in groups of unequal sizes + --> tests/ui/inconsistent_digit_grouping.rs:14:26 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); + | ^^^^^^^^^^ + | +help: group the digits with a consistent size + | +LL - let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); +LL + let bad = (1_23_456, 12_345_678, 1234_567, 1.234_5678_f32); | -LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); - | ^^^^^^^^ help: consider: `1_234_567` -error: digits grouped inconsistently by underscores - --> tests/ui/inconsistent_digit_grouping.rs:25:48 +error: digits in groups of unequal sizes + --> tests/ui/inconsistent_digit_grouping.rs:14:38 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); + | ^^^^^^^^ + | +help: group the digits with a consistent size | -LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); - | ^^^^^^^^^^^^^^ help: consider: `1_234.567_8_f32` +LL | let bad = (1_23_456, 1_234_5678, 1_234_567, 1.234_5678_f32); + | + -error: digits grouped inconsistently by underscores - --> tests/ui/inconsistent_digit_grouping.rs:25:64 +error: digits in groups of unequal sizes + --> tests/ui/inconsistent_digit_grouping.rs:14:48 | -LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); - | ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32` +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_5678_f32); + | ^^^^^^^^^^^^^^ + | +help: group the digits with a consistent size + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1.234_567_8_f32); + | + error: long literal lacking separators - --> tests/ui/inconsistent_digit_grouping.rs:33:13 + --> tests/ui/inconsistent_digit_grouping.rs:21:13 | LL | let _ = 0x100000; - | ^^^^^^^^ help: consider: `0x0010_0000` + | ^^^^^^^^ | note: the lint level is defined here --> tests/ui/inconsistent_digit_grouping.rs:2:8 | LL | #[deny(clippy::unreadable_literal)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: separate the digits + | +LL | let _ = 0x10_0000; + | + error: long literal lacking separators - --> tests/ui/inconsistent_digit_grouping.rs:35:13 + --> tests/ui/inconsistent_digit_grouping.rs:23:13 | LL | let _ = 0x1000000; - | ^^^^^^^^^ help: consider: `0x0100_0000` + | ^^^^^^^^^ + | +help: separate the digits + | +LL | let _ = 0x100_0000; + | + error: long literal lacking separators - --> tests/ui/inconsistent_digit_grouping.rs:37:13 + --> tests/ui/inconsistent_digit_grouping.rs:25:13 | LL | let _ = 0x10000000; - | ^^^^^^^^^^ help: consider: `0x1000_0000` + | ^^^^^^^^^^ + | +help: separate the digits + | +LL | let _ = 0x1000_0000; + | + error: long literal lacking separators - --> tests/ui/inconsistent_digit_grouping.rs:39:13 + --> tests/ui/inconsistent_digit_grouping.rs:27:13 | LL | let _ = 0x100000000_u64; - | ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64` + | ^^^^^^^^^^^^^^^ + | +help: separate the digits + | +LL - let _ = 0x100000000_u64; +LL + let _ = 0x1_0000_0000_u64; + | -error: digits grouped inconsistently by underscores - --> tests/ui/inconsistent_digit_grouping.rs:43:18 +error: digits in groups of unequal sizes + --> tests/ui/inconsistent_digit_grouping.rs:31:18 | LL | let _: f32 = 1_23_456.; - | ^^^^^^^^^ help: consider: `123_456.` + | ^^^^^^^^^ + | +help: group the digits with a consistent size + | +LL - let _: f32 = 1_23_456.; +LL + let _: f32 = 123_456.; + | + +error: digits in groups of unequal sizes + --> tests/ui/inconsistent_digit_grouping.rs:40:17 + | +LL | 1_23_456 + | ^^^^^^^^ +... +LL | let _ = mac1!(); + | ------- in this macro invocation + | + = note: this error originates in the macro `mac1` (in Nightly builds, run with -Z macro-backtrace for more info) +help: group the digits with a consistent size + | +LL - 1_23_456 +LL + 123_456 + | error: aborting due to 10 previous errors diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index 281a6267c8d6..02be75d97935 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -1,38 +1,66 @@ -error: digits of hex, binary or octal literal not in groups of equal size +error: digits in groups of unequal sizes --> tests/ui/large_digit_groups.rs:22:9 | LL | 0xd_e_adbee_f_usize, - | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` + | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unusual_byte_groupings)]` +help: group the digits with a consistent size + | +LL - 0xd_e_adbee_f_usize, +LL + 0xdead_beef_usize, + | -error: digit groups should be smaller +error: digits separated into large groups --> tests/ui/large_digit_groups.rs:24:9 | LL | 1_23456_f32, - | ^^^^^^^^^^^ help: consider: `123_456_f32` + | ^^^^^^^^^^^ | = note: `-D clippy::large-digit-groups` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_digit_groups)]` +help: split the digits into smaller groups + | +LL - 1_23456_f32, +LL + 123_456_f32, + | -error: digit groups should be smaller +error: digits separated into large groups --> tests/ui/large_digit_groups.rs:26:9 | LL | 1_23456.12_f32, - | ^^^^^^^^^^^^^^ help: consider: `123_456.12_f32` + | ^^^^^^^^^^^^^^ + | +help: split the digits into smaller groups + | +LL - 1_23456.12_f32, +LL + 123_456.12_f32, + | -error: digit groups should be smaller +error: digits separated into large groups --> tests/ui/large_digit_groups.rs:28:9 | LL | 1_23456.12345_f64, - | ^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_45_f64` + | ^^^^^^^^^^^^^^^^^ + | +help: split the digits into smaller groups + | +LL - 1_23456.12345_f64, +LL + 123_456.123_45_f64, + | -error: digit groups should be smaller +error: digits separated into large groups --> tests/ui/large_digit_groups.rs:30:9 | LL | 1_23456.12345_6_f64, - | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` + | ^^^^^^^^^^^^^^^^^^^ + | +help: split the digits into smaller groups + | +LL - 1_23456.12345_6_f64, +LL + 123_456.123_456_f64, + | error: aborting due to 5 previous errors diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 29b0c97a41cc..c3fb3fd42a22 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -127,35 +127,57 @@ error: integer type suffix should not be separated by an underscore LL | let ok17 = 0x123_4567_8901_usize; | ^^^^^^^^^^^^^^^^^^^^^ help: remove the underscore: `0x123_4567_8901usize` -error: digits grouped inconsistently by underscores +error: digits in groups of unequal sizes --> tests/ui/literals.rs:62:18 | LL | let fail19 = 12_3456_21; - | ^^^^^^^^^^ help: consider: `12_345_621` + | ^^^^^^^^^^ | = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` +help: group the digits with a consistent size + | +LL - let fail19 = 12_3456_21; +LL + let fail19 = 12_345_621; + | -error: digits grouped inconsistently by underscores +error: digits in groups of unequal sizes --> tests/ui/literals.rs:65:18 | LL | let fail22 = 3__4___23; - | ^^^^^^^^^ help: consider: `3_423` + | ^^^^^^^^^ + | +help: group the digits with a consistent size + | +LL - let fail22 = 3__4___23; +LL + let fail22 = 3_423; + | -error: digits grouped inconsistently by underscores +error: digits in groups of unequal sizes --> tests/ui/literals.rs:68:18 | LL | let fail23 = 3__16___23; - | ^^^^^^^^^^ help: consider: `31_623` + | ^^^^^^^^^^ + | +help: group the digits with a consistent size + | +LL - let fail23 = 3__16___23; +LL + let fail23 = 31_623; + | -error: digits of hex, binary or octal literal not in groups of equal size +error: digits in groups of unequal sizes --> tests/ui/literals.rs:71:18 | LL | let fail24 = 0xAB_ABC_AB; - | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` + | ^^^^^^^^^^^ | = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unusual_byte_groupings)]` +help: group the digits with a consistent size + | +LL - let fail24 = 0xAB_ABC_AB; +LL + let fail24 = 0xABA_BCAB; + | error: this is a decimal constant --> tests/ui/literals.rs:81:13 diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.1.fixed similarity index 88% rename from tests/ui/mistyped_literal_suffix.fixed rename to tests/ui/mistyped_literal_suffix.1.fixed index 3a7d714375a7..12d096215519 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.1.fixed @@ -23,10 +23,10 @@ fn main() { //~^^ mistyped_literal_suffixes let ok18 = 23_128; - let fail20 = 2_i8; // + let fail20 = 2__i8; // // //~^^ mistyped_literal_suffixes - let fail21 = 4_i16; // + let fail21 = 4___i16; // // //~^^ mistyped_literal_suffixes @@ -37,7 +37,7 @@ fn main() { //~^ mistyped_literal_suffixes let fail27 = 243E17_f32; //~^ mistyped_literal_suffixes - let fail28 = 241_251_235E723_f64; + let fail28 = 241251235E723_f64; //~^ mistyped_literal_suffixes let ok29 = 42279.911_32; @@ -51,10 +51,10 @@ fn main() { let ok32 = 360_8; // doesn't fit in either, should be ignored let fail33 = 0x1234_i16; //~^ mistyped_literal_suffixes - let fail34 = 0xABCD_u16; + let fail34 = 0xABCD_i16; //~^ mistyped_literal_suffixes let ok35 = 0x12345_16; - let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_i64; // u64 // //~^^ mistyped_literal_suffixes @@ -62,7 +62,7 @@ fn main() { let ok37 = 123_32.123; let ok38 = 124_64.0; - let _ = 1.123_45E1_f32; + let _ = 1.12345E1_f32; //~^ mistyped_literal_suffixes let _ = with_span!(1 2_u32); diff --git a/tests/ui/mistyped_literal_suffix.2.fixed b/tests/ui/mistyped_literal_suffix.2.fixed new file mode 100644 index 000000000000..5168c14a0834 --- /dev/null +++ b/tests/ui/mistyped_literal_suffix.2.fixed @@ -0,0 +1,69 @@ +//@aux-build: proc_macros.rs + +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( + overflowing_literals, + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings +)] + +extern crate proc_macros; +use proc_macros::with_span; + +fn main() { + let fail14 = 2_u32; + //~^ mistyped_literal_suffixes + let fail15 = 4_u64; + //~^ mistyped_literal_suffixes + let fail16 = 7_u8; // + // + //~^^ mistyped_literal_suffixes + let fail17 = 23_u16; // + // + //~^^ mistyped_literal_suffixes + let ok18 = 23_128; + + let fail20 = 2__u8; // + // + //~^^ mistyped_literal_suffixes + let fail21 = 4___u16; // + // + //~^^ mistyped_literal_suffixes + + let ok24 = 12.34_64; + let fail25 = 1E232; + //~^ mistyped_literal_suffixes + let fail26 = 43E764; + //~^ mistyped_literal_suffixes + let fail27 = 243E1732; + //~^ mistyped_literal_suffixes + let fail28 = 241251235E72364; + //~^ mistyped_literal_suffixes + let ok29 = 42279.911_32; + + // testing that the suggestion actually fits in its type + let fail30 = 127_u8; // should be i8 + // + //~^^ mistyped_literal_suffixes + let fail31 = 2_408; // should be u8 + // + //~^^ mistyped_literal_suffixes + let ok32 = 360_8; // doesn't fit in either, should be ignored + let fail33 = 0x1234_u16; + //~^ mistyped_literal_suffixes + let fail34 = 0xABCD_u16; + //~^ mistyped_literal_suffixes + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + // + //~^^ mistyped_literal_suffixes + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + + let _ = 1.12345E132; + //~^ mistyped_literal_suffixes + + let _ = with_span!(1 2_u32); +} diff --git a/tests/ui/mistyped_literal_suffix.3.fixed b/tests/ui/mistyped_literal_suffix.3.fixed new file mode 100644 index 000000000000..66026522b2b1 --- /dev/null +++ b/tests/ui/mistyped_literal_suffix.3.fixed @@ -0,0 +1,69 @@ +//@aux-build: proc_macros.rs + +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( + overflowing_literals, + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings +)] + +extern crate proc_macros; +use proc_macros::with_span; + +fn main() { + let fail14 = 232; + //~^ mistyped_literal_suffixes + let fail15 = 464; + //~^ mistyped_literal_suffixes + let fail16 = 78; // + // + //~^^ mistyped_literal_suffixes + let fail17 = 2_316; // + // + //~^^ mistyped_literal_suffixes + let ok18 = 23_128; + + let fail20 = 28; // + // + //~^^ mistyped_literal_suffixes + let fail21 = 416; // + // + //~^^ mistyped_literal_suffixes + + let ok24 = 12.34_64; + let fail25 = 1E2_f32; + //~^ mistyped_literal_suffixes + let fail26 = 43E7_f64; + //~^ mistyped_literal_suffixes + let fail27 = 243E17_f32; + //~^ mistyped_literal_suffixes + let fail28 = 241251235E723_f64; + //~^ mistyped_literal_suffixes + let ok29 = 42279.911_32; + + // testing that the suggestion actually fits in its type + let fail30 = 1_278; // should be i8 + // + //~^^ mistyped_literal_suffixes + let fail31 = 240_u8; // should be u8 + // + //~^^ mistyped_literal_suffixes + let ok32 = 360_8; // doesn't fit in either, should be ignored + let fail33 = 0x12_3416; + //~^ mistyped_literal_suffixes + let fail34 = 0xAB_CD16; + //~^ mistyped_literal_suffixes + let ok35 = 0x12345_16; + let fail36 = 0xFF_FFFF_FFFF_FFFF_FF64; // u64 + // + //~^^ mistyped_literal_suffixes + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + + let _ = 1.12345E1_f32; + //~^ mistyped_literal_suffixes + + let _ = with_span!(1 2_u32); +} diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index a986bc0e69ec..3a818ada4fd6 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,101 +1,300 @@ -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:14:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:14:19 | LL | let fail14 = 2_32; - | ^^^^ help: did you mean to write: `2_i32` + | ^^^ | = note: `-D clippy::mistyped-literal-suffixes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mistyped_literal_suffixes)]` +help: change the final digit group to a signed type + | +LL | let fail14 = 2_i32; + | + +help: change the final digit group to an unsigned type + | +LL | let fail14 = 2_u32; + | + +help: or adjust the digit groupings + | +LL - let fail14 = 2_32; +LL + let fail14 = 232; + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:16:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:16:19 | LL | let fail15 = 4_64; - | ^^^^ help: did you mean to write: `4_i64` + | ^^^ + | +help: change the final digit group to a signed type + | +LL | let fail15 = 4_i64; + | + +help: change the final digit group to an unsigned type + | +LL | let fail15 = 4_u64; + | + +help: or adjust the digit groupings + | +LL - let fail15 = 4_64; +LL + let fail15 = 464; + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:18:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:18:19 | LL | let fail16 = 7_8; // - | ^^^ help: did you mean to write: `7_i8` + | ^^ + | +help: change the final digit group to a signed type + | +LL | let fail16 = 7_i8; // + | + +help: change the final digit group to an unsigned type + | +LL | let fail16 = 7_u8; // + | + +help: or adjust the digit groupings + | +LL - let fail16 = 7_8; // +LL + let fail16 = 78; // + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:21:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:21:20 | LL | let fail17 = 23_16; // - | ^^^^^ help: did you mean to write: `23_i16` + | ^^^ + | +help: change the final digit group to a signed type + | +LL | let fail17 = 23_i16; // + | + +help: change the final digit group to an unsigned type + | +LL | let fail17 = 23_u16; // + | + +help: or adjust the digit groupings + | +LL - let fail17 = 23_16; // +LL + let fail17 = 2_316; // + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:26:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:26:20 | LL | let fail20 = 2__8; // - | ^^^^ help: did you mean to write: `2_i8` + | ^^ + | +help: change the final digit group to a signed type + | +LL | let fail20 = 2__i8; // + | + +help: change the final digit group to an unsigned type + | +LL | let fail20 = 2__u8; // + | + +help: or adjust the digit groupings + | +LL - let fail20 = 2__8; // +LL + let fail20 = 28; // + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:29:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:29:21 | LL | let fail21 = 4___16; // - | ^^^^^^ help: did you mean to write: `4_i16` + | ^^^ + | +help: change the final digit group to a signed type + | +LL | let fail21 = 4___i16; // + | + +help: change the final digit group to an unsigned type + | +LL | let fail21 = 4___u16; // + | + +help: or adjust the digit groupings + | +LL - let fail21 = 4___16; // +LL + let fail21 = 416; // + | -error: mistyped literal suffix +error: the final digit group looks like a type suffix --> tests/ui/mistyped_literal_suffix.rs:34:18 | LL | let fail25 = 1E2_32; - | ^^^^^^ help: did you mean to write: `1E2_f32` + | ^^^^^^ + | +help: change the final digit group to a float type + | +LL | let fail25 = 1E2_f32; + | + +help: or remove the digit groups from the exponent + | +LL - let fail25 = 1E2_32; +LL + let fail25 = 1E232; + | -error: mistyped literal suffix +error: the final digit group looks like a type suffix --> tests/ui/mistyped_literal_suffix.rs:36:18 | LL | let fail26 = 43E7_64; - | ^^^^^^^ help: did you mean to write: `43E7_f64` + | ^^^^^^^ + | +help: change the final digit group to a float type + | +LL | let fail26 = 43E7_f64; + | + +help: or remove the digit groups from the exponent + | +LL - let fail26 = 43E7_64; +LL + let fail26 = 43E764; + | -error: mistyped literal suffix +error: the final digit group looks like a type suffix --> tests/ui/mistyped_literal_suffix.rs:38:18 | LL | let fail27 = 243E17_32; - | ^^^^^^^^^ help: did you mean to write: `243E17_f32` + | ^^^^^^^^^ + | +help: change the final digit group to a float type + | +LL | let fail27 = 243E17_f32; + | + +help: or remove the digit groups from the exponent + | +LL - let fail27 = 243E17_32; +LL + let fail27 = 243E1732; + | -error: mistyped literal suffix +error: the final digit group looks like a type suffix --> tests/ui/mistyped_literal_suffix.rs:40:18 | LL | let fail28 = 241251235E723_64; - | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` + | ^^^^^^^^^^^^^^^^ + | +help: change the final digit group to a float type + | +LL | let fail28 = 241251235E723_f64; + | + +help: or remove the digit groups from the exponent + | +LL - let fail28 = 241251235E723_64; +LL + let fail28 = 241251235E72364; + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:45:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:45:21 | LL | let fail30 = 127_8; // should be i8 - | ^^^^^ help: did you mean to write: `127_i8` + | ^^ + | +help: change the final digit group to a signed type + | +LL | let fail30 = 127_i8; // should be i8 + | + +help: change the final digit group to an unsigned type + | +LL | let fail30 = 127_u8; // should be i8 + | + +help: or adjust the digit groupings + | +LL - let fail30 = 127_8; // should be i8 +LL + let fail30 = 1_278; // should be i8 + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:48:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:48:21 | LL | let fail31 = 240_8; // should be u8 - | ^^^^^ help: did you mean to write: `240_u8` + | ^^ + | +help: change the final digit group to an unsigned type + | +LL | let fail31 = 240_u8; // should be u8 + | + +help: or adjust the digit groupings + | +LL - let fail31 = 240_8; // should be u8 +LL + let fail31 = 2_408; // should be u8 + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:52:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:52:24 | LL | let fail33 = 0x1234_16; - | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` + | ^^^ + | +help: change the final digit group to a signed type + | +LL | let fail33 = 0x1234_i16; + | + +help: change the final digit group to an unsigned type + | +LL | let fail33 = 0x1234_u16; + | + +help: or adjust the digit groupings + | +LL - let fail33 = 0x1234_16; +LL + let fail33 = 0x12_3416; + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:54:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:54:24 | LL | let fail34 = 0xABCD_16; - | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` + | ^^^ + | +help: change the final digit group to a signed type + | +LL | let fail34 = 0xABCD_i16; + | + +help: change the final digit group to an unsigned type + | +LL | let fail34 = 0xABCD_u16; + | + +help: or adjust the digit groupings + | +LL - let fail34 = 0xABCD_16; +LL + let fail34 = 0xAB_CD16; + | -error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:57:18 +error: the final digit group looks like a type suffix + --> tests/ui/mistyped_literal_suffix.rs:57:39 | LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` + | ^^^ + | +help: change the final digit group to a signed type + | +LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_i64; // u64 + | + +help: change the final digit group to an unsigned type + | +LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + | + +help: or adjust the digit groupings + | +LL - let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 +LL + let fail36 = 0xFF_FFFF_FFFF_FFFF_FF64; // u64 + | -error: mistyped literal suffix +error: the final digit group looks like a type suffix --> tests/ui/mistyped_literal_suffix.rs:65:13 | LL | let _ = 1.12345E1_32; - | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` + | ^^^^^^^^^^^^ + | +help: change the final digit group to a float type + | +LL | let _ = 1.12345E1_f32; + | + +help: or remove the digit groups from the exponent + | +LL - let _ = 1.12345E1_32; +LL + let _ = 1.12345E132; + | error: aborting due to 16 previous errors diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 646481ab040b..18bd79758135 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -1,21 +1,5 @@ #![warn(clippy::unreadable_literal)] -struct Foo(u64); - -macro_rules! foo { - () => { - Foo(123123123123) - }; -} - -struct Bar(f32); - -macro_rules! bar { - () => { - Bar(100200300400.100200300400500) - }; -} - fn main() { let _good = ( 0b1011_i64, @@ -37,17 +21,32 @@ fn main() { let _bad_sci = 1.123_456e1; //~^ unreadable_literal - let _fail1 = 0x00ab_cdef; + let _fail1 = 0xab_cdef; //~^ unreadable_literal let _fail2: u32 = 0xBAFE_BAFE; //~^ unreadable_literal - let _fail3 = 0x0abc_deff; + let _fail3 = 0xabc_deff; //~^ unreadable_literal - let _fail4: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail4: i128 = 0xab_cabc_abca_bcab_cabc; //~^ unreadable_literal let _fail5 = 1.100_300_400; //~^ unreadable_literal - let _ = foo!(); - let _ = bar!(); + { + struct Foo(u64); + struct Bar(f32); + + macro_rules! foo { + () => { + Foo(123_123_123_123) //~ unreadable_literal + }; + } + macro_rules! bar { + () => { + Bar(100_200_300_400.100_200_300_400_500) //~ unreadable_literal + }; + } + let _ = foo!(); + let _ = bar!(); + } } diff --git a/tests/ui/unreadable_literal.rs b/tests/ui/unreadable_literal.rs index 973d1a8e196a..f166dc30442e 100644 --- a/tests/ui/unreadable_literal.rs +++ b/tests/ui/unreadable_literal.rs @@ -1,21 +1,5 @@ #![warn(clippy::unreadable_literal)] -struct Foo(u64); - -macro_rules! foo { - () => { - Foo(123123123123) - }; -} - -struct Bar(f32); - -macro_rules! bar { - () => { - Bar(100200300400.100200300400500) - }; -} - fn main() { let _good = ( 0b1011_i64, @@ -48,6 +32,21 @@ fn main() { let _fail5 = 1.100300400; //~^ unreadable_literal - let _ = foo!(); - let _ = bar!(); + { + struct Foo(u64); + struct Bar(f32); + + macro_rules! foo { + () => { + Foo(123123123123) //~ unreadable_literal + }; + } + macro_rules! bar { + () => { + Bar(100200300400.100200300400500) //~ unreadable_literal + }; + } + let _ = foo!(); + let _ = bar!(); + } } diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index d019bf1b1feb..b2d16fc69f09 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,65 +1,148 @@ error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:31:17 + --> tests/ui/unreadable_literal.rs:15:17 | LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); - | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` + | ^^^^^^^^^^^^ | = note: `-D clippy::unreadable-literal` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unreadable_literal)]` +help: separate the digits + | +LL | let _bad = (0b11_0110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:31:31 + --> tests/ui/unreadable_literal.rs:15:31 | LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); - | ^^^^^^^^^^^^^^^^ help: consider: `0x1234_5678_usize` + | ^^^^^^^^^^^^^^^^ + | +help: separate the digits + | +LL | let _bad = (0b110110_i64, 0x1234_5678_usize, 123456_f32, 1.234567_f32); + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:31:49 + --> tests/ui/unreadable_literal.rs:15:49 | LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); - | ^^^^^^^^^^ help: consider: `123_456_f32` + | ^^^^^^^^^^ + | +help: separate the digits + | +LL | let _bad = (0b110110_i64, 0x12345678_usize, 123_456_f32, 1.234567_f32); + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:31:61 + --> tests/ui/unreadable_literal.rs:15:61 | LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234567_f32); - | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` + | ^^^^^^^^^^^^ + | +help: separate the digits + | +LL | let _bad = (0b110110_i64, 0x12345678_usize, 123456_f32, 1.234_567_f32); + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:37:20 + --> tests/ui/unreadable_literal.rs:21:20 | LL | let _bad_sci = 1.123456e1; - | ^^^^^^^^^^ help: consider: `1.123_456e1` + | ^^^^^^^^^^ + | +help: separate the digits + | +LL | let _bad_sci = 1.123_456e1; + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:40:18 + --> tests/ui/unreadable_literal.rs:24:18 | LL | let _fail1 = 0xabcdef; - | ^^^^^^^^ help: consider: `0x00ab_cdef` + | ^^^^^^^^ + | +help: separate the digits + | +LL | let _fail1 = 0xab_cdef; + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:42:23 + --> tests/ui/unreadable_literal.rs:26:23 | LL | let _fail2: u32 = 0xBAFEBAFE; - | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` + | ^^^^^^^^^^ + | +help: separate the digits + | +LL | let _fail2: u32 = 0xBAFE_BAFE; + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:44:18 + --> tests/ui/unreadable_literal.rs:28:18 | LL | let _fail3 = 0xabcdeff; - | ^^^^^^^^^ help: consider: `0x0abc_deff` + | ^^^^^^^^^ + | +help: separate the digits + | +LL | let _fail3 = 0xabc_deff; + | + error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:46:24 + --> tests/ui/unreadable_literal.rs:30:24 | LL | let _fail4: i128 = 0xabcabcabcabcabcabc; - | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` + | ^^^^^^^^^^^^^^^^^^^^ + | +help: separate the digits + | +LL - let _fail4: i128 = 0xabcabcabcabcabcabc; +LL + let _fail4: i128 = 0xab_cabc_abca_bcab_cabc; + | error: long literal lacking separators - --> tests/ui/unreadable_literal.rs:48:18 + --> tests/ui/unreadable_literal.rs:32:18 | LL | let _fail5 = 1.100300400; - | ^^^^^^^^^^^ help: consider: `1.100_300_400` + | ^^^^^^^^^^^ + | +help: separate the digits + | +LL - let _fail5 = 1.100300400; +LL + let _fail5 = 1.100_300_400; + | + +error: long literal lacking separators + --> tests/ui/unreadable_literal.rs:41:21 + | +LL | Foo(123123123123) + | ^^^^^^^^^^^^ +... +LL | let _ = foo!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) +help: separate the digits + | +LL - Foo(123123123123) +LL + Foo(123_123_123_123) + | + +error: long literal lacking separators + --> tests/ui/unreadable_literal.rs:46:21 + | +LL | Bar(100200300400.100200300400500) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | let _ = bar!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) +help: separate the digits + | +LL - Bar(100200300400.100200300400500) +LL + Bar(100_200_300_400.100_200_300_400_500) + | -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors