@@ -64,29 +64,31 @@ declare_clippy_lint! {
6464
6565declare_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,82 @@ fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr) {
232234 }
233235}
234236
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+
235313impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for FloatingPointArithmetic {
236314 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
237315 if let ExprKind :: MethodCall ( ref path, _, args) = & expr. kind {
@@ -247,6 +325,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
247325 }
248326 } else {
249327 check_expm1 ( cx, expr) ;
328+ check_log_division ( cx, expr) ;
250329 }
251330 }
252331}
0 commit comments