2020-01-11 10:39:43 +00:00
use crate ::consts ::{ constant , miri_to_const , Constant } ;
2018-05-30 08:15:50 +00:00
use crate ::utils ::paths ;
use crate ::utils ::sugg ::Sugg ;
2020-01-10 16:14:17 +00:00
use crate ::utils ::usage ::is_unused ;
2018-11-27 20:14:15 +00:00
use crate ::utils ::{
2020-04-12 13:23:54 +00:00
expr_block , get_arg_name , get_parent_expr , in_macro , indent_of , is_allowed , is_expn_of , is_refutable ,
is_type_diagnostic_item , is_wild , match_qpath , match_type , match_var , multispan_sugg , remove_blocks , snippet ,
snippet_block , snippet_with_applicability , span_lint_and_help , span_lint_and_note , span_lint_and_sugg ,
span_lint_and_then , walk_ptrs_ty ,
2018-11-27 20:14:15 +00:00
} ;
use if_chain ::if_chain ;
2020-03-01 03:23:33 +00:00
use rustc_ast ::ast ::LitKind ;
2018-12-29 15:04:45 +00:00
use rustc_errors ::Applicability ;
2020-01-06 16:39:50 +00:00
use rustc_hir ::def ::CtorKind ;
2020-02-21 08:39:38 +00:00
use rustc_hir ::{
2020-07-14 12:59:59 +00:00
Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Guard , Local , MatchSource , Mutability , Node , Pat ,
PatKind , QPath , RangeEnd ,
2020-02-21 08:39:38 +00:00
} ;
2020-01-12 06:08:41 +00:00
use rustc_lint ::{ LateContext , LateLintPass , LintContext } ;
2020-03-30 09:02:14 +00:00
use rustc_middle ::lint ::in_external_macro ;
use rustc_middle ::ty ::{ self , Ty } ;
2020-01-19 10:07:13 +00:00
use rustc_session ::{ declare_tool_lint , impl_lint_pass } ;
2020-07-14 12:59:59 +00:00
use rustc_span ::source_map ::{ Span , Spanned } ;
2018-11-27 20:14:15 +00:00
use std ::cmp ::Ordering ;
use std ::collections ::Bound ;
2015-08-21 17:32:21 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for matches with a single arm where an `if let`
/// will usually suffice.
///
/// **Why is this bad?** Just readability – `if let` nests less than a `match`.
///
/// **Known problems:** None.
///
/// **Example:**
2019-03-09 07:51:23 +00:00
/// ```rust
/// # fn bar(stool: &str) {}
/// # let x = Some("abc");
2020-06-09 14:36:01 +00:00
///
/// // Bad
2019-03-05 16:50:33 +00:00
/// match x {
/// Some(ref foo) => bar(foo),
/// _ => (),
/// }
2020-06-09 14:36:01 +00:00
///
/// // Good
/// if let Some(ref foo) = x {
/// bar(foo);
/// }
2019-03-05 16:50:33 +00:00
/// ```
2018-11-27 20:49:09 +00:00
pub SINGLE_MATCH ,
style ,
2020-01-06 06:30:43 +00:00
" a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let` "
2016-02-05 23:13:29 +00:00
}
2015-12-23 00:14:10 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-08-16 12:41:35 +00:00
/// **What it does:** Checks for matches with two arms where an `if let else` will
2019-03-05 16:50:33 +00:00
/// usually suffice.
///
/// **Why is this bad?** Just readability – `if let` nests less than a `match`.
///
/// **Known problems:** Personal style preferences may differ.
///
/// **Example:**
///
/// Using `match`:
///
/// ```rust
2019-08-03 16:42:05 +00:00
/// # fn bar(foo: &usize) {}
/// # let other_ref: usize = 1;
/// # let x: Option<&usize> = Some(&1);
2019-03-05 16:50:33 +00:00
/// match x {
/// Some(ref foo) => bar(foo),
2019-08-03 16:42:05 +00:00
/// _ => bar(&other_ref),
2019-03-05 16:50:33 +00:00
/// }
/// ```
///
/// Using `if let` with `else`:
///
2019-03-09 07:51:23 +00:00
/// ```rust
2019-08-03 16:42:05 +00:00
/// # fn bar(foo: &usize) {}
/// # let other_ref: usize = 1;
/// # let x: Option<&usize> = Some(&1);
2019-03-05 16:50:33 +00:00
/// if let Some(ref foo) = x {
/// bar(foo);
/// } else {
2019-08-03 16:42:05 +00:00
/// bar(&other_ref);
2019-03-05 16:50:33 +00:00
/// }
/// ```
2018-11-27 20:49:09 +00:00
pub SINGLE_MATCH_ELSE ,
pedantic ,
2020-01-06 06:30:43 +00:00
" a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern "
2016-02-05 23:13:29 +00:00
}
2016-01-24 11:24:16 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for matches where all arms match a reference,
/// suggesting to remove the reference and deref the matched expression
/// instead. It also checks for `if let &foo = bar` blocks.
///
/// **Why is this bad?** It just makes the code less readable. That reference
/// destructuring adds nothing to the code.
///
/// **Known problems:** None.
///
/// **Example:**
2019-03-09 07:51:23 +00:00
/// ```rust,ignore
2020-06-09 14:36:01 +00:00
/// // Bad
2019-03-05 16:50:33 +00:00
/// match x {
/// &A(ref y) => foo(y),
/// &B => bar(),
/// _ => frob(&x),
/// }
2020-06-09 14:36:01 +00:00
///
/// // Good
/// match *x {
/// A(ref y) => foo(y),
/// B => bar(),
/// _ => frob(x),
/// }
2019-03-05 16:50:33 +00:00
/// ```
2016-08-06 08:18:36 +00:00
pub MATCH_REF_PATS ,
2018-03-28 13:24:26 +00:00
style ,
2020-01-06 06:30:43 +00:00
" a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression "
2016-02-05 23:13:29 +00:00
}
2015-12-23 00:14:10 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for matches where match expression is a `bool`. It
/// suggests to replace the expression with an `if...else` block.
///
/// **Why is this bad?** It makes the code less readable.
///
/// **Known problems:** None.
///
/// **Example:**
2019-03-09 07:51:23 +00:00
/// ```rust
/// # fn foo() {}
/// # fn bar() {}
2019-03-05 16:50:33 +00:00
/// let condition: bool = true;
/// match condition {
/// true => foo(),
/// false => bar(),
/// }
/// ```
/// Use if/else instead:
2019-03-09 07:51:23 +00:00
/// ```rust
/// # fn foo() {}
/// # fn bar() {}
2019-03-05 16:50:33 +00:00
/// let condition: bool = true;
/// if condition {
/// foo();
/// } else {
/// bar();
/// }
/// ```
2016-08-06 08:18:36 +00:00
pub MATCH_BOOL ,
2020-04-03 00:36:49 +00:00
pedantic ,
2020-01-06 06:30:43 +00:00
" a `match` on a boolean expression instead of an `if..else` block "
2016-02-05 23:13:29 +00:00
}
2015-08-21 17:32:21 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for overlapping match arms.
///
/// **Why is this bad?** It is likely to be an error and if not, makes the code
/// less obvious.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x = 5;
/// match x {
/// 1...10 => println!("1 ... 10"),
/// 5...15 => println!("5 ... 15"),
/// _ => (),
/// }
/// ```
2016-08-06 08:18:36 +00:00
pub MATCH_OVERLAPPING_ARM ,
2018-03-28 13:24:26 +00:00
style ,
2020-01-06 06:30:43 +00:00
" a `match` with overlapping arms "
2016-02-05 23:13:29 +00:00
}
2015-12-23 00:14:10 +00:00
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for arm which matches all errors with `Err(_)`
/// and take drastic actions like `panic!`.
///
2020-05-28 13:45:24 +00:00
/// **Why is this bad?** It is generally a bad practice, similar to
2019-03-05 16:50:33 +00:00
/// catching all exceptions in java with `catch(Exception)`
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
2019-03-05 22:23:50 +00:00
/// let x: Result<i32, &str> = Ok(3);
2019-03-05 16:50:33 +00:00
/// match x {
/// Ok(_) => println!("ok"),
/// Err(_) => panic!("err"),
/// }
/// ```
2017-02-11 06:57:50 +00:00
pub MATCH_WILD_ERR_ARM ,
2020-05-28 13:45:24 +00:00
pedantic ,
2020-01-06 06:30:43 +00:00
" a `match` with `Err(_)` arm and take drastic actions "
2017-02-11 06:57:50 +00:00
}
2018-03-28 13:24:26 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for match which is used to add a reference to an
/// `Option` value.
///
/// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x: Option<()> = None;
2020-06-09 14:36:01 +00:00
///
/// // Bad
2019-03-05 16:50:33 +00:00
/// let r: Option<&()> = match x {
/// None => None,
/// Some(ref v) => Some(v),
/// };
2020-06-09 14:36:01 +00:00
///
/// // Good
/// let r: Option<&()> = x.as_ref();
2019-03-05 16:50:33 +00:00
/// ```
2017-12-19 22:22:16 +00:00
pub MATCH_AS_REF ,
2018-03-28 13:24:26 +00:00
complexity ,
2020-01-06 06:30:43 +00:00
" a `match` on an Option value instead of using `as_ref()` or `as_mut` "
2017-12-19 22:22:16 +00:00
}
2019-01-10 20:56:28 +00:00
declare_clippy_lint! {
2019-03-05 16:50:33 +00:00
/// **What it does:** Checks for wildcard enum matches using `_`.
///
/// **Why is this bad?** New enum variants added by library updates can be missed.
///
/// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
/// variants, and also may not use correct path to enum if it's not present in the current scope.
///
/// **Example:**
/// ```rust
2019-08-03 19:24:50 +00:00
/// # enum Foo { A(usize), B(usize) }
/// # let x = Foo::B(1);
2020-06-09 14:36:01 +00:00
///
/// // Bad
2019-03-05 16:50:33 +00:00
/// match x {
2020-05-28 13:45:24 +00:00
/// Foo::A(_) => {},
2019-03-05 16:50:33 +00:00
/// _ => {},
/// }
2020-06-09 14:36:01 +00:00
///
/// // Good
/// match x {
/// Foo::A(_) => {},
/// Foo::B(_) => {},
/// }
2019-03-05 16:50:33 +00:00
/// ```
2019-01-29 20:25:40 +00:00
pub WILDCARD_ENUM_MATCH_ARM ,
2019-01-10 20:56:28 +00:00
restriction ,
2019-01-29 20:25:40 +00:00
" a wildcard enum match arm using `_` "
2019-01-10 20:56:28 +00:00
}
2020-05-28 13:45:24 +00:00
declare_clippy_lint! {
/// **What it does:** Checks for wildcard enum matches for a single variant.
///
/// **Why is this bad?** New enum variants added by library updates can be missed.
///
/// **Known problems:** Suggested replacements may not use correct path to enum
/// if it's not present in the current scope.
///
/// **Example:**
///
/// ```rust
/// # enum Foo { A, B, C }
/// # let x = Foo::B;
2020-06-09 14:36:01 +00:00
/// // Bad
2020-05-28 13:45:24 +00:00
/// match x {
/// Foo::A => {},
/// Foo::B => {},
/// _ => {},
/// }
2020-06-09 14:36:01 +00:00
///
/// // Good
2020-05-28 13:45:24 +00:00
/// match x {
/// Foo::A => {},
/// Foo::B => {},
/// Foo::C => {},
/// }
/// ```
pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS ,
pedantic ,
" a wildcard enum match for a single variant "
}
2019-12-24 15:42:09 +00:00
declare_clippy_lint! {
/// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
///
/// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
/// It makes the code less readable, especially to spot wildcard pattern use in match arm.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
2020-06-09 14:36:01 +00:00
/// // Bad
2019-12-24 15:42:09 +00:00
/// match "foo" {
/// "a" => {},
/// "bar" | _ => {},
/// }
2020-06-09 14:36:01 +00:00
///
/// // Good
/// match "foo" {
/// "a" => {},
/// _ => {},
/// }
2019-12-24 15:42:09 +00:00
/// ```
2020-01-05 14:05:16 +00:00
pub WILDCARD_IN_OR_PATTERNS ,
2019-12-27 01:03:16 +00:00
complexity ,
2019-12-24 15:42:09 +00:00
" a wildcard pattern used with others patterns in same match arm "
}
2020-01-19 10:07:13 +00:00
declare_clippy_lint! {
/// **What it does:** Checks for matches being used to destructure a single-variant enum
/// or tuple struct where a `let` will suffice.
///
/// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Wrapper {
/// Data(i32),
/// }
///
/// let wrapper = Wrapper::Data(42);
///
/// let data = match wrapper {
/// Wrapper::Data(i) => i,
/// };
/// ```
///
/// The correct use would be:
/// ```rust
/// enum Wrapper {
/// Data(i32),
/// }
///
/// let wrapper = Wrapper::Data(42);
/// let Wrapper::Data(data) = wrapper;
/// ```
pub INFALLIBLE_DESTRUCTURING_MATCH ,
style ,
" a `match` statement with a single infallible arm instead of a `let` "
}
2020-01-15 06:56:56 +00:00
declare_clippy_lint! {
/// **What it does:** Checks for useless match that binds to only one value.
///
/// **Why is this bad?** Readability and needless complexity.
///
2020-02-04 19:19:20 +00:00
/// **Known problems:** Suggested replacements may be incorrect when `match`
/// is actually binding temporary value, bringing a 'dropped while borrowed' error.
2020-01-15 06:56:56 +00:00
///
/// **Example:**
/// ```rust
/// # let a = 1;
/// # let b = 2;
///
/// // Bad
/// match (a, b) {
/// (c, d) => {
/// // useless match
/// }
/// }
///
/// // Good
/// let (c, d) = (a, b);
/// ```
pub MATCH_SINGLE_BINDING ,
complexity ,
" a match with a single binding instead of using `let` statement "
}
2020-03-01 16:36:14 +00:00
declare_clippy_lint! {
/// **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
///
/// **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after
/// matching all enum variants explicitly.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// # struct A { a: i32 }
/// let a = A { a: 5 };
///
/// // Bad
/// match a {
/// A { a: 5, .. } => {},
/// _ => {},
/// }
///
/// // Good
/// match a {
/// A { a: 5 } => {},
/// _ => {},
/// }
/// ```
pub REST_PAT_IN_FULLY_BOUND_STRUCTS ,
restriction ,
" a match on a struct that binds all fields but still uses the wildcard pattern "
}
2020-07-14 12:59:59 +00:00
declare_clippy_lint! {
/// **What it does:** Lint for redundant pattern matching over `Result` or
/// `Option`
///
/// **Why is this bad?** It's more concise and clear to just use the proper
/// utility function
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// if let Ok(_) = Ok::<i32, i32>(42) {}
/// if let Err(_) = Err::<i32, i32>(42) {}
/// if let None = None::<()> {}
/// if let Some(_) = Some(42) {}
/// match Ok::<i32, i32>(42) {
/// Ok(_) => true,
/// Err(_) => false,
/// };
/// ```
///
/// The more idiomatic use would be:
///
/// ```rust
/// if Ok::<i32, i32>(42).is_ok() {}
/// if Err::<i32, i32>(42).is_err() {}
/// if None::<()>.is_none() {}
/// if Some(42).is_some() {}
/// Ok::<i32, i32>(42).is_ok();
/// ```
pub REDUNDANT_PATTERN_MATCHING ,
style ,
" use the proper utility function avoiding an `if let` "
}
declare_clippy_lint! {
/// **What it does:** Checks for `match` or `if let` expressions producing a
/// `bool` that could be written using `matches!`
///
/// **Why is this bad?** Readability and needless complexity.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let x = Some(5);
///
/// // Bad
/// let a = match x {
/// Some(0) => true,
/// _ => false,
/// };
///
/// let a = if let Some(0) = x {
/// true
/// } else {
/// false
/// };
///
/// // Good
/// let a = matches!(x, Some(0));
/// ```
pub MATCH_LIKE_MATCHES_MACRO ,
style ,
" a match that could be written with the matches! macro "
}
2020-01-19 10:07:13 +00:00
#[ derive(Default) ]
pub struct Matches {
infallible_destructuring_match_linted : bool ,
}
impl_lint_pass! ( Matches = > [
2019-04-08 20:43:55 +00:00
SINGLE_MATCH ,
MATCH_REF_PATS ,
MATCH_BOOL ,
SINGLE_MATCH_ELSE ,
MATCH_OVERLAPPING_ARM ,
MATCH_WILD_ERR_ARM ,
MATCH_AS_REF ,
2019-12-24 15:42:09 +00:00
WILDCARD_ENUM_MATCH_ARM ,
2020-05-28 13:45:24 +00:00
MATCH_WILDCARD_FOR_SINGLE_VARIANTS ,
2020-01-15 06:56:56 +00:00
WILDCARD_IN_OR_PATTERNS ,
MATCH_SINGLE_BINDING ,
2020-03-01 16:36:14 +00:00
INFALLIBLE_DESTRUCTURING_MATCH ,
2020-07-14 12:59:59 +00:00
REST_PAT_IN_FULLY_BOUND_STRUCTS ,
REDUNDANT_PATTERN_MATCHING ,
MATCH_LIKE_MATCHES_MACRO
2019-04-08 20:43:55 +00:00
] ) ;
2015-08-21 17:32:21 +00:00
2020-06-25 20:41:36 +00:00
impl < ' tcx > LateLintPass < ' tcx > for Matches {
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > ) {
2018-07-24 06:55:38 +00:00
if in_external_macro ( cx . sess ( ) , expr . span ) {
2016-01-04 04:26:12 +00:00
return ;
}
2020-07-14 12:59:59 +00:00
redundant_pattern_match ::check ( cx , expr ) ;
check_match_like_matches ( cx , expr ) ;
2019-09-27 15:16:06 +00:00
if let ExprKind ::Match ( ref ex , ref arms , MatchSource ::Normal ) = expr . kind {
2015-12-23 01:19:32 +00:00
check_single_match ( cx , ex , arms , expr ) ;
check_match_bool ( cx , ex , arms , expr ) ;
2015-12-23 10:25:44 +00:00
check_overlapping_arms ( cx , ex , arms ) ;
2017-02-11 06:57:50 +00:00
check_wild_err_arm ( cx , ex , arms ) ;
2019-01-29 20:25:40 +00:00
check_wild_enum_match ( cx , ex , arms ) ;
2017-12-19 22:22:16 +00:00
check_match_as_ref ( cx , ex , arms , expr ) ;
2020-01-07 17:13:31 +00:00
check_wild_in_or_pats ( cx , arms ) ;
2020-01-19 10:07:13 +00:00
if self . infallible_destructuring_match_linted {
self . infallible_destructuring_match_linted = false ;
} else {
check_match_single_binding ( cx , ex , arms , expr ) ;
}
2015-12-23 01:19:32 +00:00
}
2019-09-27 15:16:06 +00:00
if let ExprKind ::Match ( ref ex , ref arms , _ ) = expr . kind {
2018-02-04 12:41:54 +00:00
check_match_ref_pats ( cx , ex , arms , expr ) ;
2015-12-23 01:19:32 +00:00
}
}
2020-01-19 10:07:13 +00:00
2020-06-25 20:41:36 +00:00
fn check_local ( & mut self , cx : & LateContext < ' tcx > , local : & ' tcx Local < '_ > ) {
2020-01-19 10:07:13 +00:00
if_chain! {
2020-03-27 19:36:00 +00:00
if ! in_external_macro ( cx . sess ( ) , local . span ) ;
if ! in_macro ( local . span ) ;
2020-01-19 10:07:13 +00:00
if let Some ( ref expr ) = local . init ;
if let ExprKind ::Match ( ref target , ref arms , MatchSource ::Normal ) = expr . kind ;
if arms . len ( ) = = 1 & & arms [ 0 ] . guard . is_none ( ) ;
if let PatKind ::TupleStruct (
QPath ::Resolved ( None , ref variant_name ) , ref args , _ ) = arms [ 0 ] . pat . kind ;
if args . len ( ) = = 1 ;
if let Some ( arg ) = get_arg_name ( & args [ 0 ] ) ;
let body = remove_blocks ( & arms [ 0 ] . body ) ;
if match_var ( body , arg ) ;
then {
let mut applicability = Applicability ::MachineApplicable ;
self . infallible_destructuring_match_linted = true ;
span_lint_and_sugg (
cx ,
INFALLIBLE_DESTRUCTURING_MATCH ,
local . span ,
" you seem to be trying to use `match` to destructure a single infallible pattern. \
Consider using ` let ` " ,
" try this " ,
format! (
" let {}({}) = {}; " ,
snippet_with_applicability ( cx , variant_name . span , " .. " , & mut applicability ) ,
snippet_with_applicability ( cx , local . pat . span , " .. " , & mut applicability ) ,
snippet_with_applicability ( cx , target . span , " .. " , & mut applicability ) ,
) ,
applicability ,
) ;
}
}
}
2020-03-01 16:36:14 +00:00
2020-06-25 20:41:36 +00:00
fn check_pat ( & mut self , cx : & LateContext < ' tcx > , pat : & ' tcx Pat < '_ > ) {
2020-03-01 16:36:14 +00:00
if_chain! {
2020-03-27 19:36:00 +00:00
if ! in_external_macro ( cx . sess ( ) , pat . span ) ;
if ! in_macro ( pat . span ) ;
2020-03-01 16:36:14 +00:00
if let PatKind ::Struct ( ref qpath , fields , true ) = pat . kind ;
if let QPath ::Resolved ( _ , ref path ) = qpath ;
if let Some ( def_id ) = path . res . opt_def_id ( ) ;
let ty = cx . tcx . type_of ( def_id ) ;
if let ty ::Adt ( def , _ ) = ty . kind ;
if def . is_struct ( ) | | def . is_union ( ) ;
if fields . len ( ) = = def . non_enum_variant ( ) . fields . len ( ) ;
then {
span_lint_and_help (
cx ,
REST_PAT_IN_FULLY_BOUND_STRUCTS ,
pat . span ,
" unnecessary use of `..` pattern in struct binding. All fields were already bound " ,
2020-04-18 10:28:29 +00:00
None ,
2020-03-01 16:36:14 +00:00
" consider removing `..` from this binding " ,
) ;
}
}
}
2015-12-23 01:19:32 +00:00
}
2015-08-21 17:49:00 +00:00
2018-05-30 16:24:44 +00:00
#[ rustfmt::skip ]
2020-06-25 20:41:36 +00:00
fn check_single_match ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] , expr : & Expr < '_ > ) {
2019-09-25 19:00:17 +00:00
if arms . len ( ) = = 2 & & arms [ 0 ] . guard . is_none ( ) & & arms [ 1 ] . guard . is_none ( ) {
2020-03-31 10:20:15 +00:00
if in_macro ( expr . span ) {
// Don't lint match expressions present in
// macro_rules! block
return ;
}
2019-09-27 15:16:06 +00:00
if let PatKind ::Or ( .. ) = arms [ 0 ] . pat . kind {
2019-09-25 20:34:55 +00:00
// don't lint for or patterns for now, this makes
// the lint noisy in unnecessary situations
return ;
}
2020-07-14 12:59:59 +00:00
let els = arms [ 1 ] . body ;
let els = if is_unit_expr ( remove_blocks ( els ) ) {
2016-02-24 16:38:57 +00:00
None
2020-07-14 12:59:59 +00:00
} else if let ExprKind ::Block ( Block { stmts , expr : block_expr , .. } , _ ) = els . kind {
if stmts . len ( ) = = 1 & & block_expr . is_none ( ) | | stmts . is_empty ( ) & & block_expr . is_some ( ) {
// single statement/expr "else" block, don't lint
return ;
} else {
// block with 2+ statements or 1 expr and 1+ statement
Some ( els )
}
2016-02-24 16:38:57 +00:00
} else {
2020-07-14 12:59:59 +00:00
// not a block, don't lint
return ;
2016-02-24 16:38:57 +00:00
} ;
2020-07-14 12:59:59 +00:00
2020-07-17 08:47:04 +00:00
let ty = cx . typeck_results ( ) . expr_ty ( ex ) ;
2019-09-26 09:03:36 +00:00
if ty . kind ! = ty ::Bool | | is_allowed ( cx , MATCH_BOOL , ex . hir_id ) {
2016-02-24 16:38:57 +00:00
check_single_match_single_pattern ( cx , ex , arms , expr , els ) ;
check_single_match_opt_like ( cx , ex , arms , expr , ty , els ) ;
}
2016-01-13 00:19:27 +00:00
}
}
2018-11-27 20:14:15 +00:00
fn check_single_match_single_pattern (
2020-06-25 20:41:36 +00:00
cx : & LateContext < '_ > ,
2019-12-27 07:12:26 +00:00
ex : & Expr < '_ > ,
arms : & [ Arm < '_ > ] ,
expr : & Expr < '_ > ,
els : Option < & Expr < '_ > > ,
2018-11-27 20:14:15 +00:00
) {
2019-09-25 19:00:17 +00:00
if is_wild ( & arms [ 1 ] . pat ) {
2017-03-03 14:44:23 +00:00
report_single_match_single_pattern ( cx , ex , arms , expr , els ) ;
2015-12-23 01:19:32 +00:00
}
}
2018-11-27 20:14:15 +00:00
fn report_single_match_single_pattern (
2020-06-25 20:41:36 +00:00
cx : & LateContext < '_ > ,
2019-12-27 07:12:26 +00:00
ex : & Expr < '_ > ,
arms : & [ Arm < '_ > ] ,
expr : & Expr < '_ > ,
els : Option < & Expr < '_ > > ,
2018-11-27 20:14:15 +00:00
) {
let lint = if els . is_some ( ) { SINGLE_MATCH_ELSE } else { SINGLE_MATCH } ;
let els_str = els . map_or ( String ::new ( ) , | els | {
2020-02-04 15:07:09 +00:00
format! ( " else {} " , expr_block ( cx , els , None , " .. " , Some ( expr . span ) ) )
2018-11-27 20:14:15 +00:00
} ) ;
2017-08-09 07:30:56 +00:00
span_lint_and_sugg (
cx ,
lint ,
expr . span ,
" you seem to be trying to use match for destructuring a single pattern. Consider using `if \
2017-09-05 09:33:04 +00:00
let ` " ,
2017-08-09 07:30:56 +00:00
" try this " ,
format! (
" if let {} = {} {}{} " ,
2019-09-25 19:00:17 +00:00
snippet ( cx , arms [ 0 ] . pat . span , " .. " ) ,
2017-08-09 07:30:56 +00:00
snippet ( cx , ex . span , " .. " ) ,
2020-02-04 15:07:09 +00:00
expr_block ( cx , & arms [ 0 ] . body , None , " .. " , Some ( expr . span ) ) ,
2018-11-20 13:06:29 +00:00
els_str ,
2017-08-09 07:30:56 +00:00
) ,
2018-11-27 14:13:57 +00:00
Applicability ::HasPlaceholders ,
2017-08-09 07:30:56 +00:00
) ;
2017-03-03 14:44:23 +00:00
}
2018-11-27 20:14:15 +00:00
fn check_single_match_opt_like (
2020-06-25 20:41:36 +00:00
cx : & LateContext < '_ > ,
2019-12-27 07:12:26 +00:00
ex : & Expr < '_ > ,
arms : & [ Arm < '_ > ] ,
expr : & Expr < '_ > ,
2018-11-27 20:14:15 +00:00
ty : Ty < '_ > ,
2019-12-27 07:12:26 +00:00
els : Option < & Expr < '_ > > ,
2018-11-27 20:14:15 +00:00
) {
2019-03-10 17:19:47 +00:00
// list of candidate `Enum`s we know will never get any more members
2017-08-09 07:30:56 +00:00
let candidates = & [
2019-05-17 21:53:54 +00:00
( & paths ::COW , " Borrowed " ) ,
( & paths ::COW , " Cow::Borrowed " ) ,
( & paths ::COW , " Cow::Owned " ) ,
( & paths ::COW , " Owned " ) ,
( & paths ::OPTION , " None " ) ,
( & paths ::RESULT , " Err " ) ,
( & paths ::RESULT , " Ok " ) ,
2017-08-09 07:30:56 +00:00
] ;
2016-01-13 00:19:27 +00:00
2019-09-27 15:16:06 +00:00
let path = match arms [ 1 ] . pat . kind {
2016-05-27 12:24:28 +00:00
PatKind ::TupleStruct ( ref path , ref inner , _ ) = > {
2019-03-10 17:19:47 +00:00
// Contains any non wildcard patterns (e.g., `Err(err)`)?
2018-07-14 22:00:27 +00:00
if ! inner . iter ( ) . all ( is_wild ) {
2016-01-24 11:24:16 +00:00
return ;
}
2020-03-27 14:34:29 +00:00
rustc_hir_pretty ::to_string ( rustc_hir_pretty ::NO_ANN , | s | s . print_qpath ( path , false ) )
2016-12-20 17:21:30 +00:00
} ,
2019-02-03 07:12:07 +00:00
PatKind ::Binding ( BindingAnnotation ::Unannotated , .. , ident , None ) = > ident . to_string ( ) ,
2020-03-27 14:34:29 +00:00
PatKind ::Path ( ref path ) = > {
rustc_hir_pretty ::to_string ( rustc_hir_pretty ::NO_ANN , | s | s . print_qpath ( path , false ) )
} ,
2016-01-30 12:48:39 +00:00
_ = > return ,
2016-01-13 00:19:27 +00:00
} ;
for & ( ty_path , pat_path ) in candidates {
2017-03-09 09:58:31 +00:00
if path = = * pat_path & & match_type ( cx , ty , ty_path ) {
2017-03-03 14:44:23 +00:00
report_single_match_single_pattern ( cx , ex , arms , expr , els ) ;
2016-01-13 00:19:27 +00:00
}
}
}
2020-06-25 20:41:36 +00:00
fn check_match_bool ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] , expr : & Expr < '_ > ) {
2019-03-10 17:19:47 +00:00
// Type of expression is `bool`.
2020-07-17 08:47:04 +00:00
if cx . typeck_results ( ) . expr_ty ( ex ) . kind = = ty ::Bool {
2017-08-09 07:30:56 +00:00
span_lint_and_then (
cx ,
MATCH_BOOL ,
expr . span ,
" you seem to be trying to match on a boolean expression " ,
2020-04-17 06:08:00 +00:00
move | diag | {
2019-09-25 19:00:17 +00:00
if arms . len ( ) = = 2 {
2017-08-09 07:30:56 +00:00
// no guards
2019-09-27 15:16:06 +00:00
let exprs = if let PatKind ::Lit ( ref arm_bool ) = arms [ 0 ] . pat . kind {
if let ExprKind ::Lit ( ref lit ) = arm_bool . kind {
2017-08-09 07:30:56 +00:00
match lit . node {
LitKind ::Bool ( true ) = > Some ( ( & * arms [ 0 ] . body , & * arms [ 1 ] . body ) ) ,
LitKind ::Bool ( false ) = > Some ( ( & * arms [ 1 ] . body , & * arms [ 0 ] . body ) ) ,
_ = > None ,
}
} else {
None
2016-07-03 17:13:01 +00:00
}
} else {
None
} ;
2017-08-09 07:30:56 +00:00
if let Some ( ( true_expr , false_expr ) ) = exprs {
let sugg = match ( is_unit_expr ( true_expr ) , is_unit_expr ( false_expr ) ) {
2017-09-05 09:33:04 +00:00
( false , false ) = > Some ( format! (
" if {} {} else {} " ,
snippet ( cx , ex . span , " b " ) ,
2020-02-04 15:07:09 +00:00
expr_block ( cx , true_expr , None , " .. " , Some ( expr . span ) ) ,
expr_block ( cx , false_expr , None , " .. " , Some ( expr . span ) )
2017-09-05 09:33:04 +00:00
) ) ,
( false , true ) = > Some ( format! (
" if {} {} " ,
snippet ( cx , ex . span , " b " ) ,
2020-02-04 15:07:09 +00:00
expr_block ( cx , true_expr , None , " .. " , Some ( expr . span ) )
2017-09-05 09:33:04 +00:00
) ) ,
2017-08-09 07:30:56 +00:00
( true , false ) = > {
let test = Sugg ::hir ( cx , ex , " .. " ) ;
2020-02-04 15:07:09 +00:00
Some ( format! (
" if {} {} " ,
! test ,
expr_block ( cx , false_expr , None , " .. " , Some ( expr . span ) )
) )
2017-08-09 07:30:56 +00:00
} ,
( true , true ) = > None ,
} ;
if let Some ( sugg ) = sugg {
2020-04-17 06:08:00 +00:00
diag . span_suggestion (
2018-09-18 15:07:54 +00:00
expr . span ,
2020-01-06 06:30:43 +00:00
" consider using an `if`/`else` expression " ,
2018-09-18 15:07:54 +00:00
sugg ,
2018-09-18 19:43:52 +00:00
Applicability ::HasPlaceholders ,
2018-09-18 15:07:54 +00:00
) ;
2017-08-09 07:30:56 +00:00
}
2016-04-14 18:14:03 +00:00
}
2015-10-21 06:24:56 +00:00
}
2017-08-09 07:30:56 +00:00
} ,
) ;
2015-12-23 01:19:32 +00:00
}
}
2015-12-23 00:14:10 +00:00
2020-06-25 20:41:36 +00:00
fn check_overlapping_arms < ' tcx > ( cx : & LateContext < ' tcx > , ex : & ' tcx Expr < '_ > , arms : & ' tcx [ Arm < '_ > ] ) {
2020-07-17 08:47:04 +00:00
if arms . len ( ) > = 2 & & cx . typeck_results ( ) . expr_ty ( ex ) . is_integral ( ) {
let ranges = all_ranges ( cx , arms , cx . typeck_results ( ) . expr_ty ( ex ) ) ;
2016-03-15 19:09:53 +00:00
let type_ranges = type_ranges ( & ranges ) ;
if ! type_ranges . is_empty ( ) {
if let Some ( ( start , end ) ) = overlapping ( & type_ranges ) {
2020-01-27 02:26:42 +00:00
span_lint_and_note (
2017-08-09 07:30:56 +00:00
cx ,
MATCH_OVERLAPPING_ARM ,
start . span ,
" some ranges overlap " ,
2020-04-18 10:29:36 +00:00
Some ( end . span ) ,
2017-08-09 07:30:56 +00:00
" overlaps with this " ,
) ;
2016-03-15 19:09:53 +00:00
}
2015-08-21 17:32:21 +00:00
}
2015-12-23 01:19:32 +00:00
}
}
2020-06-25 20:41:36 +00:00
fn check_wild_err_arm ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] ) {
2020-07-17 08:47:04 +00:00
let ex_ty = walk_ptrs_ty ( cx . typeck_results ( ) . expr_ty ( ex ) ) ;
2020-04-12 13:23:07 +00:00
if is_type_diagnostic_item ( cx , ex_ty , sym! ( result_type ) ) {
2017-02-11 06:57:50 +00:00
for arm in arms {
2019-09-27 15:16:06 +00:00
if let PatKind ::TupleStruct ( ref path , ref inner , _ ) = arm . pat . kind {
2020-03-27 14:34:29 +00:00
let path_str = rustc_hir_pretty ::to_string ( rustc_hir_pretty ::NO_ANN , | s | s . print_qpath ( path , false ) ) ;
2020-01-10 09:56:09 +00:00
if path_str = = " Err " {
let mut matching_wild = inner . iter ( ) . any ( is_wild ) ;
let mut ident_bind_name = String ::from ( " _ " ) ;
if ! matching_wild {
// Looking for unused bindings (i.e.: `_e`)
inner . iter ( ) . for_each ( | pat | {
if let PatKind ::Binding ( .. , ident , None ) = & pat . kind {
if ident . as_str ( ) . starts_with ( '_' ) & & is_unused ( ident , arm . body ) {
ident_bind_name = ( & ident . name . as_str ( ) ) . to_string ( ) ;
matching_wild = true ;
}
}
} ) ;
}
if_chain! {
if matching_wild ;
if let ExprKind ::Block ( ref block , _ ) = arm . body . kind ;
if is_panic_block ( block ) ;
then {
// `Err(_)` or `Err(_e)` arm with `panic!` found
2020-01-27 02:26:42 +00:00
span_lint_and_note ( cx ,
2020-01-10 14:09:37 +00:00
MATCH_WILD_ERR_ARM ,
arm . pat . span ,
2020-01-10 14:12:21 +00:00
& format! ( " `Err( {} )` matches all errors " , & ident_bind_name ) ,
2020-04-18 10:29:36 +00:00
None ,
2020-05-28 13:45:24 +00:00
" match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable " ,
2020-01-10 14:09:37 +00:00
) ;
2020-01-10 09:56:09 +00:00
}
2017-10-23 19:18:02 +00:00
}
}
2017-02-11 06:57:50 +00:00
}
}
}
}
2020-06-25 20:41:36 +00:00
fn check_wild_enum_match ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] ) {
2020-07-17 08:47:04 +00:00
let ty = cx . typeck_results ( ) . expr_ty ( ex ) ;
2019-01-31 22:01:23 +00:00
if ! ty . is_enum ( ) {
// If there isn't a nice closed set of possible values that can be conveniently enumerated,
// don't complain about not enumerating the mall.
return ;
}
// First pass - check for violation, but don't do much book-keeping because this is hopefully
// the uncommon case, and the book-keeping is slightly expensive.
let mut wildcard_span = None ;
let mut wildcard_ident = None ;
for arm in arms {
2019-09-27 15:16:06 +00:00
if let PatKind ::Wild = arm . pat . kind {
2019-09-25 19:00:17 +00:00
wildcard_span = Some ( arm . pat . span ) ;
2019-09-27 15:16:06 +00:00
} else if let PatKind ::Binding ( _ , _ , ident , None ) = arm . pat . kind {
2019-09-25 19:00:17 +00:00
wildcard_span = Some ( arm . pat . span ) ;
wildcard_ident = Some ( ident ) ;
2019-01-31 22:01:23 +00:00
}
}
if let Some ( wildcard_span ) = wildcard_span {
// Accumulate the variants which should be put in place of the wildcard because they're not
// already covered.
let mut missing_variants = vec! [ ] ;
2019-09-26 09:03:36 +00:00
if let ty ::Adt ( def , _ ) = ty . kind {
2019-01-31 22:01:23 +00:00
for variant in & def . variants {
missing_variants . push ( variant ) ;
}
}
2019-01-29 20:25:40 +00:00
for arm in arms {
2019-01-31 22:01:23 +00:00
if arm . guard . is_some ( ) {
// Guards mean that this case probably isn't exhaustively covered. Technically
// this is incorrect, as we should really check whether each variant is exhaustively
// covered by the set of guards that cover it, but that's really hard to do.
continue ;
}
2019-09-27 15:16:06 +00:00
if let PatKind ::Path ( ref path ) = arm . pat . kind {
2019-09-25 19:00:17 +00:00
if let QPath ::Resolved ( _ , p ) = path {
missing_variants . retain ( | e | e . ctor_def_id ! = Some ( p . res . def_id ( ) ) ) ;
}
2020-05-28 13:45:24 +00:00
} else if let PatKind ::TupleStruct ( ref path , ref patterns , .. ) = arm . pat . kind {
2019-09-25 19:00:17 +00:00
if let QPath ::Resolved ( _ , p ) = path {
2020-05-28 13:45:24 +00:00
// Some simple checks for exhaustive patterns.
// There is a room for improvements to detect more cases,
// but it can be more expensive to do so.
2020-07-14 12:59:59 +00:00
let is_pattern_exhaustive =
| pat : & & Pat < '_ > | matches! ( pat . kind , PatKind ::Wild | PatKind ::Binding ( .. , None ) ) ;
2020-05-28 13:45:24 +00:00
if patterns . iter ( ) . all ( is_pattern_exhaustive ) {
missing_variants . retain ( | e | e . ctor_def_id ! = Some ( p . res . def_id ( ) ) ) ;
}
2019-01-31 22:01:23 +00:00
}
2019-01-29 20:25:40 +00:00
}
2019-01-10 20:56:28 +00:00
}
2019-01-31 22:01:23 +00:00
2019-12-22 02:13:39 +00:00
let mut suggestion : Vec < String > = missing_variants
2019-01-31 22:01:23 +00:00
. iter ( )
. map ( | v | {
let suffix = match v . ctor_kind {
CtorKind ::Fn = > " (..) " ,
CtorKind ::Const | CtorKind ::Fictive = > " " ,
} ;
let ident_str = if let Some ( ident ) = wildcard_ident {
format! ( " {} @ " , ident . name )
} else {
String ::new ( )
} ;
// This path assumes that the enum type is imported into scope.
2019-03-26 09:55:03 +00:00
format! ( " {} {} {} " , ident_str , cx . tcx . def_path_str ( v . def_id ) , suffix )
2019-01-31 22:01:23 +00:00
} )
. collect ( ) ;
2019-02-18 22:55:16 +00:00
if suggestion . is_empty ( ) {
return ;
}
2019-12-22 11:51:39 +00:00
let mut message = " wildcard match will miss any future added variants " ;
2019-12-22 02:13:39 +00:00
if let ty ::Adt ( def , _ ) = ty . kind {
if def . is_variant_list_non_exhaustive ( ) {
2019-12-22 11:51:39 +00:00
message = " match on non-exhaustive enum doesn't explicitly match all known variants " ;
2019-12-22 02:13:39 +00:00
suggestion . push ( String ::from ( " _ " ) ) ;
}
}
2020-05-28 13:45:24 +00:00
if suggestion . len ( ) = = 1 {
// No need to check for non-exhaustive enum as in that case len would be greater than 1
span_lint_and_sugg (
cx ,
MATCH_WILDCARD_FOR_SINGLE_VARIANTS ,
wildcard_span ,
message ,
" try this " ,
suggestion [ 0 ] . clone ( ) ,
Applicability ::MaybeIncorrect ,
)
} ;
2019-01-31 22:01:23 +00:00
span_lint_and_sugg (
cx ,
WILDCARD_ENUM_MATCH_ARM ,
wildcard_span ,
2019-12-22 02:13:39 +00:00
message ,
2019-01-31 22:01:23 +00:00
" try this " ,
suggestion . join ( " | " ) ,
2020-05-28 13:45:24 +00:00
Applicability ::MaybeIncorrect ,
2019-01-31 22:01:23 +00:00
)
2019-01-10 20:56:28 +00:00
}
}
2017-02-11 13:42:42 +00:00
// If the block contains only a `panic!` macro (as expression or statement)
2019-12-27 07:12:26 +00:00
fn is_panic_block ( block : & Block < '_ > ) -> bool {
2017-02-11 06:57:50 +00:00
match ( & block . expr , block . stmts . len ( ) , block . stmts . first ( ) ) {
2017-02-11 13:47:26 +00:00
( & Some ( ref exp ) , 0 , _ ) = > {
2019-05-17 21:53:54 +00:00
is_expn_of ( exp . span , " panic " ) . is_some ( ) & & is_expn_of ( exp . span , " unreachable " ) . is_none ( )
2017-02-11 13:47:26 +00:00
} ,
2017-02-12 01:16:37 +00:00
( & None , 1 , Some ( stmt ) ) = > {
2019-05-17 21:53:54 +00:00
is_expn_of ( stmt . span , " panic " ) . is_some ( ) & & is_expn_of ( stmt . span , " unreachable " ) . is_none ( )
2017-02-11 13:47:26 +00:00
} ,
_ = > false ,
2017-02-11 06:57:50 +00:00
}
}
2020-06-25 20:41:36 +00:00
fn check_match_ref_pats ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] , expr : & Expr < '_ > ) {
2015-12-23 01:19:32 +00:00
if has_only_ref_pats ( arms ) {
2020-02-18 11:50:10 +00:00
let mut suggs = Vec ::with_capacity ( arms . len ( ) + 1 ) ;
2019-12-21 18:38:45 +00:00
let ( title , msg ) = if let ExprKind ::AddrOf ( BorrowKind ::Ref , Mutability ::Not , ref inner ) = ex . kind {
2019-02-16 18:35:15 +00:00
let span = ex . span . source_callsite ( ) ;
suggs . push ( ( span , Sugg ::hir_with_macro_callsite ( cx , inner , " .. " ) . to_string ( ) ) ) ;
2018-02-04 12:41:54 +00:00
(
2017-09-05 09:33:04 +00:00
" you don't need to add `&` to both the expression and the patterns " ,
2018-02-04 12:41:54 +00:00
" try " ,
)
2015-12-23 01:19:32 +00:00
} else {
2019-02-16 18:35:15 +00:00
let span = ex . span . source_callsite ( ) ;
suggs . push ( ( span , Sugg ::hir_with_macro_callsite ( cx , ex , " .. " ) . deref ( ) . to_string ( ) ) ) ;
2018-02-04 12:41:54 +00:00
(
2017-08-09 07:30:56 +00:00
" you don't need to add `&` to all patterns " ,
2018-02-04 12:41:54 +00:00
" instead of prefixing all patterns with `&`, you can dereference the expression " ,
)
} ;
2019-09-25 19:00:17 +00:00
suggs . extend ( arms . iter ( ) . filter_map ( | a | {
2019-09-27 15:16:06 +00:00
if let PatKind ::Ref ( ref refp , _ ) = a . pat . kind {
2019-09-25 19:00:17 +00:00
Some ( ( a . pat . span , snippet ( cx , refp . span , " .. " ) . to_string ( ) ) )
2018-02-04 12:41:54 +00:00
} else {
None
}
} ) ) ;
2020-04-17 06:08:00 +00:00
span_lint_and_then ( cx , MATCH_REF_PATS , expr . span , title , | diag | {
2019-08-19 16:30:32 +00:00
if ! expr . span . from_expansion ( ) {
2020-05-17 15:36:26 +00:00
multispan_sugg ( diag , msg , suggs ) ;
2018-11-15 16:03:17 +00:00
}
2018-02-04 12:41:54 +00:00
} ) ;
2015-08-21 17:32:21 +00:00
}
}
2020-06-25 20:41:36 +00:00
fn check_match_as_ref ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] , expr : & Expr < '_ > ) {
2019-09-25 19:00:17 +00:00
if arms . len ( ) = = 2 & & arms [ 0 ] . guard . is_none ( ) & & arms [ 1 ] . guard . is_none ( ) {
2017-12-20 09:39:48 +00:00
let arm_ref : Option < BindingAnnotation > = if is_none_arm ( & arms [ 0 ] ) {
is_ref_some_arm ( & arms [ 1 ] )
} else if is_none_arm ( & arms [ 1 ] ) {
is_ref_some_arm ( & arms [ 0 ] )
} else {
None
} ;
if let Some ( rb ) = arm_ref {
2018-11-27 20:14:15 +00:00
let suggestion = if rb = = BindingAnnotation ::Ref {
" as_ref "
} else {
" as_mut "
} ;
2019-08-25 05:10:45 +00:00
2020-07-17 08:47:04 +00:00
let output_ty = cx . typeck_results ( ) . expr_ty ( expr ) ;
let input_ty = cx . typeck_results ( ) . expr_ty ( ex ) ;
2019-08-25 05:10:45 +00:00
let cast = if_chain! {
2019-09-26 09:03:36 +00:00
if let ty ::Adt ( _ , substs ) = input_ty . kind ;
2019-08-25 05:10:45 +00:00
let input_ty = substs . type_at ( 0 ) ;
2019-09-26 09:03:36 +00:00
if let ty ::Adt ( _ , substs ) = output_ty . kind ;
2019-08-25 05:10:45 +00:00
let output_ty = substs . type_at ( 0 ) ;
2019-09-26 09:03:36 +00:00
if let ty ::Ref ( _ , output_ty , _ ) = output_ty . kind ;
2019-08-25 05:10:45 +00:00
if input_ty ! = output_ty ;
then {
" .map(|x| x as _) "
} else {
" "
}
} ;
2018-11-27 14:13:57 +00:00
let mut applicability = Applicability ::MachineApplicable ;
2017-12-19 22:22:16 +00:00
span_lint_and_sugg (
cx ,
MATCH_AS_REF ,
expr . span ,
2020-01-06 06:30:43 +00:00
& format! ( " use ` {} ()` instead " , suggestion ) ,
2017-12-19 22:22:16 +00:00
" try this " ,
2018-11-27 20:14:15 +00:00
format! (
2019-08-25 05:10:45 +00:00
" {}.{}(){} " ,
2018-11-27 20:14:15 +00:00
snippet_with_applicability ( cx , ex . span , " _ " , & mut applicability ) ,
2019-08-25 05:10:45 +00:00
suggestion ,
cast ,
2018-11-27 20:14:15 +00:00
) ,
2018-11-27 14:13:57 +00:00
applicability ,
2017-12-19 22:22:16 +00:00
)
}
}
}
2020-06-25 20:41:36 +00:00
fn check_wild_in_or_pats ( cx : & LateContext < '_ > , arms : & [ Arm < '_ > ] ) {
2019-12-24 15:42:09 +00:00
for arm in arms {
if let PatKind ::Or ( ref fields ) = arm . pat . kind {
2020-01-02 19:00:27 +00:00
// look for multiple fields in this arm that contains at least one Wild pattern
if fields . len ( ) > 1 & & fields . iter ( ) . any ( is_wild ) {
2020-01-27 01:56:22 +00:00
span_lint_and_help (
2019-12-24 15:42:09 +00:00
cx ,
2020-01-05 14:05:16 +00:00
WILDCARD_IN_OR_PATTERNS ,
2019-12-24 15:42:09 +00:00
arm . pat . span ,
2020-01-02 19:00:27 +00:00
" wildcard pattern covers any other pattern as it will match anyway. " ,
2020-04-18 10:28:29 +00:00
None ,
2020-01-07 17:13:31 +00:00
" Consider handling `_` separately. " ,
2020-01-02 19:00:27 +00:00
) ;
2019-12-24 15:42:09 +00:00
}
}
}
}
2020-07-14 12:59:59 +00:00
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
fn check_match_like_matches < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > ) {
if let ExprKind ::Match ( ex , arms , ref match_source ) = & expr . kind {
match match_source {
MatchSource ::Normal = > find_matches_sugg ( cx , ex , arms , expr , false ) ,
MatchSource ::IfLetDesugar { .. } = > find_matches_sugg ( cx , ex , arms , expr , true ) ,
_ = > return ,
}
}
}
/// Lint a `match` or desugared `if let` for replacement by `matches!`
fn find_matches_sugg ( cx : & LateContext < '_ > , ex : & Expr < '_ > , arms : & [ Arm < '_ > ] , expr : & Expr < '_ > , desugared : bool ) {
if_chain! {
if arms . len ( ) = = 2 ;
2020-07-17 08:47:04 +00:00
if cx . typeck_results ( ) . expr_ty ( expr ) . is_bool ( ) ;
2020-07-14 12:59:59 +00:00
if is_wild ( & arms [ 1 ] . pat ) ;
if let Some ( first ) = find_bool_lit ( & arms [ 0 ] . body . kind , desugared ) ;
if let Some ( second ) = find_bool_lit ( & arms [ 1 ] . body . kind , desugared ) ;
if first ! = second ;
then {
let mut applicability = Applicability ::MachineApplicable ;
let pat_and_guard = if let Some ( Guard ::If ( g ) ) = arms [ 0 ] . guard {
format! ( " {} if {} " , snippet_with_applicability ( cx , arms [ 0 ] . pat . span , " .. " , & mut applicability ) , snippet_with_applicability ( cx , g . span , " .. " , & mut applicability ) )
} else {
format! ( " {} " , snippet_with_applicability ( cx , arms [ 0 ] . pat . span , " .. " , & mut applicability ) )
} ;
span_lint_and_sugg (
cx ,
MATCH_LIKE_MATCHES_MACRO ,
expr . span ,
& format! ( " {} expression looks like `matches!` macro " , if desugared { " if let .. else " } else { " match " } ) ,
" try this " ,
format! (
" {}matches!({}, {}) " ,
if first { " " } else { " ! " } ,
snippet_with_applicability ( cx , ex . span , " .. " , & mut applicability ) ,
pat_and_guard ,
) ,
applicability ,
)
}
}
}
/// Extract a `bool` or `{ bool }`
fn find_bool_lit ( ex : & ExprKind < '_ > , desugared : bool ) -> Option < bool > {
match ex {
ExprKind ::Lit ( Spanned {
node : LitKind ::Bool ( b ) , ..
} ) = > Some ( * b ) ,
ExprKind ::Block (
rustc_hir ::Block {
stmts : & [ ] ,
expr : Some ( exp ) ,
..
} ,
_ ,
) if desugared = > {
if let ExprKind ::Lit ( Spanned {
node : LitKind ::Bool ( b ) , ..
} ) = exp . kind
{
Some ( b )
} else {
None
}
} ,
_ = > None ,
}
}
2020-06-25 20:41:36 +00:00
fn check_match_single_binding < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < '_ > ] , expr : & Expr < '_ > ) {
2020-01-26 16:03:39 +00:00
if in_macro ( expr . span ) | | arms . len ( ) ! = 1 | | is_refutable ( cx , arms [ 0 ] . pat ) {
2020-01-15 06:56:56 +00:00
return ;
}
2020-01-26 16:03:39 +00:00
let matched_vars = ex . span ;
let bind_names = arms [ 0 ] . pat . span ;
let match_body = remove_blocks ( & arms [ 0 ] . body ) ;
let mut snippet_body = if match_body . span . from_expansion ( ) {
Sugg ::hir_with_macro_callsite ( cx , match_body , " .. " ) . to_string ( )
} else {
2020-02-05 08:55:48 +00:00
snippet_block ( cx , match_body . span , " .. " , Some ( expr . span ) ) . to_string ( )
2020-01-26 16:03:39 +00:00
} ;
// Do we need to add ';' to suggestion ?
2020-02-04 19:19:20 +00:00
match match_body . kind {
ExprKind ::Block ( block , _ ) = > {
// macro + expr_ty(body) == ()
2020-07-17 08:47:04 +00:00
if block . span . from_expansion ( ) & & cx . typeck_results ( ) . expr_ty ( & match_body ) . is_unit ( ) {
2020-02-04 19:19:20 +00:00
snippet_body . push ( ';' ) ;
2020-01-26 16:03:39 +00:00
}
2020-02-04 19:19:20 +00:00
} ,
_ = > {
// expr_ty(body) == ()
2020-07-17 08:47:04 +00:00
if cx . typeck_results ( ) . expr_ty ( & match_body ) . is_unit ( ) {
2020-02-04 19:19:20 +00:00
snippet_body . push ( ';' ) ;
}
} ,
2020-01-15 06:56:56 +00:00
}
2020-01-26 16:03:39 +00:00
2020-02-04 19:19:20 +00:00
let mut applicability = Applicability ::MaybeIncorrect ;
2020-01-26 16:03:39 +00:00
match arms [ 0 ] . pat . kind {
PatKind ::Binding ( .. ) | PatKind ::Tuple ( _ , _ ) | PatKind ::Struct ( .. ) = > {
2020-03-08 21:57:35 +00:00
// If this match is in a local (`let`) stmt
let ( target_span , sugg ) = if let Some ( parent_let_node ) = opt_parent_let ( cx , ex ) {
(
parent_let_node . span ,
format! (
" let {} = {}; \n {}let {} = {}; " ,
snippet_with_applicability ( cx , bind_names , " .. " , & mut applicability ) ,
snippet_with_applicability ( cx , matched_vars , " .. " , & mut applicability ) ,
" " . repeat ( indent_of ( cx , expr . span ) . unwrap_or ( 0 ) ) ,
snippet_with_applicability ( cx , parent_let_node . pat . span , " .. " , & mut applicability ) ,
snippet_body
) ,
)
} else {
2020-03-21 19:26:55 +00:00
// If we are in closure, we need curly braces around suggestion
let mut indent = " " . repeat ( indent_of ( cx , ex . span ) . unwrap_or ( 0 ) ) ;
let ( mut cbrace_start , mut cbrace_end ) = ( " " . to_string ( ) , " " . to_string ( ) ) ;
if let Some ( parent_expr ) = get_parent_expr ( cx , expr ) {
if let ExprKind ::Closure ( .. ) = parent_expr . kind {
cbrace_end = format! ( " \n {} }} " , indent ) ;
// Fix body indent due to the closure
indent = " " . repeat ( indent_of ( cx , bind_names ) . unwrap_or ( 0 ) ) ;
cbrace_start = format! ( " {{ \n {} " , indent ) ;
}
} ;
2020-03-08 21:57:35 +00:00
(
expr . span ,
format! (
2020-03-21 19:26:55 +00:00
" {}let {} = {}; \n {}{}{} " ,
cbrace_start ,
2020-03-08 21:57:35 +00:00
snippet_with_applicability ( cx , bind_names , " .. " , & mut applicability ) ,
snippet_with_applicability ( cx , matched_vars , " .. " , & mut applicability ) ,
2020-03-21 19:26:55 +00:00
indent ,
snippet_body ,
cbrace_end
2020-03-08 21:57:35 +00:00
) ,
)
} ;
2020-01-26 16:03:39 +00:00
span_lint_and_sugg (
cx ,
MATCH_SINGLE_BINDING ,
2020-03-08 21:57:35 +00:00
target_span ,
2020-01-26 16:03:39 +00:00
" this match could be written as a `let` statement " ,
" consider using `let` statement " ,
2020-03-08 21:57:35 +00:00
sugg ,
2020-02-04 19:19:20 +00:00
applicability ,
2020-01-26 16:03:39 +00:00
) ;
} ,
PatKind ::Wild = > {
span_lint_and_sugg (
cx ,
MATCH_SINGLE_BINDING ,
expr . span ,
" this match could be replaced by its body itself " ,
" consider using the match body instead " ,
snippet_body ,
Applicability ::MachineApplicable ,
) ;
} ,
_ = > ( ) ,
}
2020-01-15 06:56:56 +00:00
}
2020-03-08 21:57:35 +00:00
/// Returns true if the `ex` match expression is in a local (`let`) statement
2020-06-25 20:41:36 +00:00
fn opt_parent_let < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > ) -> Option < & ' a Local < ' a > > {
2020-03-08 21:57:35 +00:00
if_chain! {
let map = & cx . tcx . hir ( ) ;
if let Some ( Node ::Expr ( parent_arm_expr ) ) = map . find ( map . get_parent_node ( ex . hir_id ) ) ;
if let Some ( Node ::Local ( parent_let_expr ) ) = map . find ( map . get_parent_node ( parent_arm_expr . hir_id ) ) ;
then {
return Some ( parent_let_expr ) ;
}
}
None
}
2019-01-31 01:15:29 +00:00
/// Gets all arms that are unbounded `PatRange`s.
2020-06-25 20:41:36 +00:00
fn all_ranges < ' tcx > ( cx : & LateContext < ' tcx > , arms : & ' tcx [ Arm < '_ > ] , ty : Ty < ' tcx > ) -> Vec < SpannedRange < Constant > > {
2015-12-23 00:14:10 +00:00
arms . iter ( )
2016-06-21 11:51:44 +00:00
. flat_map ( | arm | {
2017-08-09 07:30:56 +00:00
if let Arm {
2019-09-25 19:00:17 +00:00
ref pat , guard : None , ..
2017-08-09 07:30:56 +00:00
} = * arm
{
2020-01-11 10:39:43 +00:00
if let PatKind ::Range ( ref lhs , ref rhs , range_end ) = pat . kind {
2020-01-11 14:21:09 +00:00
let lhs = match lhs {
2020-07-17 08:47:04 +00:00
Some ( lhs ) = > constant ( cx , cx . typeck_results ( ) , lhs ) ? . 0 ,
2020-01-11 14:21:09 +00:00
None = > miri_to_const ( ty . numeric_min_val ( cx . tcx ) ? ) ? ,
} ;
let rhs = match rhs {
2020-07-17 08:47:04 +00:00
Some ( rhs ) = > constant ( cx , cx . typeck_results ( ) , rhs ) ? . 0 ,
2020-01-11 14:21:09 +00:00
None = > miri_to_const ( ty . numeric_max_val ( cx . tcx ) ? ) ? ,
} ;
let rhs = match range_end {
RangeEnd ::Included = > Bound ::Included ( rhs ) ,
RangeEnd ::Excluded = > Bound ::Excluded ( rhs ) ,
} ;
return Some ( SpannedRange {
span : pat . span ,
node : ( lhs , rhs ) ,
} ) ;
2017-10-23 19:18:02 +00:00
}
2015-12-23 00:14:10 +00:00
2019-09-27 15:16:06 +00:00
if let PatKind ::Lit ( ref value ) = pat . kind {
2020-07-17 08:47:04 +00:00
let value = constant ( cx , cx . typeck_results ( ) , value ) ? . 0 ;
2018-11-27 20:14:15 +00:00
return Some ( SpannedRange {
span : pat . span ,
node : ( value . clone ( ) , Bound ::Included ( value ) ) ,
} ) ;
2017-10-23 19:18:02 +00:00
}
2019-09-25 19:00:17 +00:00
}
None
2015-12-23 00:14:10 +00:00
} )
. collect ( )
}
#[ derive(Debug, Eq, PartialEq) ]
2015-12-23 16:48:30 +00:00
pub struct SpannedRange < T > {
pub span : Span ,
2017-01-30 11:30:16 +00:00
pub node : ( T , Bound < T > ) ,
2015-12-23 00:14:10 +00:00
}
2018-03-13 10:38:11 +00:00
type TypedRanges = Vec < SpannedRange < u128 > > ;
2015-12-23 00:14:10 +00:00
2019-01-31 01:15:29 +00:00
/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
2017-08-09 07:30:56 +00:00
/// and other types than
2015-12-23 00:14:10 +00:00
/// `Uint` and `Int` probably don't make sense.
2018-03-13 10:38:11 +00:00
fn type_ranges ( ranges : & [ SpannedRange < Constant > ] ) -> TypedRanges {
2017-08-09 07:30:56 +00:00
ranges
. iter ( )
2017-01-30 11:30:16 +00:00
. filter_map ( | range | match range . node {
2018-11-27 20:14:15 +00:00
( Constant ::Int ( start ) , Bound ::Included ( Constant ::Int ( end ) ) ) = > Some ( SpannedRange {
2017-09-05 09:33:04 +00:00
span : range . span ,
node : ( start , Bound ::Included ( end ) ) ,
} ) ,
2018-11-27 20:14:15 +00:00
( Constant ::Int ( start ) , Bound ::Excluded ( Constant ::Int ( end ) ) ) = > Some ( SpannedRange {
2017-09-05 09:33:04 +00:00
span : range . span ,
node : ( start , Bound ::Excluded ( end ) ) ,
} ) ,
2018-11-27 20:14:15 +00:00
( Constant ::Int ( start ) , Bound ::Unbounded ) = > Some ( SpannedRange {
2017-09-05 09:33:04 +00:00
span : range . span ,
node : ( start , Bound ::Unbounded ) ,
} ) ,
2017-01-30 11:30:16 +00:00
_ = > None ,
2016-12-20 17:21:30 +00:00
} )
. collect ( )
2015-12-23 00:14:10 +00:00
}
2019-12-27 07:12:26 +00:00
fn is_unit_expr ( expr : & Expr < '_ > ) -> bool {
2019-09-27 15:16:06 +00:00
match expr . kind {
2018-07-12 07:30:57 +00:00
ExprKind ::Tup ( ref v ) if v . is_empty ( ) = > true ,
ExprKind ::Block ( ref b , _ ) if b . stmts . is_empty ( ) & & b . expr . is_none ( ) = > true ,
2015-08-21 17:32:21 +00:00
_ = > false ,
}
}
2015-08-21 17:49:00 +00:00
2017-12-19 22:22:16 +00:00
// Checks if arm has the form `None => None`
2019-12-27 07:12:26 +00:00
fn is_none_arm ( arm : & Arm < '_ > ) -> bool {
2020-07-14 12:59:59 +00:00
matches! ( arm . pat . kind , PatKind ::Path ( ref path ) if match_qpath ( path , & paths ::OPTION_NONE ) )
2017-12-19 22:22:16 +00:00
}
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
2019-12-27 07:12:26 +00:00
fn is_ref_some_arm ( arm : & Arm < '_ > ) -> Option < BindingAnnotation > {
2017-12-19 22:22:16 +00:00
if_chain! {
2019-09-27 15:16:06 +00:00
if let PatKind ::TupleStruct ( ref path , ref pats , _ ) = arm . pat . kind ;
2019-05-17 21:53:54 +00:00
if pats . len ( ) = = 1 & & match_qpath ( path , & paths ::OPTION_SOME ) ;
2019-09-27 15:16:06 +00:00
if let PatKind ::Binding ( rb , .. , ident , _ ) = pats [ 0 ] . kind ;
2017-12-19 22:22:16 +00:00
if rb = = BindingAnnotation ::Ref | | rb = = BindingAnnotation ::RefMut ;
2019-09-27 15:16:06 +00:00
if let ExprKind ::Call ( ref e , ref args ) = remove_blocks ( & arm . body ) . kind ;
if let ExprKind ::Path ( ref some_path ) = e . kind ;
2019-05-17 21:53:54 +00:00
if match_qpath ( some_path , & paths ::OPTION_SOME ) & & args . len ( ) = = 1 ;
2019-09-27 15:16:06 +00:00
if let ExprKind ::Path ( ref qpath ) = args [ 0 ] . kind ;
2017-12-19 22:22:16 +00:00
if let & QPath ::Resolved ( _ , ref path2 ) = qpath ;
2018-06-28 13:46:58 +00:00
if path2 . segments . len ( ) = = 1 & & ident . name = = path2 . segments [ 0 ] . ident . name ;
2017-12-19 22:22:16 +00:00
then {
2017-12-20 09:39:48 +00:00
return Some ( rb )
2017-12-19 22:22:16 +00:00
}
}
2017-12-20 09:39:48 +00:00
None
2017-12-19 22:22:16 +00:00
}
2019-12-27 07:12:26 +00:00
fn has_only_ref_pats ( arms : & [ Arm < '_ > ] ) -> bool {
2018-11-27 20:14:15 +00:00
let mapped = arms
. iter ( )
2019-09-25 19:00:17 +00:00
. map ( | a | {
2019-09-27 15:16:06 +00:00
match a . pat . kind {
2017-09-05 09:33:04 +00:00
PatKind ::Ref ( .. ) = > Some ( true ) , // &-patterns
2016-12-20 17:21:30 +00:00
PatKind ::Wild = > Some ( false ) , // an "anything" wildcard is also fine
2017-09-05 09:33:04 +00:00
_ = > None , // any other pattern is not fine
2016-12-20 17:21:30 +00:00
}
} )
. collect ::< Option < Vec < bool > > > ( ) ;
2015-09-17 05:24:11 +00:00
// look for Some(v) where there's at least one true element
mapped . map_or ( false , | v | v . iter ( ) . any ( | el | * el ) )
2015-08-21 17:49:00 +00:00
}
2015-11-24 17:47:17 +00:00
2015-12-23 16:48:30 +00:00
pub fn overlapping < T > ( ranges : & [ SpannedRange < T > ] ) -> Option < ( & SpannedRange < T > , & SpannedRange < T > ) >
2017-08-09 07:30:56 +00:00
where
T : Copy + Ord ,
2016-01-04 04:26:12 +00:00
{
2015-12-23 00:14:10 +00:00
#[ derive(Copy, Clone, Debug, Eq, PartialEq) ]
2019-06-19 18:36:23 +00:00
enum Kind < ' a , T > {
2015-12-23 00:14:10 +00:00
Start ( T , & ' a SpannedRange < T > ) ,
2017-01-30 11:30:16 +00:00
End ( Bound < T > , & ' a SpannedRange < T > ) ,
2015-12-23 00:14:10 +00:00
}
impl < ' a , T : Copy > Kind < ' a , T > {
fn range ( & self ) -> & ' a SpannedRange < T > {
match * self {
2017-09-05 09:33:04 +00:00
Kind ::Start ( _ , r ) | Kind ::End ( _ , r ) = > r ,
2015-12-23 00:14:10 +00:00
}
}
2017-01-30 11:30:16 +00:00
fn value ( self ) -> Bound < T > {
2015-12-23 00:14:10 +00:00
match self {
2017-01-30 11:30:16 +00:00
Kind ::Start ( t , _ ) = > Bound ::Included ( t ) ,
2016-04-14 18:14:03 +00:00
Kind ::End ( t , _ ) = > t ,
2015-12-23 00:14:10 +00:00
}
}
}
impl < ' a , T : Copy + Ord > PartialOrd for Kind < ' a , T > {
fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
Some ( self . cmp ( other ) )
}
}
impl < ' a , T : Copy + Ord > Ord for Kind < ' a , T > {
fn cmp ( & self , other : & Self ) -> Ordering {
2017-01-30 11:30:16 +00:00
match ( self . value ( ) , other . value ( ) ) {
2017-09-05 09:33:04 +00:00
( Bound ::Included ( a ) , Bound ::Included ( b ) ) | ( Bound ::Excluded ( a ) , Bound ::Excluded ( b ) ) = > a . cmp ( & b ) ,
2017-01-31 07:08:54 +00:00
// Range patterns cannot be unbounded (yet)
2017-09-05 09:33:04 +00:00
( Bound ::Unbounded , _ ) | ( _ , Bound ::Unbounded ) = > unimplemented! ( ) ,
( Bound ::Included ( a ) , Bound ::Excluded ( b ) ) = > match a . cmp ( & b ) {
Ordering ::Equal = > Ordering ::Greater ,
other = > other ,
2017-01-30 11:30:16 +00:00
} ,
2017-09-05 09:33:04 +00:00
( Bound ::Excluded ( a ) , Bound ::Included ( b ) ) = > match a . cmp ( & b ) {
Ordering ::Equal = > Ordering ::Less ,
other = > other ,
2017-01-30 11:30:16 +00:00
} ,
}
2015-12-23 00:14:10 +00:00
}
}
2016-01-04 04:26:12 +00:00
let mut values = Vec ::with_capacity ( 2 * ranges . len ( ) ) ;
2015-12-23 00:14:10 +00:00
for r in ranges {
2016-04-26 15:05:39 +00:00
values . push ( Kind ::Start ( r . node . 0 , r ) ) ;
values . push ( Kind ::End ( r . node . 1 , r ) ) ;
2015-12-23 00:14:10 +00:00
}
values . sort ( ) ;
for ( a , b ) in values . iter ( ) . zip ( values . iter ( ) . skip ( 1 ) ) {
match ( a , b ) {
2018-11-27 20:14:15 +00:00
( & Kind ::Start ( _ , ra ) , & Kind ::End ( _ , rb ) ) = > {
if ra . node ! = rb . node {
return Some ( ( ra , rb ) ) ;
}
2016-12-20 17:21:30 +00:00
} ,
2017-01-30 11:30:16 +00:00
( & Kind ::End ( a , _ ) , & Kind ::Start ( b , _ ) ) if a ! = Bound ::Included ( b ) = > ( ) ,
2016-04-26 15:05:39 +00:00
_ = > return Some ( ( a . range ( ) , b . range ( ) ) ) ,
2015-12-23 00:14:10 +00:00
}
}
None
}
2020-04-03 19:31:47 +00:00
2020-07-14 12:59:59 +00:00
mod redundant_pattern_match {
use super ::REDUNDANT_PATTERN_MATCHING ;
use crate ::utils ::{ in_constant , match_qpath , match_trait_method , paths , snippet , span_lint_and_then } ;
use if_chain ::if_chain ;
use rustc_ast ::ast ::LitKind ;
use rustc_errors ::Applicability ;
use rustc_hir ::{ Arm , Expr , ExprKind , HirId , MatchSource , PatKind , QPath } ;
use rustc_lint ::LateContext ;
use rustc_middle ::ty ;
use rustc_mir ::const_eval ::is_const_fn ;
use rustc_span ::source_map ::Symbol ;
pub fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > ) {
if let ExprKind ::Match ( op , arms , ref match_source ) = & expr . kind {
match match_source {
MatchSource ::Normal = > find_sugg_for_match ( cx , expr , op , arms ) ,
MatchSource ::IfLetDesugar { .. } = > find_sugg_for_if_let ( cx , expr , op , arms , " if " ) ,
MatchSource ::WhileLetDesugar = > find_sugg_for_if_let ( cx , expr , op , arms , " while " ) ,
_ = > { } ,
}
}
}
fn find_sugg_for_if_let < ' tcx > (
cx : & LateContext < ' tcx > ,
expr : & ' tcx Expr < '_ > ,
op : & Expr < '_ > ,
arms : & [ Arm < '_ > ] ,
keyword : & 'static str ,
) {
fn find_suggestion ( cx : & LateContext < '_ > , hir_id : HirId , path : & QPath < '_ > ) -> Option < & 'static str > {
if match_qpath ( path , & paths ::RESULT_OK ) & & can_suggest ( cx , hir_id , sym! ( result_type ) , " is_ok " ) {
return Some ( " is_ok() " ) ;
}
if match_qpath ( path , & paths ::RESULT_ERR ) & & can_suggest ( cx , hir_id , sym! ( result_type ) , " is_err " ) {
return Some ( " is_err() " ) ;
}
if match_qpath ( path , & paths ::OPTION_SOME ) & & can_suggest ( cx , hir_id , sym! ( option_type ) , " is_some " ) {
return Some ( " is_some() " ) ;
}
if match_qpath ( path , & paths ::OPTION_NONE ) & & can_suggest ( cx , hir_id , sym! ( option_type ) , " is_none " ) {
return Some ( " is_none() " ) ;
}
None
}
let hir_id = expr . hir_id ;
let good_method = match arms [ 0 ] . pat . kind {
PatKind ::TupleStruct ( ref path , ref patterns , _ ) if patterns . len ( ) = = 1 = > {
if let PatKind ::Wild = patterns [ 0 ] . kind {
find_suggestion ( cx , hir_id , path )
} else {
None
}
} ,
PatKind ::Path ( ref path ) = > find_suggestion ( cx , hir_id , path ) ,
_ = > None ,
} ;
let good_method = match good_method {
Some ( method ) = > method ,
None = > return ,
} ;
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if keyword = = " while " ;
if let ExprKind ::MethodCall ( method_path , _ , _ , _ ) = op . kind ;
if method_path . ident . name = = sym! ( next ) ;
if match_trait_method ( cx , op , & paths ::ITERATOR ) ;
then {
return ;
}
}
2020-07-26 19:07:07 +00:00
let result_expr = match & op . kind {
ExprKind ::AddrOf ( _ , _ , borrowed ) = > borrowed ,
_ = > op ,
} ;
2020-07-14 12:59:59 +00:00
span_lint_and_then (
cx ,
REDUNDANT_PATTERN_MATCHING ,
arms [ 0 ] . pat . span ,
& format! ( " redundant pattern matching, consider using ` {} ` " , good_method ) ,
| diag | {
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr . span ;
// while let ... = ... { ... }
// ^^^
2020-07-26 19:07:07 +00:00
let op_span = result_expr . span . source_callsite ( ) ;
2020-07-14 12:59:59 +00:00
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^
let span = expr_span . until ( op_span . shrink_to_hi ( ) ) ;
diag . span_suggestion (
span ,
" try this " ,
format! ( " {} {} . {} " , keyword , snippet ( cx , op_span , " _ " ) , good_method ) ,
Applicability ::MachineApplicable , // snippet
) ;
} ,
) ;
}
fn find_sugg_for_match < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > , op : & Expr < '_ > , arms : & [ Arm < '_ > ] ) {
if arms . len ( ) = = 2 {
let node_pair = ( & arms [ 0 ] . pat . kind , & arms [ 1 ] . pat . kind ) ;
let hir_id = expr . hir_id ;
let found_good_method = match node_pair {
(
PatKind ::TupleStruct ( ref path_left , ref patterns_left , _ ) ,
PatKind ::TupleStruct ( ref path_right , ref patterns_right , _ ) ,
) if patterns_left . len ( ) = = 1 & & patterns_right . len ( ) = = 1 = > {
if let ( PatKind ::Wild , PatKind ::Wild ) = ( & patterns_left [ 0 ] . kind , & patterns_right [ 0 ] . kind ) {
find_good_method_for_match (
arms ,
path_left ,
path_right ,
& paths ::RESULT_OK ,
& paths ::RESULT_ERR ,
" is_ok() " ,
" is_err() " ,
| | can_suggest ( cx , hir_id , sym! ( result_type ) , " is_ok " ) ,
| | can_suggest ( cx , hir_id , sym! ( result_type ) , " is_err " ) ,
)
} else {
None
}
} ,
( PatKind ::TupleStruct ( ref path_left , ref patterns , _ ) , PatKind ::Path ( ref path_right ) )
| ( PatKind ::Path ( ref path_left ) , PatKind ::TupleStruct ( ref path_right , ref patterns , _ ) )
if patterns . len ( ) = = 1 = >
{
if let PatKind ::Wild = patterns [ 0 ] . kind {
find_good_method_for_match (
arms ,
path_left ,
path_right ,
& paths ::OPTION_SOME ,
& paths ::OPTION_NONE ,
" is_some() " ,
" is_none() " ,
| | can_suggest ( cx , hir_id , sym! ( option_type ) , " is_some " ) ,
| | can_suggest ( cx , hir_id , sym! ( option_type ) , " is_none " ) ,
)
} else {
None
}
} ,
_ = > None ,
} ;
if let Some ( good_method ) = found_good_method {
2020-07-26 19:07:07 +00:00
let span = expr . span . to ( op . span ) ;
let result_expr = match & op . kind {
ExprKind ::AddrOf ( _ , _ , borrowed ) = > borrowed ,
_ = > op ,
} ;
2020-07-14 12:59:59 +00:00
span_lint_and_then (
cx ,
REDUNDANT_PATTERN_MATCHING ,
expr . span ,
& format! ( " redundant pattern matching, consider using ` {} ` " , good_method ) ,
| diag | {
diag . span_suggestion (
span ,
" try this " ,
2020-07-26 19:07:07 +00:00
format! ( " {} . {} " , snippet ( cx , result_expr . span , " _ " ) , good_method ) ,
2020-07-14 12:59:59 +00:00
Applicability ::MaybeIncorrect , // snippet
) ;
} ,
) ;
}
}
}
#[ allow(clippy::too_many_arguments) ]
fn find_good_method_for_match < ' a > (
arms : & [ Arm < '_ > ] ,
path_left : & QPath < '_ > ,
path_right : & QPath < '_ > ,
expected_left : & [ & str ] ,
expected_right : & [ & str ] ,
should_be_left : & ' a str ,
should_be_right : & ' a str ,
can_suggest_left : impl Fn ( ) -> bool ,
can_suggest_right : impl Fn ( ) -> bool ,
) -> Option < & ' a str > {
let body_node_pair = if match_qpath ( path_left , expected_left ) & & match_qpath ( path_right , expected_right ) {
( & ( * arms [ 0 ] . body ) . kind , & ( * arms [ 1 ] . body ) . kind )
} else if match_qpath ( path_right , expected_left ) & & match_qpath ( path_left , expected_right ) {
( & ( * arms [ 1 ] . body ) . kind , & ( * arms [ 0 ] . body ) . kind )
} else {
return None ;
} ;
match body_node_pair {
( ExprKind ::Lit ( ref lit_left ) , ExprKind ::Lit ( ref lit_right ) ) = > match ( & lit_left . node , & lit_right . node ) {
( LitKind ::Bool ( true ) , LitKind ::Bool ( false ) ) if can_suggest_left ( ) = > Some ( should_be_left ) ,
( LitKind ::Bool ( false ) , LitKind ::Bool ( true ) ) if can_suggest_right ( ) = > Some ( should_be_right ) ,
_ = > None ,
} ,
_ = > None ,
}
}
fn can_suggest ( cx : & LateContext < '_ > , hir_id : HirId , diag_item : Symbol , name : & str ) -> bool {
if ! in_constant ( cx , hir_id ) {
return true ;
}
// Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
cx . tcx
. get_diagnostic_item ( diag_item )
. and_then ( | def_id | {
cx . tcx . inherent_impls ( def_id ) . iter ( ) . find_map ( | imp | {
cx . tcx
. associated_items ( * imp )
. in_definition_order ( )
. find_map ( | item | match item . kind {
ty ::AssocKind ::Fn if item . ident . name . as_str ( ) = = name = > Some ( item . def_id ) ,
_ = > None ,
} )
} )
} )
. map_or ( false , | def_id | is_const_fn ( cx . tcx , def_id ) )
}
}
2020-04-03 19:31:47 +00:00
#[ test ]
fn test_overlapping ( ) {
use rustc_span ::source_map ::DUMMY_SP ;
let sp = | s , e | SpannedRange {
span : DUMMY_SP ,
node : ( s , e ) ,
} ;
assert_eq! ( None , overlapping ::< u8 > ( & [ ] ) ) ;
assert_eq! ( None , overlapping ( & [ sp ( 1 , Bound ::Included ( 4 ) ) ] ) ) ;
assert_eq! (
None ,
overlapping ( & [ sp ( 1 , Bound ::Included ( 4 ) ) , sp ( 5 , Bound ::Included ( 6 ) ) ] )
) ;
assert_eq! (
None ,
overlapping ( & [
sp ( 1 , Bound ::Included ( 4 ) ) ,
sp ( 5 , Bound ::Included ( 6 ) ) ,
sp ( 10 , Bound ::Included ( 11 ) )
] , )
) ;
assert_eq! (
Some ( ( & sp ( 1 , Bound ::Included ( 4 ) ) , & sp ( 3 , Bound ::Included ( 6 ) ) ) ) ,
overlapping ( & [ sp ( 1 , Bound ::Included ( 4 ) ) , sp ( 3 , Bound ::Included ( 6 ) ) ] )
) ;
assert_eq! (
Some ( ( & sp ( 5 , Bound ::Included ( 6 ) ) , & sp ( 6 , Bound ::Included ( 11 ) ) ) ) ,
overlapping ( & [
sp ( 1 , Bound ::Included ( 4 ) ) ,
sp ( 5 , Bound ::Included ( 6 ) ) ,
sp ( 6 , Bound ::Included ( 11 ) )
] , )
) ;
}