2015-08-16 01:54:43 -05:00
use rustc ::lint ::* ;
2014-11-19 12:48:31 -06:00
use syntax ::ptr ::P ;
2015-09-03 09:42:17 -05:00
use rustc_front ::hir ::* ;
use reexport ::* ;
use rustc_front ::util ::{ is_comparison_binop , binop_to_string } ;
2015-12-21 03:03:12 -06:00
use syntax ::codemap ::{ Span , Spanned , ExpnFormat } ;
2015-11-19 08:51:30 -06:00
use rustc_front ::intravisit ::FnKind ;
2015-08-16 01:54:43 -05:00
use rustc ::middle ::ty ;
2015-11-10 04:19:33 -06:00
use rustc ::middle ::const_eval ::ConstVal ::Float ;
use rustc ::middle ::const_eval ::eval_const_expr_partial ;
use rustc ::middle ::const_eval ::EvalHint ::ExprTypeChecked ;
2014-11-19 12:48:31 -06:00
2015-12-19 17:15:31 -06:00
use utils ::{ get_item_name , match_path , snippet , get_parent_expr , span_lint } ;
2015-12-21 03:03:12 -06:00
use utils ::{ span_help_and_lint , walk_ptrs_ty , is_integer_literal } ;
2015-05-06 03:01:49 -05:00
2015-12-10 18:22:27 -06: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 03:32:35 -05:00
declare_lint! ( pub TOPLEVEL_REF_ARG , Warn ,
2015-09-22 02:08:42 -05: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 17:15:22 -06:00
2014-12-25 17:22:18 -06:00
#[ allow(missing_copy_implementations) ]
2014-12-24 17:15:22 -06:00
pub struct TopLevelRefPass ;
impl LintPass for TopLevelRefPass {
fn get_lints ( & self ) -> LintArray {
2014-12-25 17:54:44 -06:00
lint_array! ( TOPLEVEL_REF_ARG )
2014-12-24 17:15:22 -06:00
}
2015-09-18 21:53:04 -05:00
}
2014-12-24 17:15:22 -06:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for TopLevelRefPass {
fn check_fn ( & mut self , cx : & LateContext , k : FnKind , decl : & FnDecl , _ : & Block , _ : Span , _ : NodeId ) {
2015-09-01 07:28:23 -05:00
if let FnKind ::Closure = k {
2015-08-16 06:54:03 -05:00
// Does not apply to closures
2016-01-03 22:26:12 -06:00
return ;
2015-08-16 06:54:03 -05:00
}
2015-08-13 09:41:51 -05:00
for ref arg in & decl . inputs {
2014-12-24 17:15:22 -06:00
if let PatIdent ( BindByRef ( _ ) , _ , _ ) = arg . pat . node {
2015-08-11 10:02:04 -05:00
span_lint ( cx ,
2016-01-03 22:26:12 -06:00
TOPLEVEL_REF_ARG ,
arg . pat . span ,
" `ref` directly on a function argument is ignored. Consider using a reference type instead. " ) ;
2014-12-24 17:15:22 -06:00
}
}
}
2015-09-22 02:08:42 -05: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 17:15:22 -06:00
}
2015-05-04 07:11:15 -05:00
2015-12-10 18:22:27 -06: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 03:32:35 -05:00
declare_lint! ( pub CMP_NAN , Deny ,
" comparisons to NAN (which will always return false, which is probably not intended) " ) ;
2015-05-04 07:11:15 -05:00
#[ derive(Copy,Clone) ]
pub struct CmpNan ;
impl LintPass for CmpNan {
2015-08-11 08:07:21 -05:00
fn get_lints ( & self ) -> LintArray {
2015-05-04 07:11:15 -05:00
lint_array! ( CMP_NAN )
2015-08-11 08:07:21 -05:00
}
2015-09-18 21:53:04 -05:00
}
2015-08-11 10:02:04 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for CmpNan {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-11 08:07:21 -05:00
if let ExprBinary ( ref cmp , ref left , ref right ) = expr . node {
if is_comparison_binop ( cmp . node ) {
2015-11-24 11:44:40 -06:00
if let ExprPath ( _ , ref path ) = left . node {
2015-08-11 08:07:21 -05:00
check_nan ( cx , path , expr . span ) ;
}
2015-11-24 11:44:40 -06:00
if let ExprPath ( _ , ref path ) = right . node {
2015-08-11 08:07:21 -05:00
check_nan ( cx , path , expr . span ) ;
}
}
}
}
2015-05-04 07:11:15 -05:00
}
2015-09-18 21:53:04 -05:00
fn check_nan ( cx : & LateContext , path : & Path , span : Span ) {
2016-01-03 22:26:12 -06:00
path . segments . last ( ) . map ( | seg | {
if seg . identifier . name . as_str ( ) = = " NAN " {
span_lint ( cx ,
CMP_NAN ,
span ,
" doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead " ) ;
}
2015-08-11 10:02:04 -05:00
} ) ;
2015-05-04 07:11:15 -05:00
}
2015-05-06 03:01:49 -05:00
2015-12-10 18:22:27 -06: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 03:01:49 -05:00
declare_lint! ( pub FLOAT_CMP , Warn ,
2015-08-13 03:32:35 -05: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 10:02:04 -05:00
2015-05-06 03:01:49 -05:00
#[ derive(Copy,Clone) ]
pub struct FloatCmp ;
impl LintPass for FloatCmp {
2015-08-11 08:07:21 -05:00
fn get_lints ( & self ) -> LintArray {
2015-05-06 03:01:49 -05:00
lint_array! ( FLOAT_CMP )
2015-08-11 08:07:21 -05:00
}
2015-09-18 21:53:04 -05:00
}
2015-08-11 10:02:04 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for FloatCmp {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-11 08:07:21 -05: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 ) ) {
2016-01-03 22:26:12 -06:00
if is_allowed ( cx , left ) | | is_allowed ( cx , right ) {
return ;
}
2015-09-06 14:03:09 -05:00
if let Some ( name ) = get_item_name ( cx , expr ) {
2015-09-28 00:04:06 -05:00
let name = name . as_str ( ) ;
2016-01-03 22:26:12 -06:00
if name = = " eq " | | name = = " ne " | | name = = " is_nan " | | name . starts_with ( " eq_ " ) | |
name . ends_with ( " _eq " ) {
2015-09-06 14:03:09 -05:00
return ;
}
2015-09-02 03:30:11 -05:00
}
2016-01-03 22:26:12 -06:00
span_lint ( cx ,
FLOAT_CMP ,
expr . span ,
& format! ( " {} -comparison of f32 or f64 detected. Consider changing this to `abs( {} - {} ) < \
epsilon ` for some suitable value of epsilon " ,
binop_to_string ( op ) ,
snippet ( cx , left . span , " .. " ) ,
snippet ( cx , right . span , " .. " ) ) ) ;
2015-08-11 08:07:21 -05:00
}
}
}
2015-05-06 03:01:49 -05:00
}
2015-11-10 04:19:33 -06: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
2016-01-03 22:26:12 -06:00
} else {
false
}
2015-11-10 04:19:33 -06:00
}
2015-09-18 21:53:04 -05:00
fn is_float ( cx : & LateContext , expr : & Expr ) -> bool {
2015-08-11 13:57:21 -05:00
if let ty ::TyFloat ( _ ) = walk_ptrs_ty ( cx . tcx . expr_ty ( expr ) ) . sty {
2015-08-11 08:07:21 -05:00
true
2015-08-11 10:02:04 -05:00
} else {
false
2015-08-11 08:07:21 -05:00
}
2015-05-06 03:01:49 -05:00
}
2015-05-06 05:59:08 -05:00
2015-12-10 18:22:27 -06: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 07:51:43 -05:00
declare_lint! ( pub CMP_OWNED , Warn ,
2015-08-13 03:32:35 -05:00
" creating owned instances for comparing with others, e.g. `x == \" foo \" .to_string()` " ) ;
2015-08-11 10:02:04 -05:00
2015-05-21 07:51:43 -05:00
#[ derive(Copy,Clone) ]
pub struct CmpOwned ;
impl LintPass for CmpOwned {
2015-08-11 08:07:21 -05:00
fn get_lints ( & self ) -> LintArray {
2015-05-21 07:51:43 -05:00
lint_array! ( CMP_OWNED )
2015-08-11 08:07:21 -05:00
}
2015-09-18 21:53:04 -05:00
}
2015-08-11 10:02:04 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for CmpOwned {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-08-11 08:07:21 -05:00
if let ExprBinary ( ref cmp , ref left , ref right ) = expr . node {
if is_comparison_binop ( cmp . node ) {
2015-10-12 17:46:05 -05:00
check_to_owned ( cx , left , right . span , true , cmp . span ) ;
check_to_owned ( cx , right , left . span , false , cmp . span )
2015-08-11 08:07:21 -05:00
}
}
}
2015-05-21 07:51:43 -05:00
}
2015-10-12 17:46:05 -05: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 = > {
2016-01-03 22:26:12 -06:00
if name . as_str ( ) = = " to_string " | | name . as_str ( ) = = " to_owned " & & is_str_arg ( cx , args ) {
snippet ( cx , args [ 0 ] . span , " .. " )
} else {
return ;
}
2015-11-16 22:39:42 -06:00
}
2015-10-12 17:46:05 -05:00
ExprCall ( ref path , ref v ) if v . len ( ) = = 1 = > {
2015-11-24 11:44:40 -06:00
if let ExprPath ( None , ref path ) = path . node {
2016-01-03 22:26:12 -06:00
if match_path ( path , & [ " String " , " from_str " ] ) | | match_path ( path , & [ " String " , " from " ] ) {
snippet ( cx , v [ 0 ] . span , " .. " )
} else {
return ;
}
2015-10-12 17:46:05 -05:00
} else {
2016-01-03 22:26:12 -06:00
return ;
2015-08-11 10:02:04 -05:00
}
2015-11-16 22:39:42 -06:00
}
2016-01-03 22:26:12 -06:00
_ = > return ,
2015-10-12 17:46:05 -05:00
} ;
if left {
2016-01-03 22:26:12 -06:00
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 , " .. " ) ) ) ;
2015-10-12 17:46:05 -05:00
} else {
2016-01-03 22:26:12 -06:00
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 10:02:04 -05:00
}
2015-10-12 17:46:05 -05:00
2015-05-21 07:51:43 -05:00
}
2015-05-21 09:37:38 -05:00
2015-09-18 21:53:04 -05:00
fn is_str_arg ( cx : & LateContext , args : & [ P < Expr > ] ) -> bool {
2016-01-03 22:26:12 -06:00
args . len ( ) = = 1 & &
if let ty ::TyStr = walk_ptrs_ty ( cx . tcx . expr_ty ( & args [ 0 ] ) ) . sty {
true
} else {
false
}
2015-05-21 09:37:38 -05:00
}
2015-08-11 11:55:07 -05:00
2015-12-10 18:22:27 -06: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 03:32:35 -05:00
declare_lint! ( pub MODULO_ONE , Warn , " taking a number modulo 1, which always returns 0 " ) ;
2015-05-31 07:17:31 -05:00
#[ derive(Copy,Clone) ]
pub struct ModuloOne ;
impl LintPass for ModuloOne {
fn get_lints ( & self ) -> LintArray {
lint_array! ( MODULO_ONE )
}
2015-09-18 21:53:04 -05:00
}
2015-05-31 07:17:31 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for ModuloOne {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2015-05-31 07:17:31 -05:00
if let ExprBinary ( ref cmp , _ , ref right ) = expr . node {
2015-11-24 11:44:40 -06:00
if let Spanned { node : BinOp_ ::BiRem , .. } = * cmp {
2015-09-04 08:26:58 -05:00
if is_integer_literal ( right , 1 ) {
2015-08-12 03:46:49 -05:00
cx . span_lint ( MODULO_ONE , expr . span , " any number modulo 1 will be 0 " ) ;
2015-05-31 07:17:31 -05:00
}
}
}
}
}
2016-01-01 10:48:19 -06:00
/// **What it does:** This lint checks for patterns in the form `name @ _`. It is `Warn` by default.
2015-12-10 18:22:27 -06:00
///
/// **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 12:02:30 -05: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-18 21:53:04 -05:00
}
2015-08-30 12:02:30 -05:00
2015-09-18 21:53:04 -05:00
impl LateLintPass for PatternPass {
fn check_pat ( & mut self , cx : & LateContext , pat : & Pat ) {
2015-08-30 12:02:30 -05:00
if let PatIdent ( _ , ref ident , Some ( ref right ) ) = pat . node {
2015-11-04 20:50:28 -06:00
if right . node = = PatWild {
2016-01-03 22:26:12 -06:00
cx . span_lint ( REDUNDANT_PATTERN ,
pat . span ,
& format! ( " the ` {} @ _` pattern can be written as just ` {} ` " ,
ident . node . name ,
ident . node . name ) ) ;
2015-08-30 12:02:30 -05:00
}
}
}
}
2015-12-11 16:02:02 -06:00
2015-12-12 23:50:36 -06:00
/// **What it does:** This lint checks for the use of bindings with a single leading underscore
///
/// **Why is this bad?** A single leading underscore is usually used to indicate that a binding
/// will not be used. Using such a binding breaks this expectation.
///
2015-12-18 18:04:33 -06:00
/// **Known problems:** None
2015-12-12 23:50:36 -06:00
///
/// **Example**:
/// ```
/// let _x = 0;
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading underscore.
/// // We should rename `_x` to `x`
/// ```
2015-12-11 16:02:02 -06: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 {
2016-01-07 00:36:16 -06:00
#[ allow(unused_attributes) ]
2016-01-04 08:31:08 -06:00
#[ rustfmt_skip ]
2015-12-11 16:02:02 -06:00
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2016-01-03 22:26:12 -06:00
if in_attributes_expansion ( cx , expr ) {
2016-01-04 08:31:08 -06:00
// Don't lint things expanded by #[derive(...)], etc
2015-12-21 03:03:12 -06:00
return ;
}
2015-12-11 16:02:02 -06:00
let needs_lint = match expr . node {
ExprPath ( _ , ref path ) = > {
2016-01-03 22:26:12 -06:00
let ident = path . segments
. last ( )
2015-12-12 19:51:58 -06:00
. expect ( " path should always have at least one segment " )
. identifier ;
2016-01-04 08:31:08 -06:00
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 & & is_used ( cx , expr ) // not in bang macro
2016-01-03 22:26:12 -06:00
}
2015-12-12 23:59:25 -06:00
ExprField ( _ , spanned ) = > {
let name = spanned . node . as_str ( ) ;
2016-01-03 22:26:12 -06:00
name . chars ( ) . next ( ) = = Some ( '_' ) & & name . chars ( ) . skip ( 1 ) . next ( ) ! = Some ( '_' )
}
_ = > false ,
2015-12-11 16:02:02 -06:00
} ;
if needs_lint {
2016-01-03 22:26:12 -06: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 16:02:02 -06:00
}
}
}
2015-12-18 18:04:33 -06:00
2015-12-21 03:03:12 -06:00
/// Heuristic to see if an expression is used. Should be compatible with `unused_variables`'s idea
/// of what it means for an expression to be "used".
2015-12-18 18:04:33 -06:00
fn is_used ( cx : & LateContext , expr : & Expr ) -> bool {
if let Some ( ref parent ) = get_parent_expr ( cx , expr ) {
match parent . node {
ExprAssign ( _ , ref rhs ) = > * * rhs = = * expr ,
ExprAssignOp ( _ , _ , ref rhs ) = > * * rhs = = * expr ,
2016-01-03 22:26:12 -06:00
_ = > is_used ( cx , & parent ) ,
2015-12-18 18:04:33 -06:00
}
2016-01-03 22:26:12 -06:00
} else {
2015-12-18 18:04:33 -06:00
true
}
}
2015-12-21 03:03:12 -06:00
/// Test whether an expression is in a macro expansion (e.g. something generated by #[derive(...)]
/// or the like)
fn in_attributes_expansion ( cx : & LateContext , expr : & Expr ) -> bool {
cx . sess ( ) . codemap ( ) . with_expn_info ( expr . span . expn_id , | info_opt | {
info_opt . map_or ( false , | info | {
match info . callee . format {
ExpnFormat ::MacroAttribute ( _ ) = > true ,
_ = > false ,
}
} )
} )
}