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-12-21 09:03:12 +00:00
use syntax ::codemap ::{ Span , Spanned , ExpnFormat } ;
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 ;
2014-11-19 18:48:31 +00:00
2015-12-19 23:15:31 +00:00
use utils ::{ get_item_name , match_path , snippet , get_parent_expr , span_lint } ;
2016-02-07 17:28:37 +00:00
use utils ::{ span_lint_and_then , walk_ptrs_ty , is_integer_literal , implements_trait } ;
2015-05-06 08:01:49 +00:00
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`.
2015-12-11 00:22:27 +00:00
///
/// **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 { .. }`
2016-02-05 23:13:29 +00:00
declare_lint! {
pub TOPLEVEL_REF_ARG , Warn ,
" 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
2016-01-04 04:26:12 +00:00
return ;
2015-08-16 11:54:03 +00:00
}
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 ,
2016-01-04 04:26:12 +00:00
TOPLEVEL_REF_ARG ,
arg . pat . span ,
" `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 {
2016-02-07 17:28:37 +00:00
format! ( " : {} " , snippet ( cx , ty . span , " _ " ) )
2015-09-22 07:08:42 +00:00
} else {
" " . to_owned ( )
} ;
2016-02-07 17:28:37 +00:00
span_lint_and_then ( cx ,
2015-09-22 07:08:42 +00:00
TOPLEVEL_REF_ARG ,
l . pat . span ,
" `ref` on an entire `let` pattern is discouraged, take a reference with & instead " ,
2016-02-07 17:28:37 +00:00
| db | {
db . span_suggestion ( s . span ,
" try " ,
format! ( " let {} {} = & {} ; " ,
snippet ( cx , i . span , " _ " ) ,
tyopt ,
snippet ( cx , init . span , " _ " ) ) ) ;
}
2015-09-22 07:08:42 +00:00
) ;
}
} ;
}
2014-12-24 23:15:22 +00:00
}
2015-05-04 12:11:15 +00:00
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for comparisons to NAN.
2015-12-11 00:22:27 +00:00
///
/// **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 ) {
2016-01-04 04:26:12 +00: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 15:02:04 +00:00
} ) ;
2015-05-04 12:11:15 +00:00
}
2015-05-06 08:01:49 +00:00
2016-02-05 23:41:54 +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).
2015-12-11 00:22:27 +00:00
///
/// **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 ) ) {
2016-01-04 04:26:12 +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 ( ) ;
2016-01-04 04:26:12 +00:00
if name = = " eq " | | name = = " ne " | | name = = " is_nan " | | 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
}
2016-01-04 04:26:12 +00: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 13:07:21 +00:00
}
}
}
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
2016-01-04 04:26:12 +00:00
} else {
false
}
2015-11-10 10:19:33 +00:00
}
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
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison.
2015-12-11 00:22:27 +00:00
///
/// **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 ) {
2016-01-18 14:35:50 +00:00
check_to_owned ( cx , left , right , true , cmp . span ) ;
check_to_owned ( cx , right , left , false , cmp . span )
2015-08-11 13:07:21 +00:00
}
}
}
2015-05-21 12:51:43 +00:00
}
2016-01-18 14:35:50 +00:00
fn check_to_owned ( cx : & LateContext , expr : & Expr , other : & Expr , left : bool , op : Span ) {
let ( arg_ty , snip ) = match expr . node {
2015-10-12 22:46:05 +00:00
ExprMethodCall ( Spanned { node : ref name , .. } , _ , ref args ) if args . len ( ) = = 1 = > {
2016-01-04 04:26:12 +00:00
if name . as_str ( ) = = " to_string " | | name . as_str ( ) = = " to_owned " & & is_str_arg ( cx , args ) {
2016-01-18 14:35:50 +00:00
( cx . tcx . expr_ty ( & args [ 0 ] ) , snippet ( cx , args [ 0 ] . span , " .. " ) )
2016-01-04 04:26:12 +00:00
} else {
return ;
}
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 {
2016-01-04 04:26:12 +00:00
if match_path ( path , & [ " String " , " from_str " ] ) | | match_path ( path , & [ " String " , " from " ] ) {
2016-01-18 14:35:50 +00:00
( cx . tcx . expr_ty ( & v [ 0 ] ) , snippet ( cx , v [ 0 ] . span , " .. " ) )
2016-01-04 04:26:12 +00:00
} else {
return ;
}
2015-10-12 22:46:05 +00:00
} else {
2016-01-04 04:26:12 +00:00
return ;
2015-08-11 15:02:04 +00:00
}
2015-11-17 04:39:42 +00:00
}
2016-01-04 04:26:12 +00:00
_ = > return ,
2015-10-12 22:46:05 +00:00
} ;
2016-01-18 14:35:50 +00:00
let other_ty = cx . tcx . expr_ty ( other ) ;
let partial_eq_trait_id = match cx . tcx . lang_items . eq_trait ( ) {
Some ( id ) = > id ,
None = > return ,
} ;
if ! implements_trait ( cx , arg_ty , partial_eq_trait_id , Some ( vec! [ other_ty ] ) ) {
return ;
}
2015-10-12 22:46:05 +00:00
if left {
2016-01-04 04:26:12 +00: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 , " == " ) ,
2016-01-18 14:35:50 +00:00
snippet ( cx , other . span , " .. " ) ) ) ;
2015-10-12 22:46:05 +00:00
} else {
2016-01-04 04:26:12 +00:00
span_lint ( cx ,
CMP_OWNED ,
expr . span ,
& format! ( " this creates an owned instance just for comparison. Consider using ` {} {} {} ` to \
compare without allocation " ,
2016-01-18 14:35:50 +00:00
snippet ( cx , other . span , " .. " ) ,
2016-01-04 04:26:12 +00:00
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 {
2016-01-04 04:26:12 +00:00
args . len ( ) = = 1 & &
if let ty ::TyStr = 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
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for getting the remainder of a division by one.
2015-12-11 00:22:27 +00:00
///
/// **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
}
}
}
}
}
2016-02-05 23:41:54 +00:00
/// **What it does:** This lint checks for patterns in the form `name @ _`.
2015-12-11 00:22:27 +00: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 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 {
2016-01-04 04:26:12 +00: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 17:02:30 +00:00
}
}
}
}
2015-12-11 22:02:02 +00:00
2015-12-13 05:50:36 +00: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-19 00:04:33 +00:00
/// **Known problems:** None
2015-12-13 05:50:36 +00: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 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 {
2016-01-30 13:01:26 +00:00
#[ cfg_attr(rustfmt, rustfmt_skip) ]
2015-12-11 22:02:02 +00:00
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2016-01-04 04:26:12 +00:00
if in_attributes_expansion ( cx , expr ) {
2016-01-04 14:31:08 +00:00
// Don't lint things expanded by #[derive(...)], etc
2015-12-21 09:03:12 +00:00
return ;
}
2015-12-11 22:02:02 +00:00
let needs_lint = match expr . node {
ExprPath ( _ , ref path ) = > {
2016-01-04 04:26:12 +00:00
let ident = path . segments
. last ( )
2015-12-13 01:51:58 +00:00
. expect ( " path should always have at least one segment " )
. identifier ;
2016-01-20 01:23:39 +00:00
ident . name . as_str ( ) . starts_with ( '_' ) & &
! ident . name . as_str ( ) . starts_with ( " __ " ) & &
ident . name ! = ident . unhygienic_name & &
is_used ( cx , expr ) // not in bang macro
2016-01-04 04:26:12 +00:00
}
2015-12-13 05:59:25 +00:00
ExprField ( _ , spanned ) = > {
let name = spanned . node . as_str ( ) ;
2016-01-20 01:23:39 +00:00
name . starts_with ( '_' ) & & ! name . starts_with ( " __ " )
2016-01-04 04:26:12 +00:00
}
_ = > false ,
2015-12-11 22:02:02 +00:00
} ;
if needs_lint {
2016-01-04 04:26:12 +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
}
}
}
2015-12-19 00:04:33 +00:00
2015-12-21 09:03:12 +00: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-19 00:04:33 +00: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-04 04:26:12 +00:00
_ = > is_used ( cx , & parent ) ,
2015-12-19 00:04:33 +00:00
}
2016-01-04 04:26:12 +00:00
} else {
2015-12-19 00:04:33 +00:00
true
}
}
2015-12-21 09:03:12 +00: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 ,
}
} )
} )
}