@@ -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,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+
235305impl < ' 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}
0 commit comments