2014-11-19 18:48:31 +00:00
use syntax ::ptr ::P ;
use syntax ::ast ;
use syntax ::ast ::* ;
2015-05-06 08:01:49 +00:00
use syntax ::ast_util ::{ is_comparison_binop , binop_to_string } ;
2014-12-24 23:15:22 +00:00
use syntax ::visit ::{ FnKind } ;
2014-11-19 18:48:31 +00:00
use rustc ::lint ::{ Context , LintPass , LintArray , Lint , Level } ;
2015-05-06 08:01:49 +00:00
use rustc ::middle ::ty ::{ self , expr_ty , ty_str , ty_ptr , ty_rptr , ty_float } ;
2015-05-06 10:59:08 +00:00
use syntax ::codemap ::{ Span , Spanned } ;
2014-11-19 18:48:31 +00:00
2015-05-06 08:01:49 +00:00
2014-11-19 18:48:31 +00:00
use types ::span_note_and_lint ;
2015-05-06 08:01:49 +00:00
fn walk_ty < ' t > ( ty : ty ::Ty < ' t > ) -> ty ::Ty < ' t > {
match ty . sty {
ty_ptr ( ref tm ) | ty_rptr ( _ , ref tm ) = > walk_ty ( tm . ty ) ,
_ = > ty
}
}
2014-11-19 18:48:31 +00:00
/// Handles uncategorized lints
/// Currently handles linting of if-let-able matches
2014-12-25 23:04:49 +00:00
#[ allow(missing_copy_implementations) ]
2014-11-19 18:48:31 +00:00
pub struct MiscPass ;
2014-12-25 23:54:44 +00:00
declare_lint! ( pub SINGLE_MATCH , Warn ,
2014-12-19 09:11:00 +00:00
" Warn on usage of matches with a single nontrivial arm " ) ;
2014-11-19 18:48:31 +00:00
impl LintPass for MiscPass {
fn get_lints ( & self ) -> LintArray {
2014-12-25 23:54:44 +00:00
lint_array! ( SINGLE_MATCH )
2014-11-19 18:48:31 +00:00
}
fn check_expr ( & mut self , cx : & Context , expr : & Expr ) {
2014-12-25 23:04:49 +00:00
if let ExprMatch ( ref ex , ref arms , ast ::MatchSource ::Normal ) = expr . node {
2014-11-19 18:48:31 +00:00
if arms . len ( ) = = 2 {
if arms [ 0 ] . guard . is_none ( ) & & arms [ 1 ] . pats . len ( ) = = 1 {
match arms [ 1 ] . body . node {
ExprTup ( ref v ) if v . len ( ) = = 0 & & arms [ 1 ] . guard . is_none ( ) = > ( ) ,
ExprBlock ( ref b ) if b . stmts . len ( ) = = 0 & & arms [ 1 ] . guard . is_none ( ) = > ( ) ,
_ = > return
}
// In some cases, an exhaustive match is preferred to catch situations when
// an enum is extended. So we only consider cases where a `_` wildcard is used
if arms [ 1 ] . pats [ 0 ] . node = = PatWild ( PatWildSingle ) & & arms [ 0 ] . pats . len ( ) = = 1 {
let map = cx . sess ( ) . codemap ( ) ;
2014-12-25 23:54:44 +00:00
span_note_and_lint ( cx , SINGLE_MATCH , expr . span ,
2014-11-19 18:48:31 +00:00
" You seem to be trying to use match for destructuring a single type. Did you mean to use `if let`? " ,
2015-04-20 10:48:35 +00:00
& * format! ( " Try if let {} = {} {{ ... }} " ,
& * map . span_to_snippet ( arms [ 0 ] . pats [ 0 ] . span ) . unwrap_or ( " .. " . to_string ( ) ) ,
& * map . span_to_snippet ( ex . span ) . unwrap_or ( " .. " . to_string ( ) ) )
2014-12-24 23:15:22 +00:00
) ;
2014-11-19 18:48:31 +00:00
}
}
}
}
}
}
2014-12-15 14:53:45 +00:00
2015-01-10 05:22:03 +00:00
declare_lint! ( pub STR_TO_STRING , Warn , " Warn when a String could use to_owned() instead of to_string() " ) ;
2014-12-15 14:53:45 +00:00
2014-12-25 23:04:49 +00:00
#[ allow(missing_copy_implementations) ]
2014-12-15 14:53:45 +00:00
pub struct StrToStringPass ;
impl LintPass for StrToStringPass {
fn get_lints ( & self ) -> LintArray {
2014-12-25 23:54:44 +00:00
lint_array! ( STR_TO_STRING )
2014-12-15 14:53:45 +00:00
}
fn check_expr ( & mut self , cx : & Context , expr : & ast ::Expr ) {
match expr . node {
ast ::ExprMethodCall ( ref method , _ , ref args )
if method . node . as_str ( ) = = " to_string "
& & is_str ( cx , & * args [ 0 ] ) = > {
2015-01-10 05:22:03 +00:00
cx . span_lint ( STR_TO_STRING , expr . span , " str.to_owned() is faster " ) ;
2014-12-15 14:53:45 +00:00
} ,
_ = > ( )
}
fn is_str ( cx : & Context , expr : & ast ::Expr ) -> bool {
match walk_ty ( expr_ty ( cx . tcx , expr ) ) . sty {
ty_str = > true ,
_ = > false
}
}
}
}
2014-12-24 23:15:22 +00:00
2014-12-25 23:54:44 +00:00
declare_lint! ( pub TOPLEVEL_REF_ARG , Warn , " Warn about pattern matches with top-level `ref` bindings " ) ;
2014-12-24 23:15:22 +00:00
2014-12-25 23:22:18 +00:00
#[ allow(missing_copy_implementations) ]
2014-12-24 23:15:22 +00:00
pub struct TopLevelRefPass ;
impl LintPass for TopLevelRefPass {
fn get_lints ( & self ) -> LintArray {
2014-12-25 23:54:44 +00:00
lint_array! ( TOPLEVEL_REF_ARG )
2014-12-24 23:15:22 +00:00
}
fn check_fn ( & mut self , cx : & Context , _ : FnKind , decl : & FnDecl , _ : & Block , _ : Span , _ : NodeId ) {
for ref arg in decl . inputs . iter ( ) {
if let PatIdent ( BindByRef ( _ ) , _ , _ ) = arg . pat . node {
cx . span_lint (
2014-12-25 23:54:44 +00:00
TOPLEVEL_REF_ARG ,
2014-12-24 23:15:22 +00:00
arg . pat . span ,
" `ref` directly on a function argument is ignored. Have you considered using a reference type instead? "
) ;
}
}
}
}
2015-05-04 12:11:15 +00:00
2015-05-06 08:01:49 +00:00
declare_lint! ( pub CMP_NAN , Deny , " Deny comparisons to std::f32::NAN or std::f64::NAN " ) ;
2015-05-04 12:11:15 +00:00
#[ derive(Copy,Clone) ]
pub struct CmpNan ;
impl LintPass for CmpNan {
fn get_lints ( & self ) -> LintArray {
lint_array! ( CMP_NAN )
}
fn check_expr ( & mut self , cx : & Context , expr : & Expr ) {
if let ExprBinary ( ref cmp , ref left , ref right ) = expr . node {
if is_comparison_binop ( cmp . node ) {
if let & ExprPath ( _ , ref path ) = & left . node {
check_nan ( cx , path , expr . span ) ;
}
if let & ExprPath ( _ , ref path ) = & right . node {
check_nan ( cx , path , expr . span ) ;
}
}
}
}
}
fn check_nan ( cx : & Context , path : & Path , span : Span ) {
path . segments . last ( ) . map ( | seg | if seg . identifier . as_str ( ) = = " NAN " {
cx . span_lint ( CMP_NAN , span , " Doomed comparison with NAN, use std::{f32,f64}::is_nan instead " ) ;
} ) ;
}
2015-05-06 08:01:49 +00:00
declare_lint! ( pub FLOAT_CMP , Warn ,
" Warn on ==/!= comparison of floaty values " ) ;
#[ derive(Copy,Clone) ]
pub struct FloatCmp ;
impl LintPass for FloatCmp {
fn get_lints ( & self ) -> LintArray {
lint_array! ( FLOAT_CMP )
}
fn check_expr ( & mut self , cx : & Context , expr : & Expr ) {
if let ExprBinary ( ref cmp , ref left , ref right ) = expr . node {
let op = cmp . node ;
if ( op = = BiEq | | op = = BiNe ) & & ( is_float ( cx , left ) | | is_float ( cx , right ) ) {
let map = cx . sess ( ) . codemap ( ) ;
cx . span_lint ( FLOAT_CMP , expr . span , & format! (
" {}-Comparison of f32 or f64 detected. You may want to change this to 'abs({} - {}) < epsilon' for some suitable value of epsilon " ,
binop_to_string ( op ) , & * map . span_to_snippet ( left . span ) . unwrap_or ( " .. " . to_string ( ) ) ,
& * map . span_to_snippet ( right . span ) . unwrap_or ( " .. " . to_string ( ) ) ) ) ;
}
}
}
}
fn is_float ( cx : & Context , expr : & Expr ) -> bool {
if let ty_float ( _ ) = walk_ty ( expr_ty ( cx . tcx , expr ) ) . sty { true } else { false }
}
2015-05-06 10:59:08 +00:00
declare_lint! ( pub PRECEDENCE , Warn ,
" Warn on mixing bit ops with integer arithmetic without parenthesis " ) ;
#[ derive(Copy,Clone) ]
pub struct Precedence ;
impl LintPass for Precedence {
fn get_lints ( & self ) -> LintArray {
lint_array! ( PRECEDENCE )
}
fn check_expr ( & mut self , cx : & Context , expr : & Expr ) {
if let ExprBinary ( Spanned { node : op , .. } , ref left , ref right ) = expr . node {
if is_bit_op ( op ) {
if let ExprBinary ( Spanned { node : lop , .. } , _ , _ ) = left . node {
if is_arith_op ( lop ) {
cx . span_lint ( PRECEDENCE , expr . span , " Operator precedence can trip the unwary. Please consider adding parenthesis to the subexpression to make the meaning more clear. " ) ;
}
} else {
if let ExprBinary ( Spanned { node : rop , .. } , _ , _ ) = right . node {
if is_arith_op ( rop ) {
cx . span_lint ( PRECEDENCE , expr . span , " Operator precedence can trip the unwary. Please consider adding parenthesis to the subexpression to make the meaning more clear. " ) ;
}
}
}
}
}
}
}
fn is_bit_op ( op : BinOp_ ) -> bool {
match op {
BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr = > true ,
_ = > false
}
}
fn is_arith_op ( op : BinOp_ ) -> bool {
match op {
BiAdd | BiSub | BiMul | BiDiv | BiRem = > true ,
_ = > false
}
}