//! floating-point literal expressions.
use rustc::lint::*;
+use rustc::{declare_lint, lint_array};
+use if_chain::if_chain;
use syntax::ast::*;
use syntax_pos;
-use utils::{in_external_macro, snippet_opt, span_help_and_lint};
+use crate::utils::{in_external_macro, snippet_opt, span_lint_and_sugg};
/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
/// ```rust
/// 61864918973511
/// ```
-declare_lint! {
+declare_clippy_lint! {
pub UNREADABLE_LITERAL,
- Warn,
+ style,
"long integer literal without underscores"
}
/// ```rust
/// 618_64_9189_73_511
/// ```
-declare_lint! {
+declare_clippy_lint! {
pub INCONSISTENT_DIGIT_GROUPING,
- Warn,
+ style,
"integer literals with digits grouped inconsistently"
}
/// ```rust
/// 6186491_8973511
/// ```
-declare_lint! {
+declare_clippy_lint! {
pub LARGE_DIGIT_GROUPS,
- Warn,
+ style,
"grouping digits into groups that are too large"
}
/// `255` => `0xFF`
/// `65_535` => `0xFFFF`
/// `4_042_322_160` => `0xF0F0_F0F0`
-declare_lint! {
+declare_clippy_lint! {
pub DECIMAL_LITERAL_REPRESENTATION,
- Warn,
+ restriction,
"using decimal representation when hexadecimal would be better"
}
#[derive(Debug, PartialEq)]
-enum Radix {
+pub(super) enum Radix {
Binary,
Octal,
Decimal,
impl Radix {
/// Return a reasonable digit group size for this radix.
- pub fn suggest_grouping(&self) -> usize {
+ crate fn suggest_grouping(&self) -> usize {
match *self {
Radix::Binary | Radix::Hexadecimal => 4,
Radix::Octal | Radix::Decimal => 3,
}
#[derive(Debug)]
-struct DigitInfo<'a> {
+pub(super) struct DigitInfo<'a> {
/// Characters of a literal between the radix prefix and type suffix.
- pub digits: &'a str,
+ crate digits: &'a str,
/// Which radix the literal was represented in.
- pub radix: Radix,
+ crate radix: Radix,
/// The radix prefix, if present.
- pub prefix: Option<&'a str>,
+ crate prefix: Option<&'a str>,
/// The type suffix, including preceding underscore if present.
- pub suffix: Option<&'a str>,
+ crate suffix: Option<&'a str>,
/// True for floating-point literals.
- pub float: bool,
+ crate float: bool,
}
impl<'a> DigitInfo<'a> {
- pub fn new(lit: &'a str, float: bool) -> Self {
+ crate fn new(lit: &'a str, float: bool) -> Self {
// Determine delimiter for radix prefix, if present, and radix.
let radix = if lit.starts_with("0x") {
Radix::Hexadecimal
let mut last_d = '\0';
for (d_idx, d) in sans_prefix.char_indices() {
- if !float && (d == 'i' || d == 'u') || float && d == 'f' {
+ if !float && (d == 'i' || d == 'u') || float && (d == 'f' || d == 'e' || d == 'E') {
let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
let (digits, suffix) = sans_prefix.split_at(suffix_start);
return Self {
- digits: digits,
- radix: radix,
- prefix: prefix,
+ digits,
+ radix,
+ prefix,
suffix: Some(suffix),
- float: float,
+ float,
};
}
last_d = d
// No suffix found
Self {
digits: sans_prefix,
- radix: radix,
- prefix: prefix,
+ radix,
+ prefix,
suffix: None,
- float: float,
+ float,
}
}
/// Returns digits grouped in a sensible way.
- fn grouping_hint(&self) -> String {
+ crate fn grouping_hint(&self) -> String {
let group_size = self.radix.suggest_grouping();
if self.digits.contains('.') {
let mut parts = self.digits.split('.');
self.suffix.unwrap_or("")
)
} else {
- let hint = self.digits
+ let filtered_digits_vec = self.digits
.chars()
- .rev()
.filter(|&c| c != '_')
- .collect::<Vec<_>>()
+ .rev()
+ .collect::<Vec<_>>();
+ let mut hint = filtered_digits_vec
.chunks(group_size)
.map(|chunk| chunk.into_iter().rev().collect())
.rev()
.collect::<Vec<String>>()
.join("_");
+ // Forces hexadecimal values to be grouped by 4 being filled with zeroes (e.g 0x00ab_cdef)
+ let nb_digits_to_fill = filtered_digits_vec.len() % 4;
+ if self.radix == Radix::Hexadecimal && nb_digits_to_fill != 0 {
+ hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]);
+ }
format!(
"{}{}{}",
self.prefix.unwrap_or(""),
}
impl WarningType {
- pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
- match *self {
- WarningType::UnreadableLiteral => span_help_and_lint(
+ crate fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: syntax_pos::Span) {
+ match self {
+ WarningType::UnreadableLiteral => span_lint_and_sugg(
cx,
UNREADABLE_LITERAL,
- *span,
+ span,
"long literal lacking separators",
- &format!("consider: {}", grouping_hint),
+ "consider",
+ grouping_hint.to_owned(),
),
- WarningType::LargeDigitGroups => span_help_and_lint(
+ WarningType::LargeDigitGroups => span_lint_and_sugg(
cx,
LARGE_DIGIT_GROUPS,
- *span,
+ span,
"digit groups should be smaller",
- &format!("consider: {}", grouping_hint),
+ "consider",
+ grouping_hint.to_owned(),
),
- WarningType::InconsistentDigitGrouping => span_help_and_lint(
+ WarningType::InconsistentDigitGrouping => span_lint_and_sugg(
cx,
INCONSISTENT_DIGIT_GROUPING,
- *span,
+ span,
"digits grouped inconsistently by underscores",
- &format!("consider: {}", grouping_hint),
+ "consider",
+ grouping_hint.to_owned(),
),
- WarningType::DecimalRepresentation => span_help_and_lint(
+ WarningType::DecimalRepresentation => span_lint_and_sugg(
cx,
DECIMAL_LITERAL_REPRESENTATION,
- *span,
+ span,
"integer literal has a better hexadecimal representation",
- &format!("consider: {}", grouping_hint),
+ "consider",
+ grouping_hint.to_owned(),
),
};
}
}
impl LiteralDigitGrouping {
- fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
- // Lint integral literals.
- if_chain! {
- if let LitKind::Int(..) = lit.node;
- if let Some(src) = snippet_opt(cx, lit.span);
- if let Some(firstch) = src.chars().next();
- if char::to_digit(firstch, 10).is_some();
- then {
- let digit_info = DigitInfo::new(&src, false);
- let _ = Self::do_lint(digit_info.digits).map_err(|warning_type| {
- warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
- });
- }
- }
-
- // Lint floating-point literals.
- if_chain! {
- if let LitKind::Float(..) = lit.node;
- if let Some(src) = snippet_opt(cx, lit.span);
- if let Some(firstch) = src.chars().next();
- if char::to_digit(firstch, 10).is_some();
- then {
- let digit_info = DigitInfo::new(&src, true);
- // Separate digits into integral and fractional parts.
- let parts: Vec<&str> = digit_info
- .digits
- .split_terminator('.')
- .collect();
-
- // Lint integral and fractional parts separately, and then check consistency of digit
- // groups if both pass.
- let _ = Self::do_lint(parts[0])
- .map(|integral_group_size| {
- if parts.len() > 1 {
- // Lint the fractional part of literal just like integral part, but reversed.
- let fractional_part = &parts[1].chars().rev().collect::<String>();
- let _ = Self::do_lint(fractional_part)
- .map(|fractional_group_size| {
- let consistent = Self::parts_consistent(integral_group_size,
- fractional_group_size,
- parts[0].len(),
- parts[1].len());
- if !consistent {
- WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
- cx,
- &lit.span);
- }
- })
- .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
- cx,
- &lit.span));
- }
- })
- .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
- }
+ fn check_lit(self, cx: &EarlyContext, lit: &Lit) {
+ match lit.node {
+ LitKind::Int(..) => {
+ // Lint integral literals.
+ if_chain! {
+ if let Some(src) = snippet_opt(cx, lit.span);
+ if let Some(firstch) = src.chars().next();
+ if char::to_digit(firstch, 10).is_some();
+ then {
+ let digit_info = DigitInfo::new(&src, false);
+ let _ = Self::do_lint(digit_info.digits).map_err(|warning_type| {
+ warning_type.display(&digit_info.grouping_hint(), cx, lit.span)
+ });
+ }
+ }
+ },
+ LitKind::Float(..) | LitKind::FloatUnsuffixed(..) => {
+ // Lint floating-point literals.
+ if_chain! {
+ if let Some(src) = snippet_opt(cx, lit.span);
+ if let Some(firstch) = src.chars().next();
+ if char::to_digit(firstch, 10).is_some();
+ then {
+ let digit_info = DigitInfo::new(&src, true);
+ // Separate digits into integral and fractional parts.
+ let parts: Vec<&str> = digit_info
+ .digits
+ .split_terminator('.')
+ .collect();
+
+ // Lint integral and fractional parts separately, and then check consistency of digit
+ // groups if both pass.
+ let _ = Self::do_lint(parts[0])
+ .map(|integral_group_size| {
+ if parts.len() > 1 {
+ // Lint the fractional part of literal just like integral part, but reversed.
+ let fractional_part = &parts[1].chars().rev().collect::<String>();
+ let _ = Self::do_lint(fractional_part)
+ .map(|fractional_group_size| {
+ let consistent = Self::parts_consistent(integral_group_size,
+ fractional_group_size,
+ parts[0].len(),
+ parts[1].len());
+ if !consistent {
+ WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
+ cx,
+ lit.span);
+ }
+ })
+ .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
+ cx,
+ lit.span));
+ }
+ })
+ .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, lit.span));
+ }
+ }
+ },
+ _ => (),
}
}
if underscore_positions.is_empty() {
// Check if literal needs underscores.
- if digits.len() > 4 {
+ if digits.len() > 5 {
Err(WarningType::UnreadableLiteral)
} else {
Ok(0)
impl LiteralRepresentation {
pub fn new(threshold: u64) -> Self {
Self {
- threshold: threshold,
+ threshold,
}
}
- fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
+ fn check_lit(self, cx: &EarlyContext, lit: &Lit) {
// Lint integral literals.
if_chain! {
if let LitKind::Int(..) = lit.node;
.filter(|&c| c != '_')
.collect::<String>()
.parse::<u128>().unwrap();
- if val < self.threshold as u128 {
+ 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)
+ warning_type.display(&digit_info.grouping_hint(), cx, lit.span)
});
}
}