New lint: precedence_bits, with recent additions to precedence
Commit 2550530266 has extended the
`precedence` lint to include bitmasking and shift operations. The lint
is warn by default, and this generates many hits, especially in embedded
or system code, where it is very idiomatic to use expressions such as
`1 << 3 | 1 << 5` without parentheses.
This commit splits the recent addition into a new lint, which is put
into the "restriction" category, while the original one stays in
"complexity", because mixing bitmasking and arithmetic operations is
less typical.
This commit is contained in:
parent
ad05bc055c
commit
8db9ecfd74
8 changed files with 153 additions and 50 deletions
|
|
@ -5954,6 +5954,7 @@ Released 2018-09-13
|
|||
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits
|
||||
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
|
||||
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
|
||||
[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
|
||||
|
|
|
|||
|
|
@ -626,6 +626,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
|
||||
crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
|
||||
crate::precedence::PRECEDENCE_INFO,
|
||||
crate::precedence::PRECEDENCE_BITS_INFO,
|
||||
crate::ptr::CMP_NULL_INFO,
|
||||
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
|
||||
crate::ptr::MUT_FROM_REF_INFO,
|
||||
|
|
|
|||
|
|
@ -3,17 +3,14 @@ use clippy_utils::source::snippet_with_applicability;
|
|||
use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub};
|
||||
use rustc_ast::ast::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for operations where precedence may be unclear
|
||||
/// and suggests to add parentheses. Currently it catches the following:
|
||||
/// * mixed usage of arithmetic and bit shifting/combining operators without
|
||||
/// parentheses
|
||||
/// * mixed usage of bitmasking and bit shifting operators without parentheses
|
||||
/// Checks for operations where precedence may be unclear and suggests to add parentheses.
|
||||
/// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not everyone knows the precedence of those operators by
|
||||
|
|
@ -21,15 +18,32 @@ declare_clippy_lint! {
|
|||
/// code.
|
||||
///
|
||||
/// ### Example
|
||||
/// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
|
||||
/// * `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2
|
||||
/// `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub PRECEDENCE,
|
||||
complexity,
|
||||
"operations where precedence may be unclear"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Precedence => [PRECEDENCE]);
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bit shifting operations combined with bit masking/combining operators
|
||||
/// and suggest using parentheses.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Not everyone knows the precedence of those operators by
|
||||
/// heart, so expressions like these may trip others trying to reason about the
|
||||
/// code.
|
||||
///
|
||||
/// ### Example
|
||||
/// `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub PRECEDENCE_BITS,
|
||||
restriction,
|
||||
"operations mixing bit shifting with bit combining/masking"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Precedence => [PRECEDENCE, PRECEDENCE_BITS]);
|
||||
|
||||
impl EarlyLintPass for Precedence {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
|
|
@ -38,10 +52,10 @@ impl EarlyLintPass for Precedence {
|
|||
}
|
||||
|
||||
if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind {
|
||||
let span_sugg = |expr: &Expr, sugg, appl| {
|
||||
let span_sugg = |lint: &'static Lint, expr: &Expr, sugg, appl| {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PRECEDENCE,
|
||||
lint,
|
||||
expr.span,
|
||||
"operator precedence might not be obvious",
|
||||
"consider parenthesizing your expression",
|
||||
|
|
@ -57,37 +71,41 @@ impl EarlyLintPass for Precedence {
|
|||
match (op, get_bin_opt(left), get_bin_opt(right)) {
|
||||
(
|
||||
BitAnd | BitOr | BitXor,
|
||||
Some(Shl | Shr | Add | Div | Mul | Rem | Sub),
|
||||
Some(Shl | Shr | Add | Div | Mul | Rem | Sub),
|
||||
Some(left_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)),
|
||||
Some(right_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)),
|
||||
)
|
||||
| (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), Some(Add | Div | Mul | Rem | Sub)) => {
|
||||
| (
|
||||
Shl | Shr,
|
||||
Some(left_op @ (Add | Div | Mul | Rem | Sub)),
|
||||
Some(right_op @ (Add | Div | Mul | Rem | Sub)),
|
||||
) => {
|
||||
let sugg = format!(
|
||||
"({}) {} ({})",
|
||||
snippet_with_applicability(cx, left.span, "..", &mut applicability),
|
||||
op.as_str(),
|
||||
snippet_with_applicability(cx, right.span, "..", &mut applicability)
|
||||
);
|
||||
span_sugg(expr, sugg, applicability);
|
||||
span_sugg(lint_for(&[op, left_op, right_op]), expr, sugg, applicability);
|
||||
},
|
||||
(BitAnd | BitOr | BitXor, Some(Shl | Shr | Add | Div | Mul | Rem | Sub), _)
|
||||
| (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), _) => {
|
||||
(BitAnd | BitOr | BitXor, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), _)
|
||||
| (Shl | Shr, Some(side_op @ (Add | Div | Mul | Rem | Sub)), _) => {
|
||||
let sugg = format!(
|
||||
"({}) {} {}",
|
||||
snippet_with_applicability(cx, left.span, "..", &mut applicability),
|
||||
op.as_str(),
|
||||
snippet_with_applicability(cx, right.span, "..", &mut applicability)
|
||||
);
|
||||
span_sugg(expr, sugg, applicability);
|
||||
span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
|
||||
},
|
||||
(BitAnd | BitOr | BitXor, _, Some(Shl | Shr | Add | Div | Mul | Rem | Sub))
|
||||
| (Shl | Shr, _, Some(Add | Div | Mul | Rem | Sub)) => {
|
||||
(BitAnd | BitOr | BitXor, _, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)))
|
||||
| (Shl | Shr, _, Some(side_op @ (Add | Div | Mul | Rem | Sub))) => {
|
||||
let sugg = format!(
|
||||
"{} {} ({})",
|
||||
snippet_with_applicability(cx, left.span, "..", &mut applicability),
|
||||
op.as_str(),
|
||||
snippet_with_applicability(cx, right.span, "..", &mut applicability)
|
||||
);
|
||||
span_sugg(expr, sugg, applicability);
|
||||
span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -106,3 +124,11 @@ fn get_bin_opt(expr: &Expr) -> Option<BinOpKind> {
|
|||
fn is_bit_op(op: BinOpKind) -> bool {
|
||||
matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
|
||||
}
|
||||
|
||||
fn lint_for(ops: &[BinOpKind]) -> &'static Lint {
|
||||
if ops.iter().all(|op| is_bit_op(*op)) {
|
||||
PRECEDENCE_BITS
|
||||
} else {
|
||||
PRECEDENCE
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ fn main() {
|
|||
1 ^ (1 - 1);
|
||||
3 | (2 - 1);
|
||||
3 & (5 - 2);
|
||||
0x0F00 & (0x00F0 << 4);
|
||||
0x0F00 & (0xF000 >> 4);
|
||||
(0x0F00 << 1) ^ 3;
|
||||
(0x0F00 << 1) | 2;
|
||||
0x0F00 & 0x00F0 << 4;
|
||||
0x0F00 & 0xF000 >> 4;
|
||||
0x0F00 << 1 ^ 3;
|
||||
0x0F00 << 1 | 2;
|
||||
|
||||
let b = 3;
|
||||
trip!(b * 8);
|
||||
|
|
|
|||
|
|
@ -43,29 +43,5 @@ error: operator precedence might not be obvious
|
|||
LL | 3 & 5 - 2;
|
||||
| ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence.rs:23:5
|
||||
|
|
||||
LL | 0x0F00 & 0x00F0 << 4;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence.rs:24:5
|
||||
|
|
||||
LL | 0x0F00 & 0xF000 >> 4;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence.rs:25:5
|
||||
|
|
||||
LL | 0x0F00 << 1 ^ 3;
|
||||
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence.rs:26:5
|
||||
|
|
||||
LL | 0x0F00 << 1 | 2;
|
||||
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
|
|
|||
35
tests/ui/precedence_bits.fixed
Normal file
35
tests/ui/precedence_bits.fixed
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#![warn(clippy::precedence_bits)]
|
||||
#![allow(
|
||||
unused_must_use,
|
||||
clippy::no_effect,
|
||||
clippy::unnecessary_operation,
|
||||
clippy::precedence
|
||||
)]
|
||||
#![allow(clippy::identity_op)]
|
||||
#![allow(clippy::eq_op)]
|
||||
|
||||
macro_rules! trip {
|
||||
($a:expr) => {
|
||||
match $a & 0b1111_1111u8 {
|
||||
0 => println!("a is zero ({})", $a),
|
||||
_ => println!("a is {}", $a),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
1 << 2 + 3;
|
||||
1 + 2 << 3;
|
||||
4 >> 1 + 1;
|
||||
1 + 3 >> 2;
|
||||
1 ^ 1 - 1;
|
||||
3 | 2 - 1;
|
||||
3 & 5 - 2;
|
||||
0x0F00 & (0x00F0 << 4);
|
||||
0x0F00 & (0xF000 >> 4);
|
||||
(0x0F00 << 1) ^ 3;
|
||||
(0x0F00 << 1) | 2;
|
||||
|
||||
let b = 3;
|
||||
trip!(b * 8);
|
||||
}
|
||||
35
tests/ui/precedence_bits.rs
Normal file
35
tests/ui/precedence_bits.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#![warn(clippy::precedence_bits)]
|
||||
#![allow(
|
||||
unused_must_use,
|
||||
clippy::no_effect,
|
||||
clippy::unnecessary_operation,
|
||||
clippy::precedence
|
||||
)]
|
||||
#![allow(clippy::identity_op)]
|
||||
#![allow(clippy::eq_op)]
|
||||
|
||||
macro_rules! trip {
|
||||
($a:expr) => {
|
||||
match $a & 0b1111_1111u8 {
|
||||
0 => println!("a is zero ({})", $a),
|
||||
_ => println!("a is {}", $a),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
1 << 2 + 3;
|
||||
1 + 2 << 3;
|
||||
4 >> 1 + 1;
|
||||
1 + 3 >> 2;
|
||||
1 ^ 1 - 1;
|
||||
3 | 2 - 1;
|
||||
3 & 5 - 2;
|
||||
0x0F00 & 0x00F0 << 4;
|
||||
0x0F00 & 0xF000 >> 4;
|
||||
0x0F00 << 1 ^ 3;
|
||||
0x0F00 << 1 | 2;
|
||||
|
||||
let b = 3;
|
||||
trip!(b * 8);
|
||||
}
|
||||
29
tests/ui/precedence_bits.stderr
Normal file
29
tests/ui/precedence_bits.stderr
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence_bits.rs:28:5
|
||||
|
|
||||
LL | 0x0F00 & 0x00F0 << 4;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)`
|
||||
|
|
||||
= note: `-D clippy::precedence-bits` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::precedence_bits)]`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence_bits.rs:29:5
|
||||
|
|
||||
LL | 0x0F00 & 0xF000 >> 4;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence_bits.rs:30:5
|
||||
|
|
||||
LL | 0x0F00 << 1 ^ 3;
|
||||
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3`
|
||||
|
||||
error: operator precedence might not be obvious
|
||||
--> tests/ui/precedence_bits.rs:31:5
|
||||
|
|
||||
LL | 0x0F00 << 1 | 2;
|
||||
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue