+++ /dev/null
-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,
- );
- }
- );
- }
- }
- }
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-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,
- );
- }
- };
- }
-}
+++ /dev/null
-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
- );
- }
- };
- }
-}
+++ /dev/null
-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",
- );
- }
- }
-}
--- /dev/null
+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,
+ );
+ }
+ });
+ }
+}
--- /dev/null
+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,
+ );
+ }
+}
+mod as_underscore;
+mod borrow_as_ptr;
mod cast_abs_to_unsigned;
mod cast_enum_constructor;
mod cast_lossless;
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;
"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>,
}
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 {
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) = (
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);
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
};
let mut applicability = Applicability::MachineApplicable;
- if format_args.value_args.is_empty() {
- match *format_args.format_string_parts {
+ if format_args.args.is_empty() {
+ match *format_args.format_string.parts {
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
[_] => {
- if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
- // Simulate macro expansion, converting {{ and }} to { and }.
- let s_expand = s_src.replace("{{", "{").replace("}}", "}");
- let sugg = format!("{}.to_string()", s_expand);
- span_useless_format(cx, call_site, sugg, applicability);
- }
+ // Simulate macro expansion, converting {{ and }} to { and }.
+ let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+ let sugg = format!("{}.to_string()", s_expand);
+ span_useless_format(cx, call_site, sugg, applicability);
},
[..] => {},
}
- } else if let [value] = *format_args.value_args {
+ } else if let [arg] = &*format_args.args {
+ let value = arg.param.value;
if_chain! {
- if format_args.format_string_parts == [kw::Empty];
+ if format_args.format_string.parts == [kw::Empty];
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
ty::Str => true,
_ => false,
};
- if let Some(args) = format_args.args();
- if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
+ if !arg.format.has_string_formatting();
then {
let is_new_string = match value.kind {
ExprKind::Binary(..) => true,
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use if_chain::if_chain;
+use itertools::Itertools;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
if is_format_macro(cx, macro_def_id);
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
- if let Some(args) = format_args.args();
then {
- for (i, arg) in args.iter().enumerate() {
- if arg.format_trait != sym::Display {
+ for arg in &format_args.args {
+ if arg.format.has_string_formatting() {
continue;
}
- if arg.has_string_formatting() {
+ if is_aliased(&format_args, arg.param.value.hir_id) {
continue;
}
- if is_aliased(&args, i) {
- continue;
- }
- check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
- check_to_string_in_format_args(cx, name, arg.value);
+ check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
+ check_to_string_in_format_args(cx, name, arg.param.value);
}
}
}
}
}
-// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
-fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
- let value = args[i].value;
- args.iter()
- .enumerate()
- .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
+// Returns true if `hir_id` is referred to by multiple format params
+fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
+ args.params()
+ .filter(|param| param.value.hir_id == hir_id)
+ .at_most_one()
+ .is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
+use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
use if_chain::if_chain;
use rustc_errors::Applicability;
if let macro_def_id = outer_macro.def_id;
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
if is_format_macro(cx, macro_def_id);
- if let Some(args) = format_args.args();
then {
- for arg in args {
- if arg.format_trait != impl_trait.name {
+ for arg in format_args.args {
+ if arg.format.r#trait != impl_trait.name {
continue;
}
check_format_arg_self(cx, expr, &arg, impl_trait);
}
}
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
// Handle multiple dereferencing of references e.g. &&self
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
// Since the argument to fmt is itself a reference: &self
- let reference = peel_ref_operators(cx, arg.value);
+ let reference = peel_ref_operators(cx, arg.param.value);
let map = cx.tcx.hir();
// Is the reference self?
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
+++ /dev/null
-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,
- );
- }
- }
- }
-}
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),
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),
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),
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),
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),
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),
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),
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),
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),
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(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::ASSIGN_OP_PATTERN),
LintId::of(operators::BAD_BIT_MASK),
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),
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),
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),
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),
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_peekable::UNUSED_PEEKABLE),
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),
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),
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),
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),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION),
+ LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::DOUBLE_COMPARISONS),
LintId::of(operators::DURATION_SUBSEC),
LintId::of(operators::IDENTITY_OP),
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),
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),
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),
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),
])
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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),
LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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;
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;
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;
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;
mod module_style;
mod mut_key;
mod mut_mut;
-mod mut_mutex_lock;
mod mut_reference;
mod mutable_debug_assertion;
mod mutex_atomic;
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;
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;
mod ref_option_ref;
mod reference;
mod regex;
-mod repeat_once;
mod return_self_not_must_use;
mod returns;
mod same_name_method;
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;
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;
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;
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));
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;
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));
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));
);
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()));
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)));
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)));
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 || {
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();
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)));
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));
- store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
+ store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish;
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)));
+++ /dev/null
-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 }
- }
-}
+++ /dev/null
-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,
- );
- }
-}
+++ /dev/null
-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| ...`)",
- );
- }
- }
- }
- }
- }
- }
- }
-}
--- /dev/null
+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,
+ );
+ }
+ };
+}
--- /dev/null
+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
+ );
+ }
+ };
+}
--- /dev/null
+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",
+ );
+ }
+ }
+}
--- /dev/null
+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,
+ );
+ }
+ }
+}
--- /dev/null
+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 }
+ }
+}
--- /dev/null
+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,
+ );
+}
--- /dev/null
+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| ...`)",
+ );
+ }
+}
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;
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;
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;
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;
"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>,
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.
},
_ => {},
},
- ("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),
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),
}
}
},
+ ("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),
}
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) {
_ => 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);
("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);
+ }
+ },
_ => {},
}
}
--- /dev/null
+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,
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
+use rustc_ast::ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::{Span, Spanned};
+
+use super::NONSENSICAL_OPEN_OPTIONS;
+
+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);
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum Argument {
+ True,
+ False,
+ Unknown,
+}
+
+#[derive(Debug)]
+enum OpenOption {
+ Write,
+ Read,
+ Truncate,
+ Create,
+ Append,
+}
+
+fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
+ if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
+ let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
+
+ // Only proceed if this is a call on some object of type std::fs::OpenOptions
+ if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
+ let argument_option = match arguments[1].kind {
+ ExprKind::Lit(ref span) => {
+ if let Spanned {
+ node: LitKind::Bool(lit),
+ ..
+ } = *span
+ {
+ if lit { Argument::True } else { Argument::False }
+ } else {
+ // The function is called with a literal which is not a boolean literal.
+ // This is theoretically possible, but not very likely.
+ return;
+ }
+ },
+ _ => Argument::Unknown,
+ };
+
+ match path.ident.as_str() {
+ "create" => {
+ options.push((OpenOption::Create, argument_option));
+ },
+ "append" => {
+ options.push((OpenOption::Append, argument_option));
+ },
+ "truncate" => {
+ options.push((OpenOption::Truncate, argument_option));
+ },
+ "read" => {
+ options.push((OpenOption::Read, argument_option));
+ },
+ "write" => {
+ options.push((OpenOption::Write, argument_option));
+ },
+ _ => (),
+ }
+
+ get_open_options(cx, &arguments[0], options);
+ }
+ }
+}
+
+fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
+ let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
+ let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
+ (false, false, false, false, false);
+ // This code is almost duplicated (oh, the irony), but I haven't found a way to
+ // unify it.
+
+ for option in options {
+ match *option {
+ (OpenOption::Create, arg) => {
+ if create {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `create` is called more than once",
+ );
+ } else {
+ create = true;
+ }
+ create_arg = create_arg || (arg == Argument::True);
+ },
+ (OpenOption::Append, arg) => {
+ if append {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `append` is called more than once",
+ );
+ } else {
+ append = true;
+ }
+ append_arg = append_arg || (arg == Argument::True);
+ },
+ (OpenOption::Truncate, arg) => {
+ if truncate {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `truncate` is called more than once",
+ );
+ } else {
+ truncate = true;
+ }
+ truncate_arg = truncate_arg || (arg == Argument::True);
+ },
+ (OpenOption::Read, arg) => {
+ if read {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `read` is called more than once",
+ );
+ } else {
+ read = true;
+ }
+ read_arg = read_arg || (arg == Argument::True);
+ },
+ (OpenOption::Write, arg) => {
+ if write {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "the method `write` is called more than once",
+ );
+ } else {
+ write = true;
+ }
+ write_arg = write_arg || (arg == Argument::True);
+ },
+ }
+ }
+
+ if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "file opened with `truncate` and `read`",
+ );
+ }
+ if append && truncate && append_arg && truncate_arg {
+ span_lint(
+ cx,
+ NONSENSICAL_OPEN_OPTIONS,
+ span,
+ "file opened with `append` and `truncate`",
+ );
+ }
+}
--- /dev/null
+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,
+ );
+ }
+ }
+}
--- /dev/null
+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, "_"))
+ );
+ }
+ }
+}
--- /dev/null
+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,
+ );
+ }
+ }
+}
--- /dev/null
+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",
+ );
+ },
+ );
+ }
+}
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
+use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
if_chain! {
if let hir::ExprKind::Call(callee, args) = recv.kind;
if args.is_empty();
- if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
+ if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
then {
span_lint(
--- /dev/null
+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");
+ },
+ );
+ }
+}
--- /dev/null
+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;
+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;
+use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_span::sym;
+use rustc_span::symbol::Ident;
+use std::iter;
+
+use super::UNNECESSARY_SORT_BY;
+
+enum LintTrigger {
+ Sort(SortDetection),
+ SortByKey(SortByKeyDetection),
+}
+
+struct SortDetection {
+ vec_name: String,
+}
+
+struct SortByKeyDetection {
+ vec_name: String,
+ closure_arg: String,
+ closure_body: String,
+ reverse: bool,
+}
+
+/// Detect if the two expressions are mirrored (identical, except one
+/// contains a and the other replaces it with b)
+fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
+ match (&a_expr.kind, &b_expr.kind) {
+ // Two boxes with mirrored contents
+ (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
+ mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+ },
+ // Two arrays with mirrored contents
+ (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
+ iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // The two exprs are function calls.
+ // Check to see that the function itself and its arguments are mirrored
+ (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
+ mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+ && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // The two exprs are method calls.
+ // Check to see that the function is the same and the arguments are mirrored
+ // This is enough because the receiver of the method is listed in the arguments
+ (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
+ left_segment.ident == right_segment.ident
+ && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // Two tuples with mirrored contents
+ (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
+ iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ },
+ // Two binary ops, which are the same operation and which have mirrored arguments
+ (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
+ left_op.node == right_op.node
+ && mirrored_exprs(left_left, a_ident, right_left, b_ident)
+ && mirrored_exprs(left_right, a_ident, right_right, b_ident)
+ },
+ // Two unary ops, which are the same operation and which have the same argument
+ (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
+ left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
+ },
+ // The two exprs are literals of some kind
+ (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
+ (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
+ (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
+ mirrored_exprs(left_block, a_ident, right_block, b_ident)
+ },
+ (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
+ left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
+ },
+ // Two paths: either one is a and the other is b, or they're identical to each other
+ (
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ segments: left_segments,
+ ..
+ },
+ )),
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ segments: right_segments,
+ ..
+ },
+ )),
+ ) => {
+ (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
+ && left_segments
+ .iter()
+ .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
+ || (left_segments.len() == 1
+ && &left_segments[0].ident == a_ident
+ && right_segments.len() == 1
+ && &right_segments[0].ident == b_ident)
+ },
+ // Matching expressions, but one or both is borrowed
+ (
+ ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
+ ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
+ ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
+ (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
+ (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
+ _ => false,
+ }
+}
+
+fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
+ 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 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, [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,
+ left_ident,
+ right_expr,
+ right_ident
+ ) {
+ (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
+ } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
+ (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
+ } else {
+ return None;
+ };
+ 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, &[])
+ });
+ then {
+ return Some(LintTrigger::Sort(SortDetection { vec_name }));
+ }
+ }
+
+ if !expr_borrows(cx, left_expr) {
+ return Some(LintTrigger::SortByKey(SortByKeyDetection {
+ vec_name,
+ closure_arg,
+ closure_body,
+ reverse,
+ }));
+ }
+ }
+ }
+
+ None
+}
+
+fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let ty = cx.typeck_results().expr_ty(expr);
+ matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+}
+
+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 {
+ format!("std::cmp::Reverse({})", trigger.closure_body)
+ } else {
+ trigger.closure_body.to_string()
+ },
+ ),
+ 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 { "" },
+ ),
+ Applicability::MachineApplicable,
+ ),
+ None => {},
+ }
+}
--- /dev/null
+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,
+ );
+ },
+ );
+ }
+ }
+}
--- /dev/null
+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);
+ }
+}
+++ /dev/null
-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,
- );
- }
- }
- }
-}
-use std::collections::VecDeque;
-
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lint_allowed;
-use itertools::{izip, Itertools};
-use rustc_ast::{walk_list, Label, Mutability};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
+use core::cell::Cell;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
-use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
-use rustc_hir::{
- Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
- PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
-};
+use rustc_hir::hir_id::HirIdMap;
+use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
-use rustc_span::symbol::Ident;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::{self, ConstKind};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
declare_clippy_lint! {
/// ```
#[clippy::version = "1.61.0"]
pub ONLY_USED_IN_RECURSION,
- nursery,
+ complexity,
"arguments that is only used in recursion can be removed"
}
-declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
-
-impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
- fn check_fn(
- &mut self,
- cx: &LateContext<'tcx>,
- kind: FnKind<'tcx>,
- decl: &'tcx rustc_hir::FnDecl<'tcx>,
- body: &'tcx Body<'tcx>,
- _: Span,
- id: HirId,
- ) {
- if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
- return;
- }
- if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
- let def_id = id.owner.to_def_id();
- let data = cx.tcx.def_path(def_id).data;
-
- if data.len() > 1 {
- match data.get(data.len() - 2) {
- Some(DisambiguatedDefPathData {
- data: DefPathData::Impl,
- disambiguator,
- }) if *disambiguator != 0 => return,
- _ => {},
- }
- }
-
- let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
-
- let ty_res = cx.typeck_results();
- let param_span = body
- .params
- .iter()
- .flat_map(|param| {
- let mut v = Vec::new();
- param.pat.each_binding(|_, hir_id, span, ident| {
- v.push((hir_id, span, ident));
- });
- v
- })
- .skip(if has_self { 1 } else { 0 })
- .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
- .collect_vec();
-
- let params = body.params.iter().map(|param| param.pat).collect();
-
- let mut visitor = SideEffectVisit {
- graph: FxHashMap::default(),
- has_side_effect: FxHashSet::default(),
- ret_vars: Vec::new(),
- contains_side_effect: false,
- break_vars: FxHashMap::default(),
- params,
- fn_ident: ident,
- fn_def_id: def_id,
- is_method: matches!(kind, FnKind::Method(..)),
- has_self,
- ty_res,
- tcx: cx.tcx,
- visited_exprs: FxHashSet::default(),
- };
-
- visitor.visit_expr(&body.value);
- let vars = std::mem::take(&mut visitor.ret_vars);
- // this would set the return variables to side effect
- visitor.add_side_effect(vars);
-
- let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
-
- // a simple BFS to check all the variables that have side effect
- while let Some(id) = queue.pop_front() {
- if let Some(next) = visitor.graph.get(&id) {
- for i in next {
- if !visitor.has_side_effect.contains(i) {
- visitor.has_side_effect.insert(*i);
- queue.push_back(*i);
- }
- }
- }
- }
-
- for (id, span, ident) in param_span {
- // if the variable is not used in recursion, it would be marked as unused
- if !visitor.has_side_effect.contains(&id) {
- let mut queue = VecDeque::new();
- let mut visited = FxHashSet::default();
-
- queue.push_back(id);
-
- // a simple BFS to check the graph can reach to itself
- // if it can't, it means the variable is never used in recursion
- while let Some(id) = queue.pop_front() {
- if let Some(next) = visitor.graph.get(&id) {
- for i in next {
- if !visited.contains(i) {
- visited.insert(id);
- queue.push_back(*i);
- }
- }
- }
- }
+impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
+
+#[derive(Clone, Copy)]
+enum FnKind {
+ Fn,
+ TraitFn,
+ // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
+ // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
+ // equality.
+ ImplTraitFn(usize),
+}
- if visited.contains(&id) {
- span_lint_and_sugg(
- cx,
- ONLY_USED_IN_RECURSION,
- span,
- "parameter is only used in recursion",
- "if this is intentional, prefix with an underscore",
- format!("_{}", ident.name.as_str()),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
+struct Param {
+ /// The function this is a parameter for.
+ fn_id: DefId,
+ fn_kind: FnKind,
+ /// The index of this parameter.
+ idx: usize,
+ ident: Ident,
+ /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
+ apply_lint: Cell<bool>,
+ /// All the uses of this parameter.
+ uses: Vec<Usage>,
+}
+impl Param {
+ fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
+ Self {
+ fn_id,
+ fn_kind,
+ idx,
+ ident,
+ apply_lint: Cell::new(true),
+ uses: Vec::new(),
}
}
}
-pub fn is_primitive(ty: Ty<'_>) -> bool {
- let ty = ty.peel_refs();
- ty.is_primitive() || ty.is_str()
+#[derive(Debug)]
+struct Usage {
+ span: Span,
+ idx: usize,
}
-
-pub fn is_array(ty: Ty<'_>) -> bool {
- let ty = ty.peel_refs();
- ty.is_array() || ty.is_array_slice()
+impl Usage {
+ fn new(span: Span, idx: usize) -> Self {
+ Self { span, idx }
+ }
}
-/// This builds the graph of side effect.
-/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
-///
-/// There are some example in following code:
-/// ```rust, ignore
-/// let b = 1;
-/// let a = b; // a -> b
-/// let (c, d) = (a, b); // c -> b, d -> b
-///
-/// let e = if a == 0 { // e -> a
-/// c // e -> c
-/// } else {
-/// d // e -> d
-/// };
-/// ```
-pub struct SideEffectVisit<'tcx> {
- graph: FxHashMap<HirId, FxHashSet<HirId>>,
- has_side_effect: FxHashSet<HirId>,
- // bool for if the variable was dereferenced from mutable reference
- ret_vars: Vec<(HirId, bool)>,
- contains_side_effect: bool,
- // break label
- break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
- params: Vec<&'tcx Pat<'tcx>>,
- fn_ident: Ident,
- fn_def_id: DefId,
- is_method: bool,
- has_self: bool,
- ty_res: &'tcx TypeckResults<'tcx>,
- tcx: TyCtxt<'tcx>,
- visited_exprs: FxHashSet<HirId>,
+/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
+/// `DefId` of the function paired with the parameter's index.
+#[derive(Default)]
+struct Params {
+ params: Vec<Param>,
+ by_id: HirIdMap<usize>,
+ by_fn: FxHashMap<(DefId, usize), usize>,
}
-
-impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
- fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
- match s.kind {
- StmtKind::Local(Local {
- pat, init: Some(init), ..
- }) => {
- self.visit_pat_expr(pat, init, false);
- },
- StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
- walk_stmt(self, s);
- },
- StmtKind::Local(_) => {},
- }
- self.ret_vars.clear();
+impl Params {
+ fn insert(&mut self, param: Param, id: HirId) {
+ let idx = self.params.len();
+ self.by_id.insert(id, idx);
+ self.by_fn.insert((param.fn_id, param.idx), idx);
+ self.params.push(param);
}
- fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
- if !self.visited_exprs.insert(ex.hir_id) {
- return;
- }
- match ex.kind {
- ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
- self.ret_vars = exprs
- .iter()
- .flat_map(|expr| {
- self.visit_expr(expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- },
- ExprKind::Call(callee, args) => self.visit_fn(callee, args),
- ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
- ExprKind::Binary(_, lhs, rhs) => {
- self.visit_bin_op(lhs, rhs);
- },
- ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
- ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
- ExprKind::If(bind, then_expr, else_expr) => {
- self.visit_if(bind, then_expr, else_expr);
- },
- ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
- // since analysing the closure is not easy, just set all variables in it to side-effect
- ExprKind::Closure(&Closure { body, .. }) => {
- let body = self.tcx.hir().body(body);
- self.visit_body(body);
- let vars = std::mem::take(&mut self.ret_vars);
- self.add_side_effect(vars);
- },
- ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
- self.visit_block_label(block, label);
- },
- ExprKind::Assign(bind, expr, _) => {
- self.visit_assign(bind, expr);
- },
- ExprKind::AssignOp(_, bind, expr) => {
- self.visit_assign(bind, expr);
- self.visit_bin_op(bind, expr);
- },
- ExprKind::Field(expr, _) => {
- self.visit_expr(expr);
- if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
- self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
- }
- },
- ExprKind::Index(expr, index) => {
- self.visit_expr(expr);
- let mut vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(index);
- self.ret_vars.append(&mut vars);
-
- if !is_array(self.ty_res.expr_ty(expr)) {
- self.add_side_effect(self.ret_vars.clone());
- } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
- self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
- }
- },
- ExprKind::Break(dest, Some(expr)) => {
- self.visit_expr(expr);
- if let Some(label) = dest.label {
- self.break_vars
- .entry(label.ident)
- .or_insert(Vec::new())
- .append(&mut self.ret_vars);
- }
- self.contains_side_effect = true;
- },
- ExprKind::Ret(Some(expr)) => {
- self.visit_expr(expr);
- let vars = std::mem::take(&mut self.ret_vars);
- self.add_side_effect(vars);
- self.contains_side_effect = true;
- },
- ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
- self.contains_side_effect = true;
- },
- ExprKind::Struct(_, exprs, expr) => {
- let mut ret_vars = exprs
- .iter()
- .flat_map(|field| {
- self.visit_expr(field.expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
-
- walk_list!(self, visit_expr, expr);
- self.ret_vars.append(&mut ret_vars);
- },
- _ => walk_expr(self, ex),
+ fn remove_by_id(&mut self, id: HirId) {
+ if let Some(param) = self.get_by_id_mut(id) {
+ param.uses = Vec::new();
+ let key = (param.fn_id, param.idx);
+ self.by_fn.remove(&key);
+ self.by_id.remove(&id);
}
}
- fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
- if let Res::Local(id) = path.res {
- self.ret_vars.push((id, false));
- }
+ fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
+ self.params.get_mut(*self.by_id.get(&id)?)
}
-}
-impl<'tcx> SideEffectVisit<'tcx> {
- fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
- // Just support array and tuple unwrapping for now.
- //
- // ex) `(a, b) = (c, d);`
- // The graph would look like this:
- // a -> c
- // b -> d
- //
- // This would minimize the connection of the side-effect graph.
- match (&lhs.kind, &rhs.kind) {
- (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
- // if not, it is a compile error
- debug_assert!(lhs.len() == rhs.len());
- izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
- },
- // in other assigns, we have to connect all each other
- // because they can be connected somehow
- _ => {
- self.visit_expr(lhs);
- let lhs_vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(rhs);
- let rhs_vars = std::mem::take(&mut self.ret_vars);
- self.connect_assign(&lhs_vars, &rhs_vars, false);
- },
- }
+ fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
+ self.params.get(*self.by_fn.get(&(id, idx))?)
}
- fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
- self.visit_block(block);
- let _ = label.and_then(|label| {
- self.break_vars
- .remove(&label.ident)
- .map(|mut break_vars| self.ret_vars.append(&mut break_vars))
- });
- }
-
- fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
- self.visit_expr(lhs);
- let mut ret_vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(rhs);
- self.ret_vars.append(&mut ret_vars);
-
- // the binary operation between non primitive values are overloaded operators
- // so they can have side-effects
- if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
- self.ret_vars.iter().for_each(|id| {
- self.has_side_effect.insert(id.0);
- });
- self.contains_side_effect = true;
- }
+ fn clear(&mut self) {
+ self.params.clear();
+ self.by_id.clear();
+ self.by_fn.clear();
}
- fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
- self.visit_expr(expr);
- let ty = self.ty_res.expr_ty(expr);
- // dereferencing a reference has no side-effect
- if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
- self.add_side_effect(self.ret_vars.clone());
- }
-
- if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
- self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
+ /// Sets the `apply_lint` flag on each parameter.
+ fn flag_for_linting(&mut self) {
+ // Stores the list of parameters currently being resolved. Needed to avoid cycles.
+ let mut eval_stack = Vec::new();
+ for param in &self.params {
+ self.try_disable_lint_for_param(param, &mut eval_stack);
}
}
- fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
- match (&pat.kind, &expr.kind) {
- (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
- self.ret_vars = izip!(*pats, *exprs)
- .flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, connect_self);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- },
- (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
- let mut vars = izip!(*front_exprs, *exprs)
- .flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, connect_self);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
- .flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, connect_self);
- std::mem::take(&mut self.ret_vars)
- })
- .collect();
- self.ret_vars.append(&mut vars);
- },
- _ => {
- let mut lhs_vars = Vec::new();
- pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
- self.visit_expr(expr);
- let rhs_vars = std::mem::take(&mut self.ret_vars);
- self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
- self.ret_vars = rhs_vars;
- },
+ // Use by calling `flag_for_linting`.
+ fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
+ if !param.apply_lint.get() {
+ true
+ } else if param.uses.is_empty() {
+ // Don't lint on unused parameters.
+ param.apply_lint.set(false);
+ true
+ } else if eval_stack.contains(¶m.idx) {
+ // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
+ false
+ } else {
+ eval_stack.push(param.idx);
+ // Check all cases when used at a different parameter index.
+ // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
+ for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
+ if self
+ .get_by_fn(param.fn_id, usage.idx)
+ // If the parameter can't be found, then it's used for more than just recursion.
+ .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
+ {
+ param.apply_lint.set(false);
+ eval_stack.pop();
+ return true;
+ }
+ }
+ eval_stack.pop();
+ false
}
}
+}
- fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
- self.visit_expr(callee);
- let mut ret_vars = std::mem::take(&mut self.ret_vars);
- self.add_side_effect(ret_vars.clone());
-
- let mut is_recursive = false;
-
- if_chain! {
- if !self.has_self;
- if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
- if let Res::Def(DefKind::Fn, def_id) = path.res;
- if self.fn_def_id == def_id;
- then {
- is_recursive = true;
- }
- }
+#[derive(Default)]
+pub struct OnlyUsedInRecursion {
+ /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
+ entered_body: Option<HirId>,
+ params: Params,
+}
- if_chain! {
- if !self.has_self && self.is_method;
- if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
- if segment.ident == self.fn_ident;
- if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
- if let Res::SelfTy{ .. } = path.res;
- then {
- is_recursive = true;
- }
+impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
+ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+ if body.value.span.from_expansion() {
+ return;
}
-
- if is_recursive {
- izip!(self.params.clone(), args).for_each(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, true);
- self.ret_vars.clear();
- });
- } else {
- // This would set arguments used in closure that does not have side-effect.
- // Closure itself can be detected whether there is a side-effect, but the
- // value of variable that is holding closure can change.
- // So, we just check the variables.
- self.ret_vars = args
- .iter()
- .flat_map(|expr| {
- self.visit_expr(expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect_vec()
- .into_iter()
- .map(|id| {
- self.has_side_effect.insert(id.0);
- id
- })
- .collect();
- self.contains_side_effect = true;
+ // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
+ // It can't be renamed, and it can't be removed without removing it from multiple functions.
+ let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
+ Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
+ Some(Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Fn(ref sig, _),
+ def_id,
+ ..
+ })) => (
+ def_id.to_def_id(),
+ FnKind::TraitFn,
+ if sig.decl.implicit_self.has_implicit_self() {
+ 1
+ } else {
+ 0
+ },
+ ),
+ Some(Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(ref sig, _),
+ def_id,
+ ..
+ })) => {
+ #[allow(trivial_casts)]
+ if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
+ && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
+ && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
+ {
+ (
+ trait_item_id,
+ FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
+ if sig.decl.implicit_self.has_implicit_self() {
+ 1
+ } else {
+ 0
+ },
+ )
+ } else {
+ (def_id.to_def_id(), FnKind::Fn, 0)
+ }
+ },
+ _ => return,
+ };
+ body.params
+ .iter()
+ .enumerate()
+ .skip(skip_params)
+ .filter_map(|(idx, p)| match p.pat.kind {
+ PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
+ Some((id, Param::new(fn_id, fn_kind, idx, ident)))
+ },
+ _ => None,
+ })
+ .for_each(|(id, param)| self.params.insert(param, id));
+ if self.entered_body.is_none() {
+ self.entered_body = Some(body.value.hir_id);
}
-
- self.ret_vars.append(&mut ret_vars);
}
- fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
- if_chain! {
- if self.is_method;
- if path.ident == self.fn_ident;
- if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
- if let Res::Local(..) = path.res;
- let ident = path.segments.last().unwrap().ident;
- if ident.name == kw::SelfLower;
- then {
- izip!(self.params.clone(), args.iter())
- .for_each(|(pat, expr)| {
- self.visit_pat_expr(pat, expr, true);
- self.ret_vars.clear();
- });
- } else {
- self.ret_vars = args
- .iter()
- .flat_map(|expr| {
- self.visit_expr(expr);
- std::mem::take(&mut self.ret_vars)
- })
- .collect_vec()
- .into_iter()
- .map(|a| {
- self.has_side_effect.insert(a.0);
- a
- })
- .collect();
- self.contains_side_effect = true;
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
+ if let Some(id) = path_to_local(e)
+ && let Some(param) = self.params.get_by_id_mut(id)
+ {
+ let typeck = cx.typeck_results();
+ let span = e.span;
+ let mut e = e;
+ loop {
+ match get_expr_use_or_unification_node(cx.tcx, e) {
+ None | Some((Node::Stmt(_), _)) => return,
+ Some((Node::Expr(parent), child_id)) => match parent.kind {
+ // Recursive call. Track which index the parameter is used in.
+ ExprKind::Call(callee, args)
+ if path_def_id(cx, callee).map_or(false, |id| {
+ id == param.fn_id
+ && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
+ }) =>
+ {
+ if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+ param.uses.push(Usage::new(span, idx));
+ }
+ return;
+ },
+ ExprKind::MethodCall(_, args, _)
+ if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
+ id == param.fn_id
+ && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
+ }) =>
+ {
+ if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+ param.uses.push(Usage::new(span, idx));
+ }
+ return;
+ },
+ // Assignment to a parameter is fine.
+ ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
+ return;
+ },
+ // Parameter update e.g. `x = x + 1`
+ ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
+ if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
+ {
+ return;
+ },
+ // Side-effect free expressions. Walk to the parent expression.
+ ExprKind::Binary(_, lhs, rhs)
+ if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
+ {
+ e = parent;
+ continue;
+ },
+ ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
+ e = parent;
+ continue;
+ },
+ ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
+ e = parent;
+ continue;
+ },
+ // Only allow field accesses without auto-deref
+ ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
+ e = parent;
+ continue
+ }
+ _ => (),
+ },
+ _ => (),
+ }
+ self.params.remove_by_id(id);
+ return;
}
}
}
- fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
- let contains_side_effect = self.contains_side_effect;
- self.contains_side_effect = false;
- self.visit_expr(bind);
- let mut vars = std::mem::take(&mut self.ret_vars);
- self.visit_expr(then_expr);
- let mut then_vars = std::mem::take(&mut self.ret_vars);
- walk_list!(self, visit_expr, else_expr);
- if self.contains_side_effect {
- self.add_side_effect(vars.clone());
- }
- self.contains_side_effect |= contains_side_effect;
- self.ret_vars.append(&mut vars);
- self.ret_vars.append(&mut then_vars);
- }
-
- fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
- self.visit_expr(expr);
- let mut expr_vars = std::mem::take(&mut self.ret_vars);
- self.ret_vars = arms
- .iter()
- .flat_map(|arm| {
- let contains_side_effect = self.contains_side_effect;
- self.contains_side_effect = false;
- // this would visit `expr` multiple times
- // but couldn't think of a better way
- self.visit_pat_expr(arm.pat, expr, false);
- let mut vars = std::mem::take(&mut self.ret_vars);
- let _ = arm.guard.as_ref().map(|guard| {
- self.visit_expr(match guard {
- Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
- });
- vars.append(&mut self.ret_vars);
- });
- self.visit_expr(arm.body);
- if self.contains_side_effect {
- self.add_side_effect(vars.clone());
- self.add_side_effect(expr_vars.clone());
+ fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+ if self.entered_body == Some(body.value.hir_id) {
+ self.entered_body = None;
+ self.params.flag_for_linting();
+ for param in &self.params.params {
+ if param.apply_lint.get() {
+ span_lint_and_then(
+ cx,
+ ONLY_USED_IN_RECURSION,
+ param.ident.span,
+ "parameter is only used in recursion",
+ |diag| {
+ if param.ident.name != kw::SelfLower {
+ diag.span_suggestion(
+ param.ident.span,
+ "if this is intentional, prefix it with an underscore",
+ format!("_{}", param.ident.name),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ diag.span_note(
+ param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
+ "parameter used here",
+ );
+ },
+ );
}
- self.contains_side_effect |= contains_side_effect;
- vars.append(&mut self.ret_vars);
- vars
- })
- .collect();
- self.ret_vars.append(&mut expr_vars);
- }
-
- fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
- // if mutable dereference is on assignment it can have side-effect
- // (this can lead to parameter mutable dereference and change the original value)
- // too hard to detect whether this value is from parameter, so this would all
- // check mutable dereference assignment to side effect
- lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
- self.has_side_effect.insert(*id);
- self.contains_side_effect = true;
- });
-
- // there is no connection
- if lhs.is_empty() || rhs.is_empty() {
- return;
- }
-
- // by connected rhs in cycle, the connections would decrease
- // from `n * m` to `n + m`
- // where `n` and `m` are length of `lhs` and `rhs`.
-
- // unwrap is possible since rhs is not empty
- let rhs_first = rhs.first().unwrap();
- for (id, _) in lhs.iter() {
- if connect_self || *id != rhs_first.0 {
- self.graph
- .entry(*id)
- .or_insert_with(FxHashSet::default)
- .insert(rhs_first.0);
}
+ self.params.clear();
}
-
- let rhs = rhs.iter();
- izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
- if connect_self || from.0 != to.0 {
- self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
- }
- });
}
+}
- fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
- for (id, _) in v {
- self.has_side_effect.insert(id);
- self.contains_side_effect = true;
- }
+fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
+ match kind {
+ FnKind::Fn => true,
+ FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
+ GenericArgKind::Lifetime(_) => true,
+ GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
+ GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
+ }),
+ #[allow(trivial_casts)]
+ FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
}
}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint;
-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_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"
-}
-
-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);
- }
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum Argument {
- True,
- False,
- Unknown,
-}
-
-#[derive(Debug)]
-enum OpenOption {
- Write,
- Read,
- Truncate,
- Create,
- Append,
-}
-
-fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
- if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
- let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
-
- // Only proceed if this is a call on some object of type std::fs::OpenOptions
- if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
- let argument_option = match arguments[1].kind {
- ExprKind::Lit(ref span) => {
- if let Spanned {
- node: LitKind::Bool(lit),
- ..
- } = *span
- {
- if lit { Argument::True } else { Argument::False }
- } else {
- // The function is called with a literal which is not a boolean literal.
- // This is theoretically possible, but not very likely.
- return;
- }
- },
- _ => Argument::Unknown,
- };
-
- match path.ident.as_str() {
- "create" => {
- options.push((OpenOption::Create, argument_option));
- },
- "append" => {
- options.push((OpenOption::Append, argument_option));
- },
- "truncate" => {
- options.push((OpenOption::Truncate, argument_option));
- },
- "read" => {
- options.push((OpenOption::Read, argument_option));
- },
- "write" => {
- options.push((OpenOption::Write, argument_option));
- },
- _ => (),
- }
-
- get_open_options(cx, &arguments[0], options);
- }
- }
-}
-
-fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
- let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
- let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
- (false, false, false, false, false);
- // This code is almost duplicated (oh, the irony), but I haven't found a way to
- // unify it.
-
- for option in options {
- match *option {
- (OpenOption::Create, arg) => {
- if create {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `create` is called more than once",
- );
- } else {
- create = true;
- }
- create_arg = create_arg || (arg == Argument::True);
- },
- (OpenOption::Append, arg) => {
- if append {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `append` is called more than once",
- );
- } else {
- append = true;
- }
- append_arg = append_arg || (arg == Argument::True);
- },
- (OpenOption::Truncate, arg) => {
- if truncate {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `truncate` is called more than once",
- );
- } else {
- truncate = true;
- }
- truncate_arg = truncate_arg || (arg == Argument::True);
- },
- (OpenOption::Read, arg) => {
- if read {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `read` is called more than once",
- );
- } else {
- read = true;
- }
- read_arg = read_arg || (arg == Argument::True);
- },
- (OpenOption::Write, arg) => {
- if write {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "the method `write` is called more than once",
- );
- } else {
- write = true;
- }
- write_arg = write_arg || (arg == Argument::True);
- },
- }
- }
-
- if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "file opened with `truncate` and `read`",
- );
- }
- if append && truncate && append_arg && truncate_arg {
- span_lint(
- cx,
- NONSENSICAL_OPEN_OPTIONS,
- span,
- "file opened with `append` and `truncate`",
- );
- }
-}
+++ /dev/null
-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,
- );
- }
- }
- }
-}
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
}
impl_lint_pass!(Ranges => [
- RANGE_ZIP_WITH_LEN,
RANGE_PLUS_ONE,
RANGE_MINUS_ONE,
REVERSED_EMPTY_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);
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! {
impl RedundantStaticLifetimes {
// Recursively visit types
- fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
+ fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
match ty.kind {
// Be careful of nested structures (arrays and tuples)
TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
- self.visit_type(ty, cx, reason);
+ Self::visit_type(ty, cx, reason);
},
TyKind::Tup(ref tup) => {
for tup_ty in tup {
- self.visit_type(tup_ty, cx, reason);
+ Self::visit_type(tup_ty, cx, reason);
}
},
// This is what we are looking for !
_ => {},
}
}
- self.visit_type(&borrow_type.ty, cx, reason);
+ Self::visit_type(&borrow_type.ty, cx, reason);
},
_ => {},
}
if !item.span.from_expansion() {
if let ItemKind::Const(_, ref var_type, _) = item.kind {
- self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
+ Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
// Don't check associated consts because `'static` cannot be elided on those (issue
// #2438)
}
if let ItemKind::Static(ref var_type, _, _) = item.kind {
- self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
+ Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
}
}
}
+++ /dev/null
-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,
- );
- }
- }
- }
- }
-}
+++ /dev/null
-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",
- );
- },
- );
- }
- }
-}
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;
"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>,
}
UNSOUND_COLLECTION_TRANSMUTE,
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
TRANSMUTE_UNDEFINED_REPR,
+ TRANSMUTING_NULL,
]);
impl Transmute {
#[must_use]
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)
--- /dev/null
+use clippy_utils::consts::{constant_context, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_path_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_path_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
+}
+++ /dev/null
-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.
- }
- }
- }
-}
+++ /dev/null
-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");
- }
- );
- }
- }
- }
-}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-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_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]);
-
-enum LintTrigger {
- Sort(SortDetection),
- SortByKey(SortByKeyDetection),
-}
-
-struct SortDetection {
- vec_name: String,
- unstable: bool,
-}
-
-struct SortByKeyDetection {
- vec_name: String,
- closure_arg: String,
- closure_body: String,
- reverse: bool,
- unstable: bool,
-}
-
-/// Detect if the two expressions are mirrored (identical, except one
-/// contains a and the other replaces it with b)
-fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
- match (&a_expr.kind, &b_expr.kind) {
- // Two boxes with mirrored contents
- (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
- mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- },
- // Two arrays with mirrored contents
- (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
- iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // The two exprs are function calls.
- // Check to see that the function itself and its arguments are mirrored
- (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
- mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // The two exprs are method calls.
- // Check to see that the function is the same and the arguments are mirrored
- // This is enough because the receiver of the method is listed in the arguments
- (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
- left_segment.ident == right_segment.ident
- && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // Two tuples with mirrored contents
- (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
- iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
- },
- // Two binary ops, which are the same operation and which have mirrored arguments
- (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
- left_op.node == right_op.node
- && mirrored_exprs(left_left, a_ident, right_left, b_ident)
- && mirrored_exprs(left_right, a_ident, right_right, b_ident)
- },
- // Two unary ops, which are the same operation and which have the same argument
- (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
- left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- },
- // The two exprs are literals of some kind
- (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
- (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
- (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
- mirrored_exprs(left_block, a_ident, right_block, b_ident)
- },
- (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
- left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
- },
- // Two paths: either one is a and the other is b, or they're identical to each other
- (
- ExprKind::Path(QPath::Resolved(
- _,
- Path {
- segments: left_segments,
- ..
- },
- )),
- ExprKind::Path(QPath::Resolved(
- _,
- Path {
- segments: right_segments,
- ..
- },
- )),
- ) => {
- (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
- && left_segments
- .iter()
- .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
- || (left_segments.len() == 1
- && &left_segments[0].ident == a_ident
- && right_segments.len() == 1
- && &right_segments[0].ident == b_ident)
- },
- // Matching expressions, but one or both is borrowed
- (
- ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
- ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
- ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
- (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
- (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
- _ => false,
- }
-}
-
-fn detect_lint(cx: &LateContext<'_>, expr: &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 &[
- 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 method_path.ident.name == sym::cmp;
- then {
- let (closure_body, closure_arg, reverse) = if mirrored_exprs(
- left_expr,
- left_ident,
- right_expr,
- right_ident
- ) {
- (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
- } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
- (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
- } else {
- return None;
- };
- let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
- let unstable = name == "sort_unstable_by";
-
- 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, &[])
- });
- then {
- return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
- }
- }
-
- if !expr_borrows(cx, left_expr) {
- return Some(LintTrigger::SortByKey(SortByKeyDetection {
- vec_name,
- closure_arg,
- closure_body,
- reverse,
- unstable,
- }));
- }
- }
- }
-
- None
-}
-
-fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- let ty = cx.typeck_results().expr_ty(expr);
- 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()
- },
- ),
- 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 trigger.unstable { "_unstable" } else { "" },
- ),
- Applicability::MachineApplicable,
- ),
- None => {},
- }
- }
-}
+++ /dev/null
-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,
- );
- },
- );
- }
- }
- }
-}
+++ /dev/null
-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
-}
[dependencies]
arrayvec = { version = "0.7", default-features = false }
if_chain = "1.0"
+itertools = "0.10.1"
rustc-semver = "1.1"
[features]
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
+extern crate rustc_parse_format;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
///
-/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
+/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
}
-/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
-/// diagnostic item.
-pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
- path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
+/// it matches the given diagnostic item.
+pub fn is_path_diagnostic_item<'tcx>(
+ cx: &LateContext<'_>,
+ maybe_path: &impl MaybePath<'tcx>,
+ diag_item: Symbol,
+) -> bool {
+ path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
}
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
#![allow(clippy::similar_names)] // `expr` and `expn`
+use crate::is_path_diagnostic_item;
+use crate::source::snippet_opt;
use crate::visitors::expr_visitor_no_bodies;
use arrayvec::ArrayVec;
-use if_chain::if_chain;
+use itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
+use rustc_lexer::unescape::unescape_literal;
+use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use rustc_lint::LateContext;
+use rustc_parse_format::{self as rpf, Alignment};
use rustc_span::def_id::DefId;
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
use std::ops::ControlFlow;
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
}
}
-/// A parsed `format_args!` expansion
+/// The format string doesn't exist in the HIR, so we reassemble it from source code
#[derive(Debug)]
-pub struct FormatArgsExpn<'tcx> {
- /// Span of the first argument, the format string
- pub format_string_span: Span,
- /// The format string split by formatted args like `{..}`
- pub format_string_parts: Vec<Symbol>,
- /// Values passed after the format string
- pub value_args: Vec<&'tcx Expr<'tcx>>,
- /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
- pub formatters: Vec<(usize, Symbol)>,
- /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
- /// then `formatters` represents the format args (`{..}`).
- /// If this is non-empty, it represents the format args, and the `position`
- /// parameters within the struct expressions are indexes of `formatters`.
- pub specs: Vec<&'tcx Expr<'tcx>>,
+pub struct FormatString {
+ /// Span of the whole format string literal, including `[r#]"`.
+ pub span: Span,
+ /// Snippet of the whole format string literal, including `[r#]"`.
+ pub snippet: String,
+ /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
+ pub style: Option<usize>,
+ /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
+ /// `"val \u{2013} {}"`.
+ pub unescaped: String,
+ /// The format string split by format args like `{..}`.
+ pub parts: Vec<Symbol>,
}
-impl<'tcx> FormatArgsExpn<'tcx> {
- /// Parses an expanded `format_args!` or `format_args_nl!` invocation
- pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
- macro_backtrace(expr.span).find(|macro_call| {
- matches!(
- cx.tcx.item_name(macro_call.def_id),
- sym::const_format_args | sym::format_args | sym::format_args_nl
- )
- })?;
- let mut format_string_span: Option<Span> = None;
- let mut format_string_parts: Vec<Symbol> = Vec::new();
- let mut value_args: Vec<&Expr<'_>> = Vec::new();
- let mut formatters: Vec<(usize, Symbol)> = Vec::new();
- let mut specs: Vec<&Expr<'_>> = Vec::new();
- expr_visitor_no_bodies(|e| {
- // if we're still inside of the macro definition...
- if e.span.ctxt() == expr.span.ctxt() {
- // ArgumentV1::new_<format_trait>(<value>)
- if_chain! {
- if let ExprKind::Call(callee, [val]) = e.kind;
- if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
- if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
- if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
- if seg.ident.name.as_str().starts_with("new_");
- then {
- let val_idx = if_chain! {
- if val.span.ctxt() == expr.span.ctxt();
- if let ExprKind::Field(_, field) = val.kind;
- if let Ok(idx) = field.name.as_str().parse();
- then {
- // tuple index
- idx
- } else {
- // assume the value expression is passed directly
- formatters.len()
- }
- };
- let fmt_trait = match seg.ident.name.as_str() {
- "new_display" => "Display",
- "new_debug" => "Debug",
- "new_lower_exp" => "LowerExp",
- "new_upper_exp" => "UpperExp",
- "new_octal" => "Octal",
- "new_pointer" => "Pointer",
- "new_binary" => "Binary",
- "new_lower_hex" => "LowerHex",
- "new_upper_hex" => "UpperHex",
- _ => unreachable!(),
- };
- formatters.push((val_idx, Symbol::intern(fmt_trait)));
- }
- }
- if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
- if path.segments.last().unwrap().ident.name == sym::Argument {
- specs.push(e);
- }
+impl FormatString {
+ fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
+ // format_args!(r"a {} b \", 1);
+ //
+ // expands to
+ //
+ // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
+ // &[::core::fmt::ArgumentV1::new_display(&1)]);
+ //
+ // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
+ let span = pieces.span;
+ let snippet = snippet_opt(cx, span)?;
+
+ let (inner, style) = match tokenize(&snippet).next()?.kind {
+ TokenKind::Literal { kind, .. } => {
+ let style = match kind {
+ LiteralKind::Str { .. } => None,
+ LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
+ _ => return None,
+ };
+
+ let start = style.map_or(1, |n| 2 + n);
+ let end = snippet.len() - style.map_or(1, |n| 1 + n);
+
+ (&snippet[start..end], style)
+ },
+ _ => return None,
+ };
+
+ let mode = if style.is_some() {
+ unescape::Mode::RawStr
+ } else {
+ unescape::Mode::Str
+ };
+
+ let mut unescaped = String::with_capacity(inner.len());
+ unescape_literal(inner, mode, &mut |_, ch| {
+ unescaped.push(ch.unwrap());
+ });
+
+ let mut parts = Vec::new();
+ expr_visitor_no_bodies(|expr| {
+ if let ExprKind::Lit(lit) = &expr.kind {
+ if let LitKind::Str(symbol, _) = lit.node {
+ parts.push(symbol);
}
- // walk through the macro expansion
- return true;
}
- // assume that the first expr with a differing context represents
- // (and has the span of) the format string
- if format_string_span.is_none() {
- format_string_span = Some(e.span);
- let span = e.span;
- // walk the expr and collect string literals which are format string parts
- expr_visitor_no_bodies(|e| {
- if e.span.ctxt() != span.ctxt() {
- // defensive check, probably doesn't happen
- return false;
- }
- if let ExprKind::Lit(lit) = &e.kind {
- if let LitKind::Str(symbol, _s) = lit.node {
- format_string_parts.push(symbol);
- }
- }
- true
- })
- .visit_expr(e);
+
+ true
+ })
+ .visit_expr(pieces);
+
+ Some(Self {
+ span,
+ snippet,
+ style,
+ unescaped,
+ parts,
+ })
+ }
+}
+
+struct FormatArgsValues<'tcx> {
+ /// See `FormatArgsExpn::value_args`
+ value_args: Vec<&'tcx Expr<'tcx>>,
+ /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
+ /// `value_args`
+ pos_to_value_index: Vec<usize>,
+ /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
+ format_string_span: SpanData,
+}
+
+impl<'tcx> FormatArgsValues<'tcx> {
+ fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
+ let mut pos_to_value_index = Vec::new();
+ let mut value_args = Vec::new();
+ expr_visitor_no_bodies(|expr| {
+ if expr.span.ctxt() == args.span.ctxt() {
+ // ArgumentV1::new_<format_trait>(<val>)
+ // ArgumentV1::from_usize(<val>)
+ if let ExprKind::Call(callee, [val]) = expr.kind
+ && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
+ && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
+ && path.segments.last().unwrap().ident.name == sym::ArgumentV1
+ {
+ let val_idx = if val.span.ctxt() == expr.span.ctxt()
+ && let ExprKind::Field(_, field) = val.kind
+ && let Ok(idx) = field.name.as_str().parse()
+ {
+ // tuple index
+ idx
+ } else {
+ // assume the value expression is passed directly
+ pos_to_value_index.len()
+ };
+
+ pos_to_value_index.push(val_idx);
+ }
+
+ true
} else {
- // assume that any further exprs with a differing context are value args
- value_args.push(e);
+ // assume that any expr with a differing span is a value
+ value_args.push(expr);
+
+ false
}
- // don't walk anything not from the macro expansion (e.a. inputs)
- false
})
- .visit_expr(expr);
- Some(FormatArgsExpn {
- format_string_span: format_string_span?,
- format_string_parts,
+ .visit_expr(args);
+
+ Self {
value_args,
- formatters,
- specs,
+ pos_to_value_index,
+ format_string_span,
+ }
+ }
+}
+
+/// The positions of a format argument's value, precision and width
+///
+/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
+#[derive(Debug, Default, Copy, Clone)]
+struct ParamPosition {
+ /// The position stored in `rt::v1::Argument::position`.
+ value: usize,
+ /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
+ width: Option<usize>,
+ /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
+ precision: Option<usize>,
+}
+
+/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
+fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
+ fn parse_count(expr: &Expr<'_>) -> Option<usize> {
+ // ::core::fmt::rt::v1::Count::Param(1usize),
+ if let ExprKind::Call(ctor, [val]) = expr.kind
+ && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
+ && path.segments.last()?.ident.name == sym::Param
+ && let ExprKind::Lit(lit) = &val.kind
+ && let LitKind::Int(pos, _) = lit.node
+ {
+ Some(pos as usize)
+ } else {
+ None
+ }
+ }
+
+ if let ExprKind::AddrOf(.., array) = fmt_arg.kind
+ && let ExprKind::Array(specs) = array.kind
+ {
+ Some(specs.iter().map(|spec| {
+ let mut position = ParamPosition::default();
+
+ // ::core::fmt::rt::v1::Argument {
+ // position: 0usize,
+ // format: ::core::fmt::rt::v1::FormatSpec {
+ // ..
+ // precision: ::core::fmt::rt::v1::Count::Implied,
+ // width: ::core::fmt::rt::v1::Count::Implied,
+ // },
+ // }
+
+ // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field`
+ if let ExprKind::Struct(_, fields, _) = spec.kind {
+ for field in fields {
+ match (field.ident.name, &field.expr.kind) {
+ (sym::position, ExprKind::Lit(lit)) => {
+ if let LitKind::Int(pos, _) = lit.node {
+ position.value = pos as usize;
+ }
+ },
+ (sym::format, &ExprKind::Struct(_, spec_fields, _)) => {
+ for spec_field in spec_fields {
+ match spec_field.ident.name {
+ sym::precision => {
+ position.precision = parse_count(spec_field.expr);
+ },
+ sym::width => {
+ position.width = parse_count(spec_field.expr);
+ },
+ _ => {},
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+ }
+
+ position
+ }))
+ } else {
+ None
+ }
+}
+
+/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
+fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
+ Span::new(
+ base.lo + BytePos::from_usize(inner.start),
+ base.lo + BytePos::from_usize(inner.end),
+ base.ctxt,
+ base.parent,
+ )
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum FormatParamKind {
+ /// An implicit parameter , such as `{}` or `{:?}`.
+ Implicit,
+ /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`,
+ /// `{:.0$}` or `{:.*}`.
+ Numbered,
+ /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
+ Named(Symbol),
+ /// An implicit named paramter, such as the `y` in `format!("{y}")`.
+ NamedInline(Symbol),
+}
+
+/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
+///
+/// ```
+/// let precision = 2;
+/// format!("{:.precision$}", 0.1234);
+/// ```
+///
+/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
+/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
+#[derive(Debug, Copy, Clone)]
+pub struct FormatParam<'tcx> {
+ /// The expression this parameter refers to.
+ pub value: &'tcx Expr<'tcx>,
+ /// How this paramter refers to its `value`.
+ pub kind: FormatParamKind,
+ /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
+ ///
+ /// ```text
+ /// format!("{}, { }, {0}, {name}", ...);
+ /// ^ ~~ ~ ~~~~
+ /// ```
+ pub span: Span,
+}
+
+impl<'tcx> FormatParam<'tcx> {
+ fn new(
+ mut kind: FormatParamKind,
+ position: usize,
+ inner: rpf::InnerSpan,
+ values: &FormatArgsValues<'tcx>,
+ ) -> Option<Self> {
+ let value_index = *values.pos_to_value_index.get(position)?;
+ let value = *values.value_args.get(value_index)?;
+ let span = span_from_inner(values.format_string_span, inner);
+
+ // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
+ // into the format string
+ if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
+ kind = FormatParamKind::NamedInline(name);
+ }
+
+ Some(Self { value, kind, span })
+ }
+}
+
+/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
+/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
+#[derive(Debug, Copy, Clone)]
+pub enum Count<'tcx> {
+ /// Specified with a literal number, stores the value.
+ Is(usize, Span),
+ /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
+ /// `FormatParamKind::Numbered`.
+ Param(FormatParam<'tcx>),
+ /// Not specified.
+ Implied,
+}
+
+impl<'tcx> Count<'tcx> {
+ fn new(
+ count: rpf::Count<'_>,
+ position: Option<usize>,
+ inner: Option<rpf::InnerSpan>,
+ values: &FormatArgsValues<'tcx>,
+ ) -> Option<Self> {
+ Some(match count {
+ rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
+ rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new(
+ FormatParamKind::Named(Symbol::intern(name)),
+ position?,
+ span,
+ values,
+ )?),
+ rpf::Count::CountIsParam(_) => {
+ Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?)
+ },
+ rpf::Count::CountImplied => Self::Implied,
+ })
+ }
+
+ pub fn is_implied(self) -> bool {
+ matches!(self, Count::Implied)
+ }
+
+ pub fn param(self) -> Option<FormatParam<'tcx>> {
+ match self {
+ Count::Param(param) => Some(param),
+ _ => None,
+ }
+ }
+}
+
+/// Specification for the formatting of an argument in the format string. See
+/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
+#[derive(Debug)]
+pub struct FormatSpec<'tcx> {
+ /// Optionally specified character to fill alignment with.
+ pub fill: Option<char>,
+ /// Optionally specified alignment.
+ pub align: Alignment,
+ /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
+ pub flags: u32,
+ /// Represents either the maximum width or the integer precision.
+ pub precision: Count<'tcx>,
+ /// The minimum width, will be padded according to `width`/`align`
+ pub width: Count<'tcx>,
+ /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
+ /// `{:?}`.
+ pub r#trait: Symbol,
+ pub trait_span: Option<Span>,
+}
+
+impl<'tcx> FormatSpec<'tcx> {
+ fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
+ Some(Self {
+ fill: spec.fill,
+ align: spec.align,
+ flags: spec.flags,
+ precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?,
+ width: Count::new(spec.width, positions.width, spec.width_span, values)?,
+ r#trait: match spec.ty {
+ "" => sym::Display,
+ "?" => sym::Debug,
+ "o" => sym!(Octal),
+ "x" => sym!(LowerHex),
+ "X" => sym!(UpperHex),
+ "p" => sym::Pointer,
+ "b" => sym!(Binary),
+ "e" => sym!(LowerExp),
+ "E" => sym!(UpperExp),
+ _ => return None,
+ },
+ trait_span: spec
+ .ty_span
+ .map(|span| span_from_inner(values.format_string_span, span)),
})
}
- /// Finds a nested call to `format_args!` within a `format!`-like macro call
+ /// Returns true if this format spec would change the contents of a string when formatted
+ pub fn has_string_formatting(&self) -> bool {
+ self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
+ }
+}
+
+/// A format argument, such as `{}`, `{foo:?}`.
+#[derive(Debug)]
+pub struct FormatArg<'tcx> {
+ /// The parameter the argument refers to.
+ pub param: FormatParam<'tcx>,
+ /// How to format `param`.
+ pub format: FormatSpec<'tcx>,
+ /// span of the whole argument, `{..}`.
+ pub span: Span,
+}
+
+/// A parsed `format_args!` expansion.
+#[derive(Debug)]
+pub struct FormatArgsExpn<'tcx> {
+ /// The format string literal.
+ pub format_string: FormatString,
+ // The format arguments, such as `{:?}`.
+ pub args: Vec<FormatArg<'tcx>>,
+ /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
+ /// include this added newline.
+ pub newline: bool,
+ /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
+ /// `format!("{x} {} {y}", 1, z + 2)`.
+ value_args: Vec<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsExpn<'tcx> {
+ pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ let macro_name = macro_backtrace(expr.span)
+ .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+ .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
+ let newline = macro_name == sym::format_args_nl;
+
+ // ::core::fmt::Arguments::new_v1(pieces, args)
+ // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
+ if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
+ && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
+ && is_path_diagnostic_item(cx, ty, sym::Arguments)
+ && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
+ {
+ let format_string = FormatString::new(cx, pieces)?;
+
+ let mut parser = rpf::Parser::new(
+ &format_string.unescaped,
+ format_string.style,
+ Some(format_string.snippet.clone()),
+ // `format_string.unescaped` does not contain the appended newline
+ false,
+ rpf::ParseMode::Format,
+ );
+
+ let parsed_args = parser
+ .by_ref()
+ .filter_map(|piece| match piece {
+ rpf::Piece::NextArgument(a) => Some(a),
+ rpf::Piece::String(_) => None,
+ })
+ .collect_vec();
+ if !parser.errors.is_empty() {
+ return None;
+ }
+
+ let positions = if let Some(fmt_arg) = rest.first() {
+ // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
+ // them.
+
+ Either::Left(parse_rt_fmt(fmt_arg)?)
+ } else {
+ // If no format specs are given, the positions are in the given order and there are
+ // no `precision`/`width`s to consider.
+
+ Either::Right((0..).map(|n| ParamPosition {
+ value: n,
+ width: None,
+ precision: None,
+ }))
+ };
+
+ let values = FormatArgsValues::new(args, format_string.span.data());
+
+ let args = izip!(positions, parsed_args, parser.arg_places)
+ .map(|(position, parsed_arg, arg_span)| {
+ Some(FormatArg {
+ param: FormatParam::new(
+ match parsed_arg.position {
+ rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
+ rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
+ // NamedInline is handled by `FormatParam::new()`
+ rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
+ },
+ position.value,
+ parsed_arg.position_span,
+ &values,
+ )?,
+ format: FormatSpec::new(parsed_arg.format, position, &values)?,
+ span: span_from_inner(values.format_string_span, arg_span),
+ })
+ })
+ .collect::<Option<Vec<_>>>()?;
+
+ Some(Self {
+ format_string,
+ args,
+ value_args: values.value_args,
+ newline,
+ })
+ } else {
+ None
+ }
+ }
+
pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
let mut format_args = None;
expr_visitor_no_bodies(|e| {
format_args
}
- /// Returns a vector of `FormatArgsArg`.
- pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
- if self.specs.is_empty() {
- let args = std::iter::zip(&self.value_args, &self.formatters)
- .map(|(value, &(_, format_trait))| FormatArgsArg {
- value,
- format_trait,
- spec: None,
- })
- .collect();
- return Some(args);
- }
- self.specs
- .iter()
- .map(|spec| {
- if_chain! {
- // struct `core::fmt::rt::v1::Argument`
- if let ExprKind::Struct(_, fields, _) = spec.kind;
- if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
- if let ExprKind::Lit(lit) = &position_field.expr.kind;
- if let LitKind::Int(position, _) = lit.node;
- if let Ok(i) = usize::try_from(position);
- if let Some(&(j, format_trait)) = self.formatters.get(i);
- then {
- Some(FormatArgsArg {
- value: self.value_args[j],
- format_trait,
- spec: Some(spec),
- })
- } else {
- None
- }
- }
- })
- .collect()
- }
-
/// Source callsite span of all inputs
pub fn inputs_span(&self) -> Span {
match *self.value_args {
- [] => self.format_string_span,
+ [] => self.format_string.span,
[.., last] => self
- .format_string_span
- .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
+ .format_string
+ .span
+ .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
}
}
-}
-/// Type representing a `FormatArgsExpn`'s format arguments
-pub struct FormatArgsArg<'tcx> {
- /// An element of `value_args` according to `position`
- pub value: &'tcx Expr<'tcx>,
- /// An element of `args` according to `position`
- pub format_trait: Symbol,
- /// An element of `specs`
- pub spec: Option<&'tcx Expr<'tcx>>,
-}
-
-impl<'tcx> FormatArgsArg<'tcx> {
- /// Returns true if any formatting parameters are used that would have an effect on strings,
- /// like `{:+2}` instead of just `{}`.
- pub fn has_string_formatting(&self) -> bool {
- self.spec.map_or(false, |spec| {
- // `!` because these conditions check that `self` is unformatted.
- !if_chain! {
- // struct `core::fmt::rt::v1::Argument`
- if let ExprKind::Struct(_, fields, _) = spec.kind;
- if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
- // struct `core::fmt::rt::v1::FormatSpec`
- if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
- if subfields.iter().all(|field| match field.ident.name {
- sym::precision | sym::width => match field.expr.kind {
- ExprKind::Path(QPath::Resolved(_, path)) => {
- path.segments.last().unwrap().ident.name == sym::Implied
- }
- _ => false,
- }
- _ => true,
- });
- then { true } else { false }
- }
- })
+ /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
+ pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
+ self.args
+ .iter()
+ .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
+ .flatten()
}
}
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
// run-rustfix
-#![allow(unreachable_code)]
-#![allow(unused_macros)]
-#![allow(unused_variables)]
+#![allow(unused)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::eq_op)]
#![allow(clippy::print_literal)]
// https://github.com/rust-lang/rust-clippy/issues/7903
println!("{foo}{foo:?}", foo = "foo".to_string());
}
+
+fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
+ println!(
+ "{:<9} {:<10} {}",
+ format!("0x{:x}", vendor_id),
+ format!("0x{:x}", product_id),
+ name
+ );
+}
// run-rustfix
-#![allow(unreachable_code)]
-#![allow(unused_macros)]
-#![allow(unused_variables)]
+#![allow(unused)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::eq_op)]
#![allow(clippy::print_literal)]
// https://github.com/rust-lang/rust-clippy/issues/7903
println!("{foo}{foo:?}", foo = "foo".to_string());
}
+
+fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
+ println!(
+ "{:<9} {:<10} {}",
+ format!("0x{:x}", vendor_id),
+ format!("0x{:x}", product_id),
+ name
+ );
+}
error: `to_string` applied to a type that implements `Display` in `format!` args
- --> $DIR/format_args.rs:76:72
+ --> $DIR/format_args.rs:74:72
|
LL | let _ = format!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
= note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
error: `to_string` applied to a type that implements `Display` in `write!` args
- --> $DIR/format_args.rs:80:27
+ --> $DIR/format_args.rs:78:27
|
LL | Location::caller().to_string()
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `writeln!` args
- --> $DIR/format_args.rs:85:27
+ --> $DIR/format_args.rs:83:27
|
LL | Location::caller().to_string()
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `print!` args
- --> $DIR/format_args.rs:87:63
+ --> $DIR/format_args.rs:85:63
|
LL | print!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:88:65
+ --> $DIR/format_args.rs:86:65
|
LL | println!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `eprint!` args
- --> $DIR/format_args.rs:89:64
+ --> $DIR/format_args.rs:87:64
|
LL | eprint!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `eprintln!` args
- --> $DIR/format_args.rs:90:66
+ --> $DIR/format_args.rs:88:66
|
LL | eprintln!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `format_args!` args
- --> $DIR/format_args.rs:91:77
+ --> $DIR/format_args.rs:89:77
|
LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `assert!` args
- --> $DIR/format_args.rs:92:70
+ --> $DIR/format_args.rs:90:70
|
LL | assert!(true, "error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
- --> $DIR/format_args.rs:93:73
+ --> $DIR/format_args.rs:91:73
|
LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
- --> $DIR/format_args.rs:94:73
+ --> $DIR/format_args.rs:92:73
|
LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `panic!` args
- --> $DIR/format_args.rs:95:63
+ --> $DIR/format_args.rs:93:63
|
LL | panic!("error: something failed at {}", Location::caller().to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:96:20
+ --> $DIR/format_args.rs:94:20
|
LL | println!("{}", X(1).to_string());
| ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:97:20
+ --> $DIR/format_args.rs:95:20
|
LL | println!("{}", Y(&X(1)).to_string());
| ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:98:24
+ --> $DIR/format_args.rs:96:24
|
LL | println!("{}", Z(1).to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:99:20
+ --> $DIR/format_args.rs:97:20
|
LL | println!("{}", x.to_string());
| ^^^^^^^^^^^^^ help: use this: `**x`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:100:20
+ --> $DIR/format_args.rs:98:20
|
LL | println!("{}", x_ref.to_string());
| ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:102:39
+ --> $DIR/format_args.rs:100:39
|
LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:103:52
+ --> $DIR/format_args.rs:101:52
|
LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:104:39
+ --> $DIR/format_args.rs:102:39
|
LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
| ^^^^^^^^^^^^ help: remove this
error: `to_string` applied to a type that implements `Display` in `println!` args
- --> $DIR/format_args.rs:105:52
+ --> $DIR/format_args.rs:103:52
|
LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
| ^^^^^^^^^^^^ help: remove this
#![warn(clippy::only_used_in_recursion)]
-fn simple(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { simple(a - 1, b) }
+fn _simple(x: u32) -> u32 {
+ x
}
-fn with_calc(a: usize, b: isize) -> usize {
- if a == 0 { 1 } else { with_calc(a - 1, -b + 1) }
+fn _simple2(x: u32) -> u32 {
+ _simple(x)
}
-fn tuple((a, b): (usize, usize)) -> usize {
- if a == 0 { 1 } else { tuple((a - 1, b + 1)) }
+fn _one_unused(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
}
-fn let_tuple(a: usize, b: usize) -> usize {
- let (c, d) = (a, b);
- if c == 0 { 1 } else { let_tuple(c - 1, d + 1) }
+fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+ if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
}
-fn array([a, b]: [usize; 2]) -> usize {
- if a == 0 { 1 } else { array([a - 1, b + 1]) }
-}
-
-fn index(a: usize, mut b: &[usize], c: usize) -> usize {
- if a == 0 { 1 } else { index(a - 1, b, c + b[0]) }
-}
-
-fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
- let c = loop {
- b += 1;
- c += 1;
- if c == 10 {
- break b;
- }
- };
-
- if a == 0 { 1 } else { break_(a - 1, c, c) }
+fn _with_calc(flag: u32, a: i64) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ _with_calc(flag - 1, (-a + 10) * 5)
+ }
}
-// this has a side effect
-fn mut_ref(a: usize, b: &mut usize) -> usize {
- *b = 1;
- if a == 0 { 1 } else { mut_ref(a - 1, b) }
+// Don't lint
+fn _used_with_flag(flag: u32, a: u32) -> usize {
+ if flag == 0 { 0 } else { _used_with_flag(flag ^ a, a - 1) }
}
-fn mut_ref2(a: usize, b: &mut usize) -> usize {
- let mut c = *b;
- if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) }
+fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ _used_with_unused(flag - 1, -a, a + b)
+ }
}
-fn not_primitive(a: usize, b: String) -> usize {
- if a == 0 { 1 } else { not_primitive(a - 1, b) }
+fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ _codependent_unused(flag - 1, a * b, a + b)
+ }
}
-// this doesn't have a side effect,
-// but `String` is not primitive.
-fn not_primitive_op(a: usize, b: String, c: &str) -> usize {
- if a == 1 { 1 } else { not_primitive_op(a, b + c, c) }
+fn _not_primitive(flag: u32, b: String) -> usize {
+ if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
}
struct A;
impl A {
- fn method(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { A::method(a - 1, b - 1) }
+ fn _method(flag: usize, a: usize) -> usize {
+ if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
}
- fn method2(&self, a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { self.method2(a - 1, b + 1) }
+ fn _method_self(&self, flag: usize, a: usize) -> usize {
+ if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
}
}
trait B {
- fn hello(a: usize, b: usize) -> usize;
-
- fn hello2(&self, a: usize, b: usize) -> usize;
+ fn method(flag: u32, a: usize) -> usize;
+ fn method_self(&self, flag: u32, a: usize) -> usize;
}
impl B for A {
- fn hello(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { A::hello(a - 1, b + 1) }
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { Self::method(flag - 1, a) }
}
- fn hello2(&self, a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
}
}
-trait C {
- fn hello(a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { Self::hello(a - 1, b + 1) }
+impl B for () {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { a }
}
- fn hello2(&self, a: usize, b: usize) -> usize {
- if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { a }
}
}
-fn ignore(a: usize, _: usize) -> usize {
- if a == 1 { 1 } else { ignore(a - 1, 0) }
-}
+impl B for u32 {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { <() as B>::method(flag, a) }
+ }
-fn ignore2(a: usize, _b: usize) -> usize {
- if a == 1 { 1 } else { ignore2(a - 1, _b) }
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { ().method_self(flag, a) }
+ }
}
-fn f1(a: u32) -> u32 {
- a
-}
+trait C {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+ }
-fn f2(a: u32) -> u32 {
- f1(a)
+ fn method_self(&self, flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+ }
}
-fn inner_fn(a: u32) -> u32 {
- fn inner_fn(a: u32) -> u32 {
- a
- }
- inner_fn(a)
+fn _ignore(flag: usize, _a: usize) -> usize {
+ if flag == 0 { 0 } else { _ignore(flag - 1, _a) }
}
fn main() {}
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:3:21
+ --> $DIR/only_used_in_recursion.rs:11:27
|
-LL | fn simple(a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _one_unused(flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
|
= note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:12:53
+ |
+LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:15:27
+ |
+LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:16:53
+ |
+LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:15:35
+ |
+LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:16:56
+ |
+LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion.rs:19:26
+ |
+LL | fn _with_calc(flag: u32, a: i64) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:23:32
+ |
+LL | _with_calc(flag - 1, (-a + 10) * 5)
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:7:24
+ --> $DIR/only_used_in_recursion.rs:32:33
+ |
+LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:36:38
|
-LL | fn with_calc(a: usize, b: isize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | _used_with_unused(flag - 1, -a, a + b)
+ | ^ ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:11:14
+ --> $DIR/only_used_in_recursion.rs:32:41
+ |
+LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:36:45
|
-LL | fn tuple((a, b): (usize, usize)) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | _used_with_unused(flag - 1, -a, a + b)
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:15:24
+ --> $DIR/only_used_in_recursion.rs:40:35
|
-LL | fn let_tuple(a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:44:39
+ |
+LL | _codependent_unused(flag - 1, a * b, a + b)
+ | ^ ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:20:14
+ --> $DIR/only_used_in_recursion.rs:40:43
+ |
+LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
|
-LL | fn array([a, b]: [usize; 2]) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:44:43
+ |
+LL | _codependent_unused(flag - 1, a * b, a + b)
+ | ^ ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:24:20
+ --> $DIR/only_used_in_recursion.rs:48:30
+ |
+LL | fn _not_primitive(flag: u32, b: String) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
|
-LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
- | ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:49:56
+ |
+LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:24:37
+ --> $DIR/only_used_in_recursion.rs:55:29
+ |
+LL | fn _method(flag: usize, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:56:59
|
-LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_c`
+LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:28:21
+ --> $DIR/only_used_in_recursion.rs:59:22
+ |
+LL | fn _method_self(&self, flag: usize, a: usize) -> usize {
+ | ^^^^
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:60:35
|
-LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
- | ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
+LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
+ | ^^^^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:46:23
+ --> $DIR/only_used_in_recursion.rs:59:41
|
-LL | fn mut_ref2(a: usize, b: &mut usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | fn _method_self(&self, flag: usize, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:60:63
+ |
+LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:51:28
+ --> $DIR/only_used_in_recursion.rs:70:26
+ |
+LL | fn method(flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
|
-LL | fn not_primitive(a: usize, b: String) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:71:58
+ |
+LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:68:33
+ --> $DIR/only_used_in_recursion.rs:74:38
+ |
+LL | fn method_self(&self, flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
|
-LL | fn method2(&self, a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:75:62
+ |
+LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:90:24
+ --> $DIR/only_used_in_recursion.rs:100:26
+ |
+LL | fn method(flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:101:58
|
-LL | fn hello(a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) }
+ | ^
error: parameter is only used in recursion
- --> $DIR/only_used_in_recursion.rs:94:32
+ --> $DIR/only_used_in_recursion.rs:104:38
+ |
+LL | fn method_self(&self, flag: u32, a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion.rs:105:62
|
-LL | fn hello2(&self, a: usize, b: usize) -> usize {
- | ^ help: if this is intentional, prefix with an underscore: `_b`
+LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
+ | ^
-error: aborting due to 13 previous errors
+error: aborting due to 16 previous errors
--- /dev/null
+#![warn(clippy::only_used_in_recursion)]
+
+fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
+ fn inner(flag: u32, a: u32) -> u32 {
+ if flag == 0 { 0 } else { inner(flag, a) }
+ }
+
+ let x = inner(flag, a);
+ if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
+}
+
+fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
+ if let Some(x) = a.and_then(|x| f(x, x)) {
+ _with_closure(Some(x), b, f)
+ } else {
+ 0
+ }
+}
+
+// Issue #8560
+trait D {
+ fn foo(&mut self, arg: u32) -> u32;
+}
+
+mod m {
+ pub struct S(u32);
+ impl S {
+ pub fn foo(&mut self, arg: u32) -> u32 {
+ arg + self.0
+ }
+ }
+}
+
+impl D for m::S {
+ fn foo(&mut self, arg: u32) -> u32 {
+ self.foo(arg)
+ }
+}
+
+// Issue #8782
+fn only_let(x: u32) {
+ let y = 10u32;
+ let _z = x * y;
+}
+
+trait E<T: E<()>> {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ <T as E<()>>::method(flag - 1, a)
+ }
+ }
+}
+
+impl E<()> for () {
+ fn method(flag: u32, a: usize) -> usize {
+ if flag == 0 { 0 } else { a }
+ }
+}
+
+fn overwritten_param(flag: u32, mut a: usize) -> usize {
+ if flag == 0 {
+ return 0;
+ } else if flag > 5 {
+ a += flag as usize;
+ } else {
+ a = 5;
+ }
+ overwritten_param(flag, a)
+}
+
+fn field_direct(flag: u32, mut a: (usize,)) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ a.0 += 5;
+ field_direct(flag - 1, a)
+ }
+}
+
+fn field_deref(flag: u32, a: &mut Box<(usize,)>) -> usize {
+ if flag == 0 {
+ 0
+ } else {
+ a.0 += 5;
+ field_deref(flag - 1, a)
+ }
+}
+
+fn main() {}
--- /dev/null
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:3:35
+ |
+LL | fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+ = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:9:52
+ |
+LL | if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:4:25
+ |
+LL | fn inner(flag: u32, a: u32) -> u32 {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:5:47
+ |
+LL | if flag == 0 { 0 } else { inner(flag, a) }
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:12:34
+ |
+LL | fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
+ | ^ help: if this is intentional, prefix it with an underscore: `_b`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:14:32
+ |
+LL | _with_closure(Some(x), b, f)
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:62:37
+ |
+LL | fn overwritten_param(flag: u32, mut a: usize) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:70:29
+ |
+LL | overwritten_param(flag, a)
+ | ^
+
+error: parameter is only used in recursion
+ --> $DIR/only_used_in_recursion2.rs:73:32
+ |
+LL | fn field_direct(flag: u32, mut a: (usize,)) -> usize {
+ | ^ help: if this is intentional, prefix it with an underscore: `_a`
+ |
+note: parameter used here
+ --> $DIR/only_used_in_recursion2.rs:78:32
+ |
+LL | field_direct(flag - 1, a)
+ | ^
+
+error: aborting due to 5 previous errors
+
#![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")
}
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...