-use crate::utils::{
- constants, snippet_opt, snippet_with_applicability, span_help_and_lint, span_lint, span_lint_and_sugg,
- span_lint_and_then,
+use crate::utils::{snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use rustc_ast::ast::{
+ BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
+ NodeId, Pat, PatKind, UnOp,
};
-use if_chain::if_chain;
-use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintContext};
+use rustc_ast::visit::FnKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
+use rustc_hir::PrimTy;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
-use syntax::ast::*;
-use syntax::visit::{walk_expr, FnKind, Visitor};
declare_clippy_lint! {
/// **What it does:** Checks for structure field patterns bound to wildcards.
/// **Known problems:** None.
///
/// **Example:**
- /// ```ignore
- /// let { a: _, b: ref b, c: _ } = ..
+ /// ```rust
+ /// # struct Foo {
+ /// # a: i32,
+ /// # b: i32,
+ /// # c: i32,
+ /// # }
+ /// let f = Foo { a: 0, b: 0, c: 0 };
+ ///
+ /// // Bad
+ /// match f {
+ /// Foo { a: _, b: 0, .. } => {},
+ /// Foo { a: _, b: _, c: _ } => {},
+ /// }
+ ///
+ /// // Good
+ /// match f {
+ /// Foo { b: 0, .. } => {},
+ /// Foo { .. } => {},
+ /// }
/// ```
pub UNNEEDED_FIELD_PATTERN,
- style,
+ restriction,
"struct fields bound to a wildcard instead of using `..`"
}
///
/// **Example:**
/// ```rust
+ /// // Bad
/// fn foo(a: i32, _a: i32) {}
+ ///
+ /// // Good
+ /// fn bar(a: i32, _b: i32) {}
/// ```
pub DUPLICATE_UNDERSCORE_ARGUMENT,
style,
"function arguments having names which only differ by an underscore"
}
-declare_clippy_lint! {
- /// **What it does:** Detects closures called in the same expression where they
- /// are defined.
- ///
- /// **Why is this bad?** It is unnecessarily adding to the expression's
- /// complexity.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// ```rust,ignore
- /// (|| 42)()
- /// ```
- pub REDUNDANT_CLOSURE_CALL,
- complexity,
- "throwaway closures called in the expression they are defined"
-}
-
declare_clippy_lint! {
/// **What it does:** Detects expressions of the form `--x`.
///
///
/// **Example:**
/// ```rust
+ /// // Bad
/// let y = 0x1a9BAcD;
+ ///
+ /// // Good
+ /// let y = 0x1A9BACD;
/// ```
pub MIXED_CASE_HEX_LITERALS,
style,
///
/// **Example:**
/// ```rust
+ /// // Bad
/// let y = 123832i32;
+ ///
+ /// // Good
+ /// let y = 123832_i32;
/// ```
pub UNSEPARATED_LITERAL_SUFFIX,
pedantic,
/// ```rust
/// # let v = Some("abc");
///
+ /// // Bad
/// match v {
/// Some(x) => (),
- /// y @ _ => (), // easier written as `y`,
+ /// y @ _ => (),
+ /// }
+ ///
+ /// // Good
+ /// match v {
+ /// Some(x) => (),
+ /// y => (),
/// }
/// ```
pub REDUNDANT_PATTERN,
/// ```rust
/// # struct TupleStruct(u32, u32, u32);
/// # let t = TupleStruct(1, 2, 3);
- ///
+ /// // Bad
/// match t {
/// TupleStruct(0, .., _) => (),
/// _ => (),
/// }
- /// ```
- /// can be written as
- /// ```rust
- /// # struct TupleStruct(u32, u32, u32);
- /// # let t = TupleStruct(1, 2, 3);
///
+ /// // Good
/// match t {
/// TupleStruct(0, ..) => (),
/// _ => (),
declare_lint_pass!(MiscEarlyLints => [
UNNEEDED_FIELD_PATTERN,
DUPLICATE_UNDERSCORE_ARGUMENT,
- REDUNDANT_CLOSURE_CALL,
DOUBLE_NEG,
MIXED_CASE_HEX_LITERALS,
UNSEPARATED_LITERAL_SUFFIX,
UNNEEDED_WILDCARD_PATTERN,
]);
-// Used to find `return` statements or equivalents e.g., `?`
-struct ReturnVisitor {
- found_return: bool,
-}
-
-impl ReturnVisitor {
- #[must_use]
- fn new() -> Self {
- Self { found_return: false }
- }
-}
-
-impl<'ast> Visitor<'ast> for ReturnVisitor {
- fn visit_expr(&mut self, ex: &'ast Expr) {
- if let ExprKind::Ret(_) = ex.kind {
- self.found_return = true;
- } else if let ExprKind::Try(_) = ex.kind {
- self.found_return = true;
- }
-
- walk_expr(self, ex)
- }
-}
-
impl EarlyLintPass for MiscEarlyLints {
fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
for param in &gen.params {
if let GenericParamKind::Type { .. } = param.kind {
- let name = param.ident.as_str();
- if constants::BUILTIN_TYPES.contains(&&*name) {
+ if let Some(prim_ty) = PrimTy::from_name(param.ident.name) {
span_lint(
cx,
BUILTIN_TYPE_SHADOW,
param.ident.span,
- &format!("This generic shadows the built-in type `{}`", name),
+ &format!("this generic shadows the built-in type `{}`", prim_ty.name()),
);
}
}
}
}
if !pfields.is_empty() && wilds == pfields.len() {
- span_help_and_lint(
+ span_lint_and_help(
cx,
UNNEEDED_FIELD_PATTERN,
pat.span,
- "All the struct fields are matched to a wildcard pattern, consider using `..`.",
- &format!("Try with `{} {{ .. }}` instead", type_name),
+ "all the struct fields are matched to a wildcard pattern, consider using `..`",
+ None,
+ &format!("try with `{} {{ .. }}` instead", type_name),
);
return;
}
if wilds > 0 {
- let mut normal = vec![];
-
- for field in pfields {
- match field.pat.kind {
- PatKind::Wild => {},
- _ => {
- if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
- normal.push(n);
- }
- },
- }
- }
for field in pfields {
if let PatKind::Wild = field.pat.kind {
wilds -= 1;
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
- "You matched a field with a wildcard pattern. Consider using `..` instead",
+ "you matched a field with a wildcard pattern, consider using `..` instead",
);
} else {
- span_help_and_lint(
+ let mut normal = vec![];
+
+ for field in pfields {
+ match field.pat.kind {
+ PatKind::Wild => {},
+ _ => {
+ if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
+ normal.push(n);
+ }
+ },
+ }
+ }
+
+ span_lint_and_help(
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
- "You matched a field with a wildcard pattern. Consider using `..` \
+ "you matched a field with a wildcard pattern, consider using `..` \
instead",
- &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
+ None,
+ &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
);
}
}
}
}
- if let PatKind::Ident(_, ident, Some(ref right)) = pat.kind {
+ if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
+ let left_binding = match left {
+ BindingMode::ByRef(Mutability::Mut) => "ref mut ",
+ BindingMode::ByRef(Mutability::Not) => "ref ",
+ BindingMode::ByValue(..) => "",
+ };
+
if let PatKind::Wild = right.kind {
span_lint_and_sugg(
cx,
ident.name, ident.name,
),
"try",
- format!("{}", ident.name),
+ format!("{}{}", left_binding, ident.name),
Applicability::MachineApplicable,
);
}
check_unneeded_wildcard_pattern(cx, pat);
}
- fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
- for arg in &decl.inputs {
+ for arg in &fn_kind.decl().inputs {
if let PatKind::Ident(_, ident, None) = arg.pat.kind {
let arg_name = ident.to_string();
- if arg_name.starts_with('_') {
- if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
+ if let Some(arg_name) = arg_name.strip_prefix('_') {
+ if let Some(correspondence) = registered_names.get(arg_name) {
span_lint(
cx,
DUPLICATE_UNDERSCORE_ARGUMENT,
&format!(
"`{}` already exists, having another argument having almost the same \
name makes code comprehension and documentation more difficult",
- arg_name[1..].to_owned()
+ arg_name
),
);
}
return;
}
match expr.kind {
- ExprKind::Call(ref paren, _) => {
- if let ExprKind::Paren(ref closure) = paren.kind {
- if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind {
- let mut visitor = ReturnVisitor::new();
- visitor.visit_expr(block);
- if !visitor.found_return {
- span_lint_and_then(
- cx,
- REDUNDANT_CLOSURE_CALL,
- expr.span,
- "Try not to call a closure in the expression where it is declared.",
- |db| {
- if decl.inputs.is_empty() {
- let mut app = Applicability::MachineApplicable;
- let hint =
- snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
- db.span_suggestion(expr.span, "Try doing something like: ", hint, app);
- }
- },
- );
- }
- }
- }
- },
ExprKind::Unary(UnOp::Neg, ref inner) => {
if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
span_lint(
_ => (),
}
}
-
- fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
- for w in block.stmts.windows(2) {
- if_chain! {
- if let StmtKind::Local(ref local) = w[0].kind;
- if let Option::Some(ref t) = local.init;
- if let ExprKind::Closure(..) = t.kind;
- if let PatKind::Ident(_, ident, _) = local.pat.kind;
- if let StmtKind::Semi(ref second) = w[1].kind;
- if let ExprKind::Assign(_, ref call, _) = second.kind;
- if let ExprKind::Call(ref closure, _) = call.kind;
- if let ExprKind::Path(_, ref path) = closure.kind;
- then {
- if ident == path.segments[0].ident {
- span_lint(
- cx,
- REDUNDANT_CLOSURE_CALL,
- second.span,
- "Closure called just once immediately after it was declared",
- );
- }
- }
- }
- }
- }
}
impl MiscEarlyLints {
LitIntType::Unsuffixed => "",
};
- let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
+ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
+ val
+ } else {
+ return; // It's useless so shouldn't lint.
+ };
// Do not lint when literal is unsuffixed.
if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
span_lint_and_sugg(
}
if lit_snip.starts_with("0x") {
+ if maybe_last_sep_idx <= 2 {
+ // It's meaningless or causes range error.
+ return;
+ }
let mut seen = (false, false);
for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
match ch {
ZERO_PREFIXED_LITERAL,
lit.span,
"this is a decimal constant",
- |db| {
- db.span_suggestion(
+ |diag| {
+ diag.span_suggestion(
lit.span,
"if you mean to use a decimal constant, remove the `0` to avoid confusion",
lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
Applicability::MaybeIncorrect,
);
- db.span_suggestion(
+ diag.span_suggestion(
lit.span,
"if you mean to use an octal constant, use `0o`",
format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
}
} else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
let suffix = float_ty.name_str();
- let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
+ let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
+ val
+ } else {
+ return; // It's useless so shouldn't lint.
+ };
if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
span_lint_and_sugg(
cx,
);
}
- #[allow(clippy::trivially_copy_pass_by_ref)]
- fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
- if let PatKind::Wild = pat.kind {
- true
- } else {
- false
- }
- }
-
if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
if let Some((left_index, left_pat)) = patterns[..rest_index]
.iter()
.rev()
- .take_while(is_wild)
+ .take_while(|pat| matches!(pat.kind, PatKind::Wild))
.enumerate()
.last()
{
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
}
- if let Some((right_index, right_pat)) =
- patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
+ if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
+ .iter()
+ .take_while(|pat| matches!(pat.kind, PatKind::Wild))
+ .enumerate()
+ .last()
{
span_lint(
cx,