2016-02-24 10:38:57 -06:00
use reexport ::* ;
2016-04-07 10:46:48 -05:00
use rustc ::hir ::* ;
use rustc ::hir ::intravisit ::FnKind ;
2015-08-16 01:54:43 -05:00
use rustc ::lint ::* ;
2016-03-31 10:05:43 -05:00
use rustc ::middle ::const_val ::ConstVal ;
2016-03-27 13:59:02 -05:00
use rustc ::ty ;
2016-03-31 10:05:43 -05:00
use rustc_const_eval ::EvalHint ::ExprTypeChecked ;
use rustc_const_eval ::eval_const_expr_partial ;
2016-06-08 05:21:24 -05:00
use rustc_const_math ::ConstFloat ;
2015-12-21 03:03:12 -06:00
use syntax ::codemap ::{ Span , Spanned , ExpnFormat } ;
2016-02-24 10:38:57 -06:00
use syntax ::ptr ::P ;
2016-05-19 16:14:34 -05:00
use utils ::{
2016-06-15 09:27:56 -05:00
get_item_name , get_parent_expr , implements_trait , in_macro , is_integer_literal , match_path ,
snippet , span_lint , span_lint_and_then , walk_ptrs_ty
2016-05-19 16:14:34 -05:00
} ;
2016-06-29 14:25:23 -05:00
use utils ::sugg ::Sugg ;
2015-05-06 03:01:49 -05:00
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for function arguments and let bindings denoted as `ref`.
2015-12-10 18:22:27 -06:00
///
2016-07-15 17:25:44 -05: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.
2015-12-10 18:22:27 -06:00
///
2016-07-15 17:25:44 -05:00
/// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The type of `x` is more
/// obvious with the former.
2015-12-10 18:22:27 -06:00
///
2016-07-15 17:25:44 -05:00
/// **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.
2015-12-10 18:22:27 -06:00
///
2016-07-15 17:25:44 -05:00
/// **Example:**
/// ```rust
/// fn foo(ref x: u8) -> bool { .. }
/// ```
2016-02-05 17:13:29 -06: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 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 ) {
2016-03-23 10:11:24 -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
}
2016-08-01 09:59:14 -05:00
for arg in & decl . inputs {
2016-05-31 12:17:31 -05:00
if let PatKind ::Binding ( 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 ) {
2016-06-05 19:09:19 -05:00
if_let_chain! { [
2015-09-22 02:08:42 -05:00
let StmtDecl ( ref d , _ ) = s . node ,
let DeclLocal ( ref l ) = d . node ,
2016-07-15 07:22:34 -05:00
let PatKind ::Binding ( BindByRef ( mt ) , i , None ) = l . pat . node ,
2015-09-22 02:08:42 -05:00
let Some ( ref init ) = l . init
2016-06-05 19:09:19 -05:00
] , {
2016-07-15 08:32:41 -05:00
let init = Sugg ::hir ( cx , init , " .. " ) ;
let ( mutopt , initref ) = if mt = = Mutability ::MutMutable {
( " mut " , init . mut_addr ( ) )
2016-06-05 19:09:19 -05:00
} else {
2016-07-15 08:32:41 -05:00
( " " , init . addr ( ) )
2016-06-05 19:09:19 -05:00
} ;
2016-07-15 08:32:41 -05:00
let tyopt = if let Some ( ref ty ) = l . ty {
format! ( " : & {mutopt} {ty} " , mutopt = mutopt , ty = snippet ( cx , ty . span , " _ " ) )
2016-07-15 07:22:34 -05:00
} else {
2016-07-15 08:32:41 -05:00
" " . to_owned ( )
2016-07-15 07:22:34 -05:00
} ;
2016-06-05 19:09:19 -05:00
span_lint_and_then ( cx ,
TOPLEVEL_REF_ARG ,
l . pat . span ,
2016-06-29 15:45:58 -05:00
" `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead " ,
2016-06-05 19:09:19 -05:00
| db | {
db . span_suggestion ( s . span ,
" try " ,
2016-07-15 08:32:41 -05:00
format! ( " let {name} {tyopt} = {initref} ; " ,
name = snippet ( cx , i . span , " _ " ) ,
tyopt = tyopt ,
initref = initref ) ) ;
2016-06-05 19:09:19 -05:00
}
) ;
} }
2015-09-22 02:08:42 -05:00
}
2014-12-24 17:15:22 -06:00
}
2015-05-04 07:11:15 -05:00
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for comparisons to NAN.
2015-12-10 18:22:27 -06: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 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 {
2016-04-07 10:46:48 -05:00
if cmp . node . is_comparison ( ) {
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 | {
2016-05-19 16:14:34 -05:00
if seg . name . as_str ( ) = = " NAN " {
2016-01-03 22:26:12 -06:00
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
2016-02-05 17:41:54 -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).
2015-12-10 18:22:27 -06: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 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-06-29 12:47:51 -05:00
span_lint_and_then ( cx ,
FLOAT_CMP ,
expr . span ,
" strict comparison of f32 or f64 " ,
| db | {
2016-07-01 12:30:38 -05:00
let lhs = Sugg ::hir ( cx , left , " .. " ) ;
let rhs = Sugg ::hir ( cx , right , " .. " ) ;
2016-06-29 14:25:23 -05:00
2016-06-29 12:47:51 -05:00
db . span_suggestion ( expr . span ,
" consider comparing them within some error " ,
2016-06-29 14:25:23 -05:00
format! ( " ( {} ).abs() < error " , lhs - rhs ) ) ;
2016-06-29 12:47:51 -05:00
db . span_note ( expr . span , " std::f32::EPSILON and std::f64::EPSILON are available. " ) ;
} ) ;
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 ) ;
2016-03-31 10:05:43 -05:00
if let Ok ( ConstVal ::Float ( val ) ) = res {
2016-06-08 05:21:24 -05:00
use std ::cmp ::Ordering ;
let zero = ConstFloat ::FInfer {
f32 : 0.0 ,
f64 : 0.0 ,
} ;
let infinity = ConstFloat ::FInfer {
f32 : ::std ::f32 ::INFINITY ,
f64 : ::std ::f64 ::INFINITY ,
} ;
let neg_infinity = ConstFloat ::FInfer {
f32 : ::std ::f32 ::NEG_INFINITY ,
f64 : ::std ::f64 ::NEG_INFINITY ,
} ;
val . try_cmp ( zero ) = = Ok ( Ordering ::Equal )
| | val . try_cmp ( infinity ) = = Ok ( Ordering ::Equal )
| | val . try_cmp ( neg_infinity ) = = Ok ( Ordering ::Equal )
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 {
2016-06-05 13:46:42 -05:00
matches! ( walk_ptrs_ty ( cx . tcx . expr_ty ( expr ) ) . sty , ty ::TyFloat ( _ ) )
2015-05-06 03:01:49 -05:00
}
2015-05-06 05:59:08 -05:00
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for conversions to owned values just for the sake of a comparison.
2015-12-10 18:22:27 -06: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 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 {
2016-04-07 10:46:48 -05:00
if cmp . node . is_comparison ( ) {
2016-01-18 08:35:50 -06:00
check_to_owned ( cx , left , right , true , cmp . span ) ;
check_to_owned ( cx , right , left , false , cmp . span )
2015-08-11 08:07:21 -05:00
}
}
}
2015-05-21 07:51:43 -05:00
}
2016-01-18 08:35:50 -06:00
fn check_to_owned ( cx : & LateContext , expr : & Expr , other : & Expr , left : bool , op : Span ) {
let ( arg_ty , snip ) = match expr . node {
2016-04-14 13:14:03 -05:00
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 ) {
2016-01-18 08:35:50 -06:00
( cx . tcx . expr_ty ( & args [ 0 ] ) , snippet ( cx , args [ 0 ] . span , " .. " ) )
2016-01-03 22:26:12 -06:00
} 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 " ] ) {
2016-01-18 08:35:50 -06:00
( cx . tcx . expr_ty ( & v [ 0 ] ) , snippet ( cx , v [ 0 ] . span , " .. " ) )
2016-01-03 22:26:12 -06:00
} 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
} ;
2016-01-18 08:35:50 -06: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 ,
} ;
2016-03-01 09:25:15 -06:00
if ! implements_trait ( cx , arg_ty , partial_eq_trait_id , vec! [ other_ty ] ) {
2016-01-18 08:35:50 -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 , " == " ) ,
2016-01-18 08:35:50 -06:00
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 " ,
2016-01-18 08:35:50 -06:00
snippet ( cx , other . span , " .. " ) ,
2016-01-03 22:26:12 -06:00
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 & &
2016-06-05 13:46:42 -05:00
matches! ( walk_ptrs_ty ( cx . tcx . expr_ty ( & args [ 0 ] ) ) . sty , ty ::TyStr )
2015-05-21 09:37:38 -05:00
}
2015-08-11 11:55:07 -05:00
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for getting the remainder of a division by one.
2015-12-10 18:22:27 -06: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 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 {
2016-04-14 13:14:03 -05:00
if let Spanned { node : BinOp_ ::BiRem , .. } = * cmp {
2015-09-04 08:26:58 -05:00
if is_integer_literal ( right , 1 ) {
2016-03-25 19:49:45 -05:00
span_lint ( cx , MODULO_ONE , expr . span , " any number modulo 1 will be 0 " ) ;
2015-05-31 07:17:31 -05:00
}
}
}
}
}
2016-02-05 17:41:54 -06:00
/// **What it does:** This lint checks for patterns in the form `name @ _`.
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**:
2016-07-29 13:36:33 -05:00
/// ```rust
2015-12-10 18:22:27 -06:00
/// 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 ) {
2016-05-31 12:17:31 -05:00
if let PatKind ::Binding ( _ , ref ident , Some ( ref right ) ) = pat . node {
2016-02-18 14:16:39 -06:00
if right . node = = PatKind ::Wild {
2016-03-25 19:49:45 -05:00
span_lint ( cx ,
REDUNDANT_PATTERN ,
pat . span ,
& format! ( " the ` {} @ _` pattern can be written as just ` {} ` " ,
2016-05-19 16:14:34 -05:00
ident . node ,
ident . node ) ) ;
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.
///
2016-05-19 16:14:34 -05:00
/// **Known problems:** The lint does not work properly with desugaring and macro, it has been
/// allowed in the mean time.
2015-12-12 23:50:36 -06:00
///
/// **Example**:
2016-07-29 13:36:33 -05:00
/// ```rust
2015-12-12 23:50:36 -06:00
/// 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`
/// ```
2016-05-19 16:14:34 -05:00
declare_lint! ( pub USED_UNDERSCORE_BINDING , Allow ,
2015-12-11 16:02:02 -06:00
" 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 07:01:26 -06:00
#[ cfg_attr(rustfmt, 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-05-19 16:14:34 -05:00
// Don't lint things expanded by #[derive(...)], etc
2015-12-21 03:03:12 -06:00
return ;
}
2016-05-19 16:14:34 -05:00
let binding = match expr . node {
2015-12-11 16:02:02 -06:00
ExprPath ( _ , ref path ) = > {
2016-06-15 09:27:56 -05:00
let binding = path . segments
2016-01-03 22:26:12 -06:00
. last ( )
2015-12-12 19:51:58 -06:00
. expect ( " path should always have at least one segment " )
2016-06-15 09:27:56 -05:00
. name
. as_str ( ) ;
if binding . starts_with ( '_' ) & &
! binding . starts_with ( " __ " ) & &
binding ! = " _result " & & // FIXME: #944
is_used ( cx , expr ) & &
// don't lint if the declaration is in a macro
non_macro_local ( cx , & cx . tcx . expect_def ( expr . id ) ) {
Some ( binding )
2016-05-19 16:14:34 -05:00
} else {
None
}
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-05-19 16:14:34 -05:00
if name . starts_with ( '_' ) & & ! name . starts_with ( " __ " ) {
Some ( name )
} else {
None
}
2016-01-03 22:26:12 -06:00
}
2016-05-19 16:14:34 -05:00
_ = > None ,
2015-12-11 16:02:02 -06:00
} ;
2016-05-19 16:14:34 -05:00
if let Some ( binding ) = binding {
2016-06-15 09:27:56 -05:00
span_lint ( cx ,
USED_UNDERSCORE_BINDING ,
expr . span ,
& format! ( " used binding ` {} ` which is prefixed with an underscore. A leading \
underscore signals that a binding will not be used . " , binding));
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 {
2016-08-01 09:59:14 -05:00
if let Some ( parent ) = get_parent_expr ( cx , expr ) {
2015-12-18 18:04:33 -06:00
match parent . node {
2016-04-14 13:14:03 -05:00
ExprAssign ( _ , ref rhs ) |
ExprAssignOp ( _ , _ , ref rhs ) = > * * rhs = = * expr ,
2016-04-26 10:05:39 -05: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
2016-05-19 16:14:34 -05:00
/// Test whether an expression is in a macro expansion (e.g. something generated by
/// `#[derive(...)`] or the like).
2015-12-21 03:03:12 -06:00
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 | {
2016-06-05 13:46:42 -05:00
matches! ( info . callee . format , ExpnFormat ::MacroAttribute ( _ ) )
2015-12-21 03:03:12 -06:00
} )
} )
}
2016-06-15 09:27:56 -05:00
/// Test whether `def` is a variable defined outside a macro.
fn non_macro_local ( cx : & LateContext , def : & def ::Def ) -> bool {
match * def {
def ::Def ::Local ( _ , id ) | def ::Def ::Upvar ( _ , id , _ , _ ) = > {
if let Some ( span ) = cx . tcx . map . opt_span ( id ) {
! in_macro ( cx , span )
} else {
true
}
}
_ = > false ,
}
}