@@ -293,13 +293,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
293293 }
294294}
295295
296- fn check_powi ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
296+ fn check_powi ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
297297 // Check argument
298- if let Some ( ( value, _) ) = constant ( cx, cx. tables , & args[ 1 ] ) {
298+ if let Some ( ( value, _) ) = constant ( cx, cx. tables ( ) , & args[ 1 ] ) {
299299 // TODO: need more specific check. this is too wide. remember also to include tests
300300 if let Some ( parent) = get_parent_expr ( cx, expr) {
301301 if let Some ( grandparent) = get_parent_expr ( cx, parent) {
302- if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args) = grandparent. kind {
302+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args, _ ) = grandparent. kind {
303303 if method_name. as_str ( ) == "sqrt" && detect_hypot ( cx, args) . is_some ( ) {
304304 return ;
305305 }
@@ -328,7 +328,7 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
328328 }
329329}
330330
331- fn detect_hypot ( cx : & LateContext < ' _ , ' _ > , args : & [ Expr < ' _ > ] ) -> Option < String > {
331+ fn detect_hypot ( cx : & LateContext < ' _ > , args : & [ Expr < ' _ > ] ) -> Option < String > {
332332 if let ExprKind :: Binary (
333333 Spanned {
334334 node : BinOpKind :: Add , ..
@@ -350,11 +350,11 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option<String> {
350350
351351 // check if expression of the form x.powi(2) + y.powi(2)
352352 if_chain ! {
353- if let ExprKind :: MethodCall ( PathSegment { ident: lmethod_name, .. } , ref _lspan, ref largs) = add_lhs. kind;
354- if let ExprKind :: MethodCall ( PathSegment { ident: rmethod_name, .. } , ref _rspan, ref rargs) = add_rhs. kind;
353+ if let ExprKind :: MethodCall ( PathSegment { ident: lmethod_name, .. } , ref _lspan, ref largs, _ ) = add_lhs. kind;
354+ if let ExprKind :: MethodCall ( PathSegment { ident: rmethod_name, .. } , ref _rspan, ref rargs, _ ) = add_rhs. kind;
355355 if lmethod_name. as_str( ) == "powi" && rmethod_name. as_str( ) == "powi" ;
356- if let Some ( ( lvalue, _) ) = constant( cx, cx. tables, & largs[ 1 ] ) ;
357- if let Some ( ( rvalue, _) ) = constant( cx, cx. tables, & rargs[ 1 ] ) ;
356+ if let Some ( ( lvalue, _) ) = constant( cx, cx. tables( ) , & largs[ 1 ] ) ;
357+ if let Some ( ( rvalue, _) ) = constant( cx, cx. tables( ) , & rargs[ 1 ] ) ;
358358 if Int ( 2 ) == lvalue && Int ( 2 ) == rvalue;
359359 then {
360360 return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & largs[ 0 ] , ".." ) , Sugg :: hir( cx, & rargs[ 0 ] , ".." ) ) ) ;
@@ -365,7 +365,7 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option<String> {
365365 None
366366}
367367
368- fn check_hypot ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
368+ fn check_hypot ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
369369 if let Some ( message) = detect_hypot ( cx, args) {
370370 span_lint_and_sugg (
371371 cx,
@@ -431,7 +431,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
431431 ) = & expr. kind
432432 {
433433 if let Some ( parent) = get_parent_expr ( cx, expr) {
434- if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args) = parent. kind {
434+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args, _ ) = parent. kind {
435435 if method_name. as_str ( ) == "sqrt" && detect_hypot ( cx, args) . is_some ( ) {
436436 return ;
437437 }
@@ -573,6 +573,50 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
573573 }
574574}
575575
576+ fn are_same_base_logs ( cx : & LateContext < ' _ > , expr_a : & Expr < ' _ > , expr_b : & Expr < ' _ > ) -> bool {
577+ if_chain ! {
578+ if let ExprKind :: MethodCall ( PathSegment { ident: method_name_a, .. } , _, ref args_a, _) = expr_a. kind;
579+ if let ExprKind :: MethodCall ( PathSegment { ident: method_name_b, .. } , _, ref args_b, _) = expr_b. kind;
580+ then {
581+ return method_name_a. as_str( ) == method_name_b. as_str( ) &&
582+ args_a. len( ) == args_b. len( ) &&
583+ (
584+ [ "ln" , "log2" , "log10" ] . contains( &&* method_name_a. as_str( ) ) ||
585+ method_name_a. as_str( ) == "log" && args_a. len( ) == 2 && are_exprs_equal( cx, & args_a[ 1 ] , & args_b[ 1 ] )
586+ ) ;
587+ }
588+ }
589+
590+ false
591+ }
592+
593+ fn check_log_division ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
594+ // check if expression of the form x.logN() / y.logN()
595+ if_chain ! {
596+ if let ExprKind :: Binary (
597+ Spanned {
598+ node: BinOpKind :: Div , ..
599+ } ,
600+ lhs,
601+ rhs,
602+ ) = & expr. kind;
603+ if are_same_base_logs( cx, lhs, rhs) ;
604+ if let ExprKind :: MethodCall ( _, _, ref largs, _) = lhs. kind;
605+ if let ExprKind :: MethodCall ( _, _, ref rargs, _) = rhs. kind;
606+ then {
607+ span_lint_and_sugg(
608+ cx,
609+ SUBOPTIMAL_FLOPS ,
610+ expr. span,
611+ "division of logarithms can be calculated more efficiently and accurately" ,
612+ "consider using" ,
613+ format!( "{}.log({})" , Sugg :: hir( cx, & largs[ 0 ] , ".." ) , Sugg :: hir( cx, & rargs[ 0 ] , ".." ) , ) ,
614+ Applicability :: MachineApplicable ,
615+ ) ;
616+ }
617+ }
618+ }
619+
576620impl < ' tcx > LateLintPass < ' tcx > for FloatingPointArithmetic {
577621 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
578622 if let ExprKind :: MethodCall ( ref path, _, args, _) = & expr. kind {
@@ -592,6 +636,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
592636 check_expm1 ( cx, expr) ;
593637 check_mul_add ( cx, expr) ;
594638 check_custom_abs ( cx, expr) ;
639+ check_log_division ( cx, expr) ;
595640 }
596641 }
597642}
0 commit comments