2015-08-16 06:54:43 +00:00
use rustc ::lint ::* ;
2014-11-19 18:48:31 +00:00
use syntax ::ptr ::P ;
2015-09-03 14:42:17 +00:00
use rustc_front ::hir ::* ;
use reexport ::* ;
use rustc_front ::util ::{ is_comparison_binop , binop_to_string } ;
2015-05-06 10:59:08 +00:00
use syntax ::codemap ::{ Span , Spanned } ;
2015-11-19 14:51:30 +00:00
use rustc_front ::intravisit ::FnKind ;
2015-08-16 06:54:43 +00:00
use rustc ::middle ::ty ;
2015-11-10 10:19:33 +00:00
use rustc ::middle ::const_eval ::ConstVal ::Float ;
use rustc ::middle ::const_eval ::eval_const_expr_partial ;
use rustc ::middle ::const_eval ::EvalHint ::ExprTypeChecked ;
2015-12-11 22:02:02 +00:00
use rustc ::middle ::def ::Def ;
2014-11-19 18:48:31 +00:00
2015-09-04 13:26:58 +00:00
use utils ::{ get_item_name , match_path , snippet , span_lint , walk_ptrs_ty , is_integer_literal } ;
2015-09-22 07:08:42 +00:00
use utils ::span_help_and_lint ;
2015-05-06 08:01:49 +00:00
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`. It is `Warn` by default.
///
/// **Why is this bad?** The `ref` declaration makes the function take an owned value, but turns the argument into a reference (which means that the value is destroyed when exiting the function). This adds not much value: either take a reference type, or take an owned value and create references in the body.
///
/// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The type of `x` is more obvious with the former.
///
/// **Known problems:** If the argument is dereferenced within the function, removing the `ref` will lead to errors. This can be fixed by removing the dereferences, e.g. changing `*x` to `x` within the function.
///
/// **Example:** `fn foo(ref x: u8) -> bool { .. }`
2015-08-13 08:32:35 +00:00
declare_lint! ( pub TOPLEVEL_REF_ARG , Warn ,
2015-09-22 07:08:42 +00:00
" An entire binding was declared as `ref`, in a function argument (`fn foo(ref x: Bar)`), \
or a ` let ` statement ( ` let ref x = foo ( ) ` ) . In such cases , it is preferred to take \
references with ` & ` . " );
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
}
2015-09-19 02:53:04 +00:00
}
2014-12-24 23:15:22 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for TopLevelRefPass {
fn check_fn ( & mut self , cx : & LateContext , k : FnKind , decl : & FnDecl , _ : & Block , _ : Span , _ : NodeId ) {
2015-09-01 12:28:23 +00:00
if let FnKind ::Closure = k {
2015-08-16 11:54:03 +00:00
// Does not apply to closures
return
}
2015-08-13 14:41:51 +00:00
for ref arg in & decl . inputs {
2014-12-24 23:15:22 +00:00
if let PatIdent ( BindByRef ( _ ) , _ , _ ) = arg . pat . node {
2015-08-11 15:02:04 +00:00
span_lint ( cx ,
2014-12-25 23:54:44 +00:00
TOPLEVEL_REF_ARG ,
2014-12-24 23:15:22 +00:00
arg . pat . span ,
2015-08-12 08:46:49 +00:00
" `ref` directly on a function argument is ignored. Consider using a reference type instead. "
2014-12-24 23:15:22 +00:00
) ;
}
}
}
2015-09-22 07:08:42 +00:00
fn check_stmt ( & mut self , cx : & LateContext , s : & Stmt ) {
if_let_chain! {
[
let StmtDecl ( ref d , _ ) = s . node ,
let DeclLocal ( ref l ) = d . node ,
let PatIdent ( BindByRef ( _ ) , i , None ) = l . pat . node ,
let Some ( ref init ) = l . init
] , {
let tyopt = if let Some ( ref ty ) = l . ty {
format! ( " : {:?} " , ty )
} else {
" " . to_owned ( )
} ;
span_help_and_lint ( cx ,
TOPLEVEL_REF_ARG ,
l . pat . span ,
" `ref` on an entire `let` pattern is discouraged, take a reference with & instead " ,
& format! ( " try `let {} {} = & {} ;` " , snippet ( cx , i . span , " _ " ) ,
tyopt , snippet ( cx , init . span , " _ " ) )
) ;
}
} ;
}
2014-12-24 23:15:22 +00:00
}
2015-05-04 12:11:15 +00:00
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for comparisons to NAN. It is `Deny` by default.
///
/// **Why is this bad?** NAN does not compare meaningfully to anything – not even itself – so those comparisons are simply wrong.
///
/// **Known problems:** None
///
/// **Example:** `x == NAN`
2015-08-13 08:32:35 +00:00
declare_lint! ( pub CMP_NAN , Deny ,
" comparisons to NAN (which will always return false, which is probably not intended) " ) ;
2015-05-04 12:11:15 +00:00
#[ derive(Copy,Clone) ]
pub struct CmpNan ;
impl LintPass for CmpNan {
2015-08-11 13:07:21 +00:00
fn get_lints ( & self ) -> LintArray {
2015-05-04 12:11:15 +00:00
lint_array! ( CMP_NAN )
2015-08-11 13:07:21 +00:00
}
2015-09-19 02:53:04 +00:00
}
2015-08-11 15:02:04 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for CmpNan {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-11 13:07:21 +00:00
if let ExprBinary ( ref cmp , ref left , ref right ) = expr . node {
if is_comparison_binop ( cmp . node ) {
2015-11-24 17:44:40 +00:00
if let ExprPath ( _ , ref path ) = left . node {
2015-08-11 13:07:21 +00:00
check_nan ( cx , path , expr . span ) ;
}
2015-11-24 17:44:40 +00:00
if let ExprPath ( _ , ref path ) = right . node {
2015-08-11 13:07:21 +00:00
check_nan ( cx , path , expr . span ) ;
}
}
}
}
2015-05-04 12:11:15 +00:00
}
2015-09-19 02:53:04 +00:00
fn check_nan ( cx : & LateContext , path : & Path , span : Span ) {
2015-09-28 05:04:06 +00:00
path . segments . last ( ) . map ( | seg | if seg . identifier . name . as_str ( ) = = " NAN " {
2015-08-11 15:02:04 +00:00
span_lint ( cx , CMP_NAN , span ,
2015-08-27 05:39:40 +00:00
" doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead " ) ;
2015-08-11 15:02:04 +00:00
} ) ;
2015-05-04 12:11:15 +00:00
}
2015-05-06 08:01:49 +00:00
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably implement equality for a type involving floats). It is `Warn` by default.
///
/// **Why is this bad?** Floating point calculations are usually imprecise, so asking if two values are *exactly* equal is asking for trouble. For a good guide on what to do, see [the floating point guide](http://www.floating-point-gui.de/errors/comparison).
///
/// **Known problems:** None
///
/// **Example:** `y == 1.23f64`
2015-05-06 08:01:49 +00:00
declare_lint! ( pub FLOAT_CMP , Warn ,
2015-08-13 08:32:35 +00:00
" using `==` or `!=` on float values (as floating-point operations \
usually involve rounding errors , it is always better to check for approximate \
equality within small bounds ) " );
2015-08-11 15:02:04 +00:00
2015-05-06 08:01:49 +00:00
#[ derive(Copy,Clone) ]
pub struct FloatCmp ;
impl LintPass for FloatCmp {
2015-08-11 13:07:21 +00:00
fn get_lints ( & self ) -> LintArray {
2015-05-06 08:01:49 +00:00
lint_array! ( FLOAT_CMP )
2015-08-11 13:07:21 +00:00
}
2015-09-19 02:53:04 +00:00
}
2015-08-11 15:02:04 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for FloatCmp {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-11 13:07:21 +00:00
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 ) ) {
2015-11-10 10:19:33 +00:00
if is_allowed ( cx , left ) | | is_allowed ( cx , right ) { return ; }
2015-09-06 19:03:09 +00:00
if let Some ( name ) = get_item_name ( cx , expr ) {
2015-09-28 05:04:06 +00:00
let name = name . as_str ( ) ;
2015-09-11 13:30:08 +00:00
if name = = " eq " | | name = = " ne " | | name = = " is_nan " | |
2015-09-28 05:04:06 +00:00
name . starts_with ( " eq_ " ) | |
name . ends_with ( " _eq " ) {
2015-09-06 19:03:09 +00:00
return ;
}
2015-09-02 08:30:11 +00:00
}
2015-08-11 13:07:21 +00:00
span_lint ( cx , FLOAT_CMP , expr . span , & format! (
2015-08-13 06:15:42 +00:00
" {}-comparison of f32 or f64 detected. Consider changing this to \
` abs ( { } - { } ) < epsilon ` for some suitable value of epsilon " ,
2015-08-11 15:02:04 +00:00
binop_to_string ( op ) , snippet ( cx , left . span , " .. " ) ,
2015-08-11 13:07:21 +00:00
snippet ( cx , right . span , " .. " ) ) ) ;
}
}
}
2015-05-06 08:01:49 +00:00
}
2015-11-10 10:19:33 +00:00
fn is_allowed ( cx : & LateContext , expr : & Expr ) -> bool {
let res = eval_const_expr_partial ( cx . tcx , expr , ExprTypeChecked , None ) ;
if let Ok ( Float ( val ) ) = res {
val = = 0.0 | | val = = ::std ::f64 ::INFINITY | | val = = ::std ::f64 ::NEG_INFINITY
} else { false }
}
2015-09-19 02:53:04 +00:00
fn is_float ( cx : & LateContext , expr : & Expr ) -> bool {
2015-08-11 18:57:21 +00:00
if let ty ::TyFloat ( _ ) = walk_ptrs_ty ( cx . tcx . expr_ty ( expr ) ) . sty {
2015-08-11 13:07:21 +00:00
true
2015-08-11 15:02:04 +00:00
} else {
false
2015-08-11 13:07:21 +00:00
}
2015-05-06 08:01:49 +00:00
}
2015-05-06 10:59:08 +00:00
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison. It is `Warn` by default.
///
/// **Why is this bad?** The comparison can operate on a reference, so creating an owned value effectively throws it away directly afterwards, which is needlessly consuming code and heap space.
///
/// **Known problems:** None
///
/// **Example:** `x.to_owned() == y`
2015-05-21 12:51:43 +00:00
declare_lint! ( pub CMP_OWNED , Warn ,
2015-08-13 08:32:35 +00:00
" creating owned instances for comparing with others, e.g. `x == \" foo \" .to_string()` " ) ;
2015-08-11 15:02:04 +00:00
2015-05-21 12:51:43 +00:00
#[ derive(Copy,Clone) ]
pub struct CmpOwned ;
impl LintPass for CmpOwned {
2015-08-11 13:07:21 +00:00
fn get_lints ( & self ) -> LintArray {
2015-05-21 12:51:43 +00:00
lint_array! ( CMP_OWNED )
2015-08-11 13:07:21 +00:00
}
2015-09-19 02:53:04 +00:00
}
2015-08-11 15:02:04 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for CmpOwned {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-11 13:07:21 +00:00
if let ExprBinary ( ref cmp , ref left , ref right ) = expr . node {
if is_comparison_binop ( cmp . node ) {
2015-10-12 22:46:05 +00:00
check_to_owned ( cx , left , right . span , true , cmp . span ) ;
check_to_owned ( cx , right , left . span , false , cmp . span )
2015-08-11 13:07:21 +00:00
}
}
}
2015-05-21 12:51:43 +00:00
}
2015-10-12 22:46:05 +00:00
fn check_to_owned ( cx : & LateContext , expr : & Expr , other_span : Span , left : bool , op : Span ) {
let snip = match expr . node {
ExprMethodCall ( Spanned { node : ref name , .. } , _ , ref args ) if args . len ( ) = = 1 = > {
2015-09-28 05:04:06 +00:00
if name . as_str ( ) = = " to_string " | |
name . as_str ( ) = = " to_owned " & & is_str_arg ( cx , args ) {
2015-10-12 22:46:05 +00:00
snippet ( cx , args [ 0 ] . span , " .. " )
} else {
return
2015-08-11 15:02:04 +00:00
}
2015-11-17 04:39:42 +00:00
}
2015-10-12 22:46:05 +00:00
ExprCall ( ref path , ref v ) if v . len ( ) = = 1 = > {
2015-11-24 17:44:40 +00:00
if let ExprPath ( None , ref path ) = path . node {
2015-08-11 15:02:04 +00:00
if match_path ( path , & [ " String " , " from_str " ] ) | |
match_path ( path , & [ " String " , " from " ] ) {
2015-10-12 22:46:05 +00:00
snippet ( cx , v [ 0 ] . span , " .. " )
} else {
return
2015-08-11 15:02:04 +00:00
}
2015-10-12 22:46:05 +00:00
} else {
return
2015-08-11 15:02:04 +00:00
}
2015-11-17 04:39:42 +00:00
}
2015-10-12 22:46:05 +00:00
_ = > return
} ;
if left {
span_lint ( cx , CMP_OWNED , expr . span , & format! (
" this creates an owned instance just for comparison. Consider using \
` { } { } { } ` to compare without allocation " , snip,
snippet ( cx , op , " == " ) , snippet ( cx , other_span , " .. " ) ) ) ;
} else {
span_lint ( cx , CMP_OWNED , expr . span , & format! (
" this creates an owned instance just for comparison. Consider using \
` { } { } { } ` to compare without allocation " ,
snippet ( cx , other_span , " .. " ) , snippet ( cx , op , " == " ) , snip ) ) ;
2015-08-11 15:02:04 +00:00
}
2015-10-12 22:46:05 +00:00
2015-05-21 12:51:43 +00:00
}
2015-05-21 14:37:38 +00:00
2015-09-19 02:53:04 +00:00
fn is_str_arg ( cx : & LateContext , args : & [ P < Expr > ] ) -> bool {
2015-08-11 15:02:04 +00:00
args . len ( ) = = 1 & & if let ty ::TyStr =
2015-08-25 12:41:35 +00:00
walk_ptrs_ty ( cx . tcx . expr_ty ( & args [ 0 ] ) ) . sty { true } else { false }
2015-05-21 14:37:38 +00:00
}
2015-08-11 16:55:07 +00:00
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for getting the remainder of a division by one. It is `Warn` by default.
///
/// **Why is this bad?** The result can only ever be zero. No one will write such code deliberately, unless trying to win an Underhanded Rust Contest. Even for that contest, it's probably a bad idea. Use something more underhanded.
///
/// **Known problems:** None
///
/// **Example:** `x % 1`
2015-08-13 08:32:35 +00:00
declare_lint! ( pub MODULO_ONE , Warn , " taking a number modulo 1, which always returns 0 " ) ;
2015-05-31 12:17:31 +00:00
#[ derive(Copy,Clone) ]
pub struct ModuloOne ;
impl LintPass for ModuloOne {
fn get_lints ( & self ) -> LintArray {
lint_array! ( MODULO_ONE )
}
2015-09-19 02:53:04 +00:00
}
2015-05-31 12:17:31 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for ModuloOne {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-05-31 12:17:31 +00:00
if let ExprBinary ( ref cmp , _ , ref right ) = expr . node {
2015-11-24 17:44:40 +00:00
if let Spanned { node : BinOp_ ::BiRem , .. } = * cmp {
2015-09-04 13:26:58 +00:00
if is_integer_literal ( right , 1 ) {
2015-08-12 08:46:49 +00:00
cx . span_lint ( MODULO_ONE , expr . span , " any number modulo 1 will be 0 " ) ;
2015-05-31 12:17:31 +00:00
}
}
}
}
}
2015-12-11 00:22:27 +00:00
/// **What it does:** This lint checks for patterns in the form `name @ _`.
///
/// **Why is this bad?** It's almost always more readable to just use direct bindings.
///
/// **Known problems:** None
///
/// **Example**:
/// ```
/// match v {
/// Some(x) => (),
/// y @ _ => (), // easier written as `y`,
/// }
/// ```
2015-08-30 17:02:30 +00:00
declare_lint! ( pub REDUNDANT_PATTERN , Warn , " using `name @ _` in a pattern " ) ;
#[ derive(Copy,Clone) ]
pub struct PatternPass ;
impl LintPass for PatternPass {
fn get_lints ( & self ) -> LintArray {
lint_array! ( REDUNDANT_PATTERN )
}
2015-09-19 02:53:04 +00:00
}
2015-08-30 17:02:30 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for PatternPass {
fn check_pat ( & mut self , cx : & LateContext , pat : & Pat ) {
2015-08-30 17:02:30 +00:00
if let PatIdent ( _ , ref ident , Some ( ref right ) ) = pat . node {
2015-11-05 02:50:28 +00:00
if right . node = = PatWild {
2015-08-30 17:02:30 +00:00
cx . span_lint ( REDUNDANT_PATTERN , pat . span , & format! (
" the `{} @ _` pattern can be written as just `{}` " ,
ident . node . name , ident . node . name ) ) ;
}
}
}
}
2015-12-11 22:02:02 +00:00
declare_lint! ( pub USED_UNDERSCORE_BINDING , Warn ,
" using a binding which is prefixed with an underscore " ) ;
#[ derive(Copy, Clone) ]
pub struct UsedUnderscoreBinding ;
impl LintPass for UsedUnderscoreBinding {
fn get_lints ( & self ) -> LintArray {
lint_array! ( USED_UNDERSCORE_BINDING )
}
}
impl LateLintPass for UsedUnderscoreBinding {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
let needs_lint = match expr . node {
ExprPath ( _ , ref path ) = > {
2015-12-13 01:51:58 +00:00
let ident = path . segments . last ( )
. expect ( " path should always have at least one segment " )
. identifier ;
ident . name . as_str ( ) . chars ( ) . next ( ) = = Some ( '_' ) //starts with '_'
& & ident . name . as_str ( ) . chars ( ) . skip ( 1 ) . next ( ) ! = Some ( '_' ) //doesn't start with "__"
& & ident . name ! = ident . unhygienic_name //not in macro
& & cx . tcx . def_map . borrow ( ) . values ( ) . any ( | res | match res . base_def {
2015-12-11 22:02:02 +00:00
Def ::DefLocal ( _ , _ ) = > true ,
_ = > false
2015-12-13 01:51:58 +00:00
} ) //local variable
2015-12-11 22:02:02 +00:00
} ,
ExprField ( _ , spanned ) = > spanned . node . as_str ( ) . chars ( ) . next ( ) = = Some ( '_' ) ,
_ = > false
} ;
if needs_lint {
2015-12-13 01:51:58 +00:00
cx . span_lint ( USED_UNDERSCORE_BINDING , expr . span ,
" used binding which is prefixed with an underscore. A leading underscore \
signals that a binding will not be used . " );
2015-12-11 22:02:02 +00:00
}
}
}