Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 74 additions & 49 deletions clippy_lints/src/operators/manual_div_ceil.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
use clippy_utils::{SpanlessEq, sym};
use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp};
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self};
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Spanned;

use super::MANUAL_DIV_CEIL;
Expand All @@ -16,59 +17,84 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
let mut applicability = Applicability::MachineApplicable;

if op == BinOpKind::Div
&& check_int_ty_and_feature(cx, lhs)
&& check_int_ty_and_feature(cx, rhs)
&& let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = lhs.kind
&& check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(lhs))
&& check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(rhs))
&& msrv.meets(cx, msrvs::DIV_CEIL)
{
// (x + (y - 1)) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
return;
}
match lhs.kind {
ExprKind::Binary(inner_op, inner_lhs, inner_rhs) => {
// (x + (y - 1)) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
return;
}

// ((y - 1) + x) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
return;
}
// ((y - 1) + x) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
return;
}

// (x + y - 1) / y
if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Sub
&& add_op.node == BinOpKind::Add
&& check_literal(inner_rhs)
&& check_eq_expr(cx, add_rhs, rhs)
{
build_suggestion(cx, expr, add_lhs, rhs, &mut applicability);
}
// (x + y - 1) / y
if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Sub
&& add_op.node == BinOpKind::Add
&& check_literal(inner_rhs)
&& check_eq_expr(cx, add_rhs, rhs)
{
build_suggestion(cx, expr, add_lhs, rhs, &mut applicability);
}

// (x + (Y - 1)) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) {
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
}
// (x + (Y - 1)) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) {
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
}

// ((Y - 1) + x) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) {
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
}
// ((Y - 1) + x) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) {
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
}

// (x - (-Y - 1)) / Y
if inner_op.node == BinOpKind::Sub
&& let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind
&& differ_by_one(abs_div_rhs, inner_rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
// (x - (-Y - 1)) / Y
if inner_op.node == BinOpKind::Sub
&& let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind
&& differ_by_one(abs_div_rhs, inner_rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
}
},
ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) => {
// x.next_multiple_of(Y) / Y
if method.ident.name == sym::next_multiple_of
&& check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(receiver))
&& check_eq_expr(cx, next_multiple_of_arg, rhs)
{
build_suggestion(cx, expr, receiver, rhs, &mut applicability);
}
},
ExprKind::Call(callee, [receiver, next_multiple_of_arg]) => {
// int_type::next_multiple_of(x, Y) / Y
if let Some(impl_ty_binder) = callee
.ty_rel_def_if_named(cx, sym::next_multiple_of)
.assoc_fn_parent(cx)
.opt_impl_ty(cx)
&& check_int_ty_and_feature(cx, impl_ty_binder.skip_binder())
&& check_eq_expr(cx, next_multiple_of_arg, rhs)
{
build_suggestion(cx, expr, receiver, rhs, &mut applicability);
}
},
_ => (),
}
}
}
Expand All @@ -91,8 +117,7 @@ fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool {
}
}

fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
fn check_int_ty_and_feature(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool {
match expr_ty.peel_refs().kind() {
ty::Uint(_) => true,
ty::Int(_) => cx.tcx.features().enabled(sym::int_roundings),
Expand Down
1 change: 1 addition & 0 deletions clippy_utils/src/sym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ generate! {
next_back,
next_if,
next_if_eq,
next_multiple_of,
next_tuple,
nth,
ok,
Expand Down
29 changes: 29 additions & 0 deletions tests/ui/manual_div_ceil.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,32 @@ fn issue_15705(size: u64, c: &u64) {
let _ = size.div_ceil(*c);
//~^ manual_div_ceil
}

struct MyStruct(u32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: u32) -> u32 {
self.0.next_multiple_of(y)
}
}

fn issue_16219() {
let x = 33u32;

// Lint.
let _ = x.div_ceil(8);
//~^ manual_div_ceil
let _ = x.div_ceil(8);
//~^ manual_div_ceil

let y = &x;
let _ = y.div_ceil(8);
//~^ manual_div_ceil

// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;

let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}
29 changes: 29 additions & 0 deletions tests/ui/manual_div_ceil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,32 @@ fn issue_15705(size: u64, c: &u64) {
let _ = (size + c - 1) / c;
//~^ manual_div_ceil
}

struct MyStruct(u32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: u32) -> u32 {
self.0.next_multiple_of(y)
}
}

fn issue_16219() {
let x = 33u32;

// Lint.
let _ = x.next_multiple_of(8) / 8;
//~^ manual_div_ceil
let _ = u32::next_multiple_of(x, 8) / 8;
//~^ manual_div_ceil

let y = &x;
let _ = y.next_multiple_of(8) / 8;
//~^ manual_div_ceil

// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;

let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}
20 changes: 19 additions & 1 deletion tests/ui/manual_div_ceil.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,23 @@ error: manually reimplementing `div_ceil`
LL | let _ = (size + c - 1) / c;
| ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)`

error: aborting due to 20 previous errors
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil.rs:121:13
|
LL | let _ = x.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`

error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil.rs:123:13
|
LL | let _ = u32::next_multiple_of(x, 8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`

error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil.rs:127:13
|
LL | let _ = y.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(8)`

error: aborting due to 23 previous errors

29 changes: 29 additions & 0 deletions tests/ui/manual_div_ceil_with_feature.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,32 @@ fn issue_13950() {
let _ = y.div_ceil(-7);
//~^ manual_div_ceil
}

struct MyStruct(i32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: i32) -> i32 {
self.0.next_multiple_of(y)
}
}

fn issue_16219() {
let x = 33i32;

// Lint.
let _ = x.div_ceil(8);
//~^ manual_div_ceil
let _ = x.div_ceil(8);
//~^ manual_div_ceil

let y = &x;
let _ = y.div_ceil(8);
//~^ manual_div_ceil

// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;

let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}
29 changes: 29 additions & 0 deletions tests/ui/manual_div_ceil_with_feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,32 @@ fn issue_13950() {
let _ = (y - 8) / -7;
//~^ manual_div_ceil
}

struct MyStruct(i32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: i32) -> i32 {
self.0.next_multiple_of(y)
}
}

fn issue_16219() {
let x = 33i32;

// Lint.
let _ = x.next_multiple_of(8) / 8;
//~^ manual_div_ceil
let _ = i32::next_multiple_of(x, 8) / 8;
//~^ manual_div_ceil

let y = &x;
let _ = y.next_multiple_of(8) / 8;
//~^ manual_div_ceil

// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;

let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}
20 changes: 19 additions & 1 deletion tests/ui/manual_div_ceil_with_feature.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,23 @@ error: manually reimplementing `div_ceil`
LL | let _ = (y - 8) / -7;
| ^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)`

error: aborting due to 23 previous errors
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil_with_feature.rs:100:13
|
LL | let _ = x.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`

error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil_with_feature.rs:102:13
|
LL | let _ = i32::next_multiple_of(x, 8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`

error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil_with_feature.rs:106:13
|
LL | let _ = y.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(8)`

error: aborting due to 26 previous errors