Skip to content

Commit 20319e8

Browse files
Lint expressions of the form x.log(b) / y.log(b)
1 parent 4e4778b commit 20319e8

File tree

3 files changed

+182
-13
lines changed

3 files changed

+182
-13
lines changed

clippy_lints/src/floating_point_arithmetic.rs

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,31 @@ declare_clippy_lint! {
6464

6565
declare_lint_pass!(FloatingPointArithmetic => [FLOATING_POINT_IMPROVEMENTS]);
6666

67-
fn check_log_base(cx: &LateContext<'_, '_>, expr: &Expr, args: &HirVec<Expr>) {
68-
let arg = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
69-
70-
if let Some((value, _)) = constant(cx, cx.tables, &args[1]) {
71-
let method;
72-
67+
// Returns the specialized log method for a given base if base is constant
68+
// and is one of 2, 10 and e
69+
fn get_specialized_log_method(cx: &LateContext<'_, '_>, base: &Expr) -> Option<&'static str> {
70+
if let Some((value, _)) = constant(cx, cx.tables, base) {
7371
if F32(2.0) == value || F64(2.0) == value {
74-
method = "log2";
72+
return Some("log2");
7573
} else if F32(10.0) == value || F64(10.0) == value {
76-
method = "log10";
74+
return Some("log10");
7775
} else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
78-
method = "ln";
79-
} else {
80-
return;
76+
return Some("ln");
8177
}
78+
}
79+
80+
None
81+
}
8282

83+
fn check_log_base(cx: &LateContext<'_, '_>, expr: &Expr, args: &HirVec<Expr>) {
84+
if let Some(method) = get_specialized_log_method(cx, &args[1]) {
8385
span_lint_and_sugg(
8486
cx,
8587
FLOATING_POINT_IMPROVEMENTS,
8688
expr.span,
8789
"logarithm for bases 2, 10 and e can be computed more accurately",
8890
"consider using",
89-
format!("{}.{}()", arg, method),
91+
format!("{}.{}()", sugg::Sugg::hir(cx, &args[0], ".."), method),
9092
Applicability::MachineApplicable,
9193
);
9294
}
@@ -232,6 +234,74 @@ fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr) {
232234
}
233235
}
234236

237+
fn check_log_division(cx: &LateContext<'_, '_>, expr: &Expr) {
238+
let log_methods = ["log", "log2", "log10", "ln"];
239+
240+
if_chain! {
241+
if let ExprKind::Binary(op, ref lhs, ref rhs) = expr.kind;
242+
if op.node == BinOpKind::Div;
243+
if cx.tables.expr_ty(lhs).is_floating_point();
244+
if let ExprKind::MethodCall(left_path, _, left_args) = &lhs.kind;
245+
if let ExprKind::MethodCall(right_path, _, right_args) = &rhs.kind;
246+
let left_method = left_path.ident.name.as_str();
247+
if left_method == right_path.ident.name.as_str();
248+
if log_methods.iter().any(|&method| left_method == method);
249+
then {
250+
let left_recv = &left_args[0];
251+
let right_recv = &right_args[0];
252+
253+
if left_method == "log" {
254+
if_chain! {
255+
// Checks whether the bases are constant and equal
256+
if let Some((numerator_base, _)) = constant(cx, cx.tables, &left_args[1]);
257+
if let Some((denominator_base, _)) = constant(cx, cx.tables, &right_args[1]);
258+
if numerator_base == denominator_base;
259+
then { }
260+
else {
261+
if_chain! {
262+
// Checks whether the bases are the same variable
263+
if let ExprKind::Path(ref left_base_qpath) = &left_args[1].kind;
264+
if let QPath::Resolved(_, ref left_base_path) = *left_base_qpath;
265+
if left_base_path.segments.len() == 1;
266+
if let def::Res::Local(left_local_id) = qpath_res(cx, left_base_qpath, expr.hir_id);
267+
if let ExprKind::Path(ref right_base_qpath) = &right_args[1].kind;
268+
if let QPath::Resolved(_, ref right_base_path) = *right_base_qpath;
269+
if right_base_path.segments.len() == 1;
270+
if let def::Res::Local(right_local_id) = qpath_res(cx, right_base_qpath, expr.hir_id);
271+
if left_local_id == right_local_id;
272+
then { }
273+
else {
274+
return;
275+
}
276+
}
277+
}
278+
}
279+
}
280+
281+
// Reduce the expression further for bases 2, 10 and e
282+
let suggestion = if let Some(method) = get_specialized_log_method(cx, right_recv) {
283+
format!("{}.{}()", sugg::Sugg::hir(cx, left_recv, ".."), method)
284+
} else {
285+
format!(
286+
"{}.log({})",
287+
sugg::Sugg::hir(cx, left_recv, ".."),
288+
sugg::Sugg::hir(cx, right_recv, "..")
289+
)
290+
};
291+
292+
span_lint_and_sugg(
293+
cx,
294+
FLOATING_POINT_IMPROVEMENTS,
295+
expr.span,
296+
"x.log(b) / y.log(b) can be expressed more succinctly",
297+
"consider using",
298+
suggestion,
299+
Applicability::MachineApplicable,
300+
);
301+
}
302+
}
303+
}
304+
235305
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
236306
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
237307
if let ExprKind::MethodCall(ref path, _, args) = &expr.kind {
@@ -247,6 +317,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
247317
}
248318
} else {
249319
check_expm1(cx, expr);
320+
check_log_division(cx, expr);
250321
}
251322
}
252323
}

tests/ui/floating_point_arithmetic.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,30 @@ fn check_expm1() {
8282
let _ = x.exp() - 1.0 * 2.0;
8383
}
8484

85+
fn check_log_division() {
86+
let x = 3f32;
87+
let y = 2f32;
88+
let b = 4f32;
89+
90+
let _ = x.log2() / y.log2();
91+
let _ = x.log10() / y.log10();
92+
let _ = x.ln() / y.ln();
93+
let _ = x.log(4.0) / y.log(4.0);
94+
let _ = x.log(b) / y.log(b);
95+
let _ = x.log(b) / y.log(x);
96+
let _ = x.log(b) / 2f32.log(b);
97+
98+
let x = 3f64;
99+
let y = 2f64;
100+
let b = 4f64;
101+
102+
let _ = x.log2() / y.log2();
103+
let _ = x.log10() / y.log10();
104+
let _ = x.ln() / y.ln();
105+
let _ = x.log(4.0) / y.log(4.0);
106+
let _ = x.log(b) / y.log(b);
107+
let _ = x.log(b) / y.log(x);
108+
let _ = x.log(b) / 2f64.log(b);
109+
}
110+
85111
fn main() {}

tests/ui/floating_point_arithmetic.stderr

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,77 @@ error: (e.pow(x) - 1) can be computed more accurately
192192
LL | let _ = x.exp() - 1.0 + 2.0;
193193
| ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
194194

195-
error: aborting due to 32 previous errors
195+
error: x.log(b) / y.log(b) can be expressed more succinctly
196+
--> $DIR/floating_point_arithmetic.rs:90:13
197+
|
198+
LL | let _ = x.log2() / y.log2();
199+
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
200+
201+
error: x.log(b) / y.log(b) can be expressed more succinctly
202+
--> $DIR/floating_point_arithmetic.rs:91:13
203+
|
204+
LL | let _ = x.log10() / y.log10();
205+
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
206+
207+
error: x.log(b) / y.log(b) can be expressed more succinctly
208+
--> $DIR/floating_point_arithmetic.rs:92:13
209+
|
210+
LL | let _ = x.ln() / y.ln();
211+
| ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
212+
213+
error: x.log(b) / y.log(b) can be expressed more succinctly
214+
--> $DIR/floating_point_arithmetic.rs:93:13
215+
|
216+
LL | let _ = x.log(4.0) / y.log(4.0);
217+
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
218+
219+
error: x.log(b) / y.log(b) can be expressed more succinctly
220+
--> $DIR/floating_point_arithmetic.rs:94:13
221+
|
222+
LL | let _ = x.log(b) / y.log(b);
223+
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
224+
225+
error: x.log(b) / y.log(b) can be expressed more succinctly
226+
--> $DIR/floating_point_arithmetic.rs:96:13
227+
|
228+
LL | let _ = x.log(b) / 2f32.log(b);
229+
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log2()`
230+
231+
error: x.log(b) / y.log(b) can be expressed more succinctly
232+
--> $DIR/floating_point_arithmetic.rs:102:13
233+
|
234+
LL | let _ = x.log2() / y.log2();
235+
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
236+
237+
error: x.log(b) / y.log(b) can be expressed more succinctly
238+
--> $DIR/floating_point_arithmetic.rs:103:13
239+
|
240+
LL | let _ = x.log10() / y.log10();
241+
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
242+
243+
error: x.log(b) / y.log(b) can be expressed more succinctly
244+
--> $DIR/floating_point_arithmetic.rs:104:13
245+
|
246+
LL | let _ = x.ln() / y.ln();
247+
| ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
248+
249+
error: x.log(b) / y.log(b) can be expressed more succinctly
250+
--> $DIR/floating_point_arithmetic.rs:105:13
251+
|
252+
LL | let _ = x.log(4.0) / y.log(4.0);
253+
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
254+
255+
error: x.log(b) / y.log(b) can be expressed more succinctly
256+
--> $DIR/floating_point_arithmetic.rs:106:13
257+
|
258+
LL | let _ = x.log(b) / y.log(b);
259+
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
260+
261+
error: x.log(b) / y.log(b) can be expressed more succinctly
262+
--> $DIR/floating_point_arithmetic.rs:108:13
263+
|
264+
LL | let _ = x.log(b) / 2f64.log(b);
265+
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log2()`
266+
267+
error: aborting due to 44 previous errors
196268

0 commit comments

Comments
 (0)