snippet_opt, span_lint_and_sugg,
};
use if_chain::if_chain;
-use rustc::lint::in_external_macro;
use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
-use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
/// **What it does:** Warns if a long integral or floating-point constant does
/// **Example:**
///
/// ```rust
+ /// // Bad
/// let x: u64 = 61864918973511;
+ ///
+ /// // Good
+ /// let x: u64 = 61_864_918_973_511;
/// ```
pub UNREADABLE_LITERAL,
- style,
- "long integer literal without underscores"
+ pedantic,
+ "long literal without underscores"
}
declare_clippy_lint! {
/// **Example:**
///
/// ```rust
+ /// // Probably mistyped
/// 2_32;
+ ///
+ /// // Good
+ /// 2_i32;
/// ```
pub MISTYPED_LITERAL_SUFFIXES,
correctness,
/// **Example:**
///
/// ```rust
+ /// // Bad
/// let x: u64 = 618_64_9189_73_511;
+ ///
+ /// // Good
+ /// let x: u64 = 61_864_918_973_511;
/// ```
pub INCONSISTENT_DIGIT_GROUPING,
style,
"integer literals with digits grouped inconsistently"
}
+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.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x: u32 = 0xFFF_FFF;
+ /// let y: u8 = 0b01_011_101;
+ /// ```
+ pub UNUSUAL_BYTE_GROUPINGS,
+ style,
+ "binary or hex literals that aren't grouped by four"
+}
+
declare_clippy_lint! {
/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
LargeDigitGroups,
DecimalRepresentation,
MistypedLiteralSuffix,
+ UnusualByteGroupings,
}
impl WarningType {
suggested_format,
Applicability::MachineApplicable,
),
+ Self::UnusualByteGroupings => span_lint_and_sugg(
+ cx,
+ UNUSUAL_BYTE_GROUPINGS,
+ span,
+ "digits of hex or binary literal not grouped by four",
+ "consider",
+ suggested_format,
+ Applicability::MachineApplicable,
+ ),
};
}
}
-declare_lint_pass!(LiteralDigitGrouping => [
+#[allow(clippy::module_name_repetitions)]
+#[derive(Copy, Clone)]
+pub struct LiteralDigitGrouping {
+ lint_fraction_readability: bool,
+}
+
+impl_lint_pass!(LiteralDigitGrouping => [
UNREADABLE_LITERAL,
INCONSISTENT_DIGIT_GROUPING,
LARGE_DIGIT_GROUPS,
MISTYPED_LITERAL_SUFFIXES,
+ UNUSUAL_BYTE_GROUPINGS,
]);
impl EarlyLintPass for LiteralDigitGrouping {
}
if let ExprKind::Lit(ref lit) = expr.kind {
- Self::check_lit(cx, lit)
+ self.check_lit(cx, lit)
}
}
}
+// Length of each UUID hyphenated group in hex digits.
+const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
+
impl LiteralDigitGrouping {
- fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
+ pub fn new(lint_fraction_readability: bool) -> Self {
+ Self {
+ lint_fraction_readability,
+ }
+ }
+
+ fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
if_chain! {
if let Some(src) = snippet_opt(cx, lit.span);
if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit);
return;
}
+ if Self::is_literal_uuid_formatted(&mut num_lit) {
+ return;
+ }
+
let result = (|| {
- let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
+ let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?;
if let Some(fraction) = num_lit.fraction {
- let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
+ let fractional_group_size = Self::get_group_size(
+ fraction.rsplit('_'),
+ num_lit.radix,
+ self.lint_fraction_readability)?;
let consistent = Self::parts_consistent(integral_group_size,
fractional_group_size,
return Err(WarningType::InconsistentDigitGrouping);
};
}
+
Ok(())
})();
let should_warn = match warning_type {
| WarningType::UnreadableLiteral
| WarningType::InconsistentDigitGrouping
+ | WarningType::UnusualByteGroupings
| WarningType::LargeDigitGroups => {
!in_macro(lit.span)
}
let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
(exponent, &["32", "64"][..], 'f')
- } else if let Some(fraction) = &mut num_lit.fraction {
- (fraction, &["32", "64"][..], 'f')
+ } else if num_lit.fraction.is_some() {
+ (&mut num_lit.integer, &["32", "64"][..], 'f')
} else {
(&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
};
}
}
+ /// Checks whether the numeric literal matches the formatting of a UUID.
+ ///
+ /// Returns `true` if the radix is hexadecimal, and the groups match the
+ /// UUID format of 8-4-4-4-12.
+ fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool {
+ if num_lit.radix != Radix::Hexadecimal {
+ return false;
+ }
+
+ // UUIDs should not have a fraction
+ if num_lit.fraction.is_some() {
+ return false;
+ }
+
+ let group_sizes: Vec<usize> = num_lit.integer.split('_').map(str::len).collect();
+ if UUID_GROUP_LENS.len() == group_sizes.len() {
+ UUID_GROUP_LENS.iter().zip(&group_sizes).all(|(&a, &b)| a == b)
+ } else {
+ false
+ }
+ }
+
/// Given the sizes of the digit groups of both integral and fractional
/// parts, and the length
/// of both parts, determine if the digits have been grouped consistently.
/// Returns the size of the digit groups (or None if ungrouped) if successful,
/// otherwise returns a `WarningType` for linting.
- fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
+ fn get_group_size<'a>(
+ groups: impl Iterator<Item = &'a str>,
+ radix: Radix,
+ lint_unreadable: bool,
+ ) -> Result<Option<usize>, WarningType> {
let mut groups = groups.map(str::len);
let first = groups.next().expect("At least one group");
+ if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) {
+ return Err(WarningType::UnusualByteGroupings);
+ }
+
if let Some(second) = groups.next() {
if !groups.all(|x| x == second) || first > second {
Err(WarningType::InconsistentDigitGrouping)
} else {
Ok(Some(second))
}
- } else if first > 5 {
+ } else if first > 5 && lint_unreadable {
Err(WarningType::UnreadableLiteral)
} else {
Ok(None)