2016-02-24 16:38:57 +00:00
use reexport ::* ;
2016-04-07 15:46:48 +00:00
use rustc ::hir ::* ;
use rustc ::hir ::intravisit ::FnKind ;
2015-08-16 06:54:43 +00:00
use rustc ::lint ::* ;
2016-03-31 15:05:43 +00:00
use rustc ::middle ::const_val ::ConstVal ;
2016-03-27 18:59:02 +00:00
use rustc ::ty ;
2016-03-31 15:05:43 +00:00
use rustc_const_eval ::EvalHint ::ExprTypeChecked ;
use rustc_const_eval ::eval_const_expr_partial ;
2016-06-08 10:21:24 +00:00
use rustc_const_math ::ConstFloat ;
2015-12-21 09:03:12 +00:00
use syntax ::codemap ::{ Span , Spanned , ExpnFormat } ;
2016-02-24 16:38:57 +00:00
use syntax ::ptr ::P ;
2016-05-19 21:14:34 +00:00
use utils ::{
get_item_name , get_parent_expr , implements_trait , is_integer_literal , match_path , snippet ,
span_lint , span_lint_and_then , walk_ptrs_ty
} ;
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 ) {
2016-03-23 15:11:24 +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 {
2016-05-31 17:17:31 +00:00
if let PatKind ::Binding ( 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 ,
2016-05-31 17:17:31 +00:00
let PatKind ::Binding ( BindByRef ( _ ) , i , None ) = l . pat . node ,
2015-09-22 07:08:42 +00:00
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 {
2016-04-07 15:46:48 +00:00
if cmp . node . is_comparison ( ) {
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 | {
2016-05-19 21:14:34 +00:00
if seg . name . as_str ( ) = = " NAN " {
2016-01-04 04:26:12 +00:00
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 ,
2016-03-24 17:07:55 +00:00
& format! ( " {} -comparison of f32 or f64 detected. Consider changing this to `( {} - {} ).abs() < \
epsilon ` for some suitable value of epsilon . \
std ::f32 ::EPSILON and std ::f64 ::EPSILON are available . " ,
2016-04-07 15:46:48 +00:00
op . as_str ( ) ,
2016-01-04 04:26:12 +00:00
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 ) ;
2016-03-31 15:05:43 +00:00
if let Ok ( ConstVal ::Float ( val ) ) = res {
2016-06-08 10:21:24 +00: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-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 {
2016-06-05 18:46:42 +00:00
matches! ( walk_ptrs_ty ( cx . tcx . expr_ty ( expr ) ) . sty , ty ::TyFloat ( _ ) )
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 {
2016-04-07 15:46:48 +00:00
if cmp . node . is_comparison ( ) {
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 {
2016-04-14 18:14:03 +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 ,
} ;
2016-03-01 15:25:15 +00:00
if ! implements_trait ( cx , arg_ty , partial_eq_trait_id , vec! [ other_ty ] ) {
2016-01-18 14:35:50 +00:00
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 & &
2016-06-05 18:46:42 +00:00
matches! ( walk_ptrs_ty ( cx . tcx . expr_ty ( & args [ 0 ] ) ) . sty , ty ::TyStr )
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 {
2016-04-14 18:14:03 +00:00
if let Spanned { node : BinOp_ ::BiRem , .. } = * cmp {
2015-09-04 13:26:58 +00:00
if is_integer_literal ( right , 1 ) {
2016-03-26 00:49:45 +00:00
span_lint ( cx , 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 ) {
2016-05-31 17:17:31 +00:00
if let PatKind ::Binding ( _ , ref ident , Some ( ref right ) ) = pat . node {
2016-02-18 20:16:39 +00:00
if right . node = = PatKind ::Wild {
2016-03-26 00:49:45 +00:00
span_lint ( cx ,
REDUNDANT_PATTERN ,
pat . span ,
& format! ( " the ` {} @ _` pattern can be written as just ` {} ` " ,
2016-05-19 21:14:34 +00:00
ident . node ,
ident . node ) ) ;
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.
///
2016-05-19 21:14:34 +00:00
/// **Known problems:** The lint does not work properly with desugaring and macro, it has been
/// allowed in the mean time.
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`
/// ```
2016-05-19 21:14:34 +00:00
declare_lint! ( pub USED_UNDERSCORE_BINDING , Allow ,
2015-12-11 22:02:02 +00: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 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-05-19 21:14:34 +00:00
// Don't lint things expanded by #[derive(...)], etc
2015-12-21 09:03:12 +00:00
return ;
}
2016-05-19 21:14:34 +00:00
let binding = match expr . node {
2015-12-11 22:02:02 +00:00
ExprPath ( _ , ref path ) = > {
2016-05-19 21:14:34 +00:00
let segment = path . segments
2016-01-04 04:26:12 +00:00
. last ( )
2015-12-13 01:51:58 +00:00
. expect ( " path should always have at least one segment " )
2016-05-19 21:14:34 +00:00
. name ;
if segment . as_str ( ) . starts_with ( '_' ) & &
! segment . as_str ( ) . starts_with ( " __ " ) & &
segment ! = segment . unhygienize ( ) & & // not in bang macro
is_used ( cx , expr ) {
Some ( segment . as_str ( ) )
} else {
None
}
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-05-19 21:14:34 +00:00
if name . starts_with ( '_' ) & & ! name . starts_with ( " __ " ) {
Some ( name )
} else {
None
}
2016-01-04 04:26:12 +00:00
}
2016-05-19 21:14:34 +00:00
_ = > None ,
2015-12-11 22:02:02 +00:00
} ;
2016-05-19 21:14:34 +00:00
if let Some ( binding ) = binding {
if binding ! = " _result " { // FIXME: #944
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 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 {
2016-04-14 18:14:03 +00:00
ExprAssign ( _ , ref rhs ) |
ExprAssignOp ( _ , _ , ref rhs ) = > * * rhs = = * expr ,
2016-04-26 15:05:39 +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
2016-05-19 21:14:34 +00:00
/// Test whether an expression is in a macro expansion (e.g. something generated by
/// `#[derive(...)`] or the like).
2015-12-21 09:03:12 +00: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 18:46:42 +00:00
matches! ( info . callee . format , ExpnFormat ::MacroAttribute ( _ ) )
2015-12-21 09:03:12 +00:00
} )
} )
}