Merge pull request #2362 from flip1995/master
Lint for numeric literals that have a better representation
This commit is contained in:
commit
8e7f76db9a
@ -117,7 +117,7 @@ pub mod large_enum_variant;
|
||||
pub mod len_zero;
|
||||
pub mod let_if_seq;
|
||||
pub mod lifetimes;
|
||||
pub mod literal_digit_grouping;
|
||||
pub mod literal_representation;
|
||||
pub mod loops;
|
||||
pub mod map_clone;
|
||||
pub mod matches;
|
||||
@ -354,7 +354,10 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_late_lint_pass(box large_enum_variant::LargeEnumVariant::new(conf.enum_variant_size_threshold));
|
||||
reg.register_late_lint_pass(box explicit_write::Pass);
|
||||
reg.register_late_lint_pass(box needless_pass_by_value::NeedlessPassByValue);
|
||||
reg.register_early_lint_pass(box literal_digit_grouping::LiteralDigitGrouping);
|
||||
reg.register_early_lint_pass(box literal_representation::LiteralDigitGrouping);
|
||||
reg.register_early_lint_pass(box literal_representation::LiteralRepresentation::new(
|
||||
conf.literal_representation_threshold
|
||||
));
|
||||
reg.register_late_lint_pass(box use_self::UseSelf);
|
||||
reg.register_late_lint_pass(box bytecount::ByteCount);
|
||||
reg.register_late_lint_pass(box infinite_iter::Pass);
|
||||
@ -487,9 +490,10 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
||||
let_if_seq::USELESS_LET_IF_SEQ,
|
||||
lifetimes::NEEDLESS_LIFETIMES,
|
||||
lifetimes::UNUSED_LIFETIMES,
|
||||
literal_digit_grouping::INCONSISTENT_DIGIT_GROUPING,
|
||||
literal_digit_grouping::LARGE_DIGIT_GROUPS,
|
||||
literal_digit_grouping::UNREADABLE_LITERAL,
|
||||
literal_representation::INCONSISTENT_DIGIT_GROUPING,
|
||||
literal_representation::LARGE_DIGIT_GROUPS,
|
||||
literal_representation::UNREADABLE_LITERAL,
|
||||
literal_representation::DECIMAL_LITERAL_REPRESENTATION,
|
||||
loops::EMPTY_LOOP,
|
||||
loops::EXPLICIT_COUNTER_LOOP,
|
||||
loops::EXPLICIT_INTO_ITER_LOOP,
|
||||
|
@ -62,7 +62,25 @@ declare_lint! {
|
||||
"grouping digits into groups that are too large"
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// **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_lint! {
|
||||
pub DECIMAL_LITERAL_REPRESENTATION,
|
||||
Warn,
|
||||
"using decimal representation when hexadecimal would be better"
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Radix {
|
||||
Binary,
|
||||
Octal,
|
||||
@ -168,7 +186,12 @@ impl<'a> DigitInfo<'a> {
|
||||
.map(|chunk| chunk.into_iter().collect())
|
||||
.collect::<Vec<String>>()
|
||||
.join("_");
|
||||
format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
|
||||
format!(
|
||||
"{}.{}{}",
|
||||
int_part_hint,
|
||||
frac_part_hint,
|
||||
self.suffix.unwrap_or("")
|
||||
)
|
||||
} else {
|
||||
let hint = self.digits
|
||||
.chars()
|
||||
@ -180,7 +203,12 @@ impl<'a> DigitInfo<'a> {
|
||||
.rev()
|
||||
.collect::<Vec<String>>()
|
||||
.join("_");
|
||||
format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
|
||||
format!(
|
||||
"{}{}{}",
|
||||
self.prefix.unwrap_or(""),
|
||||
hint,
|
||||
self.suffix.unwrap_or("")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,9 +217,9 @@ enum WarningType {
|
||||
UnreadableLiteral,
|
||||
InconsistentDigitGrouping,
|
||||
LargeDigitGroups,
|
||||
DecimalRepresentation,
|
||||
}
|
||||
|
||||
|
||||
impl WarningType {
|
||||
pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
|
||||
match *self {
|
||||
@ -216,6 +244,13 @@ impl WarningType {
|
||||
"digits grouped inconsistently by underscores",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
),
|
||||
WarningType::DecimalRepresentation => span_help_and_lint(
|
||||
cx,
|
||||
DECIMAL_LITERAL_REPRESENTATION,
|
||||
*span,
|
||||
"integer literal has a better hexadecimal representation",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -225,7 +260,11 @@ pub struct LiteralDigitGrouping;
|
||||
|
||||
impl LintPass for LiteralDigitGrouping {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
|
||||
lint_array!(
|
||||
UNREADABLE_LITERAL,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
LARGE_DIGIT_GROUPS
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,3 +392,99 @@ impl LiteralDigitGrouping {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LiteralRepresentation {
|
||||
threshold: u64,
|
||||
}
|
||||
|
||||
impl LintPass for LiteralRepresentation {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(DECIMAL_LITERAL_REPRESENTATION)
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for LiteralRepresentation {
|
||||
fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
|
||||
if in_external_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Lit(ref lit) = expr.node {
|
||||
self.check_lit(cx, lit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralRepresentation {
|
||||
pub fn new(threshold: u64) -> Self {
|
||||
Self {
|
||||
threshold: threshold,
|
||||
}
|
||||
}
|
||||
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);
|
||||
if digit_info.radix == Radix::Decimal {
|
||||
let val = digit_info.digits
|
||||
.chars()
|
||||
.filter(|&c| c != '_')
|
||||
.collect::<String>()
|
||||
.parse::<u128>().unwrap();
|
||||
if val < self.threshold as u128 {
|
||||
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)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|| digits == "F"
|
||||
{
|
||||
return Err(WarningType::DecimalRepresentation);
|
||||
}
|
||||
} else if digits.len() < 4 {
|
||||
// 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
|
||||
|| ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
|
||||
{
|
||||
return Err(WarningType::DecimalRepresentation);
|
||||
}
|
||||
} else {
|
||||
// Lint for Literals with a hex-representation of 4 digits or more
|
||||
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'))
|
||||
// Lint for representations with only 0s and Fs, while allowing 7 as the first
|
||||
// digit
|
||||
|| ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F'))
|
||||
{
|
||||
return Err(WarningType::DecimalRepresentation);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -177,6 +177,8 @@ define_Conf! {
|
||||
(enum_variant_size_threshold, "enum_variant_size_threshold", 200 => u64),
|
||||
/// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
|
||||
(verbose_bit_mask_threshold, "verbose_bit_mask_threshold", 1 => u64),
|
||||
/// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
|
||||
(literal_representation_threshold, "literal_representation_threshold", 4096 => u64),
|
||||
}
|
||||
|
||||
/// Search for the configuration file.
|
||||
|
18
tests/ui/decimal_literal_representation.rs
Normal file
18
tests/ui/decimal_literal_representation.rs
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
|
||||
#[warn(decimal_literal_representation)]
|
||||
#[allow(unused_variables)]
|
||||
fn main() {
|
||||
// Hex: 7F, 80, 100, 1FF, 800, FFA, F0F3, 7F0F_F00D
|
||||
let good = (127, 128, 256, 511, 2048, 4090, 61_683, 2_131_750_925);
|
||||
let bad = ( // Hex:
|
||||
4096, // 0x1000
|
||||
16_371, // 0x3FF3
|
||||
32_773, // 0x8005
|
||||
65_280, // 0xFF00
|
||||
2_131_750_927, // 0x7F0F_F00F
|
||||
2_147_483_647, // 0x7FFF_FFFF
|
||||
4_042_322_160, // 0xF0F0_F0F0
|
||||
);
|
||||
}
|
59
tests/ui/decimal_literal_representation.stderr
Normal file
59
tests/ui/decimal_literal_representation.stderr
Normal file
@ -0,0 +1,59 @@
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:10:9
|
||||
|
|
||||
10 | 4096, // 0x1000
|
||||
| ^^^^
|
||||
|
|
||||
= note: `-D decimal-literal-representation` implied by `-D warnings`
|
||||
= help: consider: 0x1000
|
||||
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:11:9
|
||||
|
|
||||
11 | 16_371, // 0x3FF3
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: consider: 0x3FF3
|
||||
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:12:9
|
||||
|
|
||||
12 | 32_773, // 0x8005
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: consider: 0x8005
|
||||
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:13:9
|
||||
|
|
||||
13 | 65_280, // 0xFF00
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: consider: 0xFF00
|
||||
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:14:9
|
||||
|
|
||||
14 | 2_131_750_927, // 0x7F0F_F00F
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider: 0x7F0F_F00F
|
||||
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:15:9
|
||||
|
|
||||
15 | 2_147_483_647, // 0x7FFF_FFFF
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider: 0x7FFF_FFFF
|
||||
|
||||
error: integer literal has a better hexadecimal representation
|
||||
--> $DIR/decimal_literal_representation.rs:16:9
|
||||
|
|
||||
16 | 4_042_322_160, // 0xF0F0_F0F0
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider: 0xF0F0_F0F0
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user