-// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.
-use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass, in_external_macro, LintContext};
-use crate::rustc::{declare_tool_lint, lint_array};
-use if_chain::if_chain;
-use crate::syntax::ast::*;
-use crate::syntax_pos;
use crate::utils::{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_tool_lint, lint_array};
+use rustc_errors::Applicability;
+use syntax::ast::*;
+use syntax_pos;
-/// **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.
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-///
-/// ```rust
-/// 61864918973511
-/// ```
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.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x: u64 = 61864918973511;
+ /// ```
pub UNREADABLE_LITERAL,
style,
"long integer literal without underscores"
}
-/// **What it does:** Warns for mistyped suffix in literals
-///
-/// **Why is this bad?** This is most probably a typo
-///
-/// **Known problems:**
-/// - Recommends a signed suffix, even though the number might be too big and an unsigned
-/// suffix is required
-/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers
-///
-/// **Example:**
-///
-/// ```rust
-/// 2_32
-/// ```
declare_clippy_lint! {
+ /// **What it does:** Warns for mistyped suffix in literals
+ ///
+ /// **Why is this bad?** This is most probably a typo
+ ///
+ /// **Known problems:**
+ /// - Recommends a signed suffix, even though the number might be too big and an unsigned
+ /// suffix is required
+ /// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// 2_32;
+ /// ```
pub MISTYPED_LITERAL_SUFFIXES,
correctness,
"mistyped literal suffix"
}
-/// **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.
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-///
-/// ```rust
-/// 618_64_9189_73_511
-/// ```
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.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x: u64 = 618_64_9189_73_511;
+ /// ```
pub INCONSISTENT_DIGIT_GROUPING,
style,
"integer literals with digits grouped inconsistently"
}
-/// **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.
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-///
-/// ```rust
-/// 6186491_8973511
-/// ```
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.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x: u64 = 6186491_8973511;
+ /// ```
pub LARGE_DIGIT_GROUPS,
- style,
+ pedantic,
"grouping digits into groups that are too large"
}
-/// **What it does:** Warns if there is a better representation for a numeric literal.
-///
-/// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more
-/// readable than a decimal representation.
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-///
-/// `255` => `0xFF`
-/// `65_535` => `0xFFFF`
-/// `4_042_322_160` => `0xF0F0_F0F0`
declare_clippy_lint! {
+ /// **What it does:** Warns if there is a better representation for a numeric literal.
+ ///
+ /// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more
+ /// readable than a decimal representation.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// `255` => `0xFF`
+ /// `65_535` => `0xFFFF`
+ /// `4_042_322_160` => `0xF0F0_F0F0`
pub DECIMAL_LITERAL_REPRESENTATION,
restriction,
"using decimal representation when hexadecimal would be better"
}
impl Radix {
- /// Return a reasonable digit group size for this radix.
+ /// Returns a reasonable digit group size for this radix.
crate fn suggest_grouping(&self) -> usize {
match *self {
Radix::Binary | Radix::Hexadecimal => 4,
let len = sans_prefix.len();
let mut last_d = '\0';
for (d_idx, d) in sans_prefix.char_indices() {
- let suffix_start = if last_d == '_' {
- d_idx - 1
- } else {
- d_idx
- };
- if float && (d == 'f' || d == 'e' || d == 'E') ||
- !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len)) {
- let (digits, suffix) = sans_prefix.split_at(suffix_start);
- return Self {
- digits,
- radix,
- prefix,
- suffix: Some(suffix),
- float,
- };
+ let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
+ if float
+ && (d == 'f'
+ || is_possible_float_suffix_index(&sans_prefix, suffix_start, len)
+ || ((d == 'E' || d == 'e') && !has_possible_float_suffix(&sans_prefix)))
+ || !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len))
+ {
+ let (digits, suffix) = sans_prefix.split_at(suffix_start);
+ return Self {
+ digits,
+ radix,
+ prefix,
+ suffix: Some(suffix),
+ float,
+ };
}
last_d = d
}
.filter(|&c| c != '_')
.collect::<Vec<_>>()
.chunks(group_size)
- .map(|chunk| chunk.into_iter().rev().collect())
+ .map(|chunk| chunk.iter().rev().collect())
.rev()
.collect::<Vec<String>>()
.join("_");
.filter(|&c| c != '_')
.collect::<Vec<_>>()
.chunks(group_size)
- .map(|chunk| chunk.into_iter().collect())
+ .map(|chunk| chunk.iter().collect())
.collect::<Vec<String>>()
.join("_");
+ let suffix_hint = match self.suffix {
+ Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
+ Some(suffix) => suffix.to_string(),
+ None => String::new(),
+ };
+ format!("{}.{}{}", int_part_hint, frac_part_hint, suffix_hint)
+ } else if self.float && (self.digits.contains('E') || self.digits.contains('e')) {
+ let which_e = if self.digits.contains('E') { 'E' } else { 'e' };
+ let parts: Vec<&str> = self.digits.split(which_e).collect();
+ let filtered_digits_vec_0 = parts[0].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
+ let filtered_digits_vec_1 = parts[1].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
+ let before_e_hint = filtered_digits_vec_0
+ .chunks(group_size)
+ .map(|chunk| chunk.iter().rev().collect())
+ .rev()
+ .collect::<Vec<String>>()
+ .join("_");
+ let after_e_hint = filtered_digits_vec_1
+ .chunks(group_size)
+ .map(|chunk| chunk.iter().rev().collect())
+ .rev()
+ .collect::<Vec<String>>()
+ .join("_");
+ let suffix_hint = match self.suffix {
+ Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]),
+ Some(suffix) => suffix.to_string(),
+ None => String::new(),
+ };
format!(
- "{}.{}{}",
- int_part_hint,
- frac_part_hint,
- self.suffix.unwrap_or("")
+ "{}{}{}{}{}",
+ self.prefix.unwrap_or(""),
+ before_e_hint,
+ which_e,
+ after_e_hint,
+ suffix_hint
)
} else {
- let filtered_digits_vec = self.digits
- .chars()
- .filter(|&c| c != '_')
- .rev()
- .collect::<Vec<_>>();
+ let filtered_digits_vec = self.digits.chars().filter(|&c| c != '_').rev().collect::<Vec<_>>();
let mut hint = filtered_digits_vec
.chunks(group_size)
- .map(|chunk| chunk.into_iter().rev().collect())
+ .map(|chunk| chunk.iter().rev().collect())
.rev()
.collect::<Vec<String>>()
.join("_");
hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]);
}
let suffix_hint = match self.suffix {
- Some(suffix) if is_mistyped_suffix(suffix) => {
- format!("_i{}", &suffix[1..])
- },
+ Some(suffix) if is_mistyped_suffix(suffix) => format!("_i{}", &suffix[1..]),
Some(suffix) => suffix.to_string(),
- None => String::new()
+ None => String::new(),
};
- format!(
- "{}{}{}",
- self.prefix.unwrap_or(""),
- hint,
- suffix_hint
- )
+ format!("{}{}{}", self.prefix.unwrap_or(""), hint, suffix_hint)
}
}
}
InconsistentDigitGrouping,
LargeDigitGroups,
DecimalRepresentation,
- MistypedLiteralSuffix
+ MistypedLiteralSuffix,
}
impl WarningType {
crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
match self {
- WarningType::MistypedLiteralSuffix => {
- span_lint_and_sugg(
- cx,
- MISTYPED_LITERAL_SUFFIXES,
- span,
- "mistyped literal suffix",
- "did you mean to write",
- grouping_hint.to_string()
- )
- },
+ WarningType::MistypedLiteralSuffix => span_lint_and_sugg(
+ cx,
+ MISTYPED_LITERAL_SUFFIXES,
+ span,
+ "mistyped literal suffix",
+ "did you mean to write",
+ grouping_hint.to_string(),
+ Applicability::MaybeIncorrect,
+ ),
WarningType::UnreadableLiteral => span_lint_and_sugg(
cx,
UNREADABLE_LITERAL,
"long literal lacking separators",
"consider",
grouping_hint.to_owned(),
+ Applicability::MachineApplicable,
),
WarningType::LargeDigitGroups => span_lint_and_sugg(
cx,
"digit groups should be smaller",
"consider",
grouping_hint.to_owned(),
+ Applicability::MachineApplicable,
),
WarningType::InconsistentDigitGrouping => span_lint_and_sugg(
cx,
"digits grouped inconsistently by underscores",
"consider",
grouping_hint.to_owned(),
+ Applicability::MachineApplicable,
),
WarningType::DecimalRepresentation => span_lint_and_sugg(
cx,
"integer literal has a better hexadecimal representation",
"consider",
grouping_hint.to_owned(),
+ Applicability::MachineApplicable,
),
};
}
MISTYPED_LITERAL_SUFFIXES,
)
}
+
+ fn name(&self) -> &'static str {
+ "LiteralDigitGrouping"
+ }
}
impl EarlyLintPass for LiteralDigitGrouping {
// Lint integral and fractional parts separately, and then check consistency of digit
// groups if both pass.
- let _ = Self::do_lint(parts[0], None)
+ let _ = Self::do_lint(parts[0], digit_info.suffix)
.map(|integral_group_size| {
if parts.len() > 1 {
// Lint the fractional part of literal just like integral part, but reversed.
fractional_group_size,
parts[0].len(),
parts[1].len());
- if !consistent {
- WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
- cx,
- lit.span);
- }
+ if !consistent {
+ WarningType::InconsistentDigitGrouping.display(
+ &digit_info.grouping_hint(),
+ cx,
+ lit.span,
+ );
+ }
})
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
cx,
fn get_lints(&self) -> LintArray {
lint_array!(DECIMAL_LITERAL_REPRESENTATION)
}
+
+ fn name(&self) -> &'static str {
+ "DecimalLiteralRepresentation"
+ }
}
impl EarlyLintPass for LiteralRepresentation {
impl LiteralRepresentation {
pub fn new(threshold: u64) -> Self {
- Self {
- threshold,
- }
+ Self { threshold }
}
fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
// Lint integral literals.
if let Some(src) = snippet_opt(cx, lit.span);
if let Some(firstch) = src.chars().next();
if char::to_digit(firstch, 10).is_some();
+ let digit_info = DigitInfo::new(&src, false);
+ if digit_info.radix == Radix::Decimal;
+ if let Ok(val) = digit_info.digits
+ .chars()
+ .filter(|&c| c != '_')
+ .collect::<String>()
+ .parse::<u128>();
+ if val >= u128::from(self.threshold);
then {
- let digit_info = DigitInfo::new(&src, false);
- if digit_info.radix == Radix::Decimal {
- let val = digit_info.digits
- .chars()
- .filter(|&c| c != '_')
- .collect::<String>()
- .parse::<u128>().unwrap();
- if val < u128::from(self.threshold) {
- return
- }
- let hex = format!("{:#X}", val);
- let digit_info = DigitInfo::new(&hex[..], false);
- let _ = Self::do_lint(digit_info.digits).map_err(|warning_type| {
- warning_type.display(&digit_info.grouping_hint(), cx, lit.span)
- });
- }
+ let hex = format!("{:#X}", val);
+ let digit_info = DigitInfo::new(&hex, false);
+ let _ = Self::do_lint(digit_info.digits).map_err(|warning_type| {
+ warning_type.display(&digit_info.grouping_hint(), cx, lit.span)
+ });
}
}
}
fn do_lint(digits: &str) -> Result<(), WarningType> {
if digits.len() == 1 {
// Lint for 1 digit literals, if someone really sets the threshold that low
- if digits == "1" || digits == "2" || digits == "4" || digits == "8" || digits == "3" || digits == "7"
+ if digits == "1"
+ || digits == "2"
+ || digits == "4"
+ || digits == "8"
+ || digits == "3"
+ || digits == "7"
|| digits == "F"
{
return Err(WarningType::DecimalRepresentation);
// Lint for Literals with a hex-representation of 2 or 3 digits
let f = &digits[0..1]; // first digit
let s = &digits[1..]; // suffix
+
// Powers of 2
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
// Powers of 2 minus 1
let f = &digits[0..1]; // first digit
let m = &digits[1..digits.len() - 1]; // middle digits, except last
let s = &digits[1..]; // suffix
+
// Powers of 2 with a margin of +15/-16
if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
}
fn is_possible_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
- ((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) &&
- is_mistyped_suffix(lit.split_at(idx).1)
+ ((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && is_mistyped_suffix(lit.split_at(idx).1)
+}
+
+fn is_mistyped_float_suffix(suffix: &str) -> bool {
+ ["_32", "_64"].contains(&suffix)
+}
+
+fn is_possible_float_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
+ (len > 3 && idx == len - 3) && is_mistyped_float_suffix(lit.split_at(idx).1)
+}
+
+fn has_possible_float_suffix(lit: &str) -> bool {
+ lit.ends_with("_32") || lit.ends_with("_64")
}