use crate::utils::{in_macro, snippet_opt, span_lint_and_sugg};
use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
-use rustc::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
+use rustc::{declare_lint_pass, impl_lint_pass};
use rustc_errors::Applicability;
+use rustc_session::declare_tool_lint;
use syntax::ast::*;
use syntax_pos;
impl Radix {
/// Returns a reasonable digit group size for this radix.
#[must_use]
- crate fn suggest_grouping(&self) -> usize {
+ fn suggest_grouping(&self) -> usize {
match *self {
Self::Binary | Self::Hexadecimal => 4,
Self::Octal | Self::Decimal => 3,
}
}
+/// A helper method to format numeric literals with digit grouping.
+/// `lit` must be a valid numeric literal without suffix.
+pub fn format_numeric_literal(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
+ NumericLiteral::new(lit, type_suffix, float).format()
+}
+
#[derive(Debug)]
-pub(super) struct DigitInfo<'a> {
+pub(super) struct NumericLiteral<'a> {
/// Which radix the literal was represented in.
- crate radix: Radix,
+ radix: Radix,
/// The radix prefix, if present.
- crate prefix: Option<&'a str>,
+ prefix: Option<&'a str>,
/// The integer part of the number.
integer: &'a str,
exponent: Option<(char, &'a str)>,
/// The type suffix, including preceding underscore if present.
- crate suffix: Option<&'a str>,
+ suffix: Option<&'a str>,
}
-impl<'a> DigitInfo<'a> {
- fn from_lit(src: &'a str, lit: &Lit) -> Option<DigitInfo<'a>> {
+impl<'a> NumericLiteral<'a> {
+ fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
if lit.kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
let (unsuffixed, suffix) = split_suffix(&src, &lit.kind);
let float = if let LitKind::Float(..) = lit.kind { true } else { false };
- Some(DigitInfo::new(unsuffixed, suffix, float))
+ Some(NumericLiteral::new(unsuffixed, suffix, float))
} else {
None
}
}
#[must_use]
- crate fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
+ fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
// Determine delimiter for radix prefix, if present, and radix.
let radix = if lit.starts_with("0x") {
Radix::Hexadecimal
}
/// Returns literal formatted in a sensible way.
- crate fn grouping_hint(&self) -> String {
+ fn format(&self) -> String {
let mut output = String::new();
if let Some(prefix) = self.prefix {
let first_group_size;
if partial_group_first {
- first_group_size = (digits.clone().count() + group_size - 1) % group_size + 1;
+ first_group_size = (digits.clone().count() - 1) % group_size + 1;
if pad {
for _ in 0..group_size - first_group_size {
output.push('0');
}
impl WarningType {
- crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
+ fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
match self {
Self::MistypedLiteralSuffix => span_lint_and_sugg(
cx,
span,
"mistyped literal suffix",
"did you mean to write",
- grouping_hint.to_string(),
+ suggested_format,
Applicability::MaybeIncorrect,
),
Self::UnreadableLiteral => span_lint_and_sugg(
span,
"long literal lacking separators",
"consider",
- grouping_hint.to_owned(),
+ suggested_format,
Applicability::MachineApplicable,
),
Self::LargeDigitGroups => span_lint_and_sugg(
span,
"digit groups should be smaller",
"consider",
- grouping_hint.to_owned(),
+ suggested_format,
Applicability::MachineApplicable,
),
Self::InconsistentDigitGrouping => span_lint_and_sugg(
span,
"digits grouped inconsistently by underscores",
"consider",
- grouping_hint.to_owned(),
+ suggested_format,
Applicability::MachineApplicable,
),
Self::DecimalRepresentation => span_lint_and_sugg(
span,
"integer literal has a better hexadecimal representation",
"consider",
- grouping_hint.to_owned(),
+ suggested_format,
Applicability::MachineApplicable,
),
};
impl LiteralDigitGrouping {
fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
- let in_macro = in_macro(lit.span);
-
if_chain! {
if let Some(src) = snippet_opt(cx, lit.span);
- if let Some(mut digit_info) = DigitInfo::from_lit(&src, &lit);
+ if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit);
then {
- if !Self::check_for_mistyped_suffix(cx, lit.span, &mut digit_info) {
+ if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) {
return;
}
let result = (|| {
- let integral_group_size = Self::get_group_size(digit_info.integer.split('_'), in_macro)?;
- if let Some(fraction) = digit_info.fraction {
- let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), in_macro)?;
+ let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
+ if let Some(fraction) = num_lit.fraction {
+ let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
let consistent = Self::parts_consistent(integral_group_size,
fractional_group_size,
- digit_info.integer.len(),
+ num_lit.integer.len(),
fraction.len());
if !consistent {
return Err(WarningType::InconsistentDigitGrouping);
if let Err(warning_type) = result {
- warning_type.display(&digit_info.grouping_hint(), cx, lit.span)
+ let should_warn = match warning_type {
+ | WarningType::UnreadableLiteral
+ | WarningType::InconsistentDigitGrouping
+ | WarningType::LargeDigitGroups => {
+ !in_macro(lit.span)
+ }
+ WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
+ true
+ }
+ };
+ if should_warn {
+ warning_type.display(num_lit.format(), cx, lit.span)
+ }
}
}
}
fn check_for_mistyped_suffix(
cx: &EarlyContext<'_>,
span: syntax_pos::Span,
- digit_info: &mut DigitInfo<'_>,
+ num_lit: &mut NumericLiteral<'_>,
) -> bool {
- if digit_info.suffix.is_some() {
+ if num_lit.suffix.is_some() {
return true;
}
- let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut digit_info.exponent {
+ let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
(exponent, &["32", "64"][..], 'f')
- } else if let Some(fraction) = &mut digit_info.fraction {
+ } else if let Some(fraction) = &mut num_lit.fraction {
(fraction, &["32", "64"][..], 'f')
} else {
- (&mut digit_info.integer, &["8", "16", "32", "64"][..], 'i')
+ (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
};
let mut split = part.rsplit('_');
let last_group = split.next().expect("At least one group");
if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
*part = &part[..part.len() - last_group.len()];
- let mut hint = digit_info.grouping_hint();
- hint.push('_');
- hint.push(missing_char);
- hint.push_str(last_group);
- WarningType::MistypedLiteralSuffix.display(&hint, cx, span);
+ let mut sugg = num_lit.format();
+ sugg.push('_');
+ sugg.push(missing_char);
+ sugg.push_str(last_group);
+ WarningType::MistypedLiteralSuffix.display(sugg, cx, span);
false
} else {
true
/// 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>, in_macro: bool) -> Result<Option<usize>, WarningType> {
+ fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
let mut groups = groups.map(str::len);
let first = groups.next().expect("At least one group");
} else {
Ok(Some(second))
}
- } else if first > 5 && !in_macro {
+ } else if first > 5 {
Err(WarningType::UnreadableLiteral)
} else {
Ok(None)
if_chain! {
if let LitKind::Int(val, _) = lit.kind;
if let Some(src) = snippet_opt(cx, lit.span);
- if let Some(digit_info) = DigitInfo::from_lit(&src, &lit);
- if digit_info.radix == Radix::Decimal;
+ if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit);
+ if num_lit.radix == Radix::Decimal;
if val >= u128::from(self.threshold);
then {
let hex = format!("{:#X}", val);
- let digit_info = DigitInfo::new(&hex, None, false);
- let _ = Self::do_lint(digit_info.integer).map_err(|warning_type| {
- warning_type.display(&digit_info.grouping_hint(), cx, lit.span)
+ let num_lit = NumericLiteral::new(&hex, None, false);
+ let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
+ warning_type.display(num_lit.format(), cx, lit.span)
});
}
}