@@ -64,29 +64,31 @@ declare_clippy_lint! {
64
64
65
65
declare_lint_pass ! ( FloatingPointArithmetic => [ FLOATING_POINT_IMPROVEMENTS ] ) ;
66
66
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) {
73
71
if F32 ( 2.0 ) == value || F64 ( 2.0 ) == value {
74
- method = "log2" ;
72
+ return Some ( "log2" ) ;
75
73
} else if F32 ( 10.0 ) == value || F64 ( 10.0 ) == value {
76
- method = "log10" ;
74
+ return Some ( "log10" ) ;
77
75
} else if F32 ( f32_consts:: E ) == value || F64 ( f64_consts:: E ) == value {
78
- method = "ln" ;
79
- } else {
80
- return ;
76
+ return Some ( "ln" ) ;
81
77
}
78
+ }
79
+
80
+ None
81
+ }
82
82
83
+ fn check_log_base ( cx : & LateContext < ' _ , ' _ > , expr : & Expr , args : & HirVec < Expr > ) {
84
+ if let Some ( method) = get_specialized_log_method ( cx, & args[ 1 ] ) {
83
85
span_lint_and_sugg (
84
86
cx,
85
87
FLOATING_POINT_IMPROVEMENTS ,
86
88
expr. span ,
87
89
"logarithm for bases 2, 10 and e can be computed more accurately" ,
88
90
"consider using" ,
89
- format ! ( "{}.{}()" , arg , method) ,
91
+ format ! ( "{}.{}()" , sugg :: Sugg :: hir ( cx , & args [ 0 ] , ".." ) , method) ,
90
92
Applicability :: MachineApplicable ,
91
93
) ;
92
94
}
@@ -232,6 +234,82 @@ fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr) {
232
234
}
233
235
}
234
236
237
+ // Checks whether two expressions evaluate to the same value
238
+ fn are_exprs_equivalent ( cx : & LateContext < ' _ , ' _ > , left : & Expr , right : & Expr ) -> bool {
239
+ // Checks whether the values are constant and equal
240
+ if_chain ! {
241
+ if let Some ( ( left_value, _) ) = constant( cx, cx. tables, left) ;
242
+ if let Some ( ( right_value, _) ) = constant( cx, cx. tables, right) ;
243
+ if left_value == right_value;
244
+ then {
245
+ return true ;
246
+ }
247
+ }
248
+
249
+ // Checks whether the expressions resolve to the same variable
250
+ if_chain ! {
251
+ if let ExprKind :: Path ( ref left_qpath) = left. kind;
252
+ if let QPath :: Resolved ( _, ref left_path) = * left_qpath;
253
+ if left_path. segments. len( ) == 1 ;
254
+ if let def:: Res :: Local ( left_local_id) = qpath_res( cx, left_qpath, left. hir_id) ;
255
+ if let ExprKind :: Path ( ref right_qpath) = right. kind;
256
+ if let QPath :: Resolved ( _, ref right_path) = * right_qpath;
257
+ if right_path. segments. len( ) == 1 ;
258
+ if let def:: Res :: Local ( right_local_id) = qpath_res( cx, right_qpath, right. hir_id) ;
259
+ if left_local_id == right_local_id;
260
+ then {
261
+ return true ;
262
+ }
263
+ }
264
+
265
+ false
266
+ }
267
+
268
+ fn check_log_division ( cx : & LateContext < ' _ , ' _ > , expr : & Expr ) {
269
+ let log_methods = [ "log" , "log2" , "log10" , "ln" ] ;
270
+
271
+ if_chain ! {
272
+ if let ExprKind :: Binary ( op, ref lhs, ref rhs) = expr. kind;
273
+ if op. node == BinOpKind :: Div ;
274
+ if cx. tables. expr_ty( lhs) . is_floating_point( ) ;
275
+ if let ExprKind :: MethodCall ( left_path, _, left_args) = & lhs. kind;
276
+ if let ExprKind :: MethodCall ( right_path, _, right_args) = & rhs. kind;
277
+ let left_method = left_path. ident. name. as_str( ) ;
278
+ if left_method == right_path. ident. name. as_str( ) ;
279
+ if log_methods. iter( ) . any( |& method| left_method == method) ;
280
+ then {
281
+ let left_recv = & left_args[ 0 ] ;
282
+ let right_recv = & right_args[ 0 ] ;
283
+
284
+ // Return early when bases are not equal
285
+ if left_method == "log" && !are_exprs_equivalent( cx, & left_args[ 1 ] , & right_args[ 1 ] ) {
286
+ return ;
287
+ }
288
+
289
+ // Reduce the expression further for bases 2, 10 and e
290
+ let suggestion = if let Some ( method) = get_specialized_log_method( cx, right_recv) {
291
+ format!( "{}.{}()" , sugg:: Sugg :: hir( cx, left_recv, ".." ) , method)
292
+ } else {
293
+ format!(
294
+ "{}.log({})" ,
295
+ sugg:: Sugg :: hir( cx, left_recv, ".." ) ,
296
+ sugg:: Sugg :: hir( cx, right_recv, ".." )
297
+ )
298
+ } ;
299
+
300
+ span_lint_and_sugg(
301
+ cx,
302
+ FLOATING_POINT_IMPROVEMENTS ,
303
+ expr. span,
304
+ "x.log(b) / y.log(b) can be reduced to x.log(y)" ,
305
+ "consider using" ,
306
+ suggestion,
307
+ Applicability :: MachineApplicable ,
308
+ ) ;
309
+ }
310
+ }
311
+ }
312
+
235
313
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for FloatingPointArithmetic {
236
314
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
237
315
if let ExprKind :: MethodCall ( ref path, _, args) = & expr. kind {
@@ -247,6 +325,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
247
325
}
248
326
} else {
249
327
check_expm1 ( cx, expr) ;
328
+ check_log_division ( cx, expr) ;
250
329
}
251
330
}
252
331
}
0 commit comments