2015-08-30 15:32:35 +00:00
use rustc ::lint ::* ;
2015-09-19 02:53:04 +00:00
use syntax ::ast ::* ;
2016-02-24 16:38:57 +00:00
use syntax ::codemap ::Spanned ;
2015-10-13 11:48:48 +00:00
use utils ::{ span_lint , snippet } ;
2015-08-30 15:32:35 +00:00
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:
2015-12-11 00:22:27 +00:00
/// * mixed usage of arithmetic and bit shifting/combining operators without parentheses
/// * a "negative" numeric literal (which is really a unary `-` followed by a numeric literal) followed by a method call
///
/// **Why is this bad?** Because not everyone knows the precedence of those operators by heart, so expressions like these may trip others trying to reason about the code.
///
/// **Known problems:** None
///
/// **Examples:**
/// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
/// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
2016-02-05 23:13:29 +00:00
declare_lint! {
pub PRECEDENCE , Warn ,
" catches operations where precedence may be unclear. See the wiki for a \
list of cases caught "
}
2015-08-30 15:32:35 +00:00
#[ derive(Copy,Clone) ]
pub struct Precedence ;
impl LintPass for Precedence {
fn get_lints ( & self ) -> LintArray {
lint_array! ( PRECEDENCE )
}
2015-09-19 02:53:04 +00:00
}
2015-08-30 15:32:35 +00:00
2015-09-19 02:53:04 +00:00
impl EarlyLintPass for Precedence {
fn check_expr ( & mut self , cx : & EarlyContext , expr : & Expr ) {
2016-04-14 18:14:03 +00:00
if let ExprKind ::Binary ( Spanned { node : op , .. } , ref left , ref right ) = expr . node {
2016-01-04 04:26:12 +00:00
if ! is_bit_op ( op ) {
return ;
}
2015-10-13 11:48:48 +00:00
match ( is_arith_expr ( left ) , is_arith_expr ( right ) ) {
2015-12-31 20:39:03 +00:00
( true , true ) = > {
2016-01-04 04:26:12 +00:00
span_lint ( cx ,
PRECEDENCE ,
expr . span ,
& format! ( " operator precedence can trip the unwary. Consider parenthesizing your \
expression :` ( { } ) { } ( { } ) ` " ,
snippet ( cx , left . span , " .. " ) ,
op . to_string ( ) ,
snippet ( cx , right . span , " .. " ) ) ) ;
}
2015-12-31 20:39:03 +00:00
( true , false ) = > {
2016-01-04 04:26:12 +00:00
span_lint ( cx ,
PRECEDENCE ,
expr . span ,
& format! ( " operator precedence can trip the unwary. Consider parenthesizing your \
expression :` ( { } ) { } { } ` " ,
snippet ( cx , left . span , " .. " ) ,
op . to_string ( ) ,
snippet ( cx , right . span , " .. " ) ) ) ;
}
2015-12-31 20:39:03 +00:00
( false , true ) = > {
2016-01-04 04:26:12 +00:00
span_lint ( cx ,
PRECEDENCE ,
expr . span ,
& format! ( " operator precedence can trip the unwary. Consider parenthesizing your \
expression :` { } { } ( { } ) ` " ,
snippet ( cx , left . span , " .. " ) ,
op . to_string ( ) ,
snippet ( cx , right . span , " .. " ) ) ) ;
}
2015-10-13 11:48:48 +00:00
_ = > ( ) ,
2015-08-30 15:32:35 +00:00
}
}
2016-02-12 17:35:44 +00:00
if let ExprKind ::Unary ( UnOp ::Neg , ref rhs ) = expr . node {
if let ExprKind ::MethodCall ( _ , _ , ref args ) = rhs . node {
2015-08-30 15:32:35 +00:00
if let Some ( slf ) = args . first ( ) {
2016-02-12 17:35:44 +00:00
if let ExprKind ::Lit ( ref lit ) = slf . node {
2015-08-30 15:32:35 +00:00
match lit . node {
2016-04-14 18:14:03 +00:00
LitKind ::Int ( .. ) |
LitKind ::Float ( .. ) |
LitKind ::FloatUnsuffixed ( .. ) = > {
2016-01-04 04:26:12 +00:00
span_lint ( cx ,
PRECEDENCE ,
expr . span ,
& format! ( " unary minus has lower precedence than method call. Consider \
adding parentheses to clarify your intent : - ( { } ) " ,
snippet ( cx , rhs . span , " .. " ) ) ) ;
2015-12-31 20:39:03 +00:00
}
2016-01-04 04:26:12 +00:00
_ = > ( ) ,
2015-08-30 15:32:35 +00:00
}
}
}
}
}
}
}
2015-11-17 05:22:57 +00:00
fn is_arith_expr ( expr : & Expr ) -> bool {
2015-08-30 15:32:35 +00:00
match expr . node {
2016-04-14 18:14:03 +00:00
ExprKind ::Binary ( Spanned { node : op , .. } , _ , _ ) = > is_arith_op ( op ) ,
2016-01-04 04:26:12 +00:00
_ = > false ,
2015-08-30 15:32:35 +00:00
}
}
2016-02-12 17:35:44 +00:00
fn is_bit_op ( op : BinOpKind ) -> bool {
use syntax ::ast ::BinOpKind ::* ;
2015-08-30 15:32:35 +00:00
match op {
2016-02-12 17:35:44 +00:00
BitXor | BitAnd | BitOr | Shl | Shr = > true ,
2016-01-04 04:26:12 +00:00
_ = > false ,
2015-08-30 15:32:35 +00:00
}
}
2016-02-12 17:35:44 +00:00
fn is_arith_op ( op : BinOpKind ) -> bool {
use syntax ::ast ::BinOpKind ::* ;
2015-08-30 15:32:35 +00:00
match op {
2016-02-12 17:35:44 +00:00
Add | Sub | Mul | Div | Rem = > true ,
2016-01-04 04:26:12 +00:00
_ = > false ,
2015-08-30 15:32:35 +00:00
}
}