mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 23:20:39 +00:00
Auto merge of #8957 - Jarcho:more_pass_merges, r=flip1995
More lint pass merges changelog: None
This commit is contained in:
commit
477c16d45b
52 changed files with 1727 additions and 1915 deletions
|
@ -1,74 +0,0 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
/// undetected due to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
|
||||
|
||||
let ty_resolved = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Error(_) = ty_resolved.kind() {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
AS_UNDERSCORE,
|
||||
expr.span,
|
||||
"using `as _` conversion",
|
||||
None,
|
||||
"consider giving the type explicitly",
|
||||
);
|
||||
} else {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AS_UNDERSCORE,
|
||||
expr.span,
|
||||
"using `as _` conversion",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"consider giving the type explicitly",
|
||||
ty_resolved,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_no_std_crate;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
|
||||
/// `ptr::addr_of_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `ptr::addr_of` docs for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = std::ptr::addr_of!(val);
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
|
||||
|
||||
pub struct BorrowAsPtr {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl BorrowAsPtr {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(left_expr, ty) = &expr.kind;
|
||||
if let TyKind::Ptr(_) = ty.kind;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
|
||||
|
||||
then {
|
||||
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
let macro_name = match mutability {
|
||||
Mutability::Not => "addr_of",
|
||||
Mutability::Mut => "addr_of_mut",
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BORROW_AS_PTR,
|
||||
expr.span,
|
||||
"borrow as raw pointer",
|
||||
"try",
|
||||
format!(
|
||||
"{}::ptr::{}!({})",
|
||||
core_or_std,
|
||||
macro_name,
|
||||
snippet_opt(cx, e.span).unwrap()
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for naive byte counts
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The [`bytecount`](https://crates.io/crates/bytecount)
|
||||
/// crate has methods to count your bytes faster, especially for large slices.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If you have predominantly small slices, the
|
||||
/// `bytecount::count(..)` method may actually be slower. However, if you can
|
||||
/// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
|
||||
/// faster in those cases.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = vec.iter().filter(|x| **x == 0u8).count();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = bytecount::count(&vec, 0u8);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NAIVE_BYTECOUNT,
|
||||
pedantic,
|
||||
"use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
|
||||
if count.ident.name == sym::count;
|
||||
if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
|
||||
if filter.ident.name == sym!(filter);
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, peel_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, args, _) =
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
filter_recv
|
||||
}
|
||||
} else {
|
||||
filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NAIVE_BYTECOUNT,
|
||||
expr.span,
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// It checks for `str::bytes().count()` and suggests replacing it with
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `str::bytes().count()` is longer and may not be as performant as using
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// "hello".bytes().count();
|
||||
/// String::from("hello").bytes().count();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// "hello".len();
|
||||
/// String::from("hello").len();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub BYTES_COUNT_TO_LEN,
|
||||
complexity,
|
||||
"Using `bytes().count()` when `len()` performs the same functionality"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
|
||||
|
||||
if let [bytes_expr] = &**expr_args;
|
||||
if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
|
||||
if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
|
||||
if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
|
||||
|
||||
if let [str_expr] = &**bytes_args;
|
||||
let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use if_chain::if_chain;
|
||||
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, symbol::sym, 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.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.ends_with(".rs")
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// let filename = std::path::Path::new(filename);
|
||||
/// filename.extension()
|
||||
/// .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.51.0"]
|
||||
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> {
|
||||
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 (2..=6).contains(&ext_literal.as_str().len());
|
||||
if ext_literal.as_str().starts_with('.');
|
||||
if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
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(def, _) => {
|
||||
if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
|
||||
return Some(span);
|
||||
}
|
||||
},
|
||||
_ => { return None; }
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl<'tcx> 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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
25
clippy_lints/src/casts/as_underscore.rs
Normal file
25
clippy_lints/src/casts/as_underscore.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use super::AS_UNDERSCORE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
|
||||
if matches!(ty.kind, TyKind::Infer) {
|
||||
span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
|
||||
let ty_resolved = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Error(_) = ty_resolved.kind() {
|
||||
diag.help("consider giving the type explicitly");
|
||||
} else {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"consider giving the type explicitly",
|
||||
ty_resolved,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
37
clippy_lints/src/casts/borrow_as_ptr.rs
Normal file
37
clippy_lints/src/casts/borrow_as_ptr.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_no_std_crate;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::BORROW_AS_PTR;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
cast_expr: &'tcx Expr<'_>,
|
||||
cast_to: &'tcx Ty<'_>,
|
||||
) {
|
||||
if matches!(cast_to.kind, TyKind::Ptr(_))
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
|
||||
{
|
||||
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
let macro_name = match mutability {
|
||||
Mutability::Not => "addr_of",
|
||||
Mutability::Mut => "addr_of_mut",
|
||||
};
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BORROW_AS_PTR,
|
||||
expr.span,
|
||||
"borrow as raw pointer",
|
||||
"try",
|
||||
format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
mod as_underscore;
|
||||
mod borrow_as_ptr;
|
||||
mod cast_abs_to_unsigned;
|
||||
mod cast_enum_constructor;
|
||||
mod cast_lossless;
|
||||
|
@ -16,7 +18,7 @@ mod ptr_as_ptr;
|
|||
mod unnecessary_cast;
|
||||
mod utils;
|
||||
|
||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||
use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -506,6 +508,67 @@ declare_clippy_lint! {
|
|||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
/// undetected due to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
|
||||
/// `ptr::addr_of_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `ptr::addr_of` docs for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = std::ptr::addr_of!(val);
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
@ -534,7 +597,9 @@ impl_lint_pass!(Casts => [
|
|||
PTR_AS_PTR,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ABS_TO_UNSIGNED
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
@ -547,8 +612,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to) {
|
||||
if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
|
||||
return;
|
||||
}
|
||||
let (cast_from, cast_to) = (
|
||||
|
@ -575,6 +640,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
}
|
||||
|
||||
as_underscore::check(cx, expr, cast_to_hir);
|
||||
|
||||
if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
|
||||
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
}
|
||||
}
|
||||
|
||||
cast_ref_to_mut::check(cx, expr);
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for using `x.get(0)` instead of
|
||||
/// `x.first()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `x.first()` is easier to read and has the same
|
||||
/// result.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.get(0);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.first();
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub GET_FIRST,
|
||||
style,
|
||||
"Using `x.get(0)` when `x.first()` is simpler"
|
||||
}
|
||||
declare_lint_pass!(GetFirst => [GET_FIRST]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for GetFirst {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
|
||||
|
||||
if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
|
||||
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let slice_name = snippet_with_applicability(
|
||||
cx,
|
||||
struct_calling_on.span, "..",
|
||||
&mut applicability,
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_FIRST,
|
||||
expr.span,
|
||||
&format!("accessing first element with `{0}.get(0)`", slice_name),
|
||||
"try",
|
||||
format!("{}.first()", slice_name),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
|
@ -82,7 +81,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(if_let_mutex::IF_LET_MUTEX),
|
||||
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
|
||||
LintId::of(infinite_iter::INFINITE_ITER),
|
||||
|
@ -129,7 +127,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
|
@ -151,6 +148,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(methods::BYTES_NTH),
|
||||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
|
@ -162,6 +160,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::GET_FIRST),
|
||||
LintId::of(methods::GET_LAST_WITH_LEN),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
|
@ -179,13 +178,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MANUAL_SPLIT_ONCE),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
LintId::of(methods::MAP_CLONE),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::MUT_MUTEX_LOCK),
|
||||
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
|
||||
LintId::of(methods::NEEDLESS_OPTION_TAKE),
|
||||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
|
@ -194,6 +196,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::OPTION_MAP_OR_NONE),
|
||||
LintId::of(methods::OR_FUN_CALL),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(methods::REPEAT_ONCE),
|
||||
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
|
@ -204,13 +208,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(methods::SUSPICIOUS_SPLITN),
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::UNIT_HASH),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FIND_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FOLD),
|
||||
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
|
||||
LintId::of(methods::UNNECESSARY_SORT_BY),
|
||||
LintId::of(methods::UNNECESSARY_TO_OWNED),
|
||||
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(methods::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(methods::WRONG_SELF_CONVENTION),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
|
@ -226,7 +233,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
|
||||
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
LintId::of(needless_bool::BOOL_COMPARISON),
|
||||
|
@ -246,7 +252,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::ASSIGN_OP_PATTERN),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
|
@ -276,7 +281,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(question_mark::QUESTION_MARK),
|
||||
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
|
||||
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
|
||||
|
@ -287,7 +291,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(reference::DEREF_ADDROF),
|
||||
LintId::of(regex::INVALID_REGEX),
|
||||
LintId::of(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(returns::LET_AND_RETURN),
|
||||
LintId::of(returns::NEEDLESS_RETURN),
|
||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||
|
@ -315,10 +318,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(transmute::TRANSMUTING_NULL),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
LintId::of(types::BOX_COLLECTION),
|
||||
LintId::of(types::REDUNDANT_ALLOCATION),
|
||||
|
@ -326,7 +329,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(types::VEC_BOX),
|
||||
LintId::of(unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::LET_UNIT_VALUE),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
|
@ -334,7 +336,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(unused_unit::UNUSED_UNIT),
|
||||
|
@ -344,7 +345,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(vec::USELESS_VEC),
|
||||
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
|
||||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
|
||||
LintId::of(write::PRINTLN_EMPTY_STRING),
|
||||
LintId::of(write::PRINT_LITERAL),
|
||||
|
|
|
@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
|
||||
|
@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(matches::NEEDLESS_MATCH),
|
||||
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(methods::CLONE_ON_COPY),
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
|
@ -51,10 +51,13 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(methods::REPEAT_ONCE),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FIND_MAP),
|
||||
LintId::of(methods::UNNECESSARY_SORT_BY),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
|
||||
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
|
@ -76,11 +79,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
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(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
|
||||
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
|
||||
LintId::of(swap::MANUAL_SWAP),
|
||||
|
@ -99,7 +100,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(types::TYPE_COMPLEXITY),
|
||||
LintId::of(types::VEC_BOX),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
|
||||
|
|
|
@ -39,12 +39,14 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
|||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::CLONE_DOUBLE_REF),
|
||||
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(methods::SUSPICIOUS_SPLITN),
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::UNIT_HASH),
|
||||
LintId::of(methods::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
LintId::of(operators::CMP_NAN),
|
||||
|
@ -62,17 +64,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
|||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(swap::ALMOST_SWAPPED),
|
||||
LintId::of(transmute::TRANSMUTING_NULL),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::UNIT_CMP),
|
||||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
])
|
||||
|
|
|
@ -38,7 +38,6 @@ store.register_lints(&[
|
|||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
as_conversions::AS_CONVERSIONS,
|
||||
as_underscore::AS_UNDERSCORE,
|
||||
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
||||
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||
|
@ -59,16 +58,14 @@ store.register_lints(&[
|
|||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
booleans::OVERLY_COMPLEX_BOOL_EXPR,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
bytes_count_to_len::BYTES_COUNT_TO_LEN,
|
||||
cargo::CARGO_COMMON_METADATA,
|
||||
cargo::MULTIPLE_CRATE_VERSIONS,
|
||||
cargo::NEGATIVE_FEATURE_NAMES,
|
||||
cargo::REDUNDANT_FEATURE_NAMES,
|
||||
cargo::WILDCARD_DEPENDENCIES,
|
||||
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
casts::AS_UNDERSCORE,
|
||||
casts::BORROW_AS_PTR,
|
||||
casts::CAST_ABS_TO_UNSIGNED,
|
||||
casts::CAST_ENUM_CONSTRUCTOR,
|
||||
casts::CAST_ENUM_TRUNCATION,
|
||||
|
@ -177,7 +174,6 @@ store.register_lints(&[
|
|||
functions::TOO_MANY_ARGUMENTS,
|
||||
functions::TOO_MANY_LINES,
|
||||
future_not_send::FUTURE_NOT_SEND,
|
||||
get_first::GET_FIRST,
|
||||
if_let_mutex::IF_LET_MUTEX,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
|
||||
|
@ -247,12 +243,9 @@ store.register_lints(&[
|
|||
manual_empty_string_creations::MANUAL_EMPTY_STRING_CREATIONS,
|
||||
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
manual_rem_euclid::MANUAL_REM_EUCLID,
|
||||
manual_retain::MANUAL_RETAIN,
|
||||
manual_strip::MANUAL_STRIP,
|
||||
map_clone::MAP_CLONE,
|
||||
map_err_ignore::MAP_ERR_IGNORE,
|
||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
match_result_ok::MATCH_RESULT_OK,
|
||||
|
@ -285,7 +278,9 @@ store.register_lints(&[
|
|||
mem_replace::MEM_REPLACE_WITH_DEFAULT,
|
||||
mem_replace::MEM_REPLACE_WITH_UNINIT,
|
||||
methods::BIND_INSTEAD_OF_MAP,
|
||||
methods::BYTES_COUNT_TO_LEN,
|
||||
methods::BYTES_NTH,
|
||||
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
methods::CHARS_LAST_CMP,
|
||||
methods::CHARS_NEXT_CMP,
|
||||
methods::CLONED_INSTEAD_OF_COPIED,
|
||||
|
@ -303,6 +298,7 @@ store.register_lints(&[
|
|||
methods::FLAT_MAP_IDENTITY,
|
||||
methods::FLAT_MAP_OPTION,
|
||||
methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
methods::GET_FIRST,
|
||||
methods::GET_LAST_WITH_LEN,
|
||||
methods::GET_UNWRAP,
|
||||
methods::IMPLICIT_CLONE,
|
||||
|
@ -323,17 +319,23 @@ store.register_lints(&[
|
|||
methods::ITER_WITH_DRAIN,
|
||||
methods::MANUAL_FILTER_MAP,
|
||||
methods::MANUAL_FIND_MAP,
|
||||
methods::MANUAL_OK_OR,
|
||||
methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
methods::MANUAL_SPLIT_ONCE,
|
||||
methods::MANUAL_STR_REPEAT,
|
||||
methods::MAP_CLONE,
|
||||
methods::MAP_COLLECT_RESULT_UNIT,
|
||||
methods::MAP_ERR_IGNORE,
|
||||
methods::MAP_FLATTEN,
|
||||
methods::MAP_IDENTITY,
|
||||
methods::MAP_UNWRAP_OR,
|
||||
methods::MUT_MUTEX_LOCK,
|
||||
methods::NAIVE_BYTECOUNT,
|
||||
methods::NEEDLESS_OPTION_AS_DEREF,
|
||||
methods::NEEDLESS_OPTION_TAKE,
|
||||
methods::NEEDLESS_SPLITN,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::NONSENSICAL_OPEN_OPTIONS,
|
||||
methods::NO_EFFECT_REPLACE,
|
||||
methods::OBFUSCATED_IF_ELSE,
|
||||
methods::OK_EXPECT,
|
||||
|
@ -342,25 +344,33 @@ store.register_lints(&[
|
|||
methods::OPTION_MAP_OR_NONE,
|
||||
methods::OR_FUN_CALL,
|
||||
methods::OR_THEN_UNWRAP,
|
||||
methods::PATH_BUF_PUSH_OVERWRITE,
|
||||
methods::RANGE_ZIP_WITH_LEN,
|
||||
methods::REPEAT_ONCE,
|
||||
methods::RESULT_MAP_OR_INTO_OPTION,
|
||||
methods::SEARCH_IS_SOME,
|
||||
methods::SHOULD_IMPLEMENT_TRAIT,
|
||||
methods::SINGLE_CHAR_ADD_STR,
|
||||
methods::SINGLE_CHAR_PATTERN,
|
||||
methods::SKIP_WHILE_NEXT,
|
||||
methods::STABLE_SORT_PRIMITIVE,
|
||||
methods::STRING_EXTEND_CHARS,
|
||||
methods::SUSPICIOUS_MAP,
|
||||
methods::SUSPICIOUS_SPLITN,
|
||||
methods::UNINIT_ASSUMED_INIT,
|
||||
methods::UNIT_HASH,
|
||||
methods::UNNECESSARY_FILTER_MAP,
|
||||
methods::UNNECESSARY_FIND_MAP,
|
||||
methods::UNNECESSARY_FOLD,
|
||||
methods::UNNECESSARY_JOIN,
|
||||
methods::UNNECESSARY_LAZY_EVALUATIONS,
|
||||
methods::UNNECESSARY_SORT_BY,
|
||||
methods::UNNECESSARY_TO_OWNED,
|
||||
methods::UNWRAP_OR_ELSE_DEFAULT,
|
||||
methods::UNWRAP_USED,
|
||||
methods::USELESS_ASREF,
|
||||
methods::VEC_RESIZE_TO_ZERO,
|
||||
methods::VERBOSE_FILE_READS,
|
||||
methods::WRONG_SELF_CONVENTION,
|
||||
methods::ZST_OFFSET,
|
||||
minmax::MIN_MAX,
|
||||
|
@ -389,7 +399,6 @@ store.register_lints(&[
|
|||
module_style::SELF_NAMED_MODULE_FILES,
|
||||
mut_key::MUTABLE_KEY_TYPE,
|
||||
mut_mut::MUT_MUT,
|
||||
mut_mutex_lock::MUT_MUTEX_LOCK,
|
||||
mut_reference::UNNECESSARY_MUT_PASSED,
|
||||
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
mutex_atomic::MUTEX_ATOMIC,
|
||||
|
@ -421,7 +430,6 @@ store.register_lints(&[
|
|||
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
|
||||
octal_escapes::OCTAL_ESCAPES,
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
operators::ABSURD_EXTREME_COMPARISONS,
|
||||
operators::ARITHMETIC,
|
||||
operators::ASSIGN_OP_PATTERN,
|
||||
|
@ -460,7 +468,6 @@ store.register_lints(&[
|
|||
partialeq_to_none::PARTIALEQ_TO_NONE,
|
||||
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
|
||||
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
|
||||
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
precedence::PRECEDENCE,
|
||||
ptr::CMP_NULL,
|
||||
|
@ -473,7 +480,6 @@ store.register_lints(&[
|
|||
ranges::MANUAL_RANGE_CONTAINS,
|
||||
ranges::RANGE_MINUS_ONE,
|
||||
ranges::RANGE_PLUS_ONE,
|
||||
ranges::RANGE_ZIP_WITH_LEN,
|
||||
ranges::REVERSED_EMPTY_RANGES,
|
||||
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
|
||||
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
|
||||
|
@ -489,7 +495,6 @@ store.register_lints(&[
|
|||
reference::DEREF_ADDROF,
|
||||
regex::INVALID_REGEX,
|
||||
regex::TRIVIAL_REGEX,
|
||||
repeat_once::REPEAT_ONCE,
|
||||
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
|
||||
returns::LET_AND_RETURN,
|
||||
returns::NEEDLESS_RETURN,
|
||||
|
@ -504,7 +509,6 @@ store.register_lints(&[
|
|||
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
stable_sort_primitive::STABLE_SORT_PRIMITIVE,
|
||||
std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
|
||||
std_instead_of_core::STD_INSTEAD_OF_ALLOC,
|
||||
std_instead_of_core::STD_INSTEAD_OF_CORE,
|
||||
|
@ -540,10 +544,10 @@ store.register_lints(&[
|
|||
transmute::TRANSMUTE_PTR_TO_PTR,
|
||||
transmute::TRANSMUTE_PTR_TO_REF,
|
||||
transmute::TRANSMUTE_UNDEFINED_REPR,
|
||||
transmute::TRANSMUTING_NULL,
|
||||
transmute::UNSOUND_COLLECTION_TRANSMUTE,
|
||||
transmute::USELESS_TRANSMUTE,
|
||||
transmute::WRONG_TRANSMUTE,
|
||||
transmuting_null::TRANSMUTING_NULL,
|
||||
types::BORROWED_BOX,
|
||||
types::BOX_COLLECTION,
|
||||
types::LINKEDLIST,
|
||||
|
@ -558,7 +562,6 @@ store.register_lints(&[
|
|||
unicode::NON_ASCII_LITERAL,
|
||||
unicode::UNICODE_NOT_NFC,
|
||||
uninit_vec::UNINIT_VEC,
|
||||
unit_hash::UNIT_HASH,
|
||||
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
|
||||
unit_types::LET_UNIT_VALUE,
|
||||
unit_types::UNIT_ARG,
|
||||
|
@ -567,7 +570,6 @@ store.register_lints(&[
|
|||
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
|
||||
unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
unnecessary_wraps::UNNECESSARY_WRAPS,
|
||||
unnested_or_patterns::UNNESTED_OR_PATTERNS,
|
||||
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
|
@ -584,8 +586,6 @@ store.register_lints(&[
|
|||
useless_conversion::USELESS_CONVERSION,
|
||||
vec::USELESS_VEC,
|
||||
vec_init_then_push::VEC_INIT_THEN_PUSH,
|
||||
vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
|
||||
verbose_file_reads::VERBOSE_FILE_READS,
|
||||
wildcard_imports::ENUM_GLOB_USE,
|
||||
wildcard_imports::WILDCARD_IMPORTS,
|
||||
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
|
||||
|
|
|
@ -17,6 +17,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
|
||||
LintId::of(methods::ITER_ON_SINGLE_ITEMS),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
|
||||
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
|
@ -25,7 +26,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
|
||||
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
|
||||
LintId::of(regex::TRIVIAL_REGEX),
|
||||
LintId::of(strings::STRING_LIT_AS_BYTES),
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(attrs::INLINE_ALWAYS),
|
||||
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
|
||||
LintId::of(bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(casts::BORROW_AS_PTR),
|
||||
LintId::of(casts::CAST_LOSSLESS),
|
||||
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
|
||||
LintId::of(casts::CAST_POSSIBLE_WRAP),
|
||||
|
@ -50,20 +48,23 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(macro_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(manual_assert::MANUAL_ASSERT),
|
||||
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
|
||||
LintId::of(manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(matches::MATCH_BOOL),
|
||||
LintId::of(matches::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(matches::MATCH_SAME_ARMS),
|
||||
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
|
||||
LintId::of(matches::MATCH_WILD_ERR_ARM),
|
||||
LintId::of(matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
|
||||
LintId::of(methods::FILTER_MAP_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_OPTION),
|
||||
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(methods::IMPLICIT_CLONE),
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MANUAL_OK_OR),
|
||||
LintId::of(methods::MAP_UNWRAP_OR),
|
||||
LintId::of(methods::NAIVE_BYTECOUNT),
|
||||
LintId::of(methods::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(methods::UNNECESSARY_JOIN),
|
||||
LintId::of(misc::USED_UNDERSCORE_BINDING),
|
||||
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
|
||||
|
@ -85,7 +86,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
|
||||
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
|
||||
LintId::of(types::LINKEDLIST),
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(as_conversions::AS_CONVERSIONS),
|
||||
LintId::of(as_underscore::AS_UNDERSCORE),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
|
||||
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
|
||||
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
|
||||
LintId::of(casts::AS_UNDERSCORE),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
|
||||
LintId::of(create_dir::CREATE_DIR),
|
||||
LintId::of(dbg_macro::DBG_MACRO),
|
||||
|
@ -30,7 +30,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
|
||||
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
|
||||
LintId::of(matches::TRY_ERR),
|
||||
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
|
||||
|
@ -39,7 +38,9 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(methods::EXPECT_USED),
|
||||
LintId::of(methods::FILETYPE_IS_FILE),
|
||||
LintId::of(methods::GET_UNWRAP),
|
||||
LintId::of(methods::MAP_ERR_IGNORE),
|
||||
LintId::of(methods::UNWRAP_USED),
|
||||
LintId::of(methods::VERBOSE_FILE_READS),
|
||||
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
|
||||
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
|
||||
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
|
||||
|
@ -81,7 +82,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(unicode::NON_ASCII_LITERAL),
|
||||
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
|
||||
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
|
||||
LintId::of(write::PRINT_STDERR),
|
||||
LintId::of(write::PRINT_STDOUT),
|
||||
LintId::of(write::USE_DEBUG),
|
||||
|
|
|
@ -29,7 +29,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(functions::DOUBLE_MUST_USE),
|
||||
LintId::of(functions::MUST_USE_UNIT),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
|
||||
LintId::of(len_zero::COMPARISON_TO_EMPTY),
|
||||
|
@ -46,7 +45,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_empty_string_creations::MANUAL_EMPTY_STRING_CREATIONS),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
LintId::of(matches::COLLAPSIBLE_MATCH),
|
||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
|
@ -62,6 +60,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::ERR_EXPECT),
|
||||
LintId::of(methods::GET_FIRST),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
|
||||
LintId::of(methods::ITER_CLONED_COLLECT),
|
||||
|
@ -69,7 +68,9 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(methods::ITER_NTH_ZERO),
|
||||
LintId::of(methods::ITER_SKIP_NEXT),
|
||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MAP_CLONE),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MUT_MUTEX_LOCK),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
|
@ -89,7 +90,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
|
||||
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
|
||||
LintId::of(misc_early::REDUNDANT_PATTERN),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
|
||||
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
|
||||
|
|
|
@ -170,7 +170,6 @@ mod renamed_lints;
|
|||
mod almost_complete_letter_range;
|
||||
mod approx_const;
|
||||
mod as_conversions;
|
||||
mod as_underscore;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
mod assertions_on_result_states;
|
||||
|
@ -180,12 +179,8 @@ mod await_holding_invalid;
|
|||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod borrow_as_ptr;
|
||||
mod borrow_deref_ref;
|
||||
mod bytecount;
|
||||
mod bytes_count_to_len;
|
||||
mod cargo;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod casts;
|
||||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
|
@ -238,7 +233,6 @@ mod from_over_into;
|
|||
mod from_str_radix_10;
|
||||
mod functions;
|
||||
mod future_not_send;
|
||||
mod get_first;
|
||||
mod if_let_mutex;
|
||||
mod if_not_else;
|
||||
mod if_then_some_else_none;
|
||||
|
@ -276,12 +270,9 @@ mod manual_bits;
|
|||
mod manual_empty_string_creations;
|
||||
mod manual_instant_elapsed;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
mod manual_rem_euclid;
|
||||
mod manual_retain;
|
||||
mod manual_strip;
|
||||
mod map_clone;
|
||||
mod map_err_ignore;
|
||||
mod map_unit_fn;
|
||||
mod match_result_ok;
|
||||
mod matches;
|
||||
|
@ -300,7 +291,6 @@ mod mixed_read_write_in_expression;
|
|||
mod module_style;
|
||||
mod mut_key;
|
||||
mod mut_mut;
|
||||
mod mut_mutex_lock;
|
||||
mod mut_reference;
|
||||
mod mutable_debug_assertion;
|
||||
mod mutex_atomic;
|
||||
|
@ -325,7 +315,6 @@ mod non_send_fields_in_send_ty;
|
|||
mod nonstandard_macro_braces;
|
||||
mod octal_escapes;
|
||||
mod only_used_in_recursion;
|
||||
mod open_options;
|
||||
mod operators;
|
||||
mod option_env_unwrap;
|
||||
mod option_if_let_else;
|
||||
|
@ -335,7 +324,6 @@ mod panic_unimplemented;
|
|||
mod partialeq_ne_impl;
|
||||
mod partialeq_to_none;
|
||||
mod pass_by_ref_or_value;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
|
@ -355,7 +343,6 @@ mod redundant_static_lifetimes;
|
|||
mod ref_option_ref;
|
||||
mod reference;
|
||||
mod regex;
|
||||
mod repeat_once;
|
||||
mod return_self_not_must_use;
|
||||
mod returns;
|
||||
mod same_name_method;
|
||||
|
@ -367,7 +354,6 @@ mod single_char_lifetime_names;
|
|||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod slow_vector_initialization;
|
||||
mod stable_sort_primitive;
|
||||
mod std_instead_of_core;
|
||||
mod strings;
|
||||
mod strlen_on_c_strings;
|
||||
|
@ -381,18 +367,15 @@ mod to_digit_is_some;
|
|||
mod trailing_empty_array;
|
||||
mod trait_bounds;
|
||||
mod transmute;
|
||||
mod transmuting_null;
|
||||
mod types;
|
||||
mod undocumented_unsafe_blocks;
|
||||
mod unicode;
|
||||
mod uninit_vec;
|
||||
mod unit_hash;
|
||||
mod unit_return_expecting_ord;
|
||||
mod unit_types;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_wraps;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
|
@ -408,8 +391,6 @@ mod use_self;
|
|||
mod useless_conversion;
|
||||
mod vec;
|
||||
mod vec_init_then_push;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
|
@ -597,7 +578,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
|
||||
store.register_late_pass(|| Box::new(unicode::Unicode));
|
||||
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
|
||||
store.register_late_pass(|| Box::new(unit_hash::UnitHash));
|
||||
store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
|
||||
store.register_late_pass(|| Box::new(strings::StringAdd));
|
||||
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
|
||||
|
@ -635,8 +615,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
|
||||
store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
|
||||
|
||||
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
|
||||
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
|
||||
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
|
||||
|
@ -646,7 +624,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
msrv,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
|
||||
store.register_late_pass(|| Box::new(shadow::Shadow::default()));
|
||||
store.register_late_pass(|| Box::new(unit_types::UnitTypes));
|
||||
store.register_late_pass(|| Box::new(loops::Loops));
|
||||
|
@ -654,7 +631,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
|
||||
store.register_late_pass(|| Box::new(entry::HashMapPass));
|
||||
store.register_late_pass(|| Box::new(minmax::MinMaxPass));
|
||||
store.register_late_pass(|| Box::new(open_options::OpenOptions));
|
||||
store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
|
||||
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
|
||||
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
|
||||
|
@ -720,7 +696,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
);
|
||||
store.register_late_pass(move || Box::new(pass_by_ref_or_value));
|
||||
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
|
||||
store.register_late_pass(|| Box::new(bytecount::ByteCount));
|
||||
store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
|
||||
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
|
||||
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
|
||||
|
@ -738,12 +713,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
|
||||
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
|
||||
store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
|
||||
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
|
||||
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
|
||||
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
|
||||
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
|
||||
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
|
||||
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
|
||||
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
|
||||
|
@ -819,7 +791,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
|
||||
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
|
||||
store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||
store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
|
||||
store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
|
||||
store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
|
||||
store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
|
||||
|
@ -828,9 +799,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
|
||||
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
|
||||
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
|
||||
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
|
||||
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
|
||||
store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
|
||||
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
store.register_early_pass(move || {
|
||||
|
@ -842,10 +811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
|
||||
store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
|
||||
store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
|
||||
store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
|
||||
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
|
||||
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
|
||||
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
|
||||
let disallowed_methods = conf.disallowed_methods.clone();
|
||||
|
@ -857,9 +823,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(strings::StringToString));
|
||||
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
|
||||
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
|
||||
store.register_late_pass(|| {
|
||||
Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
|
||||
});
|
||||
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
|
||||
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
|
||||
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
|
||||
|
@ -894,7 +857,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
|
||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
|
||||
|
@ -912,18 +874,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
||||
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
||||
store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
|
||||
store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
|
||||
let max_include_file_size = conf.max_include_file_size;
|
||||
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
|
||||
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
|
||||
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
|
||||
store.register_late_pass(|| Box::new(get_first::GetFirst));
|
||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
||||
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
|
||||
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
|
||||
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
||||
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Finds patterns that reimplement `Option::ok_or`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Concise code helps focusing on behavior instead of boilerplate.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.ok_or("error");
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_OK_OR,
|
||||
pedantic,
|
||||
"finds patterns that can be encoded more concisely with `Option::ok_or`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), scrutinee.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
|
||||
if method_segment.ident.name == sym!(map_or);
|
||||
let ty = cx.typeck_results().expr_ty(receiver);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, scrutinee.span);
|
||||
then {
|
||||
let reindented_err_arg_snippet =
|
||||
reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
scrutinee.span,
|
||||
"this pattern reimplements `Option::ok_or`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.ok_or({})",
|
||||
method_receiver_snippet,
|
||||
reindented_err_arg_snippet
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `map(|x| x.clone())` or
|
||||
/// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
|
||||
/// and suggests `cloned()` or `copied()` instead
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability, this can be written more concisely
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.map(|i| *i);
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.cloned();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MAP_CLONE,
|
||||
style,
|
||||
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
|
||||
}
|
||||
|
||||
pub struct MapClone {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(MapClone => [MAP_CLONE]);
|
||||
|
||||
impl MapClone {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MapClone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
|
||||
if args.len() == 2;
|
||||
if method.ident.name == sym::map;
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind;
|
||||
then {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::Unannotated, .., name, None
|
||||
) = inner.kind {
|
||||
if ident_eq(name, closure_expr) {
|
||||
self.lint_explicit_closure(cx, e.span, args[0].span, true);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||
if ident_eq(name, inner) {
|
||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
self.lint_explicit_closure(cx, e.span, args[0].span, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
|
||||
if ident_eq(name, obj) && method.ident.name == sym::clone;
|
||||
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
|
||||
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
|
||||
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
|
||||
// no autoderefs
|
||||
if !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
|
||||
then {
|
||||
let obj_ty = cx.typeck_results().expr_ty(obj);
|
||||
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
||||
if matches!(mutability, Mutability::Not) {
|
||||
let copy = is_copy(cx, *ty);
|
||||
self.lint_explicit_closure(cx, e.span, args[0].span, copy);
|
||||
}
|
||||
} else {
|
||||
lint_needless_cloning(cx, e.span, args[0].span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
||||
path.segments.len() == 1 && path.segments[0].ident == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
root.trim_start(receiver).unwrap(),
|
||||
"you are needlessly cloning iterator elements",
|
||||
"remove the `map` call",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
impl MapClone {
|
||||
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
replace,
|
||||
message,
|
||||
&format!("consider calling the dedicated `{}` method", sugg_method),
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||
sugg_method,
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for instances of `map_err(|_| Some::Enum)`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
|
||||
///
|
||||
/// ### Example
|
||||
/// Before:
|
||||
/// ```rust
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum Error {
|
||||
/// Indivisible,
|
||||
/// Remainder(u8),
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for Error {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// match self {
|
||||
/// Error::Indivisible => write!(f, "could not divide input by three"),
|
||||
/// Error::Remainder(remainder) => write!(
|
||||
/// f,
|
||||
/// "input is not divisible by three, remainder = {}",
|
||||
/// remainder
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::error::Error for Error {}
|
||||
///
|
||||
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
|
||||
/// input
|
||||
/// .parse::<i32>()
|
||||
/// .map_err(|_| Error::Indivisible)
|
||||
/// .map(|v| v % 3)
|
||||
/// .and_then(|remainder| {
|
||||
/// if remainder == 0 {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Remainder(remainder as u8))
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// After:
|
||||
/// ```rust
|
||||
/// use std::{fmt, num::ParseIntError};
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum Error {
|
||||
/// Indivisible(ParseIntError),
|
||||
/// Remainder(u8),
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for Error {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// match self {
|
||||
/// Error::Indivisible(_) => write!(f, "could not divide input by three"),
|
||||
/// Error::Remainder(remainder) => write!(
|
||||
/// f,
|
||||
/// "input is not divisible by three, remainder = {}",
|
||||
/// remainder
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::error::Error for Error {
|
||||
/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
/// match self {
|
||||
/// Error::Indivisible(source) => Some(source),
|
||||
/// _ => None,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
|
||||
/// input
|
||||
/// .parse::<i32>()
|
||||
/// .map_err(Error::Indivisible)
|
||||
/// .map(|v| v % 3)
|
||||
/// .and_then(|remainder| {
|
||||
/// if remainder == 0 {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Remainder(remainder as u8))
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub MAP_ERR_IGNORE,
|
||||
restriction,
|
||||
"`map_err` should not ignore the original error"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
|
||||
// do not try to lint if this is from a macro or desugaring
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if this is a method call (e.g. x.foo())
|
||||
if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
|
||||
// only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
|
||||
// Enum::Variant[2]))
|
||||
if method.ident.name == sym!(map_err) {
|
||||
// make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
|
||||
// fields
|
||||
if let ExprKind::Closure(&Closure {
|
||||
capture_clause,
|
||||
body,
|
||||
fn_decl_span,
|
||||
..
|
||||
}) = arg.kind
|
||||
{
|
||||
// check if this is by Reference (meaning there's no move statement)
|
||||
if capture_clause == CaptureBy::Ref {
|
||||
// Get the closure body to check the parameters and values
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
// make sure there's only one parameter (`|_|`)
|
||||
if closure_body.params.len() == 1 {
|
||||
// make sure that parameter is the wild token (`_`)
|
||||
if let PatKind::Wild = closure_body.params[0].pat.kind {
|
||||
// span the area of the closure capture and warn that the
|
||||
// original error will be thrown away
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
fn_decl_span,
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
clippy_lints/src/methods/bytecount.rs
Normal file
70
clippy_lints/src/methods/bytecount.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::NAIVE_BYTECOUNT;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
filter_recv: &'tcx Expr<'_>,
|
||||
filter_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, peel_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, args, _) =
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
filter_recv
|
||||
}
|
||||
} else {
|
||||
filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NAIVE_BYTECOUNT,
|
||||
expr.span,
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
37
clippy_lints/src/methods/bytes_count_to_len.rs
Normal file
37
clippy_lints/src/methods/bytes_count_to_len.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::BYTES_COUNT_TO_LEN;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
count_recv: &'tcx hir::Expr<'_>,
|
||||
bytes_recv: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
|
||||
if cx.tcx.type_of(impl_id).is_str();
|
||||
let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
|
||||
if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{source_map::Spanned, symbol::sym, Span};
|
||||
|
||||
use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
call_span: Span,
|
||||
recv: &'tcx Expr<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_str();
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
|
||||
if (2..=6).contains(&ext_literal.as_str().len());
|
||||
let ext_str = ext_literal.as_str();
|
||||
if ext_str.starts_with('.');
|
||||
if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
call_span,
|
||||
"case-sensitive file extension comparison",
|
||||
None,
|
||||
"consider using a case-insensitive comparison instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
39
clippy_lints/src/methods/get_first.rs
Normal file
39
clippy_lints/src/methods/get_first.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_slice_of_primitives;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::GET_FIRST;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_slice();
|
||||
if let Some(_) = is_slice_of_primitives(cx, recv);
|
||||
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
|
||||
then {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_FIRST,
|
||||
expr.span,
|
||||
&format!("accessing first element with `{0}.get(0)`", slice_name),
|
||||
"try",
|
||||
format!("{}.first()", slice_name),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
64
clippy_lints/src/methods/manual_ok_or.rs
Normal file
64
clippy_lints/src/methods/manual_ok_or.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::MANUAL_OK_OR;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
or_expr: &'tcx Expr<'_>,
|
||||
map_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let Some(recv_snippet) = snippet_opt(cx, recv.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
then {
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
expr.span,
|
||||
"this pattern reimplements `Option::ok_or`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.ok_or({})",
|
||||
recv_snippet,
|
||||
reindented_err_arg_snippet
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
}
|
||||
}
|
122
clippy_lints/src/methods/map_clone.rs
Normal file
122
clippy_lints/src/methods/map_clone.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MAP_CLONE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'_>,
|
||||
e: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
||||
if cx.tcx.impl_of_method(method_id)
|
||||
.map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
|
||||
|| is_diag_trait_item(cx, method_id, sym::Iterator);
|
||||
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
|
||||
then {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::Unannotated, .., name, None
|
||||
) = inner.kind {
|
||||
if ident_eq(name, closure_expr) {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||
if ident_eq(name, inner) {
|
||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
|
||||
if ident_eq(name, obj) && method.ident.name == sym::clone;
|
||||
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
|
||||
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
|
||||
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
|
||||
// no autoderefs
|
||||
if !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
|
||||
then {
|
||||
let obj_ty = cx.typeck_results().expr_ty(obj);
|
||||
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
||||
if matches!(mutability, Mutability::Not) {
|
||||
let copy = is_copy(cx, *ty);
|
||||
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
|
||||
}
|
||||
} else {
|
||||
lint_needless_cloning(cx, e.span, recv.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
||||
path.segments.len() == 1 && path.segments[0].ident == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
root.trim_start(receiver).unwrap(),
|
||||
"you are needlessly cloning iterator elements",
|
||||
"remove the `map` call",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
replace,
|
||||
message,
|
||||
&format!("consider calling the dedicated `{}` method", sugg_method),
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||
sugg_method,
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
34
clippy_lints/src/methods/map_err_ignore.rs
Normal file
34
clippy_lints/src/methods/map_err_ignore.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MAP_ERR_IGNORE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
|
||||
&& let ExprKind::Closure(&Closure {
|
||||
capture_clause: CaptureBy::Ref,
|
||||
body,
|
||||
fn_decl_span,
|
||||
..
|
||||
}) = arg.kind
|
||||
&& let closure_body = cx.tcx.hir().body(body)
|
||||
&& let [param] = closure_body.params
|
||||
&& let PatKind::Wild = param.pat.kind
|
||||
{
|
||||
// span the area of the closure capture and warn that the
|
||||
// original error will be thrown away
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
fn_decl_span,
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
mod bind_instead_of_map;
|
||||
mod bytecount;
|
||||
mod bytes_count_to_len;
|
||||
mod bytes_nth;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod chars_cmp;
|
||||
mod chars_cmp_with_unwrap;
|
||||
mod chars_last_cmp;
|
||||
|
@ -21,6 +24,7 @@ mod filter_next;
|
|||
mod flat_map_identity;
|
||||
mod flat_map_option;
|
||||
mod from_iter_instead_of_collect;
|
||||
mod get_first;
|
||||
mod get_last_with_len;
|
||||
mod get_unwrap;
|
||||
mod implicit_clone;
|
||||
|
@ -38,43 +42,56 @@ mod iter_overeager_cloned;
|
|||
mod iter_skip_next;
|
||||
mod iter_with_drain;
|
||||
mod iterator_step_by_zero;
|
||||
mod manual_ok_or;
|
||||
mod manual_saturating_arithmetic;
|
||||
mod manual_str_repeat;
|
||||
mod map_clone;
|
||||
mod map_collect_result_unit;
|
||||
mod map_err_ignore;
|
||||
mod map_flatten;
|
||||
mod map_identity;
|
||||
mod map_unwrap_or;
|
||||
mod mut_mutex_lock;
|
||||
mod needless_option_as_deref;
|
||||
mod needless_option_take;
|
||||
mod no_effect_replace;
|
||||
mod obfuscated_if_else;
|
||||
mod ok_expect;
|
||||
mod open_options;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_none;
|
||||
mod option_map_unwrap_or;
|
||||
mod or_fun_call;
|
||||
mod or_then_unwrap;
|
||||
mod path_buf_push_overwrite;
|
||||
mod range_zip_with_len;
|
||||
mod repeat_once;
|
||||
mod search_is_some;
|
||||
mod single_char_add_str;
|
||||
mod single_char_insert_string;
|
||||
mod single_char_pattern;
|
||||
mod single_char_push_string;
|
||||
mod skip_while_next;
|
||||
mod stable_sort_primitive;
|
||||
mod str_splitn;
|
||||
mod string_extend_chars;
|
||||
mod suspicious_map;
|
||||
mod suspicious_splitn;
|
||||
mod uninit_assumed_init;
|
||||
mod unit_hash;
|
||||
mod unnecessary_filter_map;
|
||||
mod unnecessary_fold;
|
||||
mod unnecessary_iter_cloned;
|
||||
mod unnecessary_join;
|
||||
mod unnecessary_lazy_eval;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_to_owned;
|
||||
mod unwrap_or_else_default;
|
||||
mod unwrap_used;
|
||||
mod useless_asref;
|
||||
mod utils;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
mod wrong_self_convention;
|
||||
mod zst_offset;
|
||||
|
||||
|
@ -82,7 +99,9 @@ use bind_instead_of_map::BindInsteadOfMap;
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
|
||||
use clippy_utils::{
|
||||
contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
|
@ -2368,6 +2387,577 @@ declare_clippy_lint! {
|
|||
"Iterator for empty array"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for naive byte counts
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The [`bytecount`](https://crates.io/crates/bytecount)
|
||||
/// crate has methods to count your bytes faster, especially for large slices.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If you have predominantly small slices, the
|
||||
/// `bytecount::count(..)` method may actually be slower. However, if you can
|
||||
/// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
|
||||
/// faster in those cases.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = vec.iter().filter(|x| **x == 0u8).count();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = bytecount::count(&vec, 0u8);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NAIVE_BYTECOUNT,
|
||||
pedantic,
|
||||
"use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// It checks for `str::bytes().count()` and suggests replacing it with
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `str::bytes().count()` is longer and may not be as performant as using
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// "hello".bytes().count();
|
||||
/// String::from("hello").bytes().count();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// "hello".len();
|
||||
/// String::from("hello").len();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub BYTES_COUNT_TO_LEN,
|
||||
complexity,
|
||||
"Using `bytes().count()` when `len()` performs the same functionality"
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.ends_with(".rs")
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// let filename = std::path::Path::new(filename);
|
||||
/// filename.extension()
|
||||
/// .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.51.0"]
|
||||
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
pedantic,
|
||||
"Checks for calls to ends_with with case-sensitive file extensions"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for using `x.get(0)` instead of
|
||||
/// `x.first()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `x.first()` is easier to read and has the same
|
||||
/// result.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.get(0);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.first();
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub GET_FIRST,
|
||||
style,
|
||||
"Using `x.get(0)` when `x.first()` is simpler"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Finds patterns that reimplement `Option::ok_or`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Concise code helps focusing on behavior instead of boilerplate.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.ok_or("error");
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_OK_OR,
|
||||
pedantic,
|
||||
"finds patterns that can be encoded more concisely with `Option::ok_or`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `map(|x| x.clone())` or
|
||||
/// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
|
||||
/// and suggests `cloned()` or `copied()` instead
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability, this can be written more concisely
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.map(|i| *i);
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.cloned();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MAP_CLONE,
|
||||
style,
|
||||
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for instances of `map_err(|_| Some::Enum)`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
|
||||
///
|
||||
/// ### Example
|
||||
/// Before:
|
||||
/// ```rust
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum Error {
|
||||
/// Indivisible,
|
||||
/// Remainder(u8),
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for Error {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// match self {
|
||||
/// Error::Indivisible => write!(f, "could not divide input by three"),
|
||||
/// Error::Remainder(remainder) => write!(
|
||||
/// f,
|
||||
/// "input is not divisible by three, remainder = {}",
|
||||
/// remainder
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::error::Error for Error {}
|
||||
///
|
||||
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
|
||||
/// input
|
||||
/// .parse::<i32>()
|
||||
/// .map_err(|_| Error::Indivisible)
|
||||
/// .map(|v| v % 3)
|
||||
/// .and_then(|remainder| {
|
||||
/// if remainder == 0 {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Remainder(remainder as u8))
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// After:
|
||||
/// ```rust
|
||||
/// use std::{fmt, num::ParseIntError};
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum Error {
|
||||
/// Indivisible(ParseIntError),
|
||||
/// Remainder(u8),
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for Error {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// match self {
|
||||
/// Error::Indivisible(_) => write!(f, "could not divide input by three"),
|
||||
/// Error::Remainder(remainder) => write!(
|
||||
/// f,
|
||||
/// "input is not divisible by three, remainder = {}",
|
||||
/// remainder
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::error::Error for Error {
|
||||
/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
/// match self {
|
||||
/// Error::Indivisible(source) => Some(source),
|
||||
/// _ => None,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
|
||||
/// input
|
||||
/// .parse::<i32>()
|
||||
/// .map_err(Error::Indivisible)
|
||||
/// .map(|v| v % 3)
|
||||
/// .and_then(|remainder| {
|
||||
/// if remainder == 0 {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Remainder(remainder as u8))
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub MAP_ERR_IGNORE,
|
||||
restriction,
|
||||
"`map_err` should not ignore the original error"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `&mut Mutex::lock` calls
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Mutex::lock` is less efficient than
|
||||
/// calling `Mutex::get_mut`. In addition you also have a statically
|
||||
/// guarantee that the mutex isn't locked, instead of just a runtime
|
||||
/// guarantee.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
|
||||
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
|
||||
///
|
||||
/// let mut value = value_mutex.lock().unwrap();
|
||||
/// *value += 1;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
|
||||
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
|
||||
///
|
||||
/// let value = value_mutex.get_mut().unwrap();
|
||||
/// *value += 1;
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MUT_MUTEX_LOCK,
|
||||
style,
|
||||
"`&mut Mutex::lock` does unnecessary locking"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for duplicate open options as well as combinations
|
||||
/// that make no sense.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In the best case, the code will be harder to read than
|
||||
/// necessary. I don't know the worst case.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::fs::OpenOptions;
|
||||
///
|
||||
/// OpenOptions::new().read(true).truncate(true);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NONSENSICAL_OPEN_OPTIONS,
|
||||
correctness,
|
||||
"nonsensical combination of options for opening a file"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
|
||||
/// calls on `PathBuf` that can cause overwrites.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calling `push` with a root path at the start can overwrite the
|
||||
/// previous defined path.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let mut x = PathBuf::from("/foo");
|
||||
/// x.push("/bar");
|
||||
/// assert_eq!(x, PathBuf::from("/bar"));
|
||||
/// ```
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let mut x = PathBuf::from("/foo");
|
||||
/// x.push("bar");
|
||||
/// assert_eq!(x, PathBuf::from("/foo/bar"));
|
||||
/// ```
|
||||
#[clippy::version = "1.36.0"]
|
||||
pub PATH_BUF_PUSH_OVERWRITE,
|
||||
nursery,
|
||||
"calling `push` with file system root on `PathBuf` can overwrite it"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for zipping a collection with the range of
|
||||
/// `0.._.len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The code is better expressed with `.enumerate()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = vec![1];
|
||||
/// let _ = x.iter().zip(0..x.len());
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = vec![1];
|
||||
/// let _ = x.iter().enumerate();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub RANGE_ZIP_WITH_LEN,
|
||||
complexity,
|
||||
"zipping iterator with a range when `enumerate()` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.repeat(1)` and suggest the following method for each types.
|
||||
/// - `.to_string()` for `str`
|
||||
/// - `.clone()` for `String`
|
||||
/// - `.to_vec()` for `slice`
|
||||
///
|
||||
/// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
|
||||
/// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
|
||||
/// the string is the intention behind this, `clone()` should be used.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").repeat(1);
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").clone();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub REPEAT_ONCE,
|
||||
complexity,
|
||||
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// When sorting primitive values (integers, bools, chars, as well
|
||||
/// as arrays, slices, and tuples of such items), it is typically better to
|
||||
/// use an unstable sort than a stable sort.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Typically, using a stable sort consumes more memory and cpu cycles.
|
||||
/// Because values which compare equal are identical, preserving their
|
||||
/// relative order (the guarantee that a stable sort provides) means
|
||||
/// nothing, while the extra costs still apply.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// As pointed out in
|
||||
/// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
|
||||
/// a stable sort can instead be significantly faster for certain scenarios
|
||||
/// (eg. when a sorted vector is extended with new data and resorted).
|
||||
///
|
||||
/// For more information and benchmarking results, please refer to the
|
||||
/// issue linked above.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort_unstable();
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub STABLE_SORT_PRIMITIVE,
|
||||
pedantic,
|
||||
"use of sort() when sort_unstable() is equivalent"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects `().hash(_)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::hash::Hash;
|
||||
/// # use std::collections::hash_map::DefaultHasher;
|
||||
/// # enum Foo { Empty, WithValue(u8) }
|
||||
/// # use Foo::*;
|
||||
/// # let mut state = DefaultHasher::new();
|
||||
/// # let my_enum = Foo::Empty;
|
||||
/// match my_enum {
|
||||
/// Empty => ().hash(&mut state),
|
||||
/// WithValue(x) => x.hash(&mut state),
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::hash::Hash;
|
||||
/// # use std::collections::hash_map::DefaultHasher;
|
||||
/// # enum Foo { Empty, WithValue(u8) }
|
||||
/// # use Foo::*;
|
||||
/// # let mut state = DefaultHasher::new();
|
||||
/// # let my_enum = Foo::Empty;
|
||||
/// match my_enum {
|
||||
/// Empty => 0_u8.hash(&mut state),
|
||||
/// WithValue(x) => x.hash(&mut state),
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
pub UNIT_HASH,
|
||||
correctness,
|
||||
"hashing a unit value, which does nothing"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects uses of `Vec::sort_by` passing in a closure
|
||||
/// which compares the two arguments, either directly or indirectly.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
|
||||
/// possible) than to use `Vec::sort_by` and a more complicated
|
||||
/// closure.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
|
||||
/// imported by a use statement, then it will need to be added manually.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by_key(|a| a.foo());
|
||||
/// ```
|
||||
#[clippy::version = "1.46.0"]
|
||||
pub UNNECESSARY_SORT_BY,
|
||||
complexity,
|
||||
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds occurrences of `Vec::resize(0, an_int)`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably an argument inversion mistake.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// vec!(1, 2, 3, 4, 5).resize(0, 5)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// vec!(1, 2, 3, 4, 5).clear()
|
||||
/// ```
|
||||
#[clippy::version = "1.46.0"]
|
||||
pub VEC_RESIZE_TO_ZERO,
|
||||
correctness,
|
||||
"emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for use of File::read_to_end and File::read_to_string.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
|
||||
/// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// # use std::io::Read;
|
||||
/// # use std::fs::File;
|
||||
/// let mut f = File::open("foo.txt").unwrap();
|
||||
/// let mut bytes = Vec::new();
|
||||
/// f.read_to_end(&mut bytes).unwrap();
|
||||
/// ```
|
||||
/// Can be written more concisely as
|
||||
/// ```rust,no_run
|
||||
/// # use std::fs;
|
||||
/// let mut bytes = fs::read("foo.txt").unwrap();
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
pub VERBOSE_FILE_READS,
|
||||
restriction,
|
||||
"use of `File::read_to_end` or `File::read_to_string`"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
|
@ -2471,7 +3061,24 @@ impl_lint_pass!(Methods => [
|
|||
NO_EFFECT_REPLACE,
|
||||
OBFUSCATED_IF_ELSE,
|
||||
ITER_ON_SINGLE_ITEMS,
|
||||
ITER_ON_EMPTY_COLLECTIONS
|
||||
ITER_ON_EMPTY_COLLECTIONS,
|
||||
NAIVE_BYTECOUNT,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
GET_FIRST,
|
||||
MANUAL_OK_OR,
|
||||
MAP_CLONE,
|
||||
MAP_ERR_IGNORE,
|
||||
MUT_MUTEX_LOCK,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
REPEAT_ONCE,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
UNIT_HASH,
|
||||
UNNECESSARY_SORT_BY,
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
VERBOSE_FILE_READS,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
@ -2726,17 +3333,24 @@ impl Methods {
|
|||
},
|
||||
_ => {},
|
||||
},
|
||||
("count", []) => match method_call(recv) {
|
||||
("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
|
||||
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
|
||||
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
|
||||
iter_count::check(cx, expr, recv2, name2);
|
||||
},
|
||||
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
|
||||
Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg),
|
||||
Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
|
||||
_ => {},
|
||||
},
|
||||
("drain", [arg]) => {
|
||||
iter_with_drain::check(cx, expr, recv, span, arg);
|
||||
},
|
||||
("ends_with", [arg]) => {
|
||||
if let ExprKind::MethodCall(_, _, span) = expr.kind {
|
||||
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
|
||||
}
|
||||
},
|
||||
("expect", [_]) => match method_call(recv) {
|
||||
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
|
||||
Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
|
||||
|
@ -2769,8 +3383,14 @@ impl Methods {
|
|||
inspect_for_each::check(cx, expr, span2);
|
||||
}
|
||||
},
|
||||
("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
|
||||
("get", [arg]) => {
|
||||
get_first::check(cx, expr, recv, arg);
|
||||
get_last_with_len::check(cx, expr, recv, arg);
|
||||
},
|
||||
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
|
||||
("hash", [arg]) => {
|
||||
unit_hash::check(cx, expr, recv, arg);
|
||||
},
|
||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
|
||||
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||
|
@ -2790,7 +3410,15 @@ impl Methods {
|
|||
}
|
||||
}
|
||||
},
|
||||
("lock", []) => {
|
||||
mut_mutex_lock::check(cx, expr, recv, span);
|
||||
},
|
||||
(name @ ("map" | "map_err"), [m_arg]) => {
|
||||
if name == "map" {
|
||||
map_clone::check(cx, expr, recv, m_arg, self.msrv);
|
||||
} else {
|
||||
map_err_ignore::check(cx, expr, m_arg);
|
||||
}
|
||||
if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
|
||||
match (name, args) {
|
||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
|
||||
|
@ -2806,7 +3434,10 @@ impl Methods {
|
|||
}
|
||||
map_identity::check(cx, expr, recv, m_arg, name, span);
|
||||
},
|
||||
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
||||
("map_or", [def, map]) => {
|
||||
option_map_or_none::check(cx, expr, recv, def, map);
|
||||
manual_ok_or::check(cx, expr, recv, def, map);
|
||||
},
|
||||
("next", []) => {
|
||||
if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
|
||||
match (name2, args2) {
|
||||
|
@ -2828,11 +3459,38 @@ impl Methods {
|
|||
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
|
||||
},
|
||||
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
|
||||
("open", [_]) => {
|
||||
open_options::check(cx, expr, recv);
|
||||
},
|
||||
("or_else", [arg]) => {
|
||||
if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
|
||||
}
|
||||
},
|
||||
("push", [arg]) => {
|
||||
path_buf_push_overwrite::check(cx, expr, arg);
|
||||
},
|
||||
("read_to_end", [_]) => {
|
||||
verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
|
||||
},
|
||||
("read_to_string", [_]) => {
|
||||
verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
|
||||
},
|
||||
("repeat", [arg]) => {
|
||||
repeat_once::check(cx, expr, recv, arg);
|
||||
},
|
||||
("resize", [count_arg, default_arg]) => {
|
||||
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
|
||||
},
|
||||
("sort", []) => {
|
||||
stable_sort_primitive::check(cx, expr, recv);
|
||||
},
|
||||
("sort_by", [arg]) => {
|
||||
unnecessary_sort_by::check(cx, expr, recv, arg, false);
|
||||
},
|
||||
("sort_unstable_by", [arg]) => {
|
||||
unnecessary_sort_by::check(cx, expr, recv, arg, true);
|
||||
},
|
||||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
|
@ -2901,6 +3559,13 @@ impl Methods {
|
|||
("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
|
||||
no_effect_replace::check(cx, expr, arg1, arg2);
|
||||
},
|
||||
("zip", [arg]) => {
|
||||
if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind
|
||||
&& name.ident.name == sym::iter
|
||||
{
|
||||
range_zip_with_len::check(cx, expr, iter_recv, arg);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
30
clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
30
clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MUT_MUTEX_LOCK;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MUT_MUTEX_LOCK,
|
||||
name_span,
|
||||
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
|
||||
"change this to",
|
||||
"get_mut".to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,43 +3,19 @@ use clippy_utils::paths;
|
|||
use clippy_utils::ty::match_type;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for duplicate open options as well as combinations
|
||||
/// that make no sense.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In the best case, the code will be harder to read than
|
||||
/// necessary. I don't know the worst case.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::fs::OpenOptions;
|
||||
///
|
||||
/// OpenOptions::new().read(true).truncate(true);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NONSENSICAL_OPEN_OPTIONS,
|
||||
correctness,
|
||||
"nonsensical combination of options for opening a file"
|
||||
}
|
||||
use super::NONSENSICAL_OPEN_OPTIONS;
|
||||
|
||||
declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for OpenOptions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &e.kind {
|
||||
let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
|
||||
let mut options = Vec::new();
|
||||
get_open_options(cx, self_arg, &mut options);
|
||||
check_open_options(cx, &options, e.span);
|
||||
}
|
||||
}
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
|
||||
{
|
||||
let mut options = Vec::new();
|
||||
get_open_options(cx, recv, &mut options);
|
||||
check_open_options(cx, &options, e.span);
|
||||
}
|
||||
}
|
||||
|
37
clippy_lints/src/methods/path_buf_push_overwrite.rs
Normal file
37
clippy_lints/src/methods/path_buf_push_overwrite.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::path::{Component, Path};
|
||||
|
||||
use super::PATH_BUF_PUSH_OVERWRITE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
if let Some(pushed_path_lit) = pushed_path.to_str();
|
||||
if pushed_path.has_root();
|
||||
if let Some(root) = pushed_path.components().next();
|
||||
if root == Component::RootDir;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
lit.span,
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
34
clippy_lints/src/methods/range_zip_with_len.rs
Normal file
34
clippy_lints/src/methods/range_zip_with_len.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use clippy_utils::{is_integer_const, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::RANGE_ZIP_WITH_LEN;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, expr, sym::Iterator);
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
|
||||
if len_path.ident.name == sym::len;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, recv.span, "_"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
52
clippy_lints/src/methods/repeat_once.rs
Normal file
52
clippy_lints/src/methods/repeat_once.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::REPEAT_ONCE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
repeat_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on str",
|
||||
"consider using `.to_string()` instead",
|
||||
format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ty.builtin_index().is_some() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on slice",
|
||||
"consider using `.to_vec()` instead",
|
||||
format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::String) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on a string literal",
|
||||
"consider using `.clone()` instead",
|
||||
format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
31
clippy_lints/src/methods/stable_sort_primitive.rs
Normal file
31
clippy_lints/src/methods/stable_sort_primitive.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_slice_of_primitives;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::STABLE_SORT_PRIMITIVE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& cx.tcx.type_of(impl_id).is_slice()
|
||||
&& let Some(slice_type) = is_slice_of_primitives(cx, recv)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
e.span,
|
||||
&format!("used `sort` on primitive type `{}`", slice_type),
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
|
||||
diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
|
||||
diag.note(
|
||||
"an unstable sort typically performs faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
29
clippy_lints/src/methods/unit_hash.rs
Normal file
29
clippy_lints/src/methods/unit_hash.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::UNIT_HASH;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNIT_HASH,
|
||||
expr.span,
|
||||
"this call to `hash` on the unit type will do nothing",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"remove the call to `hash` or consider using",
|
||||
format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note("the implementation of `Hash` for `()` is a no-op");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,51 +1,17 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects uses of `Vec::sort_by` passing in a closure
|
||||
/// which compares the two arguments, either directly or indirectly.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
|
||||
/// possible) than to use `Vec::sort_by` and a more complicated
|
||||
/// closure.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
|
||||
/// imported by a use statement, then it will need to be added manually.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by_key(|a| a.foo());
|
||||
/// ```
|
||||
#[clippy::version = "1.46.0"]
|
||||
pub UNNECESSARY_SORT_BY,
|
||||
complexity,
|
||||
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
|
||||
use super::UNNECESSARY_SORT_BY;
|
||||
|
||||
enum LintTrigger {
|
||||
Sort(SortDetection),
|
||||
|
@ -54,7 +20,6 @@ enum LintTrigger {
|
|||
|
||||
struct SortDetection {
|
||||
vec_name: String,
|
||||
unstable: bool,
|
||||
}
|
||||
|
||||
struct SortByKeyDetection {
|
||||
|
@ -62,7 +27,6 @@ struct SortByKeyDetection {
|
|||
closure_arg: String,
|
||||
closure_body: String,
|
||||
reverse: bool,
|
||||
unstable: bool,
|
||||
}
|
||||
|
||||
/// Detect if the two expressions are mirrored (identical, except one
|
||||
|
@ -150,20 +114,20 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
|
|||
}
|
||||
}
|
||||
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
|
||||
if let name = name_ident.ident.name.to_ident_string();
|
||||
if name == "sort_by" || name == "sort_unstable_by";
|
||||
if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
|
||||
if let closure_body = cx.tcx.hir().body(*closure_body_id);
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_slice();
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
|
||||
if let closure_body = cx.tcx.hir().body(body);
|
||||
if let &[
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
|
||||
] = &closure_body.params;
|
||||
if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
|
||||
if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
|
||||
if method_path.ident.name == sym::cmp;
|
||||
if is_trait_method(cx, &closure_body.value, sym::Ord);
|
||||
then {
|
||||
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
|
||||
left_expr,
|
||||
|
@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
|||
} else {
|
||||
return None;
|
||||
};
|
||||
let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
|
||||
let unstable = name == "sort_unstable_by";
|
||||
let vec_name = Sugg::hir(cx, recv, "..").to_string();
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
|
||||
});
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
|
||||
});
|
||||
then {
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
|||
closure_arg,
|
||||
closure_body,
|
||||
reverse,
|
||||
unstable,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for UnnecessarySortBy {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
match detect_lint(cx, expr) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|{}| {})",
|
||||
trigger.vec_name,
|
||||
if trigger.unstable { "_unstable" } else { "" },
|
||||
trigger.closure_arg,
|
||||
if trigger.reverse {
|
||||
format!("std::cmp::Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
trigger.closure_body.to_string()
|
||||
},
|
||||
),
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
is_unstable: bool,
|
||||
) {
|
||||
match detect_lint(cx, expr, recv, arg) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|{}| {})",
|
||||
trigger.vec_name,
|
||||
if is_unstable { "_unstable" } else { "" },
|
||||
trigger.closure_arg,
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
format!("std::cmp::Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
trigger.closure_body.to_string()
|
||||
},
|
||||
),
|
||||
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
trigger.vec_name,
|
||||
if trigger.unstable { "_unstable" } else { "" },
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
),
|
||||
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
trigger.vec_name,
|
||||
if is_unstable { "_unstable" } else { "" },
|
||||
),
|
||||
None => {},
|
||||
}
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
None => {},
|
||||
}
|
||||
}
|
45
clippy_lints/src/methods/vec_resize_to_zero.rs
Normal file
45
clippy_lints/src/methods/vec_resize_to_zero.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::VEC_RESIZE_TO_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
count_arg: &'tcx Expr<'_>,
|
||||
default_arg: &'tcx Expr<'_>,
|
||||
name_span: Span,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
|
||||
then {
|
||||
let method_call_span = expr.span.with_lo(name_span.lo());
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
expr.span,
|
||||
"emptying a vector with `resize`",
|
||||
|db| {
|
||||
db.help("the arguments may be inverted...");
|
||||
db.span_suggestion(
|
||||
method_call_span,
|
||||
"...or you can empty the vector with",
|
||||
"clear()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
28
clippy_lints/src/methods/verbose_file_reads.rs
Normal file
28
clippy_lints/src/methods/verbose_file_reads.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::VERBOSE_FILE_READS;
|
||||
|
||||
pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
|
||||
pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
|
||||
"use of `File::read_to_string`",
|
||||
"consider using `fs::read_to_string` instead",
|
||||
);
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
(msg, help): (&str, &str),
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::IoRead)
|
||||
&& matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
|
||||
{
|
||||
span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `&mut Mutex::lock` calls
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Mutex::lock` is less efficient than
|
||||
/// calling `Mutex::get_mut`. In addition you also have a statically
|
||||
/// guarantee that the mutex isn't locked, instead of just a runtime
|
||||
/// guarantee.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
|
||||
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
|
||||
///
|
||||
/// let mut value = value_mutex.lock().unwrap();
|
||||
/// *value += 1;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
|
||||
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
|
||||
///
|
||||
/// let value = value_mutex.get_mut().unwrap();
|
||||
/// *value += 1;
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MUT_MUTEX_LOCK,
|
||||
style,
|
||||
"`&mut Mutex::lock` does unnecessary locking"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &ex.kind;
|
||||
if path.ident.name == sym!(lock);
|
||||
let ty = cx.typeck_results().expr_ty(self_arg);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
|
||||
if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MUT_MUTEX_LOCK,
|
||||
path.ident.span,
|
||||
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
|
||||
"change this to",
|
||||
"get_mut".to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
use std::path::{Component, Path};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
|
||||
/// calls on `PathBuf` that can cause overwrites.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calling `push` with a root path at the start can overwrite the
|
||||
/// previous defined path.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let mut x = PathBuf::from("/foo");
|
||||
/// x.push("/bar");
|
||||
/// assert_eq!(x, PathBuf::from("/bar"));
|
||||
/// ```
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let mut x = PathBuf::from("/foo");
|
||||
/// x.push("bar");
|
||||
/// assert_eq!(x, PathBuf::from("/foo/bar"));
|
||||
/// ```
|
||||
#[clippy::version = "1.36.0"]
|
||||
pub PATH_BUF_PUSH_OVERWRITE,
|
||||
nursery,
|
||||
"calling `push` with file system root on `PathBuf` can overwrite it"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind;
|
||||
if path.ident.name == sym!(push);
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = get_index_arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
if let Some(pushed_path_lit) = pushed_path.to_str();
|
||||
if pushed_path.has_root();
|
||||
if let Some(root) = pushed_path.components().next();
|
||||
if root == Component::RootDir;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
lit.span,
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +1,20 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::sym;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for zipping a collection with the range of
|
||||
/// `0.._.len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The code is better expressed with `.enumerate()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = vec![1];
|
||||
/// let _ = x.iter().zip(0..x.len());
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = vec![1];
|
||||
/// let _ = x.iter().enumerate();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub RANGE_ZIP_WITH_LEN,
|
||||
complexity,
|
||||
"zipping iterator with a range when `enumerate()` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for exclusive ranges where 1 is added to the
|
||||
|
@ -198,7 +172,6 @@ impl Ranges {
|
|||
}
|
||||
|
||||
impl_lint_pass!(Ranges => [
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
RANGE_PLUS_ONE,
|
||||
RANGE_MINUS_ONE,
|
||||
REVERSED_EMPTY_RANGES,
|
||||
|
@ -207,16 +180,10 @@ impl_lint_pass!(Ranges => [
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(path, args, _) => {
|
||||
check_range_zip_with_len(cx, path, args, expr.span);
|
||||
},
|
||||
ExprKind::Binary(ref op, l, r) => {
|
||||
if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
if let ExprKind::Binary(ref op, l, r) = expr.kind {
|
||||
if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||
}
|
||||
}
|
||||
|
||||
check_exclusive_range_plus_one(cx, expr);
|
||||
|
@ -380,34 +347,6 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
|
|||
None
|
||||
}
|
||||
|
||||
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
|
||||
if_chain! {
|
||||
if path.ident.as_str() == "zip";
|
||||
if let [iter, zip_arg] = args;
|
||||
// `.iter()` call
|
||||
if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
|
||||
if iter_path.ident.name == sym::iter;
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
|
||||
if len_path.ident.name == sym::len;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, iter_caller.span, "_"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exclusive range plus one: `x..(y+1)`
|
||||
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.repeat(1)` and suggest the following method for each types.
|
||||
/// - `.to_string()` for `str`
|
||||
/// - `.clone()` for `String`
|
||||
/// - `.to_vec()` for `slice`
|
||||
///
|
||||
/// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
|
||||
/// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
|
||||
/// the string is the intention behind this, `clone()` should be used.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").repeat(1);
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").clone();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub REPEAT_ONCE,
|
||||
complexity,
|
||||
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
|
||||
}
|
||||
|
||||
declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [receiver, count], _) = &expr.kind;
|
||||
if path.ident.name == sym!(repeat);
|
||||
if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
|
||||
if !receiver.span.from_expansion();
|
||||
then {
|
||||
let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on str",
|
||||
"consider using `.to_string()` instead",
|
||||
format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ty.builtin_index().is_some() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on slice",
|
||||
"consider using `.to_vec()` instead",
|
||||
format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::String) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on a string literal",
|
||||
"consider using `.clone()` instead",
|
||||
format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// When sorting primitive values (integers, bools, chars, as well
|
||||
/// as arrays, slices, and tuples of such items), it is typically better to
|
||||
/// use an unstable sort than a stable sort.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Typically, using a stable sort consumes more memory and cpu cycles.
|
||||
/// Because values which compare equal are identical, preserving their
|
||||
/// relative order (the guarantee that a stable sort provides) means
|
||||
/// nothing, while the extra costs still apply.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// As pointed out in
|
||||
/// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
|
||||
/// a stable sort can instead be significantly faster for certain scenarios
|
||||
/// (eg. when a sorted vector is extended with new data and resorted).
|
||||
///
|
||||
/// For more information and benchmarking results, please refer to the
|
||||
/// issue linked above.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort_unstable();
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub STABLE_SORT_PRIMITIVE,
|
||||
pedantic,
|
||||
"use of sort() when sort_unstable() is equivalent"
|
||||
}
|
||||
|
||||
declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
|
||||
|
||||
/// The three "kinds" of sorts
|
||||
enum SortingKind {
|
||||
Vanilla,
|
||||
/* The other kinds of lint are currently commented out because they
|
||||
* can map distinct values to equal ones. If the key function is
|
||||
* provably one-to-one, or if the Cmp function conserves equality,
|
||||
* then they could be linted on, but I don't know if we can check
|
||||
* for that. */
|
||||
|
||||
/* ByKey,
|
||||
* ByCmp, */
|
||||
}
|
||||
impl SortingKind {
|
||||
/// The name of the stable version of this kind of sort
|
||||
fn stable_name(&self) -> &str {
|
||||
match self {
|
||||
SortingKind::Vanilla => "sort",
|
||||
/* SortingKind::ByKey => "sort_by_key",
|
||||
* SortingKind::ByCmp => "sort_by", */
|
||||
}
|
||||
}
|
||||
/// The name of the unstable version of this kind of sort
|
||||
fn unstable_name(&self) -> &str {
|
||||
match self {
|
||||
SortingKind::Vanilla => "sort_unstable",
|
||||
/* SortingKind::ByKey => "sort_unstable_by_key",
|
||||
* SortingKind::ByCmp => "sort_unstable_by", */
|
||||
}
|
||||
}
|
||||
/// Takes the name of a function call and returns the kind of sort
|
||||
/// that corresponds to that function name (or None if it isn't)
|
||||
fn from_stable_name(name: &str) -> Option<SortingKind> {
|
||||
match name {
|
||||
"sort" => Some(SortingKind::Vanilla),
|
||||
// "sort_by" => Some(SortingKind::ByCmp),
|
||||
// "sort_by_key" => Some(SortingKind::ByKey),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A detected instance of this lint
|
||||
struct LintDetection {
|
||||
slice_name: String,
|
||||
method: SortingKind,
|
||||
method_args: String,
|
||||
slice_type: String,
|
||||
}
|
||||
|
||||
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind;
|
||||
if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
|
||||
if let Some(slice_type) = is_slice_of_primitives(cx, slice);
|
||||
then {
|
||||
let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
|
||||
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_then(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
expr.span,
|
||||
format!(
|
||||
"used `{}` on primitive type `{}`",
|
||||
detection.method.stable_name(),
|
||||
detection.slice_type,
|
||||
)
|
||||
.as_str(),
|
||||
|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 typically performs faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ mod transmute_ptr_to_ref;
|
|||
mod transmute_ref_to_ref;
|
||||
mod transmute_undefined_repr;
|
||||
mod transmutes_expressible_as_ptr_casts;
|
||||
mod transmuting_null;
|
||||
mod unsound_collection_transmute;
|
||||
mod useless_transmute;
|
||||
mod utils;
|
||||
|
@ -386,6 +387,28 @@ declare_clippy_lint! {
|
|||
"transmute to or from a type with an undefined representation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmute calls which would receive a null pointer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Transmuting a null pointer is undefined behavior.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Not all cases can be detected at the moment of this writing.
|
||||
/// For example, variables which hold a null pointer and are then fed to a `transmute`
|
||||
/// call, aren't detectable yet.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
|
||||
/// ```
|
||||
#[clippy::version = "1.35.0"]
|
||||
pub TRANSMUTING_NULL,
|
||||
correctness,
|
||||
"transmutes from a null pointer to a reference, which is undefined behavior"
|
||||
}
|
||||
|
||||
pub struct Transmute {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
@ -404,6 +427,7 @@ impl_lint_pass!(Transmute => [
|
|||
UNSOUND_COLLECTION_TRANSMUTE,
|
||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
TRANSMUTING_NULL,
|
||||
]);
|
||||
impl Transmute {
|
||||
#[must_use]
|
||||
|
@ -436,6 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
|
||||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||
| transmuting_null::check(cx, e, arg, to_ty)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
|
||||
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
|
|
61
clippy_lints/src/transmute/transmuting_null.rs
Normal file
61
clippy_lints/src/transmute/transmuting_null.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_expr_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::TRANSMUTING_NULL;
|
||||
|
||||
const LINT_MSG: &str = "transmuting a known null pointer into a reference";
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
|
||||
if !to_ty.is_ref() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind;
|
||||
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
|
||||
if x == 0;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
|
||||
if let ExprKind::Lit(ref lit) = inner_expr.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func1, []) = arg.kind;
|
||||
if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// Also catch transmutations of variables which are known nulls.
|
||||
// To do this, MIR const propagation seems to be the better tool.
|
||||
// Whenever MIR const prop routines are more developed, this will
|
||||
// become available. As of this writing (25/03/19) it is not yet.
|
||||
false
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_expr_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmute calls which would receive a null pointer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Transmuting a null pointer is undefined behavior.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Not all cases can be detected at the moment of this writing.
|
||||
/// For example, variables which hold a null pointer and are then fed to a `transmute`
|
||||
/// call, aren't detectable yet.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
|
||||
/// ```
|
||||
#[clippy::version = "1.35.0"]
|
||||
pub TRANSMUTING_NULL,
|
||||
correctness,
|
||||
"transmutes from a null pointer to a reference, which is undefined behavior"
|
||||
}
|
||||
|
||||
declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
|
||||
|
||||
const LINT_MSG: &str = "transmuting a known null pointer into a reference";
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
|
||||
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::Call(func, [arg]) = expr.kind;
|
||||
if is_expr_diagnostic_item(cx, func, sym::transmute);
|
||||
|
||||
then {
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind;
|
||||
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
|
||||
if x == 0;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
|
||||
if let ExprKind::Lit(ref lit) = inner_expr.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func1, []) = arg.kind;
|
||||
if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// Also catch transmutations of variables which are known nulls.
|
||||
// To do this, MIR const propagation seems to be the better tool.
|
||||
// Whenever MIR const prop routines are more developed, this will
|
||||
// become available. As of this writing (25/03/19) it is not yet.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects `().hash(_)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::hash::Hash;
|
||||
/// # use std::collections::hash_map::DefaultHasher;
|
||||
/// # enum Foo { Empty, WithValue(u8) }
|
||||
/// # use Foo::*;
|
||||
/// # let mut state = DefaultHasher::new();
|
||||
/// # let my_enum = Foo::Empty;
|
||||
/// match my_enum {
|
||||
/// Empty => ().hash(&mut state),
|
||||
/// WithValue(x) => x.hash(&mut state),
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::hash::Hash;
|
||||
/// # use std::collections::hash_map::DefaultHasher;
|
||||
/// # enum Foo { Empty, WithValue(u8) }
|
||||
/// # use Foo::*;
|
||||
/// # let mut state = DefaultHasher::new();
|
||||
/// # let my_enum = Foo::Empty;
|
||||
/// match my_enum {
|
||||
/// Empty => 0_u8.hash(&mut state),
|
||||
/// WithValue(x) => x.hash(&mut state),
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
pub UNIT_HASH,
|
||||
correctness,
|
||||
"hashing a unit value, which does nothing"
|
||||
}
|
||||
declare_lint_pass!(UnitHash => [UNIT_HASH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnitHash {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
|
||||
if name_ident.ident.name == sym::hash;
|
||||
if let [recv, state_param] = args;
|
||||
if cx.typeck_results().expr_ty(recv).is_unit();
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNIT_HASH,
|
||||
expr.span,
|
||||
"this call to `hash` on the unit type will do nothing",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"remove the call to `hash` or consider using",
|
||||
format!(
|
||||
"0_u8.hash({})",
|
||||
snippet(cx, state_param.span, ".."),
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note("the implementation of `Hash` for `()` is a no-op");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds occurrences of `Vec::resize(0, an_int)`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably an argument inversion mistake.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// vec!(1, 2, 3, 4, 5).resize(0, 5)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// vec!(1, 2, 3, 4, 5).clear()
|
||||
/// ```
|
||||
#[clippy::version = "1.46.0"]
|
||||
pub VEC_RESIZE_TO_ZERO,
|
||||
correctness,
|
||||
"emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
|
||||
}
|
||||
|
||||
declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for VecResizeToZero {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(path_segment, args, _) = expr.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind;
|
||||
then {
|
||||
let method_call_span = expr.span.with_lo(path_segment.ident.span.lo());
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
expr.span,
|
||||
"emptying a vector with `resize`",
|
||||
|db| {
|
||||
db.help("the arguments may be inverted...");
|
||||
db.span_suggestion(
|
||||
method_call_span,
|
||||
"...or you can empty the vector with",
|
||||
"clear()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::ty::match_type;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for use of File::read_to_end and File::read_to_string.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
|
||||
/// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// # use std::io::Read;
|
||||
/// # use std::fs::File;
|
||||
/// let mut f = File::open("foo.txt").unwrap();
|
||||
/// let mut bytes = Vec::new();
|
||||
/// f.read_to_end(&mut bytes).unwrap();
|
||||
/// ```
|
||||
/// Can be written more concisely as
|
||||
/// ```rust,no_run
|
||||
/// # use std::fs;
|
||||
/// let mut bytes = fs::read("foo.txt").unwrap();
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
pub VERBOSE_FILE_READS,
|
||||
restriction,
|
||||
"use of `File::read_to_end` or `File::read_to_string`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for VerboseFileReads {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if is_file_read_to_end(cx, expr) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
VERBOSE_FILE_READS,
|
||||
expr.span,
|
||||
"use of `File::read_to_end`",
|
||||
None,
|
||||
"consider using `fs::read` instead",
|
||||
);
|
||||
} else if is_file_read_to_string(cx, expr) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
VERBOSE_FILE_READS,
|
||||
expr.span,
|
||||
"use of `File::read_to_string`",
|
||||
None,
|
||||
"consider using `fs::read_to_string` instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_file_read_to_end<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, [recv, ..], _) = expr.kind;
|
||||
if method_name.ident.as_str() == "read_to_end";
|
||||
if let ExprKind::Path(QPath::Resolved(None, _)) = &recv.kind;
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
if match_type(cx, ty, &paths::FILE);
|
||||
then {
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_file_read_to_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, exprs, _) = expr.kind;
|
||||
if method_name.ident.as_str() == "read_to_string";
|
||||
if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
|
||||
let ty = cx.typeck_results().expr_ty(&exprs[0]);
|
||||
if match_type(cx, ty, &paths::FILE);
|
||||
then {
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
|
@ -1,15 +1,19 @@
|
|||
#![warn(clippy::vec_resize_to_zero)]
|
||||
|
||||
fn main() {
|
||||
let mut v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
// applicable here
|
||||
vec![1, 2, 3, 4, 5].resize(0, 5);
|
||||
v.resize(0, 5);
|
||||
|
||||
// not applicable
|
||||
vec![1, 2, 3, 4, 5].resize(2, 5);
|
||||
v.resize(2, 5);
|
||||
|
||||
let mut v = vec!["foo", "bar", "baz"];
|
||||
|
||||
// applicable here, but only implemented for integer literals for now
|
||||
vec!["foo", "bar", "baz"].resize(0, "bar");
|
||||
v.resize(0, "bar");
|
||||
|
||||
// not applicable
|
||||
vec!["foo", "bar", "baz"].resize(2, "bar")
|
||||
v.resize(2, "bar")
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
error: emptying a vector with `resize`
|
||||
--> $DIR/vec_resize_to_zero.rs:5:5
|
||||
--> $DIR/vec_resize_to_zero.rs:7:5
|
||||
|
|
||||
LL | vec![1, 2, 3, 4, 5].resize(0, 5);
|
||||
| ^^^^^^^^^^^^^^^^^^^^------------
|
||||
| |
|
||||
| help: ...or you can empty the vector with: `clear()`
|
||||
LL | v.resize(0, 5);
|
||||
| ^^------------
|
||||
| |
|
||||
| help: ...or you can empty the vector with: `clear()`
|
||||
|
|
||||
= note: `-D clippy::vec-resize-to-zero` implied by `-D warnings`
|
||||
= help: the arguments may be inverted...
|
||||
|
|
Loading…
Reference in a new issue