-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New lint: decimal_bit_mask
#15215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
New lint: decimal_bit_mask
#15215
Changes from all commits
9dc676c
0bb3452
f229784
682106f
5c7ca98
2e0b41e
a33e249
4dca87e
35d6cc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,106 @@ | ||||||||||||||||||||||||||||||
| use clippy_utils::diagnostics::span_lint_and_help; | ||||||||||||||||||||||||||||||
| use clippy_utils::source::SpanRangeExt; | ||||||||||||||||||||||||||||||
| use rustc_data_structures::packed::Pu128; | ||||||||||||||||||||||||||||||
| use rustc_hir::{AssignOpKind, BinOpKind, Expr, ExprKind}; | ||||||||||||||||||||||||||||||
| use rustc_lint::{LateContext, LateLintPass}; | ||||||||||||||||||||||||||||||
| use rustc_session::declare_lint_pass; | ||||||||||||||||||||||||||||||
| use rustc_span::Span; | ||||||||||||||||||||||||||||||
| use rustc_span::source_map::Spanned; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| declare_clippy_lint! { | ||||||||||||||||||||||||||||||
| /// ### What it does | ||||||||||||||||||||||||||||||
| /// Checks for decimal literals used as bit masks in bitwise operations. | ||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||
| /// ### Why is this bad? | ||||||||||||||||||||||||||||||
| /// Using decimal literals for bit masks can make the code less readable and obscure the intended bit pattern. | ||||||||||||||||||||||||||||||
| /// Binary or hexadecimal or octal literals make the bit pattern more explicit and easier to understand at a glance. | ||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||
| /// ### Example | ||||||||||||||||||||||||||||||
| /// ```rust,no_run | ||||||||||||||||||||||||||||||
| /// let a = 14 & 6; // Bit pattern is not immediately clear | ||||||||||||||||||||||||||||||
| /// ``` | ||||||||||||||||||||||||||||||
| /// Use instead: | ||||||||||||||||||||||||||||||
| /// ```rust,no_run | ||||||||||||||||||||||||||||||
| /// let a = 0b1110 & 0b0110; | ||||||||||||||||||||||||||||||
| /// ``` | ||||||||||||||||||||||||||||||
| #[clippy::version = "1.87.0"] | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will need to be updated^^ |
||||||||||||||||||||||||||||||
| pub DECIMAL_BIT_MASK, | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Forgot to say this earlier, but lint names should be in plural, see https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints |
||||||||||||||||||||||||||||||
| nursery, | ||||||||||||||||||||||||||||||
| "use binary, hex or octal literals for bitwise operations" | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| declare_lint_pass!(DecimalBitMask => [DECIMAL_BIT_MASK]); | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lint would actually fit quite well into the |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn check_bitwise_binary_expr(cx: &LateContext<'_>, e: &Expr<'_>) { | ||||||||||||||||||||||||||||||
| if let ExprKind::Binary( | ||||||||||||||||||||||||||||||
| Spanned { | ||||||||||||||||||||||||||||||
| node: BinOpKind::BitAnd | BinOpKind::BitOr | BinOpKind::BitXor, | ||||||||||||||||||||||||||||||
| .. | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| left, | ||||||||||||||||||||||||||||||
| right, | ||||||||||||||||||||||||||||||
| ) = &e.kind | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| for expr in [left, right] { | ||||||||||||||||||||||||||||||
| if let ExprKind::Lit(_) = &expr.kind | ||||||||||||||||||||||||||||||
| && !is_not_decimal_number(cx, expr.span) | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The double negation is a bit confusing. Could you please invert the logic of |
||||||||||||||||||||||||||||||
| && !is_power_of_twoish(expr) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| emit_lint(cx, expr.span); | ||||||||||||||||||||||||||||||
|
Comment on lines
+44
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I got really confused here:
|
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn check_bitwise_assign_expr(cx: &LateContext<'_>, e: &Expr<'_>) { | ||||||||||||||||||||||||||||||
| if let ExprKind::AssignOp( | ||||||||||||||||||||||||||||||
| Spanned { | ||||||||||||||||||||||||||||||
| node: AssignOpKind::BitAndAssign | AssignOpKind::BitOrAssign | AssignOpKind::BitXorAssign, | ||||||||||||||||||||||||||||||
| .. | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| _, | ||||||||||||||||||||||||||||||
| expr @ Expr { | ||||||||||||||||||||||||||||||
| kind: ExprKind::Lit(lit), | ||||||||||||||||||||||||||||||
| .. | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| ) = &e.kind | ||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very much a nit: imo, these big patterns are too verbose -- what do you think about something like this?
Suggested change
|
||||||||||||||||||||||||||||||
| && !is_power_of_twoish(expr) | ||||||||||||||||||||||||||||||
| && !is_not_decimal_number(cx, lit.span) | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| emit_lint(cx, lit.span); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn is_not_decimal_number(cx: &LateContext<'_>, span: Span) -> bool { | ||||||||||||||||||||||||||||||
| span.check_source_text(cx, |src| src.contains("0b") || src.contains("0x") || src.contains("0o")) | ||||||||||||||||||||||||||||||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@ada4a I simplified this part (using
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense! You might want to add a small comment about this (e.g. why you didn't use |
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn is_power_of_twoish(expr: &Expr<'_>) -> bool { | ||||||||||||||||||||||||||||||
| if let ExprKind::Lit(lit) = &expr.kind | ||||||||||||||||||||||||||||||
| && let rustc_ast::LitKind::Int(Pu128(val), _) = lit.node | ||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||
| return val.is_power_of_two() || val.wrapping_add(1).is_power_of_two(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| false | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| fn emit_lint(cx: &LateContext<'_>, span: Span) { | ||||||||||||||||||||||||||||||
| span_lint_and_help( | ||||||||||||||||||||||||||||||
| cx, | ||||||||||||||||||||||||||||||
| DECIMAL_BIT_MASK, | ||||||||||||||||||||||||||||||
| span, | ||||||||||||||||||||||||||||||
| "using decimal literal for bitwise operation", | ||||||||||||||||||||||||||||||
| None, | ||||||||||||||||||||||||||||||
| "use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability", | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| impl<'tcx> LateLintPass<'tcx> for DecimalBitMask { | ||||||||||||||||||||||||||||||
| fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | ||||||||||||||||||||||||||||||
| if e.span.from_expansion() { | ||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| check_bitwise_binary_expr(cx, e); | ||||||||||||||||||||||||||||||
| check_bitwise_assign_expr(cx, e); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| #![allow(clippy::erasing_op)] | ||
| #![allow(clippy::no_effect)] | ||
| #![warn(clippy::decimal_bit_mask)] | ||
|
|
||
| macro_rules! bitwise_op { | ||
| ($x:expr, $y:expr) => { | ||
| $x & $y; | ||
| }; | ||
| } | ||
|
|
||
| pub const SOME_CONST: i32 = 12345; | ||
|
|
||
| fn main() { | ||
| let mut x = 0; | ||
| // BAD: Bitwise operation, decimal literal, one literal | ||
| x & 99; //~ decimal_bit_mask | ||
| x | (/* comment */99); //~ decimal_bit_mask | ||
| x ^ (99); //~ decimal_bit_mask | ||
| x &= 99; //~ decimal_bit_mask | ||
| x |= 99; //~ decimal_bit_mask | ||
| x ^= (99); //~ decimal_bit_mask | ||
|
|
||
| // BAD: Bitwise operation, decimal literal, two literals | ||
| 0b1010 & 99; //~ decimal_bit_mask | ||
| 0b1010 | 99; //~ decimal_bit_mask | ||
| 0b1010 ^ 99; //~ decimal_bit_mask | ||
| 99 & 0b1010; //~ decimal_bit_mask | ||
| 99 | 0b1010; //~ decimal_bit_mask | ||
| 99 ^ 0b1010; //~ decimal_bit_mask | ||
| 0xD | 99; //~ decimal_bit_mask | ||
| 88 & 99; | ||
| //~^ decimal_bit_mask | ||
| //~| decimal_bit_mask | ||
|
|
||
| // GOOD: Bitwise operation, binary/hex/octal literal, one literal | ||
| x & 0b1010; | ||
| x | 0b1010; | ||
| x ^ 0b1010; | ||
| x &= 0b1010; | ||
| x |= 0b1010; | ||
| x ^= 0b1010; | ||
| x & 0xD; | ||
| x & 0o77; | ||
| x | 0o123; | ||
| x ^ 0o377; | ||
| x &= 0o777; | ||
| x |= 0o7; | ||
| x ^= 0o70; | ||
|
|
||
| // GOOD: Bitwise operation, binary/hex/octal literal, two literals | ||
| 0b1010 & 0b1101; | ||
| 0xD ^ 0xF; | ||
| 0o377 ^ 0o77; | ||
| 0b1101 ^ 0xFF; | ||
|
|
||
| // GOOD: Numeric operation, any literal | ||
| x += 99; | ||
| x -= 0b1010; | ||
| x *= 0xD; | ||
| 99 + 99; | ||
| 0b1010 - 0b1101; | ||
| 0xD * 0xD; | ||
|
|
||
| // GOOD: Bitwise operation, variables only | ||
| let y = 0; | ||
| x & y; | ||
| x &= y; | ||
| x + y; | ||
| x += y; | ||
|
|
||
| // GOOD: Macro expansion (should be ignored) | ||
| bitwise_op!(x, 123); | ||
| bitwise_op!(0b1010, 123); | ||
|
|
||
| // GOOD: Using const (should be ignored) | ||
| x & SOME_CONST; | ||
| x |= SOME_CONST; | ||
|
|
||
| // GOOD: Parenthesized binary/hex literal (should not trigger lint) | ||
| x & (0b1111); | ||
| x |= (0b1010); | ||
| x ^ (/* comment */0b1100); | ||
| (0xFF) & x; | ||
|
|
||
| // GOOD: Power of two and power of two minus one | ||
| x & 16; // 2^4 | ||
| x | 31; // 2^5 - 1 | ||
| x ^ 0x40; // 2^6 (hex) | ||
| x ^= 7; // 2^3 - 1 | ||
|
|
||
| // GOOD: More complex expressions | ||
| (x + 1) & 0xFF; | ||
| (x * 2) | (y & 0xF); | ||
| (x ^ y) & 0b11110000; | ||
| x | (1 << 9); | ||
|
|
||
| // GOOD: Special cases | ||
| x & 0; // All bits off | ||
| x | !0; // All bits on | ||
| x ^ 1; // Toggle LSB | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:16:9 | ||
| | | ||
| LL | x & 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
| = note: `-D clippy::decimal-bit-mask` implied by `-D warnings` | ||
| = help: to override `-D warnings` add `#[allow(clippy::decimal_bit_mask)]` | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:17:9 | ||
| | | ||
| LL | x | (/* comment */99); | ||
| | ^^^^^^^^^^^^^^^^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:18:9 | ||
| | | ||
| LL | x ^ (99); | ||
| | ^^^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:19:10 | ||
| | | ||
| LL | x &= 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:20:10 | ||
| | | ||
| LL | x |= 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:21:11 | ||
| | | ||
| LL | x ^= (99); | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:24:14 | ||
| | | ||
| LL | 0b1010 & 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:25:14 | ||
| | | ||
| LL | 0b1010 | 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:26:14 | ||
| | | ||
| LL | 0b1010 ^ 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:27:5 | ||
| | | ||
| LL | 99 & 0b1010; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:28:5 | ||
| | | ||
| LL | 99 | 0b1010; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:29:5 | ||
| | | ||
| LL | 99 ^ 0b1010; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:30:11 | ||
| | | ||
| LL | 0xD | 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:31:5 | ||
| | | ||
| LL | 88 & 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: using decimal literal for bitwise operation | ||
| --> tests/ui/decimal_bit_mask.rs:31:10 | ||
| | | ||
| LL | 88 & 99; | ||
| | ^^ | ||
| | | ||
| = help: use binary (0b...) or hex (0x...) or octal (0o...) notation for better readability | ||
|
|
||
| error: aborting due to 15 previous errors | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.