2015-08-30 15:32:35 +00:00
use rustc ::lint ::* ;
use syntax ::codemap ::Spanned ;
2015-09-19 02:53:04 +00:00
use syntax ::ast ::* ;
2015-10-13 11:48:48 +00:00
use utils ::{ span_lint , snippet } ;
2015-08-30 15:32:35 +00:00
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for operations where precedence may be unclear and `Warn`'s about them by default, suggesting to add parentheses. Currently it catches the following:
/// * 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
2015-08-30 15:32:35 +00:00
declare_lint! ( pub PRECEDENCE , Warn ,
" catches operations where precedence may be unclear. See the wiki for a \
list of cases caught " );
#[ 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 ) {
2015-08-30 15:32:35 +00:00
if let ExprBinary ( Spanned { node : op , .. } , ref left , ref right ) = expr . node {
2015-10-13 11:48:48 +00:00
if ! is_bit_op ( op ) { return ; }
match ( is_arith_expr ( left ) , is_arith_expr ( right ) ) {
( true , true ) = > span_lint ( cx , PRECEDENCE , expr . span ,
& format! ( " operator precedence can trip the unwary. \
Consider parenthesizing your expression :\
` ( { } ) { } ( { } ) ` " , snippet(cx, left.span, " .. " ),
2015-12-19 13:38:22 +00:00
op . to_string ( ) , snippet ( cx , right . span , " .. " ) ) ) ,
2015-10-13 11:48:48 +00:00
( true , false ) = > span_lint ( cx , PRECEDENCE , expr . span ,
& format! ( " operator precedence can trip the unwary. \
Consider parenthesizing your expression :\
` ( { } ) { } { } ` " , snippet(cx, left.span, " .. " ),
2015-12-19 13:38:22 +00:00
op . to_string ( ) , snippet ( cx , right . span , " .. " ) ) ) ,
2015-10-13 11:48:48 +00:00
( false , true ) = > span_lint ( cx , PRECEDENCE , expr . span ,
& format! ( " operator precedence can trip the unwary. \
Consider parenthesizing your expression :\
` { } { } ( { } ) ` " , snippet(cx, left.span, " .. " ),
2015-12-19 13:38:22 +00:00
op . to_string ( ) , snippet ( cx , right . span , " .. " ) ) ) ,
2015-10-13 11:48:48 +00:00
_ = > ( ) ,
2015-08-30 15:32:35 +00:00
}
}
if let ExprUnary ( UnNeg , ref rhs ) = expr . node {
if let ExprMethodCall ( _ , _ , ref args ) = rhs . node {
if let Some ( slf ) = args . first ( ) {
if let ExprLit ( ref lit ) = slf . node {
match lit . node {
LitInt ( .. ) | LitFloat ( .. ) | LitFloatUnsuffixed ( .. ) = >
2015-10-13 11:48:48 +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-11-17 05:22:57 +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 {
ExprBinary ( Spanned { node : op , .. } , _ , _ ) = > is_arith_op ( op ) ,
_ = > false
}
}
2015-11-17 05:22:57 +00:00
fn is_bit_op ( op : BinOp_ ) -> bool {
2015-08-30 15:32:35 +00:00
match op {
BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr = > true ,
_ = > false
}
}
2015-11-17 05:22:57 +00:00
fn is_arith_op ( op : BinOp_ ) -> bool {
2015-08-30 15:32:35 +00:00
match op {
BiAdd | BiSub | BiMul | BiDiv | BiRem = > true ,
_ = > false
}
}