Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
e2859437f9
117 changed files with 6071 additions and 461 deletions
|
|
@ -8,6 +8,14 @@ use crate::utils::span_lint_and_help;
|
|||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `as` conversions.
|
||||
///
|
||||
/// Note that this lint is specialized in linting *every single* use of `as`
|
||||
/// regardless of whether good alternatives exist or not.
|
||||
/// If you want more precise lints for `as`, please consider using these separate lints:
|
||||
/// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,
|
||||
/// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
|
||||
/// There is a good explanation the reason why this lint should work in this way and how it is useful
|
||||
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
|
||||
///
|
||||
/// **Why is this bad?** `as` conversions will perform many kinds of
|
||||
/// conversions, including silently lossy conversions and dangerous coercions.
|
||||
/// There are cases when it makes sense to use `as`, so the lint is
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ declare_clippy_lint! {
|
|||
/// other solution is to ensure the mutex is unlocked before calling await,
|
||||
/// either by introducing a scope or an explicit call to Drop::drop.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
|
|
@ -57,7 +57,7 @@ declare_clippy_lint! {
|
|||
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
|
||||
/// risks panics from a mutable ref shared while other refs are outstanding.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
use crate::utils::paths::STRING;
|
||||
use crate::utils::{match_def_path, span_lint_and_help};
|
||||
use if_chain::if_chain;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{source_map::Spanned, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks for calls to `ends_with` with possible file extensions
|
||||
/// and suggests to use a case-insensitive approach instead.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.ends_with(".rs")
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
|
||||
/// }
|
||||
/// ```
|
||||
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
pedantic,
|
||||
"Checks for calls to ends_with with case-sensitive file extensions"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
|
||||
|
||||
fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"^\.([a-z0-9]{1,5}|[A-Z0-9]{1,5})$").unwrap();
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(PathSegment { ident, .. }, _, [obj, extension, ..], span) = expr.kind;
|
||||
if ident.as_str() == "ends_with";
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
|
||||
if RE.is_match(&ext_literal.as_str());
|
||||
then {
|
||||
let mut ty = ctx.typeck_results().expr_ty(obj);
|
||||
ty = match ty.kind() {
|
||||
ty::Ref(_, ty, ..) => ty,
|
||||
_ => ty
|
||||
};
|
||||
|
||||
match ty.kind() {
|
||||
ty::Str => {
|
||||
return Some(span);
|
||||
},
|
||||
ty::Adt(&ty::AdtDef { did, .. }, _) => {
|
||||
if match_def_path(ctx, did, &STRING) {
|
||||
return Some(span);
|
||||
}
|
||||
},
|
||||
_ => { return None; }
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
|
||||
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
|
||||
span_lint_and_help(
|
||||
ctx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
span,
|
||||
"case-sensitive file extension comparison",
|
||||
None,
|
||||
"consider using a case-insensitive comparison instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,9 @@ impl CognitiveComplexity {
|
|||
|
||||
let expr = &body.value;
|
||||
|
||||
let mut helper = CCHelper { cc: 1, returns: 0 };
|
||||
let mut helper = CcHelper { cc: 1, returns: 0 };
|
||||
helper.visit_expr(expr);
|
||||
let CCHelper { cc, returns } = helper;
|
||||
let CcHelper { cc, returns } = helper;
|
||||
let ret_ty = cx.typeck_results().node_type(expr.hir_id);
|
||||
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) {
|
||||
returns
|
||||
|
|
@ -136,12 +136,12 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
|
|||
}
|
||||
}
|
||||
|
||||
struct CCHelper {
|
||||
struct CcHelper {
|
||||
cc: u64,
|
||||
returns: u64,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CCHelper {
|
||||
impl<'tcx> Visitor<'tcx> for CcHelper {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::utils::visitors::LocalUsedVisitor;
|
|||
use crate::utils::{span_lint_and_then, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{DefIdTree, TyCtxt};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -72,8 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
|
|||
if arms_inner.iter().all(|arm| arm.guard.is_none());
|
||||
// match expression must be a local binding
|
||||
// match <local> { .. }
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind;
|
||||
if let Res::Local(binding_id) = path.res;
|
||||
if let Some(binding_id) = addr_adjusted_binding(expr_in, cx);
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
|
|
@ -85,7 +84,12 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
|
|||
// the "wild-like" branches must be equal
|
||||
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
|
||||
// the binding must not be used in the if guard
|
||||
if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard));
|
||||
if match arm.guard {
|
||||
None => true,
|
||||
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => {
|
||||
!LocalUsedVisitor::new(binding_id).check_expr(expr)
|
||||
}
|
||||
};
|
||||
// ...or anywhere in the inner match
|
||||
if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm));
|
||||
then {
|
||||
|
|
@ -170,3 +174,20 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`)
|
||||
/// Returns `None` if a non-reference type is de-referenced.
|
||||
/// For example, if `Vec` is de-referenced to a slice, `None` is returned.
|
||||
fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option<HirId> {
|
||||
loop {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, e) => expr = e,
|
||||
ExprKind::Path(QPath::Resolved(None, path)) => match path.res {
|
||||
Res::Local(binding_id) => break Some(binding_id),
|
||||
_ => break None,
|
||||
},
|
||||
ExprKind::Unary(UnOp::UnDeref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
|
||||
_ => break None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,3 +179,12 @@ declare_deprecated_lint! {
|
|||
pub UNKNOWN_CLIPPY_LINTS,
|
||||
"this lint has been integrated into the `unknown_lints` rustc lint"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
|
||||
/// more specific lint.
|
||||
pub FIND_MAP,
|
||||
"this lint has been replaced by `manual_find_map`, a more specific lint"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ use crate::consts::{miri_to_const, Constant};
|
|||
use crate::utils::span_lint;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, IntTy, UintTy};
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, IntTy, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
|
|
|||
102
clippy_lints/src/exhaustive_items.rs
Normal file
102
clippy_lints/src/exhaustive_items.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
use crate::utils::{indent_of, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
|
||||
///
|
||||
/// **Why is this bad?** Exhaustive enums are typically fine, but a project which does
|
||||
/// not wish to make a stability commitment around exported enums may wish to
|
||||
/// disable them by default.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// enum Foo {
|
||||
/// Bar,
|
||||
/// Baz
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #[non_exhaustive]
|
||||
/// enum Foo {
|
||||
/// Bar,
|
||||
/// Baz
|
||||
/// }
|
||||
/// ```
|
||||
pub EXHAUSTIVE_ENUMS,
|
||||
restriction,
|
||||
"detects exported enums that have not been marked #[non_exhaustive]"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
|
||||
///
|
||||
/// **Why is this bad?** Exhaustive structs are typically fine, but a project which does
|
||||
/// not wish to make a stability commitment around exported structs may wish to
|
||||
/// disable them by default.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Foo {
|
||||
/// bar: u8,
|
||||
/// baz: String,
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #[non_exhaustive]
|
||||
/// struct Foo {
|
||||
/// bar: u8,
|
||||
/// baz: String,
|
||||
/// }
|
||||
/// ```
|
||||
pub EXHAUSTIVE_STRUCTS,
|
||||
restriction,
|
||||
"detects exported structs that have not been marked #[non_exhaustive]"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
|
||||
|
||||
impl LateLintPass<'_> for ExhaustiveItems {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
|
||||
if cx.access_levels.is_exported(item.hir_id);
|
||||
if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
|
||||
then {
|
||||
let (lint, msg) = if let ItemKind::Enum(..) = item.kind {
|
||||
(EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
|
||||
} else {
|
||||
(EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
|
||||
};
|
||||
let suggestion_span = item.span.shrink_to_lo();
|
||||
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
item.span,
|
||||
msg,
|
||||
|diag| {
|
||||
let sugg = format!("#[non_exhaustive]\n{}", indent);
|
||||
diag.span_suggestion(suggestion_span,
|
||||
"try adding #[non_exhaustive]",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
|||
|db| {
|
||||
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||
for FulfillmentError { obligation, .. } in send_errors {
|
||||
infcx.maybe_note_obligation_cause_for_async_await(
|
||||
db,
|
||||
&obligation,
|
||||
);
|
||||
if let Trait(trait_pred, _) =
|
||||
obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
|
||||
if let Trait(trait_pred, _) = obligation.predicate.kind().skip_binder() {
|
||||
db.note(&format!(
|
||||
"`{}` doesn't implement `{}`",
|
||||
trait_pred.self_ty(),
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
|
|||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Side {
|
||||
LHS,
|
||||
RHS,
|
||||
Lhs,
|
||||
Rhs,
|
||||
}
|
||||
|
||||
impl IntPlusOne {
|
||||
|
|
@ -66,11 +66,11 @@ impl IntPlusOne {
|
|||
match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
|
||||
// `-1 + x`
|
||||
(BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
|
||||
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
|
||||
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
|
||||
},
|
||||
// `x - 1`
|
||||
(BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
|
||||
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -82,10 +82,10 @@ impl IntPlusOne {
|
|||
match (&rhslhs.kind, &rhsrhs.kind) {
|
||||
// `y + 1` and `1 + y`
|
||||
(&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
|
||||
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
|
||||
},
|
||||
(_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
|
||||
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -97,10 +97,10 @@ impl IntPlusOne {
|
|||
match (&lhslhs.kind, &lhsrhs.kind) {
|
||||
// `1 + x` and `x + 1`
|
||||
(&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
|
||||
Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
|
||||
},
|
||||
(_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
|
||||
Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -110,11 +110,11 @@ impl IntPlusOne {
|
|||
match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
|
||||
// `-1 + y`
|
||||
(BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
|
||||
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
|
||||
Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
|
||||
},
|
||||
// `y - 1`
|
||||
(BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
|
||||
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
|
||||
Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
|
@ -138,8 +138,8 @@ impl IntPlusOne {
|
|||
if let Some(snippet) = snippet_opt(cx, node.span) {
|
||||
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
|
||||
let rec = match side {
|
||||
Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
|
||||
Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
|
||||
Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
|
||||
Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ mod blocks_in_if_conditions;
|
|||
mod booleans;
|
||||
mod bytecount;
|
||||
mod cargo_common_metadata;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
mod collapsible_if;
|
||||
|
|
@ -199,6 +200,7 @@ mod escape;
|
|||
mod eta_reduction;
|
||||
mod eval_order_dependence;
|
||||
mod excessive_bools;
|
||||
mod exhaustive_items;
|
||||
mod exit;
|
||||
mod explicit_write;
|
||||
mod fallible_impl_from;
|
||||
|
|
@ -300,6 +302,7 @@ mod redundant_closure_call;
|
|||
mod redundant_else;
|
||||
mod redundant_field_names;
|
||||
mod redundant_pub_crate;
|
||||
mod redundant_slicing;
|
||||
mod redundant_static_lifetimes;
|
||||
mod ref_option_ref;
|
||||
mod reference;
|
||||
|
|
@ -339,6 +342,7 @@ mod unused_self;
|
|||
mod unused_unit;
|
||||
mod unwrap;
|
||||
mod unwrap_in_result;
|
||||
mod upper_case_acronyms;
|
||||
mod use_self;
|
||||
mod useless_conversion;
|
||||
mod vec;
|
||||
|
|
@ -506,6 +510,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
"clippy::unknown_clippy_lints",
|
||||
"this lint has been integrated into the `unknown_lints` rustc lint",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::find_map",
|
||||
"this lint has been replaced by `manual_find_map`, a more specific lint",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
// begin register lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
|
@ -561,6 +569,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&booleans::NONMINIMAL_BOOL,
|
||||
&bytecount::NAIVE_BYTECOUNT,
|
||||
&cargo_common_metadata::CARGO_COMMON_METADATA,
|
||||
&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
&checked_conversions::CHECKED_CONVERSIONS,
|
||||
&cognitive_complexity::COGNITIVE_COMPLEXITY,
|
||||
&collapsible_if::COLLAPSIBLE_ELSE_IF,
|
||||
|
|
@ -610,6 +619,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&eval_order_dependence::EVAL_ORDER_DEPENDENCE,
|
||||
&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
&excessive_bools::STRUCT_EXCESSIVE_BOOLS,
|
||||
&exhaustive_items::EXHAUSTIVE_ENUMS,
|
||||
&exhaustive_items::EXHAUSTIVE_STRUCTS,
|
||||
&exit::EXIT,
|
||||
&explicit_write::EXPLICIT_WRITE,
|
||||
&fallible_impl_from::FALLIBLE_IMPL_FROM,
|
||||
|
|
@ -731,11 +742,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&methods::FILTER_MAP,
|
||||
&methods::FILTER_MAP_NEXT,
|
||||
&methods::FILTER_NEXT,
|
||||
&methods::FIND_MAP,
|
||||
&methods::FLAT_MAP_IDENTITY,
|
||||
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
&methods::GET_UNWRAP,
|
||||
&methods::INEFFICIENT_TO_STRING,
|
||||
&methods::INSPECT_FOR_EACH,
|
||||
&methods::INTO_ITER_ON_REF,
|
||||
&methods::ITERATOR_STEP_BY_ZERO,
|
||||
&methods::ITER_CLONED_COLLECT,
|
||||
|
|
@ -743,6 +754,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&methods::ITER_NTH,
|
||||
&methods::ITER_NTH_ZERO,
|
||||
&methods::ITER_SKIP_NEXT,
|
||||
&methods::MANUAL_FILTER_MAP,
|
||||
&methods::MANUAL_FIND_MAP,
|
||||
&methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
&methods::MAP_COLLECT_RESULT_UNIT,
|
||||
&methods::MAP_FLATTEN,
|
||||
|
|
@ -850,6 +863,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&redundant_else::REDUNDANT_ELSE,
|
||||
&redundant_field_names::REDUNDANT_FIELD_NAMES,
|
||||
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
|
||||
&redundant_slicing::REDUNDANT_SLICING,
|
||||
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
|
||||
&ref_option_ref::REF_OPTION_REF,
|
||||
&reference::DEREF_ADDROF,
|
||||
|
|
@ -942,6 +956,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&unwrap::PANICKING_UNWRAP,
|
||||
&unwrap::UNNECESSARY_UNWRAP,
|
||||
&unwrap_in_result::UNWRAP_IN_RESULT,
|
||||
&upper_case_acronyms::UPPER_CASE_ACRONYMS,
|
||||
&use_self::USE_SELF,
|
||||
&useless_conversion::USELESS_CONVERSION,
|
||||
&vec::USELESS_VEC,
|
||||
|
|
@ -981,7 +996,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
}
|
||||
store.register_late_pass(|| box utils::author::Author);
|
||||
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
|
||||
store.register_late_pass(|| box serde_api::SerdeAPI);
|
||||
store.register_late_pass(|| box serde_api::SerdeApi);
|
||||
let vec_box_size_threshold = conf.vec_box_size_threshold;
|
||||
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
|
||||
store.register_late_pass(|| box booleans::NonminimalBool);
|
||||
|
|
@ -1092,6 +1107,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
|
||||
store.register_late_pass(|| box missing_doc::MissingDoc::new());
|
||||
store.register_late_pass(|| box missing_inline::MissingInline);
|
||||
store.register_late_pass(move || box exhaustive_items::ExhaustiveItems);
|
||||
store.register_late_pass(|| box if_let_some_result::OkIfLet);
|
||||
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
|
||||
store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
|
||||
|
|
@ -1172,6 +1188,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
|
||||
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
|
||||
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
|
||||
store.register_early_pass(|| box upper_case_acronyms::UpperCaseAcronyms);
|
||||
store.register_late_pass(|| box default::Default::default());
|
||||
store.register_late_pass(|| box unused_self::UnusedSelf);
|
||||
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
|
||||
|
|
@ -1229,6 +1246,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
|
||||
store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default());
|
||||
store.register_late_pass(move || box types::PtrAsPtr::new(msrv));
|
||||
store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons);
|
||||
store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
|
|
@ -1239,6 +1258,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&create_dir::CREATE_DIR),
|
||||
LintId::of(&dbg_macro::DBG_MACRO),
|
||||
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
|
||||
LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS),
|
||||
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
|
||||
LintId::of(&exit::EXIT),
|
||||
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
|
||||
LintId::of(&implicit_return::IMPLICIT_RETURN),
|
||||
|
|
@ -1286,6 +1307,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
|
||||
LintId::of(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
|
||||
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
|
||||
LintId::of(©_iterator::COPY_ITERATOR),
|
||||
|
|
@ -1323,7 +1345,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(&methods::FILTER_MAP),
|
||||
LintId::of(&methods::FILTER_MAP_NEXT),
|
||||
LintId::of(&methods::FIND_MAP),
|
||||
LintId::of(&methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(&methods::MAP_FLATTEN),
|
||||
LintId::of(&methods::MAP_UNWRAP_OR),
|
||||
|
|
@ -1509,6 +1530,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&methods::FILTER_NEXT),
|
||||
LintId::of(&methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(&methods::INSPECT_FOR_EACH),
|
||||
LintId::of(&methods::INTO_ITER_ON_REF),
|
||||
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(&methods::ITER_CLONED_COLLECT),
|
||||
|
|
@ -1516,6 +1538,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&methods::ITER_NTH),
|
||||
LintId::of(&methods::ITER_NTH_ZERO),
|
||||
LintId::of(&methods::ITER_SKIP_NEXT),
|
||||
LintId::of(&methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(&methods::MANUAL_FIND_MAP),
|
||||
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(&methods::NEW_RET_NO_SELF),
|
||||
|
|
@ -1589,6 +1613,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
|
||||
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(&reference::DEREF_ADDROF),
|
||||
LintId::of(&reference::REF_IN_DEREF),
|
||||
|
|
@ -1651,6 +1676,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&unused_unit::UNUSED_UNIT),
|
||||
LintId::of(&unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
|
||||
LintId::of(&useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(&vec::USELESS_VEC),
|
||||
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
|
||||
|
|
@ -1767,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
|
||||
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(&unused_unit::UNUSED_UNIT),
|
||||
LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS),
|
||||
LintId::of(&write::PRINTLN_EMPTY_STRING),
|
||||
LintId::of(&write::PRINT_LITERAL),
|
||||
LintId::of(&write::PRINT_WITH_NEWLINE),
|
||||
|
|
@ -1808,6 +1835,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&methods::CLONE_ON_COPY),
|
||||
LintId::of(&methods::FILTER_NEXT),
|
||||
LintId::of(&methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(&methods::INSPECT_FOR_EACH),
|
||||
LintId::of(&methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(&methods::MANUAL_FIND_MAP),
|
||||
LintId::of(&methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(&methods::SEARCH_IS_SOME),
|
||||
LintId::of(&methods::SKIP_WHILE_NEXT),
|
||||
|
|
@ -1832,6 +1862,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
|
||||
LintId::of(&reference::DEREF_ADDROF),
|
||||
LintId::of(&reference::REF_IN_DEREF),
|
||||
LintId::of(&repeat_once::REPEAT_ONCE),
|
||||
|
|
|
|||
|
|
@ -1069,7 +1069,6 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>(
|
|||
) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'c {
|
||||
// As the `filter` and `map` below do different things, I think putting together
|
||||
// just increases complexity. (cc #3188 and #4193)
|
||||
#[allow(clippy::filter_map)]
|
||||
stmts
|
||||
.iter()
|
||||
.filter_map(move |stmt| match stmt.kind {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ impl LateLintPass<'_> for MainRecursion {
|
|||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, _) = &expr.kind;
|
||||
if let ExprKind::Path(path) = &func.kind;
|
||||
if let QPath::Resolved(_, path) = &path;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind;
|
||||
if let Some(def_id) = path.res.opt_def_id();
|
||||
if is_entrypoint_fn(cx, def_id);
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
///
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) ->
|
|||
Some(expr.span)
|
||||
},
|
||||
hir::ExprKind::Block(ref block, _) => {
|
||||
match (&block.stmts[..], block.expr.as_ref()) {
|
||||
match (block.stmts, block.expr.as_ref()) {
|
||||
(&[], Some(inner_expr)) => {
|
||||
// If block only contains an expression,
|
||||
// reduce `{ X }` to `X`
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ use crate::consts::{constant, miri_to_const, Constant};
|
|||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::is_unused;
|
||||
use crate::utils::{
|
||||
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, meets_msrv, multispan_sugg, remove_blocks,
|
||||
snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
|
||||
span_lint_and_sugg, span_lint_and_then,
|
||||
expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of,
|
||||
is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
|
||||
peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt,
|
||||
snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use if_chain::if_chain;
|
||||
|
|
@ -728,20 +728,60 @@ fn report_single_match_single_pattern(
|
|||
let els_str = els.map_or(String::new(), |els| {
|
||||
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
|
||||
});
|
||||
|
||||
let (msg, sugg) = if_chain! {
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
||||
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
|
||||
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
|
||||
if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
||||
if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
|
||||
then {
|
||||
// scrutinee derives PartialEq and the pattern is a constant.
|
||||
let pat_ref_count = match pat.kind {
|
||||
// string literals are already a reference.
|
||||
PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
|
||||
_ => pat_ref_count,
|
||||
};
|
||||
// References are only implicitly added to the pattern, so no overflow here.
|
||||
// e.g. will work: match &Some(_) { Some(_) => () }
|
||||
// will not: match Some(_) { &Some(_) => () }
|
||||
let ref_count_diff = ty_ref_count - pat_ref_count;
|
||||
|
||||
// Try to remove address of expressions first.
|
||||
let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
|
||||
let ref_count_diff = ref_count_diff - removed;
|
||||
|
||||
let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
|
||||
let sugg = format!(
|
||||
"if {} == {}{} {}{}",
|
||||
snippet(cx, ex.span, ".."),
|
||||
// PartialEq for different reference counts may not exist.
|
||||
"&".repeat(ref_count_diff),
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
|
||||
els_str,
|
||||
);
|
||||
(msg, sugg)
|
||||
} else {
|
||||
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
|
||||
let sugg = format!(
|
||||
"if let {} = {} {}{}",
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
snippet(cx, ex.span, ".."),
|
||||
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
|
||||
els_str,
|
||||
);
|
||||
(msg, sugg)
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
|
||||
let`",
|
||||
msg,
|
||||
"try this",
|
||||
format!(
|
||||
"if let {} = {} {}{}",
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
snippet(cx, ex.span, ".."),
|
||||
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
|
||||
els_str,
|
||||
),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
|
|
@ -1185,6 +1225,14 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
|||
} else {
|
||||
pat
|
||||
};
|
||||
|
||||
// strip potential borrows (#6503), but only if the type is a reference
|
||||
let mut ex_new = ex;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
|
||||
if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() {
|
||||
ex_new = ex_inner;
|
||||
}
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
|
|
@ -1194,7 +1242,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
|||
format!(
|
||||
"{}matches!({}, {})",
|
||||
if b0 { "" } else { "!" },
|
||||
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
|
||||
pat_and_guard,
|
||||
),
|
||||
applicability,
|
||||
|
|
|
|||
23
clippy_lints/src/methods/inspect_for_each.rs
Normal file
23
clippy_lints/src/methods/inspect_for_each.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
use crate::utils::{match_trait_method, paths, span_lint_and_help};
|
||||
|
||||
use super::INSPECT_FOR_EACH;
|
||||
|
||||
/// lint use of `inspect().for_each()` for `Iterators`
|
||||
pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) {
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `inspect(..).for_each(..)` on an `Iterator`";
|
||||
let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`";
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
INSPECT_FOR_EACH,
|
||||
inspect_span.with_hi(expr.span.hi()),
|
||||
msg,
|
||||
None,
|
||||
hint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
mod bind_instead_of_map;
|
||||
mod inefficient_to_string;
|
||||
mod inspect_for_each;
|
||||
mod manual_saturating_arithmetic;
|
||||
mod option_map_unwrap_or;
|
||||
mod unnecessary_filter_map;
|
||||
|
|
@ -14,7 +15,8 @@ use if_chain::if_chain;
|
|||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{TraitItem, TraitItemKind};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, QPath, TraitItem, TraitItemKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
|
|
@ -449,6 +451,58 @@ declare_clippy_lint! {
|
|||
"using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
|
||||
/// as `filter_map(_)`.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and
|
||||
/// less performant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// (0_i32..10)
|
||||
/// .filter(|n| n.checked_add(1).is_some())
|
||||
/// .map(|n| n.checked_add(1).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// (0_i32..10).filter_map(|n| n.checked_add(1));
|
||||
/// ```
|
||||
pub MANUAL_FILTER_MAP,
|
||||
complexity,
|
||||
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply
|
||||
/// as `find_map(_)`.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and
|
||||
/// less performant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// (0_i32..10)
|
||||
/// .find(|n| n.checked_add(1).is_some())
|
||||
/// .map(|n| n.checked_add(1).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// (0_i32..10).find_map(|n| n.checked_add(1));
|
||||
/// ```
|
||||
pub MANUAL_FIND_MAP,
|
||||
complexity,
|
||||
"using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.filter_map(_).next()`.
|
||||
///
|
||||
|
|
@ -493,28 +547,6 @@ declare_clippy_lint! {
|
|||
"call to `flat_map` where `flatten` is sufficient"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.find(_).map(_)`.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely as
|
||||
/// `_.find_map(_)`.
|
||||
///
|
||||
/// **Known problems:** Often requires a condition + Option/Iterator creation
|
||||
/// inside the closure.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// (0..3).find(|x| *x == 2).map(|x| x * 2);
|
||||
/// ```
|
||||
/// Can be written as
|
||||
/// ```rust
|
||||
/// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None });
|
||||
/// ```
|
||||
pub FIND_MAP,
|
||||
pedantic,
|
||||
"using a combination of `find` and `map` can usually be written as a single method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for an iterator or string search (such as `find()`,
|
||||
/// `position()`, or `rposition()`) followed by a call to `is_some()`.
|
||||
|
|
@ -1405,6 +1437,36 @@ declare_clippy_lint! {
|
|||
"use `.collect()` instead of `::from_iter()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `inspect().for_each()`.
|
||||
///
|
||||
/// **Why is this bad?** It is the same as performing the computation
|
||||
/// inside `inspect` at the beginning of the closure in `for_each`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// [1,2,3,4,5].iter()
|
||||
/// .inspect(|&x| println!("inspect the number: {}", x))
|
||||
/// .for_each(|&x| {
|
||||
/// assert!(x >= 0);
|
||||
/// });
|
||||
/// ```
|
||||
/// Can be written as
|
||||
/// ```rust
|
||||
/// [1,2,3,4,5].iter()
|
||||
/// .for_each(|&x| {
|
||||
/// println!("inspect the number: {}", x);
|
||||
/// assert!(x >= 0);
|
||||
/// });
|
||||
/// ```
|
||||
pub INSPECT_FOR_EACH,
|
||||
complexity,
|
||||
"using `.inspect().for_each()`, which can be replaced with `.for_each()`"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
|
@ -1442,9 +1504,10 @@ impl_lint_pass!(Methods => [
|
|||
FILTER_NEXT,
|
||||
SKIP_WHILE_NEXT,
|
||||
FILTER_MAP,
|
||||
MANUAL_FILTER_MAP,
|
||||
MANUAL_FIND_MAP,
|
||||
FILTER_MAP_NEXT,
|
||||
FLAT_MAP_IDENTITY,
|
||||
FIND_MAP,
|
||||
MAP_FLATTEN,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
ITER_NEXT_SLICE,
|
||||
|
|
@ -1467,6 +1530,7 @@ impl_lint_pass!(Methods => [
|
|||
UNNECESSARY_LAZY_EVALUATIONS,
|
||||
MAP_COLLECT_RESULT_UNIT,
|
||||
FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
INSPECT_FOR_EACH,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
|
|
@ -1508,10 +1572,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
|
||||
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
|
||||
["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, false),
|
||||
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
|
||||
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "find"] => lint_filter_map(cx, expr, true),
|
||||
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
|
||||
|
|
@ -1553,6 +1617,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
|
||||
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
|
||||
["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
|
@ -2955,18 +3020,79 @@ fn lint_skip_while_next<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// lint use of `filter().map()` for `Iterators`
|
||||
fn lint_filter_map<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
_filter_args: &'tcx [hir::Expr<'_>],
|
||||
_map_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
/// lint use of `filter().map()` or `find().map()` for `Iterators`
|
||||
fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
|
||||
if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
|
||||
if match_trait_method(cx, map_recv, &paths::ITERATOR);
|
||||
|
||||
// filter(|x| ...is_some())...
|
||||
if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
|
||||
let filter_body = cx.tcx.hir().body(filter_body_id);
|
||||
if let [filter_param] = filter_body.params;
|
||||
// optional ref pattern: `filter(|&x| ..)`
|
||||
let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
|
||||
(ref_pat, true)
|
||||
} else {
|
||||
(filter_param.pat, false)
|
||||
};
|
||||
// closure ends with is_some() or is_ok()
|
||||
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
|
||||
if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind;
|
||||
if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
|
||||
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) {
|
||||
Some(false)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
|
||||
|
||||
// ...map(|x| ...unwrap())
|
||||
if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind;
|
||||
let map_body = cx.tcx.hir().body(map_body_id);
|
||||
if let [map_param] = map_body.params;
|
||||
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
|
||||
// closure ends with expect() or unwrap()
|
||||
if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind;
|
||||
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
|
||||
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
// in `filter(|x| ..)`, replace `*x` with `x`
|
||||
let a_path = if_chain! {
|
||||
if !is_filter_param_ref;
|
||||
if let ExprKind::Unary(UnOp::UnDeref, expr_path) = a.kind;
|
||||
then { expr_path } else { a }
|
||||
};
|
||||
// let the filter closure arg and the map closure arg be equal
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(None, a_path)) = a_path.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(None, b_path)) = b.kind;
|
||||
if a_path.res == Res::Local(filter_param_id);
|
||||
if b_path.res == Res::Local(map_param_id);
|
||||
if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b));
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg);
|
||||
then {
|
||||
let span = filter_span.to(map_span);
|
||||
let (filter_name, lint) = if is_find {
|
||||
("find", MANUAL_FIND_MAP)
|
||||
} else {
|
||||
("filter", MANUAL_FILTER_MAP)
|
||||
};
|
||||
let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
|
||||
let to_opt = if is_result { ".ok()" } else { "" };
|
||||
let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
|
||||
snippet(cx, map_arg.span, ".."), to_opt);
|
||||
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3004,21 +3130,6 @@ fn lint_filter_map_next<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
/// lint use of `find().map()` for `Iterators`
|
||||
fn lint_find_map<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
_find_args: &'tcx [hir::Expr<'_>],
|
||||
map_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
|
||||
let msg = "called `find(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
|
||||
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `filter_map().map()` for `Iterators`
|
||||
fn lint_filter_map_map<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
|
@ -3026,7 +3137,7 @@ fn lint_filter_map_map<'tcx>(
|
|||
_filter_args: &'tcx [hir::Expr<'_>],
|
||||
_map_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
// lint if caller of `.filter_map().map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter_map(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
|
||||
|
|
|
|||
|
|
@ -90,10 +90,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
|||
|
|
@ -117,9 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
.filter_map(|obligation| {
|
||||
// Note that we do not want to deal with qualified predicates here.
|
||||
match obligation.predicate.kind().no_bound_vars() {
|
||||
Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => {
|
||||
Some(pred)
|
||||
},
|
||||
Some(ty::PredicateKind::Trait(pred, _)) if pred.def_id() != sized_trait => Some(pred),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -199,6 +199,10 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
|||
);
|
||||
return;
|
||||
}
|
||||
if interned_name.starts_with('_') {
|
||||
// these bindings are typically unused or represent an ignored portion of a destructuring pattern
|
||||
return;
|
||||
}
|
||||
let count = interned_name.chars().count();
|
||||
if count < 3 {
|
||||
if count == 1 {
|
||||
|
|
|
|||
|
|
@ -263,8 +263,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
|||
} else if match_type(cx, ty, &paths::COW) {
|
||||
if_chain! {
|
||||
if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
|
||||
if let TyKind::Path(ref path) = ty.kind;
|
||||
if let QPath::Resolved(None, ref pp) = *path;
|
||||
if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind;
|
||||
if let [ref bx] = *pp.segments;
|
||||
if let Some(ref params) = bx.args;
|
||||
if !params.parenthesized;
|
||||
|
|
|
|||
67
clippy_lints/src/redundant_slicing.rs
Normal file
67
clippy_lints/src/redundant_slicing.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::{lint::in_external_macro, ty::TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for redundant slicing expressions which use the full range, and
|
||||
/// do not change the type.
|
||||
///
|
||||
/// **Why is this bad?** It unnecessarily adds complexity to the expression.
|
||||
///
|
||||
/// **Known problems:** If the type being sliced has an implementation of `Index<RangeFull>`
|
||||
/// that actually changes anything then it can't be removed. However, this would be surprising
|
||||
/// to people reading the code and should have a note with it.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn get_slice(x: &[u32]) -> &[u32] {
|
||||
/// &x[..]
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// fn get_slice(x: &[u32]) -> &[u32] {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
pub REDUNDANT_SLICING,
|
||||
complexity,
|
||||
"redundant slicing of the whole range of a type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
|
||||
|
||||
impl LateLintPass<'_> for RedundantSlicing {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::AddrOf(_, _, addressee) = expr.kind;
|
||||
if let ExprKind::Index(indexed, range) = addressee.kind;
|
||||
if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
|
||||
if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed));
|
||||
then {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_SLICING,
|
||||
expr.span,
|
||||
"redundant slicing of the whole range",
|
||||
"use the original slice instead",
|
||||
hint,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
|
||||
|
|
@ -110,6 +111,12 @@ declare_clippy_lint! {
|
|||
/// let point = Point(30, 20);
|
||||
/// let x = (&point).0;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct Point(u32, u32);
|
||||
/// # let point = Point(30, 20);
|
||||
/// let x = point.0;
|
||||
/// ```
|
||||
pub REF_IN_DEREF,
|
||||
complexity,
|
||||
"Use of reference in auto dereference expression."
|
||||
|
|
@ -124,14 +131,19 @@ impl EarlyLintPass for RefInDeref {
|
|||
if let ExprKind::Paren(ref parened) = object.kind;
|
||||
if let ExprKind::AddrOf(_, _, ref inner) = parened.kind;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let applicability = if inner.span.from_expansion() {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let sugg = Sugg::ast(cx, inner, "_").maybe_par();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REF_IN_DEREF,
|
||||
object.span,
|
||||
"creating a reference that is immediately dereferenced",
|
||||
"try this",
|
||||
snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
|
||||
sugg.to_string(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,16 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
|||
_: HirId,
|
||||
) {
|
||||
match kind {
|
||||
FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty),
|
||||
FnKind::Closure(_) => {
|
||||
// when returning without value in closure, replace this `return`
|
||||
// with an empty block to prevent invalid suggestion (see #6501)
|
||||
let replacement = if let ExprKind::Ret(None) = &body.value.kind {
|
||||
RetReplacement::Block
|
||||
} else {
|
||||
RetReplacement::Empty
|
||||
};
|
||||
check_final_expr(cx, &body.value, Some(body.value.span), replacement)
|
||||
},
|
||||
FnKind::ItemFn(..) | FnKind::Method(..) => {
|
||||
if let ExprKind::Block(ref block, _) = body.value.kind {
|
||||
check_block_return(cx, block);
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ declare_clippy_lint! {
|
|||
"various things that will negatively affect your serde experience"
|
||||
}
|
||||
|
||||
declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]);
|
||||
declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SerdeAPI {
|
||||
impl<'tcx> LateLintPass<'tcx> for SerdeApi {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
|
|
|
|||
|
|
@ -35,10 +35,11 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
|
||||
|
||||
fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Ty<'tcx>> {
|
||||
fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(count_func, _func_args) => {
|
||||
if_chain! {
|
||||
if !inverted;
|
||||
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
|
||||
if match_def_path(cx, def_id, &paths::MEM_SIZE_OF)
|
||||
|
|
@ -50,10 +51,13 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Ty<'tc
|
|||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => {
|
||||
get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right))
|
||||
ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => {
|
||||
get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, inverted))
|
||||
},
|
||||
ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr),
|
||||
ExprKind::Binary(op, left, right) if BinOpKind::Div == op.node => {
|
||||
get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, !inverted))
|
||||
},
|
||||
ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr, inverted),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
|
|||
|
||||
// Find a size_of call in the count parameter expression and
|
||||
// check that it's the same type
|
||||
if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr);
|
||||
if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false);
|
||||
if TyS::same_type(pointee_ty, ty_used_for_size_of);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg};
|
||||
use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg};
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
|
|
@ -107,25 +107,32 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option
|
|||
impl LateLintPass<'_> for StableSortPrimitive {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
expr.span,
|
||||
format!(
|
||||
"used {} instead of {} to sort primitive type `{}`",
|
||||
"used `{}` on primitive type `{}`",
|
||||
detection.method.stable_name(),
|
||||
detection.method.unstable_name(),
|
||||
detection.slice_type,
|
||||
)
|
||||
.as_str(),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}({})",
|
||||
detection.slice_name,
|
||||
detection.method.unstable_name(),
|
||||
detection.method_args
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}({})",
|
||||
detection.slice_name,
|
||||
detection.method.unstable_name(),
|
||||
detection.method_args,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.note(
|
||||
"an unstable sort would perform faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1106,7 +1106,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool {
|
|||
expr.kind,
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: &[], expr: None, ..
|
||||
stmts: &[],
|
||||
expr: None,
|
||||
..
|
||||
},
|
||||
_,
|
||||
)
|
||||
|
|
|
|||
93
clippy_lints/src/upper_case_acronyms.rs
Normal file
93
clippy_lints/src/upper_case_acronyms.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
use crate::utils::span_lint_and_sugg;
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::ast::{Item, ItemKind, Variant};
|
||||
use rustc_errors::Applicability;
|
||||
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::symbol::Ident;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for camel case name containing a capitalized acronym.
|
||||
///
|
||||
/// **Why is this bad?** In CamelCase, acronyms count as one word.
|
||||
/// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)
|
||||
/// for more.
|
||||
///
|
||||
/// **Known problems:** When two acronyms are contiguous, the lint can't tell where
|
||||
/// the first acronym ends and the second starts, so it suggests to lowercase all of
|
||||
/// the letters in the second acronym.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct HTTPResponse;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct HttpResponse;
|
||||
/// ```
|
||||
pub UPPER_CASE_ACRONYMS,
|
||||
style,
|
||||
"capitalized acronyms are against the naming convention"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]);
|
||||
|
||||
fn correct_ident(ident: &str) -> String {
|
||||
let ident = ident.chars().rev().collect::<String>();
|
||||
let fragments = ident
|
||||
.split_inclusive(|x: char| !x.is_ascii_lowercase())
|
||||
.rev()
|
||||
.map(|x| x.chars().rev().collect::<String>());
|
||||
|
||||
let mut ident = fragments.clone().next().unwrap();
|
||||
for (ref prev, ref curr) in fragments.tuple_windows() {
|
||||
if [prev, curr]
|
||||
.iter()
|
||||
.all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase())
|
||||
{
|
||||
ident.push_str(&curr.to_ascii_lowercase());
|
||||
} else {
|
||||
ident.push_str(curr);
|
||||
}
|
||||
}
|
||||
ident
|
||||
}
|
||||
|
||||
fn check_ident(cx: &EarlyContext<'_>, ident: &Ident) {
|
||||
let span = ident.span;
|
||||
let ident = &ident.as_str();
|
||||
let corrected = correct_ident(ident);
|
||||
if ident != &corrected {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UPPER_CASE_ACRONYMS,
|
||||
span,
|
||||
&format!("name `{}` contains a capitalized acronym", ident),
|
||||
"consider making the acronym lowercase, except the initial letter",
|
||||
corrected,
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for UpperCaseAcronyms {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), it.span);
|
||||
if matches!(
|
||||
it.kind,
|
||||
ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..)
|
||||
);
|
||||
then {
|
||||
check_ident(cx, &it.ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) {
|
||||
check_ident(cx, &v.ident);
|
||||
}
|
||||
}
|
||||
|
|
@ -127,6 +127,7 @@ define_Conf! {
|
|||
"OAuth", "GraphQL",
|
||||
"OCaml",
|
||||
"OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
|
||||
"WebGL",
|
||||
"TensorFlow",
|
||||
"TrueType",
|
||||
"iOS", "macOS",
|
||||
|
|
|
|||
|
|
@ -158,8 +158,7 @@ pub fn for_loop<'tcx>(
|
|||
/// `while cond { body }` becomes `(cond, body)`.
|
||||
pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Loop(block, _, hir::LoopSource::While, _) = &expr.kind;
|
||||
if let hir::Block { expr: Some(expr), .. } = &**block;
|
||||
if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind;
|
||||
if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
|
||||
if let hir::ExprKind::DropTemps(cond) = &cond.kind;
|
||||
if let [hir::Arm { body, .. }, ..] = &arms[..];
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ pub struct SpanlessEq<'a, 'tcx> {
|
|||
cx: &'a LateContext<'tcx>,
|
||||
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
|
||||
allow_side_effects: bool,
|
||||
expr_fallback: Option<Box<dyn Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
|
|
@ -32,6 +33,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
cx,
|
||||
maybe_typeck_results: cx.maybe_typeck_results(),
|
||||
allow_side_effects: true,
|
||||
expr_fallback: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,6 +45,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
|
||||
Self {
|
||||
expr_fallback: Some(Box::new(expr_fallback)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether two statements are the same.
|
||||
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
match (&left.kind, &right.kind) {
|
||||
|
|
@ -81,7 +90,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
|
||||
let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
|
||||
(&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
|
||||
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
|
||||
},
|
||||
|
|
@ -158,7 +167,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
(&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
|
||||
(&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right))
|
||||
}
|
||||
|
||||
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
|
|||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::hash::BuildHasherDefault;
|
||||
use std::mem;
|
||||
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, Attribute, LitKind};
|
||||
|
|
@ -39,7 +38,7 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::{
|
||||
|
|
@ -48,6 +47,7 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::exports::Export;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
||||
|
|
@ -308,65 +308,43 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
|
|||
}
|
||||
|
||||
/// Gets the definition associated to a path.
|
||||
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
|
||||
let crates = cx.tcx.crates();
|
||||
let krate = crates
|
||||
.iter()
|
||||
.find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
|
||||
if let Some(krate) = krate {
|
||||
let krate = DefId {
|
||||
krate: *krate,
|
||||
index: CRATE_DEF_INDEX,
|
||||
};
|
||||
let mut current_item = None;
|
||||
let mut items = cx.tcx.item_children(krate);
|
||||
let mut path_it = path.iter().skip(1).peekable();
|
||||
|
||||
loop {
|
||||
let segment = match path_it.next() {
|
||||
Some(segment) => segment,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// `get_def_path` seems to generate these empty segments for extern blocks.
|
||||
// We can just ignore them.
|
||||
if segment.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = SmallVec::<[_; 8]>::new();
|
||||
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
|
||||
if item.ident.name.as_str() == *segment {
|
||||
if path_it.peek().is_none() {
|
||||
return Some(item.res);
|
||||
}
|
||||
|
||||
current_item = Some(item);
|
||||
items = cx.tcx.item_children(item.res.def_id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The segment isn't a child_item.
|
||||
// Try to find it under an inherent impl.
|
||||
if_chain! {
|
||||
if path_it.peek().is_none();
|
||||
if let Some(current_item) = current_item;
|
||||
let item_def_id = current_item.res.def_id();
|
||||
if cx.tcx.def_kind(item_def_id) == DefKind::Struct;
|
||||
then {
|
||||
// Bad `find_map` suggestion. See #4193.
|
||||
#[allow(clippy::find_map)]
|
||||
return cx.tcx.inherent_impls(item_def_id).iter()
|
||||
.flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id))
|
||||
.find(|item| item.ident.name.as_str() == *segment)
|
||||
.map(|item| item.res);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
#[allow(clippy::shadow_unrelated)] // false positive #6563
|
||||
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<Res> {
|
||||
fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export<HirId>> {
|
||||
tcx.item_children(def_id)
|
||||
.iter()
|
||||
.find(|item| item.ident.name.as_str() == name)
|
||||
}
|
||||
|
||||
let (krate, first, path) = match *path {
|
||||
[krate, first, ref path @ ..] => (krate, first, path),
|
||||
_ => return None,
|
||||
};
|
||||
let tcx = cx.tcx;
|
||||
let crates = tcx.crates();
|
||||
let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?;
|
||||
let first = item_child_by_name(tcx, krate.as_def_id(), first)?;
|
||||
let last = path
|
||||
.iter()
|
||||
.copied()
|
||||
// `get_def_path` seems to generate these empty segments for extern blocks.
|
||||
// We can just ignore them.
|
||||
.filter(|segment| !segment.is_empty())
|
||||
// for each segment, find the child item
|
||||
.try_fold(first, |item, segment| {
|
||||
let def_id = item.res.def_id();
|
||||
if let Some(item) = item_child_by_name(tcx, def_id, segment) {
|
||||
Some(item)
|
||||
} else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
|
||||
// it is not a child item so check inherent impl items
|
||||
tcx.inherent_impls(def_id)
|
||||
.iter()
|
||||
.find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
Some(last.res)
|
||||
}
|
||||
|
||||
/// Convenience function to get the `DefId` of a trait by path.
|
||||
|
|
@ -1134,8 +1112,7 @@ pub fn is_self(slf: &Param<'_>) -> bool {
|
|||
|
||||
pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let TyKind::Path(ref qp) = slf.kind;
|
||||
if let QPath::Resolved(None, ref path) = *qp;
|
||||
if let TyKind::Path(QPath::Resolved(None, ref path)) = slf.kind;
|
||||
if let Res::SelfTy(..) = path.res;
|
||||
then {
|
||||
return true
|
||||
|
|
@ -1655,6 +1632,44 @@ where
|
|||
match_expr_list
|
||||
}
|
||||
|
||||
/// Peels off all references on the pattern. Returns the underlying pattern and the number of
|
||||
/// references removed.
|
||||
pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
|
||||
fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
|
||||
if let PatKind::Ref(pat, _) = pat.kind {
|
||||
peel(pat, count + 1)
|
||||
} else {
|
||||
(pat, count)
|
||||
}
|
||||
}
|
||||
peel(pat, 0)
|
||||
}
|
||||
|
||||
/// Peels off up to the given number of references on the expression. Returns the underlying
|
||||
/// expression and the number of references removed.
|
||||
pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
|
||||
fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
|
||||
_ => (expr, count),
|
||||
}
|
||||
}
|
||||
f(expr, 0, count)
|
||||
}
|
||||
|
||||
/// Peels off all references on the type. Returns the underlying type and the number of references
|
||||
/// removed.
|
||||
pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
|
||||
fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
|
||||
if let ty::Ref(_, ty, _) = ty.kind() {
|
||||
peel(ty, count + 1)
|
||||
} else {
|
||||
(ty, count)
|
||||
}
|
||||
}
|
||||
peel(ty, 0)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use std::borrow::Cow;
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||
use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle};
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -437,7 +438,7 @@ impl Write {
|
|||
return (Some(fmtstr), None);
|
||||
};
|
||||
match &token_expr.kind {
|
||||
ExprKind::Lit(_) => {
|
||||
ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => {
|
||||
let mut all_simple = true;
|
||||
let mut seen = false;
|
||||
for arg in &args {
|
||||
|
|
@ -457,8 +458,11 @@ impl Write {
|
|||
idx += 1;
|
||||
},
|
||||
ExprKind::Assign(lhs, rhs, _) => {
|
||||
if let ExprKind::Lit(_) = rhs.kind {
|
||||
if let ExprKind::Path(_, p) = &lhs.kind {
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref lit) = rhs.kind;
|
||||
if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..));
|
||||
if let ExprKind::Path(_, p) = &lhs.kind;
|
||||
then {
|
||||
let mut all_simple = true;
|
||||
let mut seen = false;
|
||||
for arg in &args {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|||
use rustc_target::abi::LayoutOf as _;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help};
|
||||
use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
|
||||
|
|
@ -50,6 +50,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
|
|||
if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP);
|
||||
if let Adt(_, ref substs) = ty.kind();
|
||||
let ty = substs.type_at(1);
|
||||
// Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`.
|
||||
if is_normalizable(cx, cx.param_env, ty);
|
||||
if let Ok(layout) = cx.layout_of(ty);
|
||||
if layout.is_zst();
|
||||
then {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue