Skip to content

Commit 1561476

Browse files
committed
New lint: manual_midpoint
1 parent e9c2d82 commit 1561476

13 files changed

+172
-27
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5718,6 +5718,7 @@ Released 2018-09-13
57185718
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
57195719
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
57205720
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
5721+
[`manual_midpoint`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint
57215722
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
57225723
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
57235724
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
593593
crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
594594
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
595595
crate::operators::INTEGER_DIVISION_INFO,
596+
crate::operators::MANUAL_MIDPOINT_INFO,
596597
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
597598
crate::operators::MODULO_ARITHMETIC_INFO,
598599
crate::operators::MODULO_ONE_INFO,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::sugg::Sugg;
4+
use clippy_utils::{is_floating_point_integer_literal, is_integer_literal};
5+
use rustc_ast::BinOpKind;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::{Expr, ExprKind};
8+
use rustc_lint::LateContext;
9+
use rustc_middle::ty;
10+
11+
use super::MANUAL_MIDPOINT;
12+
13+
pub(super) fn check<'tcx>(
14+
cx: &LateContext<'tcx>,
15+
expr: &'tcx Expr<'_>,
16+
op: BinOpKind,
17+
left: &'tcx Expr<'_>,
18+
right: &'tcx Expr<'_>,
19+
msrv: &Msrv,
20+
) {
21+
if msrv.meets(msrvs::UINT_FLOAT_MIDPOINT)
22+
&& op == BinOpKind::Div
23+
&& (is_integer_literal(right, 2) || is_floating_point_integer_literal(right, 2))
24+
&& let ExprKind::Binary(left_op, ll_expr, lr_expr) = left.kind
25+
&& left_op.node == BinOpKind::Add
26+
&& let left_ty = cx.typeck_results().expr_ty_adjusted(ll_expr)
27+
&& let right_ty = cx.typeck_results().expr_ty_adjusted(lr_expr)
28+
&& left_ty == right_ty
29+
&& matches!(left_ty.kind(), ty::Uint(_) | ty::Float(_))
30+
{
31+
let mut app = Applicability::MachineApplicable;
32+
let left_sugg = Sugg::hir_with_applicability(cx, ll_expr, "..", &mut app);
33+
let right_sugg = Sugg::hir_with_applicability(cx, lr_expr, "..", &mut app);
34+
let sugg = format!("{left_ty}::midpoint({left_sugg}, {right_sugg})");
35+
span_lint_and_sugg(
36+
cx,
37+
MANUAL_MIDPOINT,
38+
expr.span,
39+
"manual implementation of `midpoint`",
40+
"use instead",
41+
sugg,
42+
app,
43+
);
44+
}
45+
}

clippy_lints/src/operators/mod.rs

+32
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod float_cmp;
1111
mod float_equality_without_abs;
1212
mod identity_op;
1313
mod integer_division;
14+
mod manual_midpoint;
1415
mod misrefactored_assign_op;
1516
mod modulo_arithmetic;
1617
mod modulo_one;
@@ -24,6 +25,7 @@ mod verbose_bit_mask;
2425
pub(crate) mod arithmetic_side_effects;
2526

2627
use clippy_config::Conf;
28+
use clippy_utils::msrvs::Msrv;
2729
use rustc_hir::{Body, Expr, ExprKind, UnOp};
2830
use rustc_lint::{LateContext, LateLintPass};
2931
use rustc_session::impl_lint_pass;
@@ -837,17 +839,43 @@ declare_clippy_lint! {
837839
"explicit self-assignment"
838840
}
839841

842+
declare_clippy_lint! {
843+
/// ### What it does
844+
/// Checks for manual implementation of `midpoint`.
845+
///
846+
/// ### Why is this bad?
847+
/// Using `(x + y) / 2` might cause an overflow on the intermediate
848+
/// addition result.
849+
///
850+
/// ### Example
851+
/// ```no_run
852+
/// # let a: u32 = 0;
853+
/// let c = (a + 10) / 2;
854+
/// ```
855+
/// Use instead:
856+
/// ```no_run
857+
/// # let a: u32 = 0;
858+
/// let c = u32::midpoint(a, 10);
859+
/// ```
860+
#[clippy::version = "1.85.0"]
861+
pub MANUAL_MIDPOINT,
862+
correctness,
863+
"manual implementation of `midpoint`"
864+
}
865+
840866
pub struct Operators {
841867
arithmetic_context: numeric_arithmetic::Context,
842868
verbose_bit_mask_threshold: u64,
843869
modulo_arithmetic_allow_comparison_to_zero: bool,
870+
msrv: Msrv,
844871
}
845872
impl Operators {
846873
pub fn new(conf: &'static Conf) -> Self {
847874
Self {
848875
arithmetic_context: numeric_arithmetic::Context::default(),
849876
verbose_bit_mask_threshold: conf.verbose_bit_mask_threshold,
850877
modulo_arithmetic_allow_comparison_to_zero: conf.allow_comparison_to_zero,
878+
msrv: conf.msrv.clone(),
851879
}
852880
}
853881
}
@@ -879,6 +907,7 @@ impl_lint_pass!(Operators => [
879907
NEEDLESS_BITWISE_BOOL,
880908
PTR_EQ,
881909
SELF_ASSIGNMENT,
910+
MANUAL_MIDPOINT,
882911
]);
883912

884913
impl<'tcx> LateLintPass<'tcx> for Operators {
@@ -896,6 +925,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
896925
identity_op::check(cx, e, op.node, lhs, rhs);
897926
needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
898927
ptr_eq::check(cx, e, op.node, lhs, rhs);
928+
manual_midpoint::check(cx, e, op.node, lhs, rhs, &self.msrv);
899929
}
900930
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
901931
bit_mask::check(cx, e, op.node, lhs, rhs);
@@ -946,6 +976,8 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
946976
fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &Body<'_>) {
947977
self.arithmetic_context.body_post(cx, b);
948978
}
979+
980+
extract_msrv_attr!(LateContext);
949981
}
950982

951983
fn macro_with_not_op(e: &Expr<'_>) -> bool {

clippy_utils/src/msrvs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ macro_rules! msrv_aliases {
1818

1919
// names may refer to stabilized feature flags or library items
2020
msrv_aliases! {
21+
1,85,0 { UINT_FLOAT_MIDPOINT }
2122
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
2223
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
2324
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE }

tests/ui/manual_midpoint.fixed

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![warn(clippy::manual_midpoint)]
2+
3+
macro_rules! mac {
4+
($a: expr, $b: expr) => {{ ($a + $b) / 2 }};
5+
}
6+
7+
fn main() {
8+
let a: u32 = 10;
9+
let _ = u32::midpoint(a, 5); //~ ERROR: manual implementation of `midpoint`
10+
11+
let f: f32 = 10.0;
12+
let _ = f32::midpoint(f, 5.0); //~ ERROR: manual implementation of `midpoint`
13+
14+
// Do not lint if a literal is not present
15+
let _ = (f + 5.0) / (1.0 + 1.0);
16+
17+
// Do not lint on signed integer types
18+
let i: i32 = 10;
19+
let _ = (i + 5) / 2;
20+
21+
// Do not lint
22+
let _ = mac!(10, 20);
23+
}

tests/ui/manual_midpoint.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![warn(clippy::manual_midpoint)]
2+
3+
macro_rules! mac {
4+
($a: expr, $b: expr) => {{ ($a + $b) / 2 }};
5+
}
6+
7+
fn main() {
8+
let a: u32 = 10;
9+
let _ = (a + 5) / 2; //~ ERROR: manual implementation of `midpoint`
10+
11+
let f: f32 = 10.0;
12+
let _ = (f + 5.0) / 2.0; //~ ERROR: manual implementation of `midpoint`
13+
14+
// Do not lint if a literal is not present
15+
let _ = (f + 5.0) / (1.0 + 1.0);
16+
17+
// Do not lint on signed integer types
18+
let i: i32 = 10;
19+
let _ = (i + 5) / 2;
20+
21+
// Do not lint
22+
let _ = mac!(10, 20);
23+
}

tests/ui/manual_midpoint.stderr

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: manual implementation of `midpoint`
2+
--> tests/ui/manual_midpoint.rs:9:13
3+
|
4+
LL | let _ = (a + 5) / 2;
5+
| ^^^^^^^^^^^ help: use instead: `u32::midpoint(a, 5)`
6+
|
7+
= note: `-D clippy::manual-midpoint` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::manual_midpoint)]`
9+
10+
error: manual implementation of `midpoint`
11+
--> tests/ui/manual_midpoint.rs:12:13
12+
|
13+
LL | let _ = (f + 5.0) / 2.0;
14+
| ^^^^^^^^^^^^^^^ help: use instead: `f32::midpoint(f, 5.0)`
15+
16+
error: aborting due to 2 previous errors
17+

tests/ui/option_if_let_else.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
clippy::equatable_if_let,
55
clippy::let_unit_value,
66
clippy::redundant_locals,
7+
clippy::manual_midpoint,
78
clippy::manual_unwrap_or_default,
89
clippy::manual_unwrap_or
910
)]

tests/ui/option_if_let_else.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
clippy::equatable_if_let,
55
clippy::let_unit_value,
66
clippy::redundant_locals,
7+
clippy::manual_midpoint,
78
clippy::manual_unwrap_or_default,
89
clippy::manual_unwrap_or
910
)]

0 commit comments

Comments
 (0)