use crate::utils::paths;
use crate::utils::sugg::Sugg;
use crate::utils::{
- expr_block, in_macro, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
+ expr_block, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
snippet_with_applicability, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty,
};
use if_chain::if_chain;
use rustc::hir::def::CtorKind;
use rustc::hir::*;
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
-use rustc::ty::{self, Ty, TyKind};
-use rustc::{declare_tool_lint, lint_array};
+use rustc::ty::{self, Ty};
+use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::cmp::Ordering;
use std::collections::Bound;
use syntax::ast::LitKind;
use syntax::source_map::Span;
-/// **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:**
-/// ```rust
-/// match x {
-/// Some(ref foo) => bar(foo),
-/// _ => (),
-/// }
-/// ```
declare_clippy_lint! {
+ /// **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:**
+ /// ```rust
+ /// # fn bar(stool: &str) {}
+ /// # let x = Some("abc");
+ /// match x {
+ /// Some(ref foo) => bar(foo),
+ /// _ => (),
+ /// }
+ /// ```
pub SINGLE_MATCH,
style,
- "a match statement with a single nontrivial arm (i.e. where the other arm is `_ => {}`) instead of `if let`"
+ "a match statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
}
-/// **What it does:** Checks for matches with a two arms where an `if let else` will
-/// 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
-/// match x {
-/// Some(ref foo) => bar(foo),
-/// _ => bar(other_ref),
-/// }
-/// ```
-///
-/// Using `if let` with `else`:
-///
-/// ```rust
-/// if let Some(ref foo) = x {
-/// bar(foo);
-/// } else {
-/// bar(other_ref);
-/// }
-/// ```
declare_clippy_lint! {
+ /// **What it does:** Checks for matches with two arms where an `if let else` will
+ /// 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
+ /// # fn bar(foo: &usize) {}
+ /// # let other_ref: usize = 1;
+ /// # let x: Option<&usize> = Some(&1);
+ /// match x {
+ /// Some(ref foo) => bar(foo),
+ /// _ => bar(&other_ref),
+ /// }
+ /// ```
+ ///
+ /// Using `if let` with `else`:
+ ///
+ /// ```rust
+ /// # fn bar(foo: &usize) {}
+ /// # let other_ref: usize = 1;
+ /// # let x: Option<&usize> = Some(&1);
+ /// if let Some(ref foo) = x {
+ /// bar(foo);
+ /// } else {
+ /// bar(&other_ref);
+ /// }
+ /// ```
pub SINGLE_MATCH_ELSE,
pedantic,
- "a match statement with a two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
+ "a match statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
}
-/// **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:**
-/// ```rust
-/// match x {
-/// &A(ref y) => foo(y),
-/// &B => bar(),
-/// _ => frob(&x),
-/// }
-/// ```
declare_clippy_lint! {
+ /// **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:**
+ /// ```rust,ignore
+ /// match x {
+ /// &A(ref y) => foo(y),
+ /// &B => bar(),
+ /// _ => frob(&x),
+ /// }
+ /// ```
pub MATCH_REF_PATS,
style,
"a match or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
}
-/// **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:**
-/// ```rust
-/// let condition: bool = true;
-/// match condition {
-/// true => foo(),
-/// false => bar(),
-/// }
-/// ```
-/// Use if/else instead:
-/// ```rust
-/// let condition: bool = true;
-/// if condition {
-/// foo();
-/// } else {
-/// bar();
-/// }
-/// ```
declare_clippy_lint! {
+ /// **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:**
+ /// ```rust
+ /// # fn foo() {}
+ /// # fn bar() {}
+ /// let condition: bool = true;
+ /// match condition {
+ /// true => foo(),
+ /// false => bar(),
+ /// }
+ /// ```
+ /// Use if/else instead:
+ /// ```rust
+ /// # fn foo() {}
+ /// # fn bar() {}
+ /// let condition: bool = true;
+ /// if condition {
+ /// foo();
+ /// } else {
+ /// bar();
+ /// }
+ /// ```
pub MATCH_BOOL,
style,
"a match on a boolean expression instead of an `if..else` block"
}
-/// **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"),
-/// _ => (),
-/// }
-/// ```
declare_clippy_lint! {
+ /// **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"),
+ /// _ => (),
+ /// }
+ /// ```
pub MATCH_OVERLAPPING_ARM,
style,
"a match with overlapping arms"
}
-/// **What it does:** Checks for arm which matches all errors with `Err(_)`
-/// and take drastic actions like `panic!`.
-///
-/// **Why is this bad?** It is generally a bad practice, just like
-/// catching all exceptions in java with `catch(Exception)`
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-/// ```rust
-/// let x: Result(i32, &str) = Ok(3);
-/// match x {
-/// Ok(_) => println!("ok"),
-/// Err(_) => panic!("err"),
-/// }
-/// ```
declare_clippy_lint! {
+ /// **What it does:** Checks for arm which matches all errors with `Err(_)`
+ /// and take drastic actions like `panic!`.
+ ///
+ /// **Why is this bad?** It is generally a bad practice, just like
+ /// catching all exceptions in java with `catch(Exception)`
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let x: Result<i32, &str> = Ok(3);
+ /// match x {
+ /// Ok(_) => println!("ok"),
+ /// Err(_) => panic!("err"),
+ /// }
+ /// ```
pub MATCH_WILD_ERR_ARM,
style,
"a match with `Err(_)` arm and take drastic actions"
}
-/// **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;
-/// let r: Option<&()> = match x {
-/// None => None,
-/// Some(ref v) => Some(v),
-/// };
-/// ```
declare_clippy_lint! {
+ /// **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;
+ /// let r: Option<&()> = match x {
+ /// None => None,
+ /// Some(ref v) => Some(v),
+ /// };
+ /// ```
pub MATCH_AS_REF,
complexity,
"a match on an Option value instead of using `as_ref()` or `as_mut`"
}
-/// **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
-/// match x {
-/// A => {},
-/// _ => {},
-/// }
-/// ```
declare_clippy_lint! {
+ /// **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
+ /// # enum Foo { A(usize), B(usize) }
+ /// # let x = Foo::B(1);
+ /// match x {
+ /// A => {},
+ /// _ => {},
+ /// }
+ /// ```
pub WILDCARD_ENUM_MATCH_ARM,
restriction,
"a wildcard enum match arm using `_`"
}
-#[allow(missing_copy_implementations)]
-pub struct MatchPass;
-
-impl LintPass for MatchPass {
- fn get_lints(&self) -> LintArray {
- lint_array!(
- SINGLE_MATCH,
- MATCH_REF_PATS,
- MATCH_BOOL,
- SINGLE_MATCH_ELSE,
- MATCH_OVERLAPPING_ARM,
- MATCH_WILD_ERR_ARM,
- MATCH_AS_REF,
- WILDCARD_ENUM_MATCH_ARM
- )
- }
-
- fn name(&self) -> &'static str {
- "Matches"
- }
-}
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchPass {
+declare_lint_pass!(Matches => [
+ SINGLE_MATCH,
+ MATCH_REF_PATS,
+ MATCH_BOOL,
+ SINGLE_MATCH_ELSE,
+ MATCH_OVERLAPPING_ARM,
+ MATCH_WILD_ERR_ARM,
+ MATCH_AS_REF,
+ WILDCARD_ENUM_MATCH_ARM
+]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if in_external_macro(cx.sess(), expr.span) {
return;
ty: Ty<'_>,
els: Option<&Expr>,
) {
- // list of candidate Enums we know will never get any more members
+ // list of candidate `Enum`s we know will never get any more members
let candidates = &[
(&paths::COW, "Borrowed"),
(&paths::COW, "Cow::Borrowed"),
let path = match arms[1].pats[0].node {
PatKind::TupleStruct(ref path, ref inner, _) => {
- // contains any non wildcard patterns? e.g. Err(err)
+ // Contains any non wildcard patterns (e.g., `Err(err)`)?
if !inner.iter().all(is_wild) {
return;
}
}
fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
- // type of expression == bool
+ // Type of expression is `bool`.
if cx.tables.expr_ty(ex).sty == ty::Bool {
span_lint_and_then(
cx,
for pat in &arm.pats {
if let PatKind::Wild = pat.node {
wildcard_span = Some(pat.span);
- } else if let PatKind::Binding(_, _, _, ident, None) = pat.node {
+ } else if let PatKind::Binding(_, _, ident, None) = pat.node {
wildcard_span = Some(pat.span);
wildcard_ident = Some(ident);
}
// already covered.
let mut missing_variants = vec![];
- if let TyKind::Adt(def, _) = ty.sty {
+ if let ty::Adt(def, _) = ty.sty {
for variant in &def.variants {
missing_variants.push(variant);
}
for pat in &arm.pats {
if let PatKind::Path(ref path) = pat.deref().node {
if let QPath::Resolved(_, p) = path {
- missing_variants.retain(|e| e.did != p.def.def_id());
+ missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
}
} else if let PatKind::TupleStruct(ref path, ..) = pat.deref().node {
if let QPath::Resolved(_, p) = path {
- missing_variants.retain(|e| e.did != p.def.def_id());
+ missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
}
}
}
String::new()
};
// This path assumes that the enum type is imported into scope.
- format!("{}{}{}", ident_str, cx.tcx.item_path_str(v.did), suffix)
+ format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
})
.collect();
}));
span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |db| {
- if !in_macro(expr.span) {
+ if !expr.span.from_expansion() {
multispan_sugg(db, msg.to_owned(), suggs);
}
});
} else {
"as_mut"
};
+
+ let output_ty = cx.tables.expr_ty(expr);
+ let input_ty = cx.tables.expr_ty(ex);
+
+ let cast = if_chain! {
+ if let ty::Adt(_, substs) = input_ty.sty;
+ let input_ty = substs.type_at(0);
+ if let ty::Adt(_, substs) = output_ty.sty;
+ let output_ty = substs.type_at(0);
+ if let ty::Ref(_, output_ty, _) = output_ty.sty;
+ if input_ty != output_ty;
+ then {
+ ".map(|x| x as _)"
+ } else {
+ ""
+ }
+ };
+
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
&format!("use {}() instead", suggestion),
"try this",
format!(
- "{}.{}()",
+ "{}.{}(){}",
snippet_with_applicability(cx, ex.span, "_", &mut applicability),
- suggestion
+ suggestion,
+ cast,
),
applicability,
)
}
}
-/// Get all arms that are unbounded `PatRange`s.
+/// Gets all arms that are unbounded `PatRange`s.
fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &'tcx [Arm]) -> Vec<SpannedRange<Constant>> {
arms.iter()
.flat_map(|arm| {
type TypedRanges = Vec<SpannedRange<u128>>;
-/// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
+/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
/// and other types than
/// `Uint` and `Int` probably don't make sense.
fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
T: Copy + Ord,
{
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
- enum Kind<'a, T: 'a> {
+ enum Kind<'a, T> {
Start(T, &'a SpannedRange<T>),
End(Bound<T>, &'a SpannedRange<T>),
}