rust/clippy_lints/src/methods/mod.rs
Timo e8185ec091
Extend implicit_clone to handle to_string calls (#14177)
Put another way, merge `string_to_string` into `implicit_clone`, as
suggested here:
https://github.com/rust-lang/rust-clippy/issues/14173#issuecomment-2645846915

Note: [I
wrote](b8913894a1)
this comment:
6cdb7f68c3/clippy_lints/src/methods/implicit_clone.rs (L43-L45)

Here is the context for why I wrote it:
https://github.com/rust-lang/rust-clippy/pull/7978#discussion_r769128853

Regardless, it's probably time for the comment to go away. Extending
`implicit_clone` to handle `to_string` calls yields many hits within
Clippy's codebase.

changelog: extend `implicit_clone` to handle `to_string` calls
2025-08-02 11:41:00 +00:00

5827 lines
194 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 chars_last_cmp_with_unwrap;
mod chars_next_cmp;
mod chars_next_cmp_with_unwrap;
mod clear_with_drain;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
mod collapsible_str_replace;
mod double_ended_iterator_last;
mod drain_collect;
mod err_expect;
mod expect_fun_call;
mod extend_with_drain;
mod filetype_is_file;
mod filter_map;
mod filter_map_bool_then;
mod filter_map_identity;
mod filter_map_next;
mod filter_next;
mod flat_map_identity;
mod flat_map_option;
mod format_collect;
mod from_iter_instead_of_collect;
mod get_first;
mod get_last_with_len;
mod get_unwrap;
mod implicit_clone;
mod inefficient_to_string;
mod inspect_for_each;
mod into_iter_on_ref;
mod io_other_error;
mod ip_constant;
mod is_digit_ascii_radix;
mod is_empty;
mod iter_cloned_collect;
mod iter_count;
mod iter_filter;
mod iter_kv_map;
mod iter_next_slice;
mod iter_nth;
mod iter_nth_zero;
mod iter_on_single_or_empty_collections;
mod iter_out_of_bounds;
mod iter_overeager_cloned;
mod iter_skip_next;
mod iter_skip_zero;
mod iter_with_drain;
mod iterator_step_by_zero;
mod join_absolute_paths;
mod manual_c_str_literals;
mod manual_contains;
mod manual_inspect;
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
mod manual_repeat_n;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
mod manual_try_fold;
mod map_all_any_identity;
mod map_clone;
mod map_collect_result_unit;
mod map_err_ignore;
mod map_flatten;
mod map_identity;
mod map_unwrap_or;
mod map_with_unused_argument_over_ranges;
mod mut_mutex_lock;
mod needless_as_bytes;
mod needless_character_iteration;
mod needless_collect;
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_cloned;
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 path_ends_with_ext;
mod range_zip_with_len;
mod read_line_without_trim;
mod readonly_write_lock;
mod redundant_as_str;
mod repeat_once;
mod result_map_or_else_none;
mod return_and_then;
mod search_is_some;
mod seek_from_current;
mod seek_to_start_instead_of_rewind;
mod single_char_add_str;
mod single_char_insert_string;
mod single_char_push_string;
mod skip_while_next;
mod sliced_string_as_bytes;
mod stable_sort_primitive;
mod str_split;
mod str_splitn;
mod string_extend_chars;
mod string_lit_chars_any;
mod suspicious_command_arg_space;
mod suspicious_map;
mod suspicious_splitn;
mod suspicious_to_owned;
mod swap_with_temporary;
mod type_id_on_box;
mod unbuffered_bytes;
mod uninit_assumed_init;
mod unit_hash;
mod unnecessary_fallible_conversions;
mod unnecessary_filter_map;
mod unnecessary_first_then_check;
mod unnecessary_fold;
mod unnecessary_get_then_check;
mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
mod unnecessary_literal_unwrap;
mod unnecessary_map_or;
mod unnecessary_min_or_max;
mod unnecessary_result_map_or_else;
mod unnecessary_sort_by;
mod unnecessary_to_owned;
mod unused_enumerate_index;
mod unwrap_expect_used;
mod useless_asref;
mod useless_nonzero_new_unchecked;
mod utils;
mod vec_resize_to_zero;
mod verbose_file_reads;
mod waker_clone_wake;
mod wrong_self_convention;
mod zst_offset;
use clippy_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::macros::FormatArgsStorage;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym};
pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
use rustc_abi::ExternAbi;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{self, TraitRef, Ty};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, Symbol, kw};
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `cloned()` on an `Iterator` or `Option` where
/// `copied()` could be used instead.
///
/// ### Why is this bad?
/// `copied()` is better because it guarantees that the type being cloned
/// implements `Copy`.
///
/// ### Example
/// ```no_run
/// [1, 2, 3].iter().cloned();
/// ```
/// Use instead:
/// ```no_run
/// [1, 2, 3].iter().copied();
/// ```
#[clippy::version = "1.53.0"]
pub CLONED_INSTEAD_OF_COPIED,
pedantic,
"used `cloned` where `copied` could be used instead"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for consecutive calls to `str::replace` (2 or more)
/// that can be collapsed into a single call.
///
/// ### Why is this bad?
/// Consecutive `str::replace` calls scan the string multiple times
/// with repetitive code.
///
/// ### Example
/// ```no_run
/// let hello = "hesuo worpd"
/// .replace('s', "l")
/// .replace("u", "l")
/// .replace('p', "l");
/// ```
/// Use instead:
/// ```no_run
/// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l");
/// ```
#[clippy::version = "1.65.0"]
pub COLLAPSIBLE_STR_REPLACE,
perf,
"collapse consecutive calls to str::replace (2 or more) into a single call"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
///
/// ### Why is this bad?
/// It's often inefficient to clone all elements of an iterator, when eventually, only some
/// of them will be consumed.
///
/// ### Known Problems
/// This `lint` removes the side of effect of cloning items in the iterator.
/// A code that relies on that side-effect could fail.
///
/// ### Examples
/// ```no_run
/// # let vec = vec!["string".to_string()];
/// vec.iter().cloned().take(10);
/// vec.iter().cloned().last();
/// ```
///
/// Use instead:
/// ```no_run
/// # let vec = vec!["string".to_string()];
/// vec.iter().take(10).cloned();
/// vec.iter().last().cloned();
/// ```
#[clippy::version = "1.60.0"]
pub ITER_OVEREAGER_CLONED,
perf,
"using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `Iterator::flat_map()` where `filter_map()` could be
/// used instead.
///
/// ### Why is this bad?
/// `filter_map()` is known to always produce 0 or 1 output items per input item,
/// rather than however many the inner iterator type produces.
/// Therefore, it maintains the upper bound in `Iterator::size_hint()`,
/// and communicates to the reader that the input items are not being expanded into
/// multiple output items without their having to notice that the mapping function
/// returns an `Option`.
///
/// ### Example
/// ```no_run
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
/// ```
/// Use instead:
/// ```no_run
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
/// ```
#[clippy::version = "1.53.0"]
pub FLAT_MAP_OPTION,
pedantic,
"used `flat_map` where `filter_map` could be used instead"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
///
/// ### Why restrict this?
/// It is better to handle the `None` or `Err` case,
/// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
/// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
/// `Allow` by default.
///
/// `result.unwrap()` will let the thread panic on `Err` values.
/// Normally, you want to implement more sophisticated error handling,
/// and propagate errors upwards with `?` operator.
///
/// Even if you want to panic on errors, not all `Error`s implement good
/// messages on display. Therefore, it may be beneficial to look at the places
/// where they may get displayed. Activate this lint to do just that.
///
/// ### Examples
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.unwrap();
/// result.unwrap();
/// ```
///
/// Use instead:
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.expect("more helpful message");
/// result.expect("more helpful message");
/// ```
///
/// If [expect_used](#expect_used) is enabled, instead:
/// ```rust,ignore
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option?;
///
/// // or
///
/// result?;
/// ```
#[clippy::version = "1.45.0"]
pub UNWRAP_USED,
restriction,
"using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.unwrap()` related calls on `Result`s and `Option`s that are constructed.
///
/// ### Why is this bad?
/// It is better to write the value directly without the indirection.
///
/// ### Examples
/// ```no_run
/// let val1 = Some(1).unwrap();
/// let val2 = Ok::<_, ()>(1).unwrap();
/// let val3 = Err::<(), _>(1).unwrap_err();
/// ```
///
/// Use instead:
/// ```no_run
/// let val1 = 1;
/// let val2 = 1;
/// let val3 = 1;
/// ```
#[clippy::version = "1.72.0"]
pub UNNECESSARY_LITERAL_UNWRAP,
complexity,
"using `unwrap()` related calls on `Result` and `Option` constructors"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
///
/// ### Why restrict this?
/// Usually it is better to handle the `None` or `Err` case.
/// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
/// this lint is `Allow` by default.
///
/// `result.expect()` will let the thread panic on `Err`
/// values. Normally, you want to implement more sophisticated error handling,
/// and propagate errors upwards with `?` operator.
///
/// ### Examples
/// ```rust,ignore
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.expect("one");
/// result.expect("one");
/// ```
///
/// Use instead:
/// ```rust,ignore
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option?;
///
/// // or
///
/// result?;
/// ```
#[clippy::version = "1.45.0"]
pub EXPECT_USED,
restriction,
"using `.expect()` on `Result` or `Option`, which might be better handled"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for methods that should live in a trait
/// implementation of a `std` trait (see [llogiq's blog
/// post](http://llogiq.github.io/2015/07/30/traits.html) for further
/// information) instead of an inherent implementation.
///
/// ### Why is this bad?
/// Implementing the traits improve ergonomics for users of
/// the code, often with very little cost. Also people seeing a `mul(...)`
/// method
/// may expect `*` to work equally, so you should have good reason to disappoint
/// them.
///
/// ### Example
/// ```no_run
/// struct X;
/// impl X {
/// fn add(&self, other: &X) -> X {
/// // ..
/// # X
/// }
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub SHOULD_IMPLEMENT_TRAIT,
style,
"defining a method that should be implementing a std trait"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for methods with certain name prefixes or suffixes, and which
/// do not adhere to standard conventions regarding how `self` is taken.
/// The actual rules are:
///
/// |Prefix |Postfix |`self` taken | `self` type |
/// |-------|------------|-------------------------------|--------------|
/// |`as_` | none |`&self` or `&mut self` | any |
/// |`from_`| none | none | any |
/// |`into_`| none |`self` | any |
/// |`is_` | none |`&mut self` or `&self` or none | any |
/// |`to_` | `_mut` |`&mut self` | any |
/// |`to_` | not `_mut` |`self` | `Copy` |
/// |`to_` | not `_mut` |`&self` | not `Copy` |
///
/// Note: Clippy doesn't trigger methods with `to_` prefix in:
/// - Traits definition.
/// Clippy can not tell if a type that implements a trait is `Copy` or not.
/// - Traits implementation, when `&self` is taken.
/// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
/// (see e.g. the `std::string::ToString` trait).
///
/// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
///
/// Please find more info here:
/// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
///
/// ### Why is this bad?
/// Consistency breeds readability. If you follow the
/// conventions, your users won't be surprised that they, e.g., need to supply a
/// mutable reference to a `as_..` function.
///
/// ### Example
/// ```no_run
/// # struct X;
/// impl X {
/// fn as_str(self) -> &'static str {
/// // ..
/// # ""
/// }
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// # struct X;
/// impl X {
/// fn as_str(&self) -> &'static str {
/// // ..
/// # ""
/// }
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRONG_SELF_CONVENTION,
style,
"defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `ok().expect(..)`.
///
/// ### Why is this bad?
/// Because you usually call `expect()` on the `Result`
/// directly to get a better error message.
///
/// ### Example
/// ```no_run
/// # let x = Ok::<_, ()>(());
/// x.ok().expect("why did I do this again?");
/// ```
///
/// Use instead:
/// ```no_run
/// # let x = Ok::<_, ()>(());
/// x.expect("why did I do this again?");
/// ```
#[clippy::version = "pre 1.29.0"]
pub OK_EXPECT,
style,
"using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.err().expect()` calls on the `Result` type.
///
/// ### Why is this bad?
/// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
///
/// ### Example
/// ```should_panic
/// let x: Result<u32, &str> = Ok(10);
/// x.err().expect("Testing err().expect()");
/// ```
/// Use instead:
/// ```should_panic
/// let x: Result<u32, &str> = Ok(10);
/// x.expect_err("Testing expect_err");
/// ```
#[clippy::version = "1.62.0"]
pub ERR_EXPECT,
style,
r#"using `.err().expect("")` when `.expect_err("")` can be used"#
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usages of the following functions with an argument that constructs a default value
/// (e.g., `Default::default` or `String::new`):
/// - `unwrap_or`
/// - `unwrap_or_else`
/// - `or_insert`
/// - `or_insert_with`
///
/// ### Why is this bad?
/// Readability. Using `unwrap_or_default` in place of `unwrap_or`/`unwrap_or_else`, or `or_default`
/// in place of `or_insert`/`or_insert_with`, is simpler and more concise.
///
/// ### Known problems
/// In some cases, the argument of `unwrap_or`, etc. is needed for type inference. The lint uses a
/// heuristic to try to identify such cases. However, the heuristic can produce false negatives.
///
/// ### Examples
/// ```no_run
/// # let x = Some(1);
/// # let mut map = std::collections::HashMap::<u64, String>::new();
/// x.unwrap_or(Default::default());
/// map.entry(42).or_insert_with(String::new);
/// ```
///
/// Use instead:
/// ```no_run
/// # let x = Some(1);
/// # let mut map = std::collections::HashMap::<u64, String>::new();
/// x.unwrap_or_default();
/// map.entry(42).or_default();
/// ```
#[clippy::version = "1.56.0"]
pub UNWRAP_OR_DEFAULT,
style,
"using `.unwrap_or`, etc. with an argument that constructs a default value"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
/// `result.map(_).unwrap_or_else(_)`.
///
/// ### Why is this bad?
/// Readability, these can be written more concisely (resp.) as
/// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
///
/// ### Known problems
/// The order of the arguments is not in execution order
///
/// ### Examples
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// # fn some_function(foo: ()) -> usize { 1 }
/// option.map(|a| a + 1).unwrap_or(0);
/// option.map(|a| a > 10).unwrap_or(false);
/// result.map(|a| a + 1).unwrap_or_else(some_function);
/// ```
///
/// Use instead:
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// # fn some_function(foo: ()) -> usize { 1 }
/// option.map_or(0, |a| a + 1);
/// option.is_some_and(|a| a > 10);
/// result.map_or_else(some_function, |a| a + 1);
/// ```
#[clippy::version = "1.45.0"]
pub MAP_UNWRAP_OR,
pedantic,
"using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.map_or(None, _)`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.and_then(_)`.
///
/// ### Known problems
/// The order of the arguments is not in execution order.
///
/// ### Example
/// ```no_run
/// # let opt = Some(1);
/// opt.map_or(None, |a| Some(a + 1));
/// ```
///
/// Use instead:
/// ```no_run
/// # let opt = Some(1);
/// opt.and_then(|a| Some(a + 1));
/// ```
#[clippy::version = "pre 1.29.0"]
pub OPTION_MAP_OR_NONE,
style,
"using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.map_or(None, Some)`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.ok()`.
///
/// ### Example
/// ```no_run
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.map_or(None, Some));
/// ```
///
/// Use instead:
/// ```no_run
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.ok());
/// ```
#[clippy::version = "1.44.0"]
pub RESULT_MAP_OR_INTO_OPTION,
style,
"using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))`
/// or `_.or_else(|x| Err(y))`.
///
/// ### Why is this bad?
/// This can be written more concisely as `_.map(|x| y)` or `_.map_err(|x| y)`.
///
/// ### Example
/// ```no_run
/// # fn opt() -> Option<&'static str> { Some("42") }
/// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
/// let _ = opt().and_then(|s| Some(s.len()));
/// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
/// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
/// ```
///
/// The correct use would be:
///
/// ```no_run
/// # fn opt() -> Option<&'static str> { Some("42") }
/// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
/// let _ = opt().map(|s| s.len());
/// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
/// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
/// ```
#[clippy::version = "1.45.0"]
pub BIND_INSTEAD_OF_MAP,
complexity,
"using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.filter(_).next()`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.find(_)`.
///
/// ### Example
/// ```no_run
/// # let vec = vec![1];
/// vec.iter().filter(|x| **x == 0).next();
/// ```
///
/// Use instead:
/// ```no_run
/// # let vec = vec![1];
/// vec.iter().find(|x| **x == 0);
/// ```
#[clippy::version = "pre 1.29.0"]
pub FILTER_NEXT,
complexity,
"using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.skip_while(condition).next()`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.find(!condition)`.
///
/// ### Example
/// ```no_run
/// # let vec = vec![1];
/// vec.iter().skip_while(|x| **x == 0).next();
/// ```
///
/// Use instead:
/// ```no_run
/// # let vec = vec![1];
/// vec.iter().find(|x| **x != 0);
/// ```
#[clippy::version = "1.42.0"]
pub SKIP_WHILE_NEXT,
complexity,
"using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
///
/// ### Example
/// ```no_run
/// let vec = vec![vec![1]];
/// let opt = Some(5);
///
/// vec.iter().map(|x| x.iter()).flatten();
/// opt.map(|x| Some(x * 2)).flatten();
/// ```
///
/// Use instead:
/// ```no_run
/// # let vec = vec![vec![1]];
/// # let opt = Some(5);
/// vec.iter().flat_map(|x| x.iter());
/// opt.and_then(|x| Some(x * 2));
/// ```
#[clippy::version = "1.31.0"]
pub MAP_FLATTEN,
complexity,
"using combinations of `flatten` and `map` which can usually be written as a single method call"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.filter(_).map(_)` that can be written more simply
/// as `filter_map(_)`.
///
/// ### Why is this bad?
/// Redundant code in the `filter` and `map` operations is poor style and
/// less performant.
///
/// ### Example
/// ```no_run
/// (0_i32..10)
/// .filter(|n| n.checked_add(1).is_some())
/// .map(|n| n.checked_add(1).unwrap());
/// ```
///
/// Use instead:
/// ```no_run
/// (0_i32..10).filter_map(|n| n.checked_add(1));
/// ```
#[clippy::version = "1.51.0"]
pub MANUAL_FILTER_MAP,
complexity,
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.find(_).map(_)` that can be written more simply
/// as `find_map(_)`.
///
/// ### Why is this bad?
/// Redundant code in the `find` and `map` operations is poor style and
/// less performant.
///
/// ### Example
/// ```no_run
/// (0_i32..10)
/// .find(|n| n.checked_add(1).is_some())
/// .map(|n| n.checked_add(1).unwrap());
/// ```
///
/// Use instead:
/// ```no_run
/// (0_i32..10).find_map(|n| n.checked_add(1));
/// ```
#[clippy::version = "1.51.0"]
pub MANUAL_FIND_MAP,
complexity,
"using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.filter_map(_).next()`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.find_map(_)`.
///
/// ### Example
/// ```no_run
/// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
/// ```
/// Can be written as
///
/// ```no_run
/// (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
/// ```
#[clippy::version = "1.36.0"]
pub FILTER_MAP_NEXT,
pedantic,
"using combination of `filter_map` and `next` which can usually be written as a single method call"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `flat_map(|x| x)`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely by using `flatten`.
///
/// ### Example
/// ```no_run
/// # let iter = vec![vec![0]].into_iter();
/// iter.flat_map(|x| x);
/// ```
/// Can be written as
/// ```no_run
/// # let iter = vec![vec![0]].into_iter();
/// iter.flatten();
/// ```
#[clippy::version = "1.39.0"]
pub FLAT_MAP_IDENTITY,
complexity,
"call to `flat_map` where `flatten` is sufficient"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for an iterator or string search (such as `find()`,
/// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as:
/// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
/// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
///
/// ### Example
/// ```no_run
/// let vec = vec![1];
/// vec.iter().find(|x| **x == 0).is_some();
///
/// "hello world".find("world").is_none();
/// ```
///
/// Use instead:
/// ```no_run
/// let vec = vec![1];
/// vec.iter().any(|x| *x == 0);
///
/// !"hello world".contains("world");
/// ```
#[clippy::version = "pre 1.29.0"]
pub SEARCH_IS_SOME,
complexity,
"using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.chars().next()` on a `str` to check
/// if it starts with a given char.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.starts_with(_)`.
///
/// ### Example
/// ```no_run
/// let name = "foo";
/// if name.chars().next() == Some('_') {};
/// ```
///
/// Use instead:
/// ```no_run
/// let name = "foo";
/// if name.starts_with('_') {};
/// ```
#[clippy::version = "pre 1.29.0"]
pub CHARS_NEXT_CMP,
style,
"using `.chars().next()` to check if a string starts with a char"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
/// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
/// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
/// etc. instead.
///
/// ### Why is this bad?
/// The function will always be called. This is only bad if it allocates or
/// does some non-trivial amount of work.
///
/// ### Known problems
/// If the function has side-effects, not calling it will change the
/// semantic of the program, but you shouldn't rely on that.
///
/// The lint also cannot figure out whether the function you call is
/// actually expensive to call or not.
///
/// ### Example
/// ```no_run
/// # let foo = Some(String::new());
/// foo.unwrap_or(String::from("empty"));
/// ```
///
/// Use instead:
/// ```no_run
/// # let foo = Some(String::new());
/// foo.unwrap_or_else(|| String::from("empty"));
/// ```
#[clippy::version = "pre 1.29.0"]
pub OR_FUN_CALL,
nursery,
"using any `*or` method with a function call, which suggests `*or_else`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.or(…).unwrap()` calls to Options and Results.
///
/// ### Why is this bad?
/// You should use `.unwrap_or(…)` instead for clarity.
///
/// ### Example
/// ```no_run
/// # let fallback = "fallback";
/// // Result
/// # type Error = &'static str;
/// # let result: Result<&str, Error> = Err("error");
/// let value = result.or::<Error>(Ok(fallback)).unwrap();
///
/// // Option
/// # let option: Option<&str> = None;
/// let value = option.or(Some(fallback)).unwrap();
/// ```
/// Use instead:
/// ```no_run
/// # let fallback = "fallback";
/// // Result
/// # let result: Result<&str, &str> = Err("error");
/// let value = result.unwrap_or(fallback);
///
/// // Option
/// # let option: Option<&str> = None;
/// let value = option.unwrap_or(fallback);
/// ```
#[clippy::version = "1.61.0"]
pub OR_THEN_UNWRAP,
complexity,
"checks for `.or(…).unwrap()` calls to Options and Results."
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
/// etc., and suggests to use `unwrap_or_else` instead
///
/// ### Why is this bad?
/// The function will always be called.
///
/// ### Known problems
/// If the function has side-effects, not calling it will
/// change the semantics of the program, but you shouldn't rely on that anyway.
///
/// ### Example
/// ```no_run
/// # let foo = Some(String::new());
/// # let err_code = "418";
/// # let err_msg = "I'm a teapot";
/// foo.expect(&format!("Err {}: {}", err_code, err_msg));
///
/// // or
///
/// # let foo = Some(String::new());
/// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
/// ```
///
/// Use instead:
/// ```no_run
/// # let foo = Some(String::new());
/// # let err_code = "418";
/// # let err_msg = "I'm a teapot";
/// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
/// ```
#[clippy::version = "pre 1.29.0"]
pub EXPECT_FUN_CALL,
perf,
"using any `expect` method with a function call"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.clone()` on a `Copy` type.
///
/// ### Why is this bad?
/// The only reason `Copy` types implement `Clone` is for
/// generics, not for using the `clone` method on a concrete type.
///
/// ### Example
/// ```no_run
/// 42u64.clone();
/// ```
#[clippy::version = "pre 1.29.0"]
pub CLONE_ON_COPY,
complexity,
"using `clone` on a `Copy` type"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.clone()` on a ref-counted pointer,
/// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
/// function syntax instead (e.g., `Rc::clone(foo)`).
///
/// ### Why restrict this?
/// Calling `.clone()` on an `Rc`, `Arc`, or `Weak`
/// can obscure the fact that only the pointer is being cloned, not the underlying
/// data.
///
/// ### Example
/// ```no_run
/// # use std::rc::Rc;
/// let x = Rc::new(1);
///
/// x.clone();
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::rc::Rc;
/// # let x = Rc::new(1);
/// Rc::clone(&x);
/// ```
#[clippy::version = "pre 1.29.0"]
pub CLONE_ON_REF_PTR,
restriction,
"using `clone` on a ref-counted pointer"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.to_string()` on an `&&T` where
/// `T` implements `ToString` directly (like `&&str` or `&&String`).
///
/// ### Why is this bad?
/// This bypasses the specialized implementation of
/// `ToString` and instead goes through the more expensive string formatting
/// facilities.
///
/// ### Example
/// ```no_run
/// // Generic implementation for `T: Display` is used (slow)
/// ["foo", "bar"].iter().map(|s| s.to_string());
///
/// // OK, the specialized impl is used
/// ["foo", "bar"].iter().map(|&s| s.to_string());
/// ```
#[clippy::version = "1.40.0"]
pub INEFFICIENT_TO_STRING,
pedantic,
"using `to_string` on `&&T` where `T: ToString`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `new` not returning a type that contains `Self`.
///
/// ### Why is this bad?
/// As a convention, `new` methods are used to make a new
/// instance of a type.
///
/// ### Example
/// In an impl block:
/// ```no_run
/// # struct Foo;
/// # struct NotAFoo;
/// impl Foo {
/// fn new() -> NotAFoo {
/// # NotAFoo
/// }
/// }
/// ```
///
/// ```no_run
/// # struct Foo;
/// struct Bar(Foo);
/// impl Foo {
/// // Bad. The type name must contain `Self`
/// fn new() -> Bar {
/// # Bar(Foo)
/// }
/// }
/// ```
///
/// ```no_run
/// # struct Foo;
/// # struct FooError;
/// impl Foo {
/// // Good. Return type contains `Self`
/// fn new() -> Result<Foo, FooError> {
/// # Ok(Foo)
/// }
/// }
/// ```
///
/// Or in a trait definition:
/// ```no_run
/// pub trait Trait {
/// // Bad. The type name must contain `Self`
/// fn new();
/// }
/// ```
///
/// ```no_run
/// pub trait Trait {
/// // Good. Return type contains `Self`
/// fn new() -> Self;
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub NEW_RET_NO_SELF,
style,
"not returning type containing `Self` in a `new` method"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calling `.step_by(0)` on iterators which panics.
///
/// ### Why is this bad?
/// This very much looks like an oversight. Use `panic!()` instead if you
/// actually intend to panic.
///
/// ### Example
/// ```rust,should_panic
/// for x in (0..100).step_by(0) {
/// //..
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub ITERATOR_STEP_BY_ZERO,
correctness,
"using `Iterator::step_by(0)`, which will panic at runtime"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for iterators of `Option`s using `.filter(Option::is_some).map(Option::unwrap)` that may
/// be replaced with a `.flatten()` call.
///
/// ### Why is this bad?
/// `Option` is like a collection of 0-1 things, so `flatten`
/// automatically does this without suspicious-looking `unwrap` calls.
///
/// ### Example
/// ```no_run
/// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::iter::empty::<Option<i32>>().flatten();
/// ```
#[clippy::version = "1.53.0"]
pub OPTION_FILTER_MAP,
complexity,
"filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the use of `iter.nth(0)`.
///
/// ### Why is this bad?
/// `iter.next()` is equivalent to
/// `iter.nth(0)`, as they both consume the next element,
/// but is more readable.
///
/// ### Example
/// ```no_run
/// # use std::collections::HashSet;
/// # let mut s = HashSet::new();
/// # s.insert(1);
/// let x = s.iter().nth(0);
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::collections::HashSet;
/// # let mut s = HashSet::new();
/// # s.insert(1);
/// let x = s.iter().next();
/// ```
#[clippy::version = "1.42.0"]
pub ITER_NTH_ZERO,
style,
"replace `iter.nth(0)` with `iter.next()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.iter().nth()`/`.iter_mut().nth()` on standard library types that have
/// equivalent `.get()`/`.get_mut()` methods.
///
/// ### Why is this bad?
/// `.get()` and `.get_mut()` are equivalent but more concise.
///
/// ### Example
/// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.iter().nth(3);
/// let bad_slice = &some_vec[..].iter().nth(3);
/// ```
/// The correct use would be:
/// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.get(3);
/// let bad_slice = &some_vec[..].get(3);
/// ```
#[clippy::version = "pre 1.29.0"]
pub ITER_NTH,
style,
"using `.iter().nth()` on a standard library type with O(1) element access"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.skip(x).next()` on iterators.
///
/// ### Why is this bad?
/// `.nth(x)` is cleaner
///
/// ### Example
/// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.iter().skip(3).next();
/// let bad_slice = &some_vec[..].iter().skip(3).next();
/// ```
/// The correct use would be:
/// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
/// let bad_vec = some_vec.iter().nth(3);
/// let bad_slice = &some_vec[..].iter().nth(3);
/// ```
#[clippy::version = "pre 1.29.0"]
pub ITER_SKIP_NEXT,
style,
"using `.skip(x).next()` on an iterator"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.drain(..)` on `Vec` and `VecDeque` for iteration.
///
/// ### Why is this bad?
/// `.into_iter()` is simpler with better performance.
///
/// ### Example
/// ```no_run
/// # use std::collections::HashSet;
/// let mut foo = vec![0, 1, 2, 3];
/// let bar: HashSet<usize> = foo.drain(..).collect();
/// ```
/// Use instead:
/// ```no_run
/// # use std::collections::HashSet;
/// let foo = vec![0, 1, 2, 3];
/// let bar: HashSet<usize> = foo.into_iter().collect();
/// ```
#[clippy::version = "1.61.0"]
pub ITER_WITH_DRAIN,
nursery,
"replace `.drain(..)` with `.into_iter()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `x.get(x.len() - 1)` instead of
/// `x.last()`.
///
/// ### Why is this bad?
/// Using `x.last()` is easier to read and has the same
/// result.
///
/// Note that using `x[x.len() - 1]` is semantically different from
/// `x.last()`. Indexing into the array will panic on out-of-bounds
/// accesses, while `x.get()` and `x.last()` will return `None`.
///
/// There is another lint (get_unwrap) that covers the case of using
/// `x.get(index).unwrap()` instead of `x[index]`.
///
/// ### Example
/// ```no_run
/// let x = vec![2, 3, 5];
/// let last_element = x.get(x.len() - 1);
/// ```
///
/// Use instead:
/// ```no_run
/// let x = vec![2, 3, 5];
/// let last_element = x.last();
/// ```
#[clippy::version = "1.37.0"]
pub GET_LAST_WITH_LEN,
complexity,
"Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.get().unwrap()` (or
/// `.get_mut().unwrap`) on a standard library type which implements `Index`
///
/// ### Why restrict this?
/// Using the Index trait (`[]`) is more clear and more
/// concise.
///
/// ### Known problems
/// Not a replacement for error handling: Using either
/// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
/// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
/// temporary placeholder for dealing with the `Option` type, then this does
/// not mitigate the need for error handling. If there is a chance that `.get()`
/// will be `None` in your program, then it is advisable that the `None` case
/// is handled in a future refactor instead of using `.unwrap()` or the Index
/// trait.
///
/// ### Example
/// ```no_run
/// let mut some_vec = vec![0, 1, 2, 3];
/// let last = some_vec.get(3).unwrap();
/// *some_vec.get_mut(0).unwrap() = 1;
/// ```
/// The correct use would be:
/// ```no_run
/// let mut some_vec = vec![0, 1, 2, 3];
/// let last = some_vec[3];
/// some_vec[0] = 1;
/// ```
#[clippy::version = "pre 1.29.0"]
pub GET_UNWRAP,
restriction,
"using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for occurrences where one vector gets extended instead of append
///
/// ### Why is this bad?
/// Using `append` instead of `extend` is more concise and faster
///
/// ### Example
/// ```no_run
/// let mut a = vec![1, 2, 3];
/// let mut b = vec![4, 5, 6];
///
/// a.extend(b.drain(..));
/// ```
///
/// Use instead:
/// ```no_run
/// let mut a = vec![1, 2, 3];
/// let mut b = vec![4, 5, 6];
///
/// a.append(&mut b);
/// ```
#[clippy::version = "1.55.0"]
pub EXTEND_WITH_DRAIN,
perf,
"using vec.append(&mut vec) to move the full range of a vector to another"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the use of `.extend(s.chars())` where s is a
/// `&str` or `String`.
///
/// ### Why is this bad?
/// `.push_str(s)` is clearer
///
/// ### Example
/// ```no_run
/// let abc = "abc";
/// let def = String::from("def");
/// let mut s = String::new();
/// s.extend(abc.chars());
/// s.extend(def.chars());
/// ```
/// The correct use would be:
/// ```no_run
/// let abc = "abc";
/// let def = String::from("def");
/// let mut s = String::new();
/// s.push_str(abc);
/// s.push_str(&def);
/// ```
#[clippy::version = "pre 1.29.0"]
pub STRING_EXTEND_CHARS,
style,
"using `x.extend(s.chars())` where s is a `&str` or `String`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the use of `.cloned().collect()` on slice to
/// create a `Vec`.
///
/// ### Why is this bad?
/// `.to_vec()` is clearer
///
/// ### Example
/// ```no_run
/// let s = [1, 2, 3, 4, 5];
/// let s2: Vec<isize> = s[..].iter().cloned().collect();
/// ```
/// The better use would be:
/// ```no_run
/// let s = [1, 2, 3, 4, 5];
/// let s2: Vec<isize> = s.to_vec();
/// ```
#[clippy::version = "pre 1.29.0"]
pub ITER_CLONED_COLLECT,
style,
"using `.cloned().collect()` on slice to create a `Vec`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.chars().last()` or
/// `_.chars().next_back()` on a `str` to check if it ends with a given char.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.ends_with(_)`.
///
/// ### Example
/// ```no_run
/// # let name = "_";
/// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
/// ```
///
/// Use instead:
/// ```no_run
/// # let name = "_";
/// name.ends_with('_') || name.ends_with('-');
/// ```
#[clippy::version = "pre 1.29.0"]
pub CHARS_LAST_CMP,
style,
"using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.as_ref()` or `.as_mut()` where the
/// types before and after the call are the same.
///
/// ### Why is this bad?
/// The call is unnecessary.
///
/// ### Example
/// ```no_run
/// # fn do_stuff(x: &[i32]) {}
/// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x.as_ref());
/// ```
/// The correct use would be:
/// ```no_run
/// # fn do_stuff(x: &[i32]) {}
/// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x);
/// ```
#[clippy::version = "pre 1.29.0"]
pub USELESS_ASREF,
complexity,
"using `as_ref` where the types before and after the call are the same"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `fold` when a more succinct alternative exists.
/// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
/// `sum` or `product`.
///
/// ### Why is this bad?
/// Readability.
///
/// ### Example
/// ```no_run
/// (0..3).fold(false, |acc, x| acc || x > 2);
/// ```
///
/// Use instead:
/// ```no_run
/// (0..3).any(|x| x > 2);
/// ```
#[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_FOLD,
style,
"using `fold` when a more succinct alternative exists"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
/// More specifically it checks if the closure provided is only performing one of the
/// filter or map operations and suggests the appropriate option.
///
/// ### Why is this bad?
/// Complexity. The intent is also clearer if only a single
/// operation is being performed.
///
/// ### Example
/// ```no_run
/// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
///
/// // As there is no transformation of the argument this could be written as:
/// let _ = (0..3).filter(|&x| x > 2);
/// ```
///
/// ```no_run
/// let _ = (0..4).filter_map(|x| Some(x + 1));
///
/// // As there is no conditional check on the argument this could be written as:
/// let _ = (0..4).map(|x| x + 1);
/// ```
#[clippy::version = "1.31.0"]
pub UNNECESSARY_FILTER_MAP,
complexity,
"using `filter_map` when a more succinct alternative exists"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `find_map` calls that could be replaced by `find` or `map`. More
/// specifically it checks if the closure provided is only performing one of the
/// find or map operations and suggests the appropriate option.
///
/// ### Why is this bad?
/// Complexity. The intent is also clearer if only a single
/// operation is being performed.
///
/// ### Example
/// ```no_run
/// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
///
/// // As there is no transformation of the argument this could be written as:
/// let _ = (0..3).find(|&x| x > 2);
/// ```
///
/// ```no_run
/// let _ = (0..4).find_map(|x| Some(x + 1));
///
/// // As there is no conditional check on the argument this could be written as:
/// let _ = (0..4).map(|x| x + 1).next();
/// ```
#[clippy::version = "1.61.0"]
pub UNNECESSARY_FIND_MAP,
complexity,
"using `find_map` when a more succinct alternative exists"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `into_iter` calls on references which should be replaced by `iter`
/// or `iter_mut`.
///
/// ### Why is this bad?
/// Readability. Calling `into_iter` on a reference will not move out its
/// content into the resulting iterator, which is confusing. It is better just call `iter` or
/// `iter_mut` directly.
///
/// ### Example
/// ```no_run
/// # let vec = vec![3, 4, 5];
/// (&vec).into_iter();
/// ```
///
/// Use instead:
/// ```no_run
/// # let vec = vec![3, 4, 5];
/// (&vec).iter();
/// ```
#[clippy::version = "1.32.0"]
pub INTO_ITER_ON_REF,
style,
"using `.into_iter()` on a reference"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `map` followed by a `count`.
///
/// ### Why is this bad?
/// It looks suspicious. Maybe `map` was confused with `filter`.
/// If the `map` call is intentional, this should be rewritten
/// using `inspect`. Or, if you intend to drive the iterator to
/// completion, you can just use `for_each` instead.
///
/// ### Example
/// ```no_run
/// let _ = (0..3).map(|x| x + 2).count();
/// ```
#[clippy::version = "1.39.0"]
pub SUSPICIOUS_MAP,
suspicious,
"suspicious usage of map"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `MaybeUninit::uninit().assume_init()`.
///
/// ### Why is this bad?
/// For most types, this is undefined behavior.
///
/// ### Known problems
/// For now, we accept empty tuples and tuples / arrays
/// of `MaybeUninit`. There may be other types that allow uninitialized
/// data, but those are not yet rigorously defined.
///
/// ### Example
/// ```no_run
/// // Beware the UB
/// use std::mem::MaybeUninit;
///
/// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
/// ```
///
/// Note that the following is OK:
///
/// ```no_run
/// use std::mem::MaybeUninit;
///
/// let _: [MaybeUninit<bool>; 5] = unsafe {
/// MaybeUninit::uninit().assume_init()
/// };
/// ```
#[clippy::version = "1.39.0"]
pub UNINIT_ASSUMED_INIT,
correctness,
"`MaybeUninit::uninit().assume_init()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
///
/// ### Why is this bad?
/// These can be written simply with `saturating_add/sub` methods.
///
/// ### Example
/// ```no_run
/// # let y: u32 = 0;
/// # let x: u32 = 100;
/// let add = x.checked_add(y).unwrap_or(u32::MAX);
/// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
/// ```
///
/// can be written using dedicated methods for saturating addition/subtraction as:
///
/// ```no_run
/// # let y: u32 = 0;
/// # let x: u32 = 100;
/// let add = x.saturating_add(y);
/// let sub = x.saturating_sub(y);
/// ```
#[clippy::version = "1.39.0"]
pub MANUAL_SATURATING_ARITHMETIC,
style,
"`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
/// zero-sized types
///
/// ### Why is this bad?
/// This is a no-op, and likely unintended
///
/// ### Example
/// ```no_run
/// unsafe { (&() as *const ()).offset(1) };
/// ```
#[clippy::version = "1.41.0"]
pub ZST_OFFSET,
correctness,
"Check for offset calculations on raw pointers to zero-sized types"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `FileType::is_file()`.
///
/// ### Why restrict this?
/// When people testing a file type with `FileType::is_file`
/// they are testing whether a path is something they can get bytes from. But
/// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
/// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
///
/// ### Example
/// ```no_run
/// # || {
/// let metadata = std::fs::metadata("foo.txt")?;
/// let filetype = metadata.file_type();
///
/// if filetype.is_file() {
/// // read file
/// }
/// # Ok::<_, std::io::Error>(())
/// # };
/// ```
///
/// should be written as:
///
/// ```no_run
/// # || {
/// let metadata = std::fs::metadata("foo.txt")?;
/// let filetype = metadata.file_type();
///
/// if !filetype.is_dir() {
/// // read file
/// }
/// # Ok::<_, std::io::Error>(())
/// # };
/// ```
#[clippy::version = "1.42.0"]
pub FILETYPE_IS_FILE,
restriction,
"`FileType::is_file` is not recommended to test for readable file type"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str).
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.as_deref()`.
///
/// ### Example
/// ```no_run
/// # let opt = Some("".to_string());
/// opt.as_ref().map(String::as_str)
/// # ;
/// ```
/// Can be written as
/// ```no_run
/// # let opt = Some("".to_string());
/// opt.as_deref()
/// # ;
/// ```
#[clippy::version = "1.42.0"]
pub OPTION_AS_REF_DEREF,
complexity,
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `iter().next()` on a Slice or an Array
///
/// ### Why is this bad?
/// These can be shortened into `.get()`
///
/// ### Example
/// ```no_run
/// # let a = [1, 2, 3];
/// # let b = vec![1, 2, 3];
/// a[2..].iter().next();
/// b.iter().next();
/// ```
/// should be written as:
/// ```no_run
/// # let a = [1, 2, 3];
/// # let b = vec![1, 2, 3];
/// a.get(2);
/// b.get(0);
/// ```
#[clippy::version = "1.46.0"]
pub ITER_NEXT_SLICE,
style,
"using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
}
declare_clippy_lint! {
/// ### What it does
/// Warns when using `push_str`/`insert_str` with a single-character string literal
/// where `push`/`insert` with a `char` would work fine.
///
/// ### Why is this bad?
/// It's less clear that we are pushing a single character.
///
/// ### Example
/// ```no_run
/// # let mut string = String::new();
/// string.insert_str(0, "R");
/// string.push_str("R");
/// ```
///
/// Use instead:
/// ```no_run
/// # let mut string = String::new();
/// string.insert(0, 'R');
/// string.push('R');
/// ```
#[clippy::version = "1.49.0"]
pub SINGLE_CHAR_ADD_STR,
style,
"`push_str()` or `insert_str()` used with a single-character string literal as parameter"
}
declare_clippy_lint! {
/// ### What it does
/// As the counterpart to `or_fun_call`, this lint looks for unnecessary
/// lazily evaluated closures on `Option` and `Result`.
///
/// This lint suggests changing the following functions, when eager evaluation results in
/// simpler code:
/// - `unwrap_or_else` to `unwrap_or`
/// - `and_then` to `and`
/// - `or_else` to `or`
/// - `get_or_insert_with` to `get_or_insert`
/// - `ok_or_else` to `ok_or`
/// - `then` to `then_some` (for msrv >= 1.62.0)
///
/// ### Why is this bad?
/// Using eager evaluation is shorter and simpler in some cases.
///
/// ### Known problems
/// It is possible, but not recommended for `Deref` and `Index` to have
/// side effects. Eagerly evaluating them can change the semantics of the program.
///
/// ### Example
/// ```no_run
/// let opt: Option<u32> = None;
///
/// opt.unwrap_or_else(|| 42);
/// ```
/// Use instead:
/// ```no_run
/// let opt: Option<u32> = None;
///
/// opt.unwrap_or(42);
/// ```
#[clippy::version = "1.48.0"]
pub UNNECESSARY_LAZY_EVALUATIONS,
style,
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
///
/// ### Why is this bad?
/// Using `try_for_each` instead is more readable and idiomatic.
///
/// ### Example
/// ```no_run
/// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
/// ```
/// Use instead:
/// ```no_run
/// (0..3).try_for_each(|t| Err(t));
/// ```
#[clippy::version = "1.49.0"]
pub MAP_COLLECT_RESULT_UNIT,
style,
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `from_iter()` function calls on types that implement the `FromIterator`
/// trait.
///
/// ### Why is this bad?
/// If it's needed to create a collection from the contents of an iterator, the `Iterator::collect(_)`
/// method is preferred. However, when it's needed to specify the container type,
/// `Vec::from_iter(_)` can be more readable than using a turbofish (e.g. `_.collect::<Vec<_>>()`). See
/// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
///
/// ### Example
/// ```no_run
/// let five_fives = std::iter::repeat(5).take(5);
///
/// let v = Vec::from_iter(five_fives);
///
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
/// Use instead:
/// ```no_run
/// let five_fives = std::iter::repeat(5).take(5);
///
/// let v: Vec<i32> = five_fives.collect();
///
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
/// but prefer to use
/// ```no_run
/// let numbers: Vec<i32> = FromIterator::from_iter(1..=5);
/// ```
/// instead of
/// ```no_run
/// let numbers = (1..=5).collect::<Vec<_>>();
/// ```
#[clippy::version = "1.49.0"]
pub FROM_ITER_INSTEAD_OF_COLLECT,
pedantic,
"use `.collect()` instead of `::from_iter()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `inspect().for_each()`.
///
/// ### Why is this bad?
/// It is the same as performing the computation
/// inside `inspect` at the beginning of the closure in `for_each`.
///
/// ### Example
/// ```no_run
/// [1,2,3,4,5].iter()
/// .inspect(|&x| println!("inspect the number: {}", x))
/// .for_each(|&x| {
/// assert!(x >= 0);
/// });
/// ```
/// Can be written as
/// ```no_run
/// [1,2,3,4,5].iter()
/// .for_each(|&x| {
/// println!("inspect the number: {}", x);
/// assert!(x >= 0);
/// });
/// ```
#[clippy::version = "1.51.0"]
pub INSPECT_FOR_EACH,
complexity,
"using `.inspect().for_each()`, which can be replaced with `.for_each()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `filter_map(|x| x)`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely by using `flatten`.
///
/// ### Example
/// ```no_run
/// # let iter = vec![Some(1)].into_iter();
/// iter.filter_map(|x| x);
/// ```
/// Use instead:
/// ```no_run
/// # let iter = vec![Some(1)].into_iter();
/// iter.flatten();
/// ```
#[clippy::version = "1.52.0"]
pub FILTER_MAP_IDENTITY,
complexity,
"call to `filter_map` where `flatten` is sufficient"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for instances of `map(f)` where `f` is the identity function.
///
/// ### Why is this bad?
/// It can be written more concisely without the call to `map`.
///
/// ### Example
/// ```no_run
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
/// ```no_run
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
#[clippy::version = "1.47.0"]
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the use of `.bytes().nth()`.
///
/// ### Why is this bad?
/// `.as_bytes().get()` is more efficient and more
/// readable.
///
/// ### Example
/// ```no_run
/// "Hello".bytes().nth(3);
/// ```
///
/// Use instead:
/// ```no_run
/// "Hello".as_bytes().get(3);
/// ```
#[clippy::version = "1.52.0"]
pub BYTES_NTH,
style,
"replace `.bytes().nth()` with `.as_bytes().get()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
///
/// ### Why is this bad?
/// These methods do the same thing as `_.clone()` but may be confusing as
/// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
///
/// ### Example
/// ```no_run
/// let a = vec![1, 2, 3];
/// let b = a.to_vec();
/// let c = a.to_owned();
/// ```
/// Use instead:
/// ```no_run
/// let a = vec![1, 2, 3];
/// let b = a.clone();
/// let c = a.clone();
/// ```
#[clippy::version = "1.52.0"]
pub IMPLICIT_CLONE,
pedantic,
"implicitly cloning a value by invoking a function on its dereferenced type"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the use of `.iter().count()`.
///
/// ### Why is this bad?
/// `.len()` is more efficient and more
/// readable.
///
/// ### Example
/// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
///
/// some_vec.iter().count();
/// &some_vec[..].iter().count();
/// ```
///
/// Use instead:
/// ```no_run
/// let some_vec = vec![0, 1, 2, 3];
///
/// some_vec.len();
/// &some_vec[..].len();
/// ```
#[clippy::version = "1.52.0"]
pub ITER_COUNT,
complexity,
"replace `.iter().count()` with `.len()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
///
/// ### Why is this bad?
/// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
/// itself, without taking ownership of the `Cow` contents (i.e.
/// it's equivalent to calling `Cow::clone`).
/// The similarly named `into_owned` method, on the other hand,
/// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
/// into a `Cow::Owned`.
///
/// Given the potential ambiguity, consider replacing `to_owned`
/// with `clone` for better readability or, if getting a `Cow::Owned`
/// was the original intent, using `into_owned` instead.
///
/// ### Example
/// ```no_run
/// # use std::borrow::Cow;
/// let s = "Hello world!";
/// let cow = Cow::Borrowed(s);
///
/// let data = cow.to_owned();
/// assert!(matches!(data, Cow::Borrowed(_)))
/// ```
/// Use instead:
/// ```no_run
/// # use std::borrow::Cow;
/// let s = "Hello world!";
/// let cow = Cow::Borrowed(s);
///
/// let data = cow.clone();
/// assert!(matches!(data, Cow::Borrowed(_)))
/// ```
/// or
/// ```no_run
/// # use std::borrow::Cow;
/// let s = "Hello world!";
/// let cow = Cow::Borrowed(s);
///
/// let _data: String = cow.into_owned();
/// ```
#[clippy::version = "1.65.0"]
pub SUSPICIOUS_TO_OWNED,
suspicious,
"calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to [`splitn`]
/// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
/// related functions with either zero or one splits.
///
/// ### Why is this bad?
/// These calls don't actually split the value and are
/// likely to be intended as a different number.
///
/// ### Example
/// ```no_run
/// # let s = "";
/// for x in s.splitn(1, ":") {
/// // ..
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// # let s = "";
/// for x in s.splitn(2, ":") {
/// // ..
/// }
/// ```
#[clippy::version = "1.54.0"]
pub SUSPICIOUS_SPLITN,
correctness,
"checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for manual implementations of `str::repeat`
///
/// ### Why is this bad?
/// These are both harder to read, as well as less performant.
///
/// ### Example
/// ```no_run
/// let x: String = std::iter::repeat('x').take(10).collect();
/// ```
///
/// Use instead:
/// ```no_run
/// let x: String = "x".repeat(10);
/// ```
#[clippy::version = "1.54.0"]
pub MANUAL_STR_REPEAT,
perf,
"manual implementation of `str::repeat`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `str::splitn(2, _)`
///
/// ### Why is this bad?
/// `split_once` is both clearer in intent and slightly more efficient.
///
/// ### Example
/// ```rust,ignore
/// let s = "key=value=add";
/// let (key, value) = s.splitn(2, '=').next_tuple()?;
/// let value = s.splitn(2, '=').nth(1)?;
///
/// let mut parts = s.splitn(2, '=');
/// let key = parts.next()?;
/// let value = parts.next()?;
/// ```
///
/// Use instead:
/// ```rust,ignore
/// let s = "key=value=add";
/// let (key, value) = s.split_once('=')?;
/// let value = s.split_once('=')?.1;
///
/// let (key, value) = s.split_once('=')?;
/// ```
///
/// ### Limitations
/// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
/// in two separate `let` statements that immediately follow the `splitn()`
#[clippy::version = "1.57.0"]
pub MANUAL_SPLIT_ONCE,
complexity,
"replace `.splitn(2, pat)` with `.split_once(pat)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
/// ### Why is this bad?
/// The function `split` is simpler and there is no performance difference in these cases, considering
/// that both functions return a lazy iterator.
/// ### Example
/// ```no_run
/// let str = "key=value=add";
/// let _ = str.splitn(3, '=').next().unwrap();
/// ```
///
/// Use instead:
/// ```no_run
/// let str = "key=value=add";
/// let _ = str.split('=').next().unwrap();
/// ```
#[clippy::version = "1.59.0"]
pub NEEDLESS_SPLITN,
complexity,
"usages of `str::splitn` that can be replaced with `str::split`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
/// and other `to_owned`-like functions.
///
/// ### Why is this bad?
/// The unnecessary calls result in useless allocations.
///
/// ### Known problems
/// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
/// owned copy of a resource and the resource is later used mutably. See
/// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
///
/// ### Example
/// ```no_run
/// let path = std::path::Path::new("x");
/// foo(&path.to_string_lossy().to_string());
/// fn foo(s: &str) {}
/// ```
/// Use instead:
/// ```no_run
/// let path = std::path::Path::new("x");
/// foo(&path.to_string_lossy());
/// fn foo(s: &str) {}
/// ```
#[clippy::version = "1.59.0"]
pub UNNECESSARY_TO_OWNED,
perf,
"unnecessary calls to `to_owned`-like functions"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.collect::<Vec<String>>().join("")` on iterators.
///
/// ### Why is this bad?
/// `.collect::<String>()` is more concise and might be more performant
///
/// ### Example
/// ```no_run
/// let vector = vec!["hello", "world"];
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
/// println!("{}", output);
/// ```
/// The correct use would be:
/// ```no_run
/// let vector = vec!["hello", "world"];
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
/// println!("{}", output);
/// ```
/// ### Known problems
/// While `.collect::<String>()` is sometimes more performant, there are cases where
/// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
/// will prevent loop unrolling and will result in a negative performance impact.
///
/// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
/// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
#[clippy::version = "1.61.0"]
pub UNNECESSARY_JOIN,
pedantic,
"using `.collect::<Vec<String>>().join(\"\")` on an iterator"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
/// for example, `Option<&T>::as_deref()` returns the same type.
///
/// ### Why is this bad?
/// Redundant code and improving readability.
///
/// ### Example
/// ```no_run
/// let a = Some(&1);
/// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
/// ```
///
/// Use instead:
/// ```no_run
/// let a = Some(&1);
/// let b = a;
/// ```
#[clippy::version = "1.57.0"]
pub NEEDLESS_OPTION_AS_DEREF,
complexity,
"no-op use of `deref` or `deref_mut` method to `Option`."
}
declare_clippy_lint! {
/// ### What it does
/// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
/// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
/// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
///
/// ### Why is this bad?
/// `is_digit(..)` is slower and requires specifying the radix.
///
/// ### Example
/// ```no_run
/// let c: char = '6';
/// c.is_digit(10);
/// c.is_digit(16);
/// ```
/// Use instead:
/// ```no_run
/// let c: char = '6';
/// c.is_ascii_digit();
/// c.is_ascii_hexdigit();
/// ```
#[clippy::version = "1.62.0"]
pub IS_DIGIT_ASCII_RADIX,
style,
"use of `char::is_digit(..)` with literal radix of 10 or 16"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calling `take` function after `as_ref`.
///
/// ### Why is this bad?
/// Redundant code. `take` writes `None` to its argument.
/// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
///
/// ### Example
/// ```no_run
/// let x = Some(3);
/// x.as_ref().take();
/// ```
/// Use instead:
/// ```no_run
/// let x = Some(3);
/// x.as_ref();
/// ```
#[clippy::version = "1.62.0"]
pub NEEDLESS_OPTION_TAKE,
complexity,
"using `.as_ref().take()` on a temporary value"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `replace` statements which have no effect.
///
/// ### Why is this bad?
/// It's either a mistake or confusing.
///
/// ### Example
/// ```no_run
/// "1234".replace("12", "12");
/// "1234".replacen("12", "12", 1);
/// ```
#[clippy::version = "1.63.0"]
pub NO_EFFECT_REPLACE,
suspicious,
"replace with no effect"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for unnecessary method chains that can be simplified into `if .. else ..`.
///
/// ### Why is this bad?
/// This can be written more clearly with `if .. else ..`
///
/// ### Limitations
/// This lint currently only looks for usages of
/// `.{then, then_some}(..).{unwrap_or, unwrap_or_else, unwrap_or_default}(..)`, but will be expanded
/// to account for similar patterns.
///
/// ### Example
/// ```no_run
/// let x = true;
/// x.then_some("a").unwrap_or("b");
/// ```
/// Use instead:
/// ```no_run
/// let x = true;
/// if x { "a" } else { "b" };
/// ```
#[clippy::version = "1.64.0"]
pub OBFUSCATED_IF_ELSE,
style,
"use of `.then_some(..).unwrap_or(..)` can be written \
more clearly with `if .. else ..`"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
///
/// ### Why is this bad?
///
/// It is simpler to use the once function from the standard library:
///
/// ### Example
///
/// ```no_run
/// let a = [123].iter();
/// let b = Some(123).into_iter();
/// ```
/// Use instead:
/// ```no_run
/// use std::iter;
/// let a = iter::once(&123);
/// let b = iter::once(123);
/// ```
///
/// ### Known problems
///
/// The type of the resulting iterator might become incompatible with its usage
#[clippy::version = "1.65.0"]
pub ITER_ON_SINGLE_ITEMS,
nursery,
"Iterator for array of length 1"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
///
/// ### Why is this bad?
///
/// It is simpler to use the empty function from the standard library:
///
/// ### Example
///
/// ```no_run
/// use std::{slice, option};
/// let a: slice::Iter<i32> = [].iter();
/// let f: option::IntoIter<i32> = None.into_iter();
/// ```
/// Use instead:
/// ```no_run
/// use std::iter;
/// let a: iter::Empty<i32> = iter::empty();
/// let b: iter::Empty<i32> = iter::empty();
/// ```
///
/// ### Known problems
///
/// The type of the resulting iterator might become incompatible with its usage
#[clippy::version = "1.65.0"]
pub ITER_ON_EMPTY_COLLECTIONS,
nursery,
"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
/// ```no_run
/// # 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
/// ```no_run
/// "hello".bytes().count();
/// String::from("hello").bytes().count();
/// ```
/// Use instead:
/// ```no_run
/// "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
/// ```no_run
/// fn is_rust_file(filename: &str) -> bool {
/// filename.ends_with(".rs")
/// }
/// ```
/// Use instead:
/// ```no_run
/// 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 usage of `x.get(0)` instead of
/// `x.first()` or `x.front()`.
///
/// ### Why is this bad?
/// Using `x.first()` for `Vec`s and slices or `x.front()`
/// for `VecDeque`s is easier to read and has the same result.
///
/// ### Example
/// ```no_run
/// let x = vec![2, 3, 5];
/// let first_element = x.get(0);
/// ```
///
/// Use instead:
/// ```no_run
/// 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()` or `x.front()` 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
/// ```no_run
/// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v));
/// ```
///
/// Use instead:
/// ```no_run
/// let foo: Option<i32> = None;
/// foo.ok_or("error");
/// ```
#[clippy::version = "1.49.0"]
pub MANUAL_OK_OR,
style,
"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
/// ```no_run
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.map(|i| *i);
/// ```
///
/// The correct use would be:
///
/// ```no_run
/// 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 restrict this?
/// This `map_err` throws away the original error rather than allowing the enum to
/// contain and report the cause of the error.
///
/// ### Example
/// Before:
/// ```no_run
/// 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
/// ```no_run
/// 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:
/// ```no_run
/// 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
/// ```no_run
/// 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 the suspicious use of `OpenOptions::create()`
/// without an explicit `OpenOptions::truncate()`.
///
/// ### Why is this bad?
/// `create()` alone will either create a new file or open an
/// existing file. If the file already exists, it will be
/// overwritten when written to, but the file will not be
/// truncated by default.
/// If less data is written to the file
/// than it already contains, the remainder of the file will
/// remain unchanged, and the end of the file will contain old
/// data.
/// In most cases, one should either use `create_new` to ensure
/// the file is created from scratch, or ensure `truncate` is
/// called so that the truncation behaviour is explicit. `truncate(true)`
/// will ensure the file is entirely overwritten with new data, whereas
/// `truncate(false)` will explicitly keep the default behavior.
///
/// ### Example
/// ```rust,no_run
/// use std::fs::OpenOptions;
///
/// OpenOptions::new().create(true);
/// ```
/// Use instead:
/// ```rust,no_run
/// use std::fs::OpenOptions;
///
/// OpenOptions::new().create(true).truncate(true);
/// ```
#[clippy::version = "1.77.0"]
pub SUSPICIOUS_OPEN_OPTIONS,
suspicious,
"suspicious 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
/// ```no_run
/// use std::path::PathBuf;
///
/// let mut x = PathBuf::from("/foo");
/// x.push("/bar");
/// assert_eq!(x, PathBuf::from("/bar"));
/// ```
/// Could be written:
///
/// ```no_run
/// 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
/// ```no_run
/// # let x = vec![1];
/// let _ = x.iter().zip(0..x.len());
/// ```
///
/// Use instead:
/// ```no_run
/// # 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
/// ```no_run
/// fn main() {
/// let x = String::from("hello world").repeat(1);
/// }
/// ```
/// Use instead:
/// ```no_run
/// 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
/// ```no_run
/// let mut vec = vec![2, 1, 3];
/// vec.sort();
/// ```
/// Use instead:
/// ```no_run
/// 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
/// Looks for calls to `.type_id()` on a `Box<dyn _>`.
///
/// ### Why is this bad?
/// This almost certainly does not do what the user expects and can lead to subtle bugs.
/// Calling `.type_id()` on a `Box<dyn Trait>` returns a fixed `TypeId` of the `Box` itself,
/// rather than returning the `TypeId` of the underlying type behind the trait object.
///
/// For `Box<dyn Any>` specifically (and trait objects that have `Any` as its supertrait),
/// this lint will provide a suggestion, which is to dereference the receiver explicitly
/// to go from `Box<dyn Any>` to `dyn Any`.
/// This makes sure that `.type_id()` resolves to a dynamic call on the trait object
/// and not on the box.
///
/// If the fixed `TypeId` of the `Box` is the intended behavior, it's better to be explicit about it
/// and write `TypeId::of::<Box<dyn Trait>>()`:
/// this makes it clear that a fixed `TypeId` is returned and not the `TypeId` of the implementor.
///
/// ### Example
/// ```rust,ignore
/// use std::any::{Any, TypeId};
///
/// let any_box: Box<dyn Any> = Box::new(42_i32);
/// assert_eq!(any_box.type_id(), TypeId::of::<i32>()); // ⚠️ this fails!
/// ```
/// Use instead:
/// ```no_run
/// use std::any::{Any, TypeId};
///
/// let any_box: Box<dyn Any> = Box::new(42_i32);
/// assert_eq!((*any_box).type_id(), TypeId::of::<i32>());
/// // ^ dereference first, to call `type_id` on `dyn Any`
/// ```
#[clippy::version = "1.73.0"]
pub TYPE_ID_ON_BOX,
suspicious,
"calling `.type_id()` on a boxed trait object"
}
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
/// ```no_run
/// # 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:
/// ```no_run
/// # 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
/// Checks for usage 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
/// ```no_run
/// # 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:
/// ```no_run
/// # 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
/// ```no_run
/// vec![1, 2, 3, 4, 5].resize(0, 5)
/// ```
///
/// Use instead:
/// ```no_run
/// 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 usage of File::read_to_end and File::read_to_string.
///
/// ### Why restrict this?
/// `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_clippy_lint! {
/// ### What it does
///
/// Checks for iterating a map (`HashMap` or `BTreeMap`) and
/// ignoring either the keys or values.
///
/// ### Why is this bad?
///
/// Readability. There are `keys` and `values` methods that
/// can be used to express that we only need the keys or the values.
///
/// ### Example
///
/// ```no_run
/// # use std::collections::HashMap;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::collections::HashMap;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.values().collect::<Vec<_>>();
/// ```
#[clippy::version = "1.66.0"]
pub ITER_KV_MAP,
complexity,
"iterating on map using `iter` when `keys` or `values` would do"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks if the `seek` method of the `Seek` trait is called with `SeekFrom::Current(0)`,
/// and if it is, suggests using `stream_position` instead.
///
/// ### Why is this bad?
///
/// Readability. Use dedicated method.
///
/// ### Example
///
/// ```rust,no_run
/// use std::fs::File;
/// use std::io::{self, Write, Seek, SeekFrom};
///
/// fn main() -> io::Result<()> {
/// let mut f = File::create("foo.txt")?;
/// f.write_all(b"Hello")?;
/// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?);
///
/// Ok(())
/// }
/// ```
/// Use instead:
/// ```rust,no_run
/// use std::fs::File;
/// use std::io::{self, Write, Seek, SeekFrom};
///
/// fn main() -> io::Result<()> {
/// let mut f = File::create("foo.txt")?;
/// f.write_all(b"Hello")?;
/// eprintln!("Written {} bytes", f.stream_position()?);
///
/// Ok(())
/// }
/// ```
#[clippy::version = "1.67.0"]
pub SEEK_FROM_CURRENT,
complexity,
"use dedicated method for seek from current position"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for jumps to the start of a stream that implements `Seek`
/// and uses the `seek` method providing `Start` as parameter.
///
/// ### Why is this bad?
///
/// Readability. There is a specific method that was implemented for
/// this exact scenario.
///
/// ### Example
/// ```no_run
/// # use std::io;
/// fn foo<T: io::Seek>(t: &mut T) {
/// t.seek(io::SeekFrom::Start(0));
/// }
/// ```
/// Use instead:
/// ```no_run
/// # use std::io;
/// fn foo<T: io::Seek>(t: &mut T) {
/// t.rewind();
/// }
/// ```
#[clippy::version = "1.67.0"]
pub SEEK_TO_START_INSTEAD_OF_REWIND,
complexity,
"jumping to the start of stream using `seek` method"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for functions collecting an iterator when collect
/// is not needed.
///
/// ### Why is this bad?
/// `collect` causes the allocation of a new data structure,
/// when this allocation may not be needed.
///
/// ### Example
/// ```no_run
/// # let iterator = vec![1].into_iter();
/// let len = iterator.collect::<Vec<_>>().len();
/// ```
/// Use instead:
/// ```no_run
/// # let iterator = vec![1].into_iter();
/// let len = iterator.count();
/// ```
#[clippy::version = "1.30.0"]
pub NEEDLESS_COLLECT,
nursery,
"collecting an iterator when collect is not needed"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `Command::arg()` invocations that look like they
/// should be multiple arguments instead, such as `arg("-t ext2")`.
///
/// ### Why is this bad?
///
/// `Command::arg()` does not split arguments by space. An argument like `arg("-t ext2")`
/// will be passed as a single argument to the command,
/// which is likely not what was intended.
///
/// ### Example
/// ```no_run
/// std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
/// ```
/// Use instead:
/// ```no_run
/// std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
/// ```
#[clippy::version = "1.69.0"]
pub SUSPICIOUS_COMMAND_ARG_SPACE,
suspicious,
"single command line argument that looks like it should be multiple arguments"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.drain(..)` for the sole purpose of clearing a container.
///
/// ### Why is this bad?
/// This creates an unnecessary iterator that is dropped immediately.
///
/// Calling `.clear()` also makes the intent clearer.
///
/// ### Example
/// ```no_run
/// let mut v = vec![1, 2, 3];
/// v.drain(..);
/// ```
/// Use instead:
/// ```no_run
/// let mut v = vec![1, 2, 3];
/// v.clear();
/// ```
#[clippy::version = "1.70.0"]
pub CLEAR_WITH_DRAIN,
nursery,
"calling `drain` in order to `clear` a container"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `.rev().next()` on a `DoubleEndedIterator`
///
/// ### Why is this bad?
/// `.next_back()` is cleaner.
///
/// ### Example
/// ```no_run
/// # let foo = [0; 10];
/// foo.iter().rev().next();
/// ```
/// Use instead:
/// ```no_run
/// # let foo = [0; 10];
/// foo.iter().next_back();
/// ```
#[clippy::version = "1.71.0"]
pub MANUAL_NEXT_BACK,
style,
"manual reverse iteration of `DoubleEndedIterator`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `.drain()` that clear the collection, immediately followed by a call to `.collect()`.
///
/// > "Collection" in this context refers to any type with a `drain` method:
/// > `Vec`, `VecDeque`, `BinaryHeap`, `HashSet`,`HashMap`, `String`
///
/// ### Why is this bad?
/// Using `mem::take` is faster as it avoids the allocation.
/// When using `mem::take`, the old collection is replaced with an empty one and ownership of
/// the old collection is returned.
///
/// ### Known issues
/// `mem::take(&mut vec)` is almost equivalent to `vec.drain(..).collect()`, except that
/// it also moves the **capacity**. The user might have explicitly written it this way
/// to keep the capacity on the original `Vec`.
///
/// ### Example
/// ```no_run
/// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
/// v.drain(..).collect()
/// }
/// ```
/// Use instead:
/// ```no_run
/// use std::mem;
/// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
/// mem::take(v)
/// }
/// ```
#[clippy::version = "1.72.0"]
pub DRAIN_COLLECT,
perf,
"calling `.drain(..).collect()` to move all elements into a new collection"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `Iterator::fold` with a type that implements `Try`.
///
/// ### Why is this bad?
/// The code should use `try_fold` instead, which short-circuits on failure, thus opening the
/// door for additional optimizations not possible with `fold` as rustc can guarantee the
/// function is never called on `None`, `Err`, etc., alleviating otherwise necessary checks. It's
/// also slightly more idiomatic.
///
/// ### Known issues
/// This lint doesn't take into account whether a function does something on the failure case,
/// i.e., whether short-circuiting will affect behavior. Refactoring to `try_fold` is not
/// desirable in those cases.
///
/// ### Example
/// ```no_run
/// vec![1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i));
/// ```
/// Use instead:
/// ```no_run
/// vec![1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i));
/// ```
#[clippy::version = "1.72.0"]
pub MANUAL_TRY_FOLD,
perf,
"checks for usage of `Iterator::fold` with a type that implements `Try`"
}
declare_clippy_lint! {
/// ### What it does
/// Looks for calls to [`Stdin::read_line`] to read a line from the standard input
/// into a string, then later attempting to use that string for an operation that will never
/// work for strings with a trailing newline character in it (e.g. parsing into a `i32`).
///
/// ### Why is this bad?
/// The operation will always fail at runtime no matter what the user enters, thus
/// making it a useless operation.
///
/// ### Example
/// ```rust,ignore
/// let mut input = String::new();
/// std::io::stdin().read_line(&mut input).expect("Failed to read a line");
/// let num: i32 = input.parse().expect("Not a number!");
/// assert_eq!(num, 42); // we never even get here!
/// ```
/// Use instead:
/// ```rust,ignore
/// let mut input = String::new();
/// std::io::stdin().read_line(&mut input).expect("Failed to read a line");
/// let num: i32 = input.trim_end().parse().expect("Not a number!");
/// // ^^^^^^^^^^^ remove the trailing newline
/// assert_eq!(num, 42);
/// ```
#[clippy::version = "1.73.0"]
pub READ_LINE_WITHOUT_TRIM,
correctness,
"calling `Stdin::read_line`, then trying to parse it without first trimming"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `<string_lit>.chars().any(|i| i == c)`.
///
/// ### Why is this bad?
/// It's significantly slower than using a pattern instead, like
/// `matches!(c, '\\' | '.' | '+')`.
///
/// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice
/// way to check if a `char` is any in a set. In any case, this `restriction` lint is available
/// for situations where that additional performance is absolutely necessary.
///
/// ### Example
/// ```no_run
/// # let c = 'c';
/// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
/// ```
/// Use instead:
/// ```no_run
/// # let c = 'c';
/// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
/// ```
#[clippy::version = "1.73.0"]
pub STRING_LIT_CHARS_ANY,
restriction,
"checks for `<string_lit>.chars().any(|i| i == c)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.map(|_| format!(..)).collect::<String>()`.
///
/// ### Why is this bad?
/// This allocates a new string for every element in the iterator.
/// This can be done more efficiently by creating the `String` once and appending to it in `Iterator::fold`,
/// using either the `write!` macro which supports exactly the same syntax as the `format!` macro,
/// or concatenating with `+` in case the iterator yields `&str`/`String`.
///
/// Note also that `write!`-ing into a `String` can never fail, despite the return type of `write!` being `std::fmt::Result`,
/// so it can be safely ignored or unwrapped.
///
/// ### Example
/// ```no_run
/// fn hex_encode(bytes: &[u8]) -> String {
/// bytes.iter().map(|b| format!("{b:02X}")).collect()
/// }
/// ```
/// Use instead:
/// ```no_run
/// use std::fmt::Write;
/// fn hex_encode(bytes: &[u8]) -> String {
/// bytes.iter().fold(String::new(), |mut output, b| {
/// let _ = write!(output, "{b:02X}");
/// output
/// })
/// }
/// ```
#[clippy::version = "1.73.0"]
pub FORMAT_COLLECT,
pedantic,
"`format!`ing every element in a collection, then collecting the strings into a new `String`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.skip(0)` on iterators.
///
/// ### Why is this bad?
/// This was likely intended to be `.skip(1)` to skip the first element, as `.skip(0)` does
/// nothing. If not, the call should be removed.
///
/// ### Example
/// ```no_run
/// let v = vec![1, 2, 3];
/// let x = v.iter().skip(0).collect::<Vec<_>>();
/// let y = v.iter().collect::<Vec<_>>();
/// assert_eq!(x, y);
/// ```
#[clippy::version = "1.73.0"]
pub ITER_SKIP_ZERO,
correctness,
"disallows `.skip(0)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `bool::then` in `Iterator::filter_map`.
///
/// ### Why is this bad?
/// This can be written with `filter` then `map` instead, which would reduce nesting and
/// separates the filtering from the transformation phase. This comes with no cost to
/// performance and is just cleaner.
///
/// ### Limitations
/// Does not lint `bool::then_some`, as it eagerly evaluates its arguments rather than lazily.
/// This can create differing behavior, so better safe than sorry.
///
/// ### Example
/// ```no_run
/// # fn really_expensive_fn(i: i32) -> i32 { i }
/// # let v = vec![];
/// _ = v.into_iter().filter_map(|i| (i % 2 == 0).then(|| really_expensive_fn(i)));
/// ```
/// Use instead:
/// ```no_run
/// # fn really_expensive_fn(i: i32) -> i32 { i }
/// # let v = vec![];
/// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i));
/// ```
#[clippy::version = "1.73.0"]
pub FILTER_MAP_BOOL_THEN,
style,
"checks for usage of `bool::then` in `Iterator::filter_map`"
}
declare_clippy_lint! {
/// ### What it does
/// Looks for calls to `RwLock::write` where the lock is only used for reading.
///
/// ### Why is this bad?
/// The write portion of `RwLock` is exclusive, meaning that no other thread
/// can access the lock while this writer is active.
///
/// ### Example
/// ```no_run
/// use std::sync::RwLock;
/// fn assert_is_zero(lock: &RwLock<i32>) {
/// let num = lock.write().unwrap();
/// assert_eq!(*num, 0);
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// use std::sync::RwLock;
/// fn assert_is_zero(lock: &RwLock<i32>) {
/// let num = lock.read().unwrap();
/// assert_eq!(*num, 0);
/// }
/// ```
#[clippy::version = "1.73.0"]
pub READONLY_WRITE_LOCK,
perf,
"acquiring a write lock when a read lock would work"
}
declare_clippy_lint! {
/// ### What it does
/// Looks for iterator combinator calls such as `.take(x)` or `.skip(x)`
/// where `x` is greater than the amount of items that an iterator will produce.
///
/// ### Why is this bad?
/// Taking or skipping more items than there are in an iterator either creates an iterator
/// with all items from the original iterator or an iterator with no items at all.
/// This is most likely not what the user intended to do.
///
/// ### Example
/// ```no_run
/// for _ in [1, 2, 3].iter().take(4) {}
/// ```
/// Use instead:
/// ```no_run
/// for _ in [1, 2, 3].iter() {}
/// ```
#[clippy::version = "1.74.0"]
pub ITER_OUT_OF_BOUNDS,
suspicious,
"calls to `.take()` or `.skip()` that are out of bounds"
}
declare_clippy_lint! {
/// ### What it does
/// Looks for calls to `Path::ends_with` calls where the argument looks like a file extension.
///
/// By default, Clippy has a short list of known filenames that start with a dot
/// but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default.
/// The `allowed-dotfiles` configuration can be used to allow additional
/// file extensions that Clippy should not lint.
///
/// ### Why is this bad?
/// This doesn't actually compare file extensions. Rather, `ends_with` compares the given argument
/// to the last **component** of the path and checks if it matches exactly.
///
/// ### Known issues
/// File extensions are often at most three characters long, so this only lints in those cases
/// in an attempt to avoid false positives.
/// Any extension names longer than that are assumed to likely be real path components and are
/// therefore ignored.
///
/// ### Example
/// ```no_run
/// # use std::path::Path;
/// fn is_markdown(path: &Path) -> bool {
/// path.ends_with(".md")
/// }
/// ```
/// Use instead:
/// ```no_run
/// # use std::path::Path;
/// fn is_markdown(path: &Path) -> bool {
/// path.extension().is_some_and(|ext| ext == "md")
/// }
/// ```
#[clippy::version = "1.74.0"]
pub PATH_ENDS_WITH_EXT,
suspicious,
"attempting to compare file extensions using `Path::ends_with`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `as_str()` on a `String` chained with a method available on the `String` itself.
///
/// ### Why is this bad?
/// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness.
///
/// ### Example
/// ```no_run
/// let owned_string = "This is a string".to_owned();
/// owned_string.as_str().as_bytes()
/// # ;
/// ```
///
/// Use instead:
/// ```no_run
/// let owned_string = "This is a string".to_owned();
/// owned_string.as_bytes()
/// # ;
/// ```
#[clippy::version = "1.74.0"]
pub REDUNDANT_AS_STR,
complexity,
"`as_str` used to call a method on `str` that is also available on `String`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `waker.clone().wake()`
///
/// ### Why is this bad?
/// Cloning the waker is not necessary, `wake_by_ref()` enables the same operation
/// without extra cloning/dropping.
///
/// ### Example
/// ```rust,ignore
/// waker.clone().wake();
/// ```
/// Should be written
/// ```rust,ignore
/// waker.wake_by_ref();
/// ```
#[clippy::version = "1.75.0"]
pub WAKER_CLONE_WAKE,
perf,
"cloning a `Waker` only to wake it"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `TryInto::try_into` and `TryFrom::try_from` when their infallible counterparts
/// could be used.
///
/// ### Why is this bad?
/// In those cases, the `TryInto` and `TryFrom` trait implementation is a blanket impl that forwards
/// to `Into` or `From`, which always succeeds.
/// The returned `Result<_, Infallible>` requires error handling to get the contained value
/// even though the conversion can never fail.
///
/// ### Example
/// ```rust
/// let _: Result<i64, _> = 1i32.try_into();
/// let _: Result<i64, _> = <_>::try_from(1i32);
/// ```
/// Use `from`/`into` instead:
/// ```rust
/// let _: i64 = 1i32.into();
/// let _: i64 = <_>::from(1i32);
/// ```
#[clippy::version = "1.75.0"]
pub UNNECESSARY_FALLIBLE_CONVERSIONS,
style,
"calling the `try_from` and `try_into` trait methods when `From`/`Into` is implemented"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `Path::join` that start with a path separator (`\\` or `/`).
///
/// ### Why is this bad?
/// If the argument to `Path::join` starts with a separator, it will overwrite
/// the original path. If this is intentional, prefer using `Path::new` instead.
///
/// Note the behavior is platform dependent. A leading `\\` will be accepted
/// on unix systems as part of the file name
///
/// See [`Path::join`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
///
/// ### Example
/// ```rust
/// # use std::path::{Path, PathBuf};
/// let path = Path::new("/bin");
/// let joined_path = path.join("/sh");
/// assert_eq!(joined_path, PathBuf::from("/sh"));
/// ```
///
/// Use instead;
/// ```rust
/// # use std::path::{Path, PathBuf};
/// let path = Path::new("/bin");
///
/// // If this was unintentional, remove the leading separator
/// let joined_path = path.join("sh");
/// assert_eq!(joined_path, PathBuf::from("/bin/sh"));
///
/// // If this was intentional, create a new path instead
/// let new = Path::new("/sh");
/// assert_eq!(new, PathBuf::from("/sh"));
/// ```
#[clippy::version = "1.76.0"]
pub JOIN_ABSOLUTE_PATHS,
suspicious,
"calls to `Path::join` which will overwrite the original path"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for iterators of `Result`s using `.filter(Result::is_ok).map(Result::unwrap)` that may
/// be replaced with a `.flatten()` call.
///
/// ### Why is this bad?
/// `Result` implements `IntoIterator<Item = T>`. This means that `Result` can be flattened
/// automatically without suspicious-looking `unwrap` calls.
///
/// ### Example
/// ```no_run
/// let _ = std::iter::empty::<Result<i32, ()>>().filter(Result::is_ok).map(Result::unwrap);
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::iter::empty::<Result<i32, ()>>().flatten();
/// ```
#[clippy::version = "1.77.0"]
pub RESULT_FILTER_MAP,
complexity,
"filtering `Result` for `Ok` then force-unwrapping, which can be one type-safe operation"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.filter(Option::is_some)` that may be replaced with a `.flatten()` call.
/// This lint will require additional changes to the follow-up calls as it affects the type.
///
/// ### Why is this bad?
/// This pattern is often followed by manual unwrapping of the `Option`. The simplification
/// results in more readable and succinct code without the need for manual unwrapping.
///
/// ### Example
/// ```no_run
/// vec![Some(1)].into_iter().filter(Option::is_some);
///
/// ```
/// Use instead:
/// ```no_run
/// vec![Some(1)].into_iter().flatten();
/// ```
#[clippy::version = "1.77.0"]
pub ITER_FILTER_IS_SOME,
pedantic,
"filtering an iterator over `Option`s for `Some` can be achieved with `flatten`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.filter(Result::is_ok)` that may be replaced with a `.flatten()` call.
/// This lint will require additional changes to the follow-up calls as it affects the type.
///
/// ### Why is this bad?
/// This pattern is often followed by manual unwrapping of `Result`. The simplification
/// results in more readable and succinct code without the need for manual unwrapping.
///
/// ### Example
/// ```no_run
/// vec![Ok::<i32, String>(1)].into_iter().filter(Result::is_ok);
///
/// ```
/// Use instead:
/// ```no_run
/// vec![Ok::<i32, String>(1)].into_iter().flatten();
/// ```
#[clippy::version = "1.77.0"]
pub ITER_FILTER_IS_OK,
pedantic,
"filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type.
/// Also checks for equality comparisons like `option.map(f) == Some(true)` and `result.map(f) == Ok(true)`.
///
/// ### Why is this bad?
/// Readability. These can be written more concisely as `option.is_some_and(f)` and `result.is_ok_and(f)`.
///
/// ### Example
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.map(|a| a > 10).unwrap_or_default();
/// result.map(|a| a > 10).unwrap_or_default();
///
/// option.map(|a| a > 10) == Some(true);
/// result.map(|a| a > 10) == Ok(true);
/// option.map(|a| a > 10) != Some(true);
/// result.map(|a| a > 10) != Ok(true);
/// ```
/// Use instead:
/// ```no_run
/// # let option = Some(1);
/// # let result: Result<usize, ()> = Ok(1);
/// option.is_some_and(|a| a > 10);
/// result.is_ok_and(|a| a > 10);
///
/// option.is_some_and(|a| a > 10);
/// result.is_ok_and(|a| a > 10);
/// option.is_none_or(|a| a > 10);
/// !result.is_ok_and(|a| a > 10);
/// ```
#[clippy::version = "1.77.0"]
pub MANUAL_IS_VARIANT_AND,
pedantic,
"using `.map(f).unwrap_or_default()` or `.map(f) == Some/Ok(true)`, which are more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for usages of `str.trim().split("\n")` and `str.trim().split("\r\n")`.
///
/// ### Why is this bad?
///
/// Hard-coding the line endings makes the code less compatible. `str.lines` should be used instead.
///
/// ### Example
/// ```no_run
/// "some\ntext\nwith\nnewlines\n".trim().split('\n');
/// ```
/// Use instead:
/// ```no_run
/// "some\ntext\nwith\nnewlines\n".lines();
/// ```
///
/// ### Known Problems
///
/// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or
/// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
/// valid.
#[clippy::version = "1.77.0"]
pub STR_SPLIT_AT_NEWLINE,
pedantic,
"splitting a trimmed string at hard-coded newlines"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.as_ref().cloned()` and `.as_mut().cloned()` on `Option`s
///
/// ### Why is this bad?
/// This can be written more concisely by cloning the `Option` directly.
///
/// ### Example
/// ```no_run
/// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
/// bar.as_ref().cloned()
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
/// bar.clone()
/// }
/// ```
#[clippy::version = "1.77.0"]
pub OPTION_AS_REF_CLONED,
pedantic,
"cloning an `Option` via `as_ref().cloned()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for unnecessary calls to `min()` or `max()` in the following cases
/// - Either both side is constant
/// - One side is clearly larger than the other, like i32::MIN and an i32 variable
///
/// ### Why is this bad?
///
/// In the aforementioned cases it is not necessary to call `min()` or `max()`
/// to compare values, it may even cause confusion.
///
/// ### Example
/// ```no_run
/// let _ = 0.min(7_u32);
/// ```
/// Use instead:
/// ```no_run
/// let _ = 0;
/// ```
#[clippy::version = "1.81.0"]
pub UNNECESSARY_MIN_OR_MAX,
complexity,
"using 'min()/max()' when there is no need for it"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.map_or_else()` "map closure" for `Result` type.
///
/// ### Why is this bad?
/// This can be written more concisely by using `unwrap_or_else()`.
///
/// ### Example
/// ```no_run
/// # fn handle_error(_: ()) -> u32 { 0 }
/// let x: Result<u32, ()> = Ok(0);
/// let y = x.map_or_else(|err| handle_error(err), |n| n);
/// ```
/// Use instead:
/// ```no_run
/// # fn handle_error(_: ()) -> u32 { 0 }
/// let x: Result<u32, ()> = Ok(0);
/// let y = x.unwrap_or_else(|err| handle_error(err));
/// ```
#[clippy::version = "1.78.0"]
pub UNNECESSARY_RESULT_MAP_OR_ELSE,
suspicious,
"making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the manual creation of C strings (a string with a `NUL` byte at the end), either
/// through one of the `CStr` constructor functions, or more plainly by calling `.as_ptr()`
/// on a (byte) string literal with a hardcoded `\0` byte at the end.
///
/// ### Why is this bad?
/// This can be written more concisely using `c"str"` literals and is also less error-prone,
/// because the compiler checks for interior `NUL` bytes and the terminating `NUL` byte is inserted automatically.
///
/// ### Example
/// ```no_run
/// # use std::ffi::CStr;
/// # mod libc { pub unsafe fn puts(_: *const i8) {} }
/// fn needs_cstr(_: &CStr) {}
///
/// needs_cstr(CStr::from_bytes_with_nul(b"Hello\0").unwrap());
/// unsafe { libc::puts("World\0".as_ptr().cast()) }
/// ```
/// Use instead:
/// ```no_run
/// # use std::ffi::CStr;
/// # mod libc { pub unsafe fn puts(_: *const i8) {} }
/// fn needs_cstr(_: &CStr) {}
///
/// needs_cstr(c"Hello");
/// unsafe { libc::puts(c"World".as_ptr()) }
/// ```
#[clippy::version = "1.78.0"]
pub MANUAL_C_STR_LITERALS,
complexity,
r#"creating a `CStr` through functions when `c""` literals can be used"#
}
declare_clippy_lint! {
/// ### What it does
/// Checks the usage of `.get().is_some()` or `.get().is_none()` on std map types.
///
/// ### Why is this bad?
/// It can be done in one call with `.contains()`/`.contains_key()`.
///
/// ### Example
/// ```no_run
/// # use std::collections::HashSet;
/// let s: HashSet<String> = HashSet::new();
/// if s.get("a").is_some() {
/// // code
/// }
/// ```
/// Use instead:
/// ```no_run
/// # use std::collections::HashSet;
/// let s: HashSet<String> = HashSet::new();
/// if s.contains("a") {
/// // code
/// }
/// ```
#[clippy::version = "1.78.0"]
pub UNNECESSARY_GET_THEN_CHECK,
suspicious,
"calling `.get().is_some()` or `.get().is_none()` instead of `.contains()` or `.contains_key()`"
}
declare_clippy_lint! {
/// ### What it does
/// It identifies calls to `.is_empty()` on constant values.
///
/// ### Why is this bad?
/// String literals and constant values are known at compile time. Checking if they
/// are empty will always return the same value. This might not be the intention of
/// the expression.
///
/// ### Example
/// ```no_run
/// let value = "";
/// if value.is_empty() {
/// println!("the string is empty");
/// }
/// ```
/// Use instead:
/// ```no_run
/// println!("the string is empty");
/// ```
#[clippy::version = "1.79.0"]
pub CONST_IS_EMPTY,
suspicious,
"is_empty() called on strings known at compile time"
}
declare_clippy_lint! {
/// ### What it does
/// Converts some constructs mapping an Enum value for equality comparison.
///
/// ### Why is this bad?
/// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome,
/// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`.
/// Also, calls such as `opt.map_or(true, |val| val == 5)` can be reduced to
/// `opt.is_none_or(|val| val == 5)`.
/// This lint offers readability and conciseness improvements.
///
/// ### Example
/// ```no_run
/// pub fn a(x: Option<i32>) -> (bool, bool) {
/// (
/// x.map_or(false, |n| n == 5),
/// x.map_or(true, |n| n > 5),
/// )
/// }
/// ```
/// Use instead:
/// ```no_run
/// pub fn a(x: Option<i32>) -> (bool, bool) {
/// (
/// x == Some(5),
/// x.is_none_or(|n| n > 5),
/// )
/// }
/// ```
#[clippy::version = "1.84.0"]
pub UNNECESSARY_MAP_OR,
style,
"reduce unnecessary calls to `.map_or(bool, …)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks if an iterator is used to check if a string is ascii.
///
/// ### Why is this bad?
/// The `str` type already implements the `is_ascii` method.
///
/// ### Example
/// ```no_run
/// "foo".chars().all(|c| c.is_ascii());
/// ```
/// Use instead:
/// ```no_run
/// "foo".is_ascii();
/// ```
#[clippy::version = "1.81.0"]
pub NEEDLESS_CHARACTER_ITERATION,
suspicious,
"is_ascii() called on a char iterator"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for uses of `map` which return the original item.
///
/// ### Why is this bad?
/// `inspect` is both clearer in intent and shorter.
///
/// ### Example
/// ```no_run
/// let x = Some(0).map(|x| { println!("{x}"); x });
/// ```
/// Use instead:
/// ```no_run
/// let x = Some(0).inspect(|x| println!("{x}"));
/// ```
#[clippy::version = "1.81.0"]
pub MANUAL_INSPECT,
complexity,
"use of `map` returning the original item"
}
declare_clippy_lint! {
/// ### What it does
/// Checks the usage of `.first().is_some()` or `.first().is_none()` to check if a slice is
/// empty.
///
/// ### Why is this bad?
/// Using `.is_empty()` is shorter and better communicates the intention.
///
/// ### Example
/// ```no_run
/// let v = vec![1, 2, 3];
/// if v.first().is_none() {
/// // The vector is empty...
/// }
/// ```
/// Use instead:
/// ```no_run
/// let v = vec![1, 2, 3];
/// if v.is_empty() {
/// // The vector is empty...
/// }
/// ```
#[clippy::version = "1.83.0"]
pub UNNECESSARY_FIRST_THEN_CHECK,
complexity,
"calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
}
declare_clippy_lint! {
/// ### What it does
/// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
///
/// ### Why is this bad?
/// The `len()` and `is_empty()` methods are also directly available on strings, and they
/// return identical results. In particular, `len()` on a string returns the number of
/// bytes.
///
/// ### Example
/// ```
/// let len = "some string".as_bytes().len();
/// let b = "some string".as_bytes().is_empty();
/// ```
/// Use instead:
/// ```
/// let len = "some string".len();
/// let b = "some string".is_empty();
/// ```
#[clippy::version = "1.84.0"]
pub NEEDLESS_AS_BYTES,
complexity,
"detect useless calls to `as_bytes()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.map(…)`, followed by `.all(identity)` or `.any(identity)`.
///
/// ### Why is this bad?
/// The `.all(…)` or `.any(…)` methods can be called directly in place of `.map(…)`.
///
/// ### Example
/// ```
/// # let mut v = [""];
/// let e1 = v.iter().map(|s| s.is_empty()).all(|a| a);
/// let e2 = v.iter().map(|s| s.is_empty()).any(std::convert::identity);
/// ```
/// Use instead:
/// ```
/// # let mut v = [""];
/// let e1 = v.iter().all(|s| s.is_empty());
/// let e2 = v.iter().any(|s| s.is_empty());
/// ```
#[clippy::version = "1.84.0"]
pub MAP_ALL_ANY_IDENTITY,
complexity,
"combine `.map(_)` followed by `.all(identity)`/`.any(identity)` into a single call"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `Iterator::map` over ranges without using the parameter which
/// could be more clearly expressed using `std::iter::repeat(...).take(...)`
/// or `std::iter::repeat_n`.
///
/// ### Why is this bad?
///
/// It expresses the intent more clearly to `take` the correct number of times
/// from a generating function than to apply a closure to each number in a
/// range only to discard them.
///
/// ### Example
///
/// ```no_run
/// let random_numbers : Vec<_> = (0..10).map(|_| { 3 + 1 }).collect();
/// ```
/// Use instead:
/// ```no_run
/// let f : Vec<_> = std::iter::repeat( 3 + 1 ).take(10).collect();
/// ```
///
/// ### Known Issues
///
/// This lint may suggest replacing a `Map<Range>` with a `Take<RepeatWith>`.
/// The former implements some traits that the latter does not, such as
/// `DoubleEndedIterator`.
#[clippy::version = "1.84.0"]
pub MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
restriction,
"map of a trivial closure (not dependent on parameter) over a range"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced
/// with `DoubleEndedIterator::next_back`.
///
/// ### Why is this bad?
///
/// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if
/// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization,
/// `Iterator::last` cannot be optimized for `DoubleEndedIterator`.
///
/// ### Example
/// ```no_run
/// let last_arg = "echo hello world".split(' ').last();
/// ```
/// Use instead:
/// ```no_run
/// let last_arg = "echo hello world".split(' ').next_back();
/// ```
#[clippy::version = "1.86.0"]
pub DOUBLE_ENDED_ITERATOR_LAST,
perf,
"using `Iterator::last` on a `DoubleEndedIterator`"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `NonZero*::new_unchecked()` being used in a `const` context.
///
/// ### Why is this bad?
///
/// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a
/// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical
/// runtime performances while not requiring `unsafe`.
///
/// ### Example
/// ```no_run
/// use std::num::NonZeroUsize;
/// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
/// ```
/// Use instead:
/// ```no_run
/// use std::num::NonZeroUsize;
/// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
/// ```
#[clippy::version = "1.86.0"]
pub USELESS_NONZERO_NEW_UNCHECKED,
complexity,
"using `NonZero::new_unchecked()` in a `const` context"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `repeat().take()` that can be replaced with `repeat_n()`.
///
/// ### Why is this bad?
///
/// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning.
///
/// ### Example
/// ```no_run
/// let _ = std::iter::repeat(10).take(3);
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::iter::repeat_n(10, 3);
/// ```
#[clippy::version = "1.86.0"]
pub MANUAL_REPEAT_N,
style,
"detect `repeat().take()` that can be replaced with `repeat_n()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for string slices immediately followed by `as_bytes`.
///
/// ### Why is this bad?
/// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
///
/// ### Known problems
/// In some cases, the UTF-8 validation and potential panic from string slicing may be required for
/// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character
/// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred.
///
/// ### Example
/// ```rust
/// let s = "Lorem ipsum";
/// s[1..5].as_bytes();
/// ```
/// Use instead:
/// ```rust
/// let s = "Lorem ipsum";
/// &s.as_bytes()[1..5];
/// ```
#[clippy::version = "1.86.0"]
pub SLICED_STRING_AS_BYTES,
perf,
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
}
declare_clippy_lint! {
/// ### What it does
/// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using
/// the `?` operator instead.
///
/// ### Why is this bad?
/// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`.
/// This can be replaced with the `?` operator, which is more concise and idiomatic.
///
/// ### Example
///
/// ```no_run
/// fn test(opt: Option<i32>) -> Option<i32> {
/// opt.and_then(|n| {
/// if n > 1 {
/// Some(n + 1)
/// } else {
/// None
/// }
/// })
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn test(opt: Option<i32>) -> Option<i32> {
/// let n = opt?;
/// if n > 1 {
/// Some(n + 1)
/// } else {
/// None
/// }
/// }
/// ```
#[clippy::version = "1.86.0"]
pub RETURN_AND_THEN,
restriction,
"using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `Read::bytes` on types which don't implement `BufRead`.
///
/// ### Why is this bad?
/// The default implementation calls `read` for each byte, which can be very inefficient for data thats not in memory, such as `File`.
///
/// ### Example
/// ```no_run
/// use std::io::Read;
/// use std::fs::File;
/// let file = File::open("./bytes.txt").unwrap();
/// file.bytes();
/// ```
/// Use instead:
/// ```no_run
/// use std::io::{BufReader, Read};
/// use std::fs::File;
/// let file = BufReader::new(File::open("./bytes.txt").unwrap());
/// file.bytes();
/// ```
#[clippy::version = "1.87.0"]
pub UNBUFFERED_BYTES,
perf,
"calling .bytes() is very inefficient when data is not in memory"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `iter().any()` on slices when it can be replaced with `contains()` and suggests doing so.
///
/// ### Why is this bad?
/// `contains()` is more concise and idiomatic, while also being faster in some cases.
///
/// ### Example
/// ```no_run
/// fn foo(values: &[u8]) -> bool {
/// values.iter().any(|&v| v == 10)
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn foo(values: &[u8]) -> bool {
/// values.contains(&10)
/// }
/// ```
#[clippy::version = "1.87.0"]
pub MANUAL_CONTAINS,
perf,
"unnecessary `iter().any()` on slices that can be replaced with `contains()`"
}
declare_clippy_lint! {
/// This lint warns on calling `io::Error::new(..)` with a kind of
/// `io::ErrorKind::Other`.
///
/// ### Why is this bad?
/// Since Rust 1.74, there's the `io::Error::other(_)` shortcut.
///
/// ### Example
/// ```no_run
/// use std::io;
/// let _ = io::Error::new(io::ErrorKind::Other, "bad".to_string());
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::io::Error::other("bad".to_string());
/// ```
#[clippy::version = "1.87.0"]
pub IO_OTHER_ERROR,
style,
"calling `std::io::Error::new(std::io::ErrorKind::Other, _)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `std::mem::swap` with temporary values.
///
/// ### Why is this bad?
/// Storing a new value in place of a temporary value which will
/// be dropped right after the `swap` is an inefficient way of performing
/// an assignment. The same result can be achieved by using a regular
/// assignment.
///
/// ### Examples
/// ```no_run
/// fn replace_string(s: &mut String) {
/// std::mem::swap(s, &mut String::from("replaced"));
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn replace_string(s: &mut String) {
/// *s = String::from("replaced");
/// }
/// ```
///
/// Also, swapping two temporary values has no effect, as they will
/// both be dropped right after swapping them. This is likely an indication
/// of a bug. For example, the following code swaps the references to
/// the last element of the vectors, instead of swapping the elements
/// themselves:
///
/// ```no_run
/// fn bug(v1: &mut [i32], v2: &mut [i32]) {
/// // Incorrect: swapping temporary references (`&mut &mut` passed to swap)
/// std::mem::swap(&mut v1.last_mut().unwrap(), &mut v2.last_mut().unwrap());
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn correct(v1: &mut [i32], v2: &mut [i32]) {
/// std::mem::swap(v1.last_mut().unwrap(), v2.last_mut().unwrap());
/// }
/// ```
#[clippy::version = "1.88.0"]
pub SWAP_WITH_TEMPORARY,
complexity,
"detect swap with a temporary value"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for IP addresses that could be replaced with predefined constants such as
/// `Ipv4Addr::new(127, 0, 0, 1)` instead of using the appropriate constants.
///
/// ### Why is this bad?
/// Using specific IP addresses like `127.0.0.1` or `::1` is less clear and less maintainable than using the
/// predefined constants `Ipv4Addr::LOCALHOST` or `Ipv6Addr::LOCALHOST`. These constants improve code
/// readability, make the intent explicit, and are less error-prone.
///
/// ### Example
/// ```no_run
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// // IPv4 loopback
/// let addr_v4 = Ipv4Addr::new(127, 0, 0, 1);
///
/// // IPv6 loopback
/// let addr_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
/// ```
/// Use instead:
/// ```no_run
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// // IPv4 loopback
/// let addr_v4 = Ipv4Addr::LOCALHOST;
///
/// // IPv6 loopback
/// let addr_v6 = Ipv6Addr::LOCALHOST;
/// ```
#[clippy::version = "1.89.0"]
pub IP_CONSTANT,
pedantic,
"hardcoded localhost IP address"
}
#[expect(clippy::struct_excessive_bools)]
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
allow_expect_in_tests: bool,
allow_unwrap_in_tests: bool,
allow_expect_in_consts: bool,
allow_unwrap_in_consts: bool,
allowed_dotfiles: FxHashSet<&'static str>,
format_args: FormatArgsStorage,
}
impl Methods {
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
let mut allowed_dotfiles: FxHashSet<_> = conf.allowed_dotfiles.iter().map(|s| &**s).collect();
allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES);
Self {
avoid_breaking_exported_api: conf.avoid_breaking_exported_api,
msrv: conf.msrv,
allow_expect_in_tests: conf.allow_expect_in_tests,
allow_unwrap_in_tests: conf.allow_unwrap_in_tests,
allow_expect_in_consts: conf.allow_expect_in_consts,
allow_unwrap_in_consts: conf.allow_unwrap_in_consts,
allowed_dotfiles,
format_args,
}
}
}
impl_lint_pass!(Methods => [
UNWRAP_USED,
EXPECT_USED,
SHOULD_IMPLEMENT_TRAIT,
WRONG_SELF_CONVENTION,
OK_EXPECT,
UNWRAP_OR_DEFAULT,
MAP_UNWRAP_OR,
RESULT_MAP_OR_INTO_OPTION,
OPTION_MAP_OR_NONE,
BIND_INSTEAD_OF_MAP,
OR_FUN_CALL,
OR_THEN_UNWRAP,
EXPECT_FUN_CALL,
CHARS_NEXT_CMP,
CHARS_LAST_CMP,
CLONE_ON_COPY,
CLONE_ON_REF_PTR,
COLLAPSIBLE_STR_REPLACE,
CONST_IS_EMPTY,
ITER_OVEREAGER_CLONED,
CLONED_INSTEAD_OF_COPIED,
FLAT_MAP_OPTION,
INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF,
SINGLE_CHAR_ADD_STR,
SEARCH_IS_SOME,
FILTER_NEXT,
SKIP_WHILE_NEXT,
FILTER_MAP_IDENTITY,
MAP_IDENTITY,
MANUAL_FILTER_MAP,
MANUAL_FIND_MAP,
OPTION_FILTER_MAP,
FILTER_MAP_NEXT,
FLAT_MAP_IDENTITY,
MAP_FLATTEN,
ITERATOR_STEP_BY_ZERO,
ITER_NEXT_SLICE,
ITER_COUNT,
ITER_NTH,
ITER_NTH_ZERO,
BYTES_NTH,
ITER_SKIP_NEXT,
GET_UNWRAP,
GET_LAST_WITH_LEN,
STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT,
ITER_WITH_DRAIN,
TYPE_ID_ON_BOX,
USELESS_ASREF,
UNNECESSARY_FOLD,
UNNECESSARY_FILTER_MAP,
UNNECESSARY_FIND_MAP,
INTO_ITER_ON_REF,
SUSPICIOUS_MAP,
UNINIT_ASSUMED_INIT,
MANUAL_SATURATING_ARITHMETIC,
ZST_OFFSET,
FILETYPE_IS_FILE,
OPTION_AS_REF_DEREF,
UNNECESSARY_LAZY_EVALUATIONS,
MAP_COLLECT_RESULT_UNIT,
FROM_ITER_INSTEAD_OF_COLLECT,
INSPECT_FOR_EACH,
IMPLICIT_CLONE,
SUSPICIOUS_TO_OWNED,
SUSPICIOUS_SPLITN,
MANUAL_STR_REPEAT,
EXTEND_WITH_DRAIN,
MANUAL_SPLIT_ONCE,
NEEDLESS_SPLITN,
UNNECESSARY_TO_OWNED,
UNNECESSARY_JOIN,
ERR_EXPECT,
NEEDLESS_OPTION_AS_DEREF,
IS_DIGIT_ASCII_RADIX,
NEEDLESS_OPTION_TAKE,
NO_EFFECT_REPLACE,
OBFUSCATED_IF_ELSE,
ITER_ON_SINGLE_ITEMS,
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,
SUSPICIOUS_OPEN_OPTIONS,
PATH_BUF_PUSH_OVERWRITE,
RANGE_ZIP_WITH_LEN,
REPEAT_ONCE,
STABLE_SORT_PRIMITIVE,
UNIT_HASH,
READ_LINE_WITHOUT_TRIM,
UNNECESSARY_SORT_BY,
VEC_RESIZE_TO_ZERO,
VERBOSE_FILE_READS,
ITER_KV_MAP,
SEEK_FROM_CURRENT,
SEEK_TO_START_INSTEAD_OF_REWIND,
NEEDLESS_COLLECT,
SUSPICIOUS_COMMAND_ARG_SPACE,
CLEAR_WITH_DRAIN,
MANUAL_NEXT_BACK,
UNNECESSARY_LITERAL_UNWRAP,
DRAIN_COLLECT,
MANUAL_TRY_FOLD,
FORMAT_COLLECT,
STRING_LIT_CHARS_ANY,
ITER_SKIP_ZERO,
FILTER_MAP_BOOL_THEN,
READONLY_WRITE_LOCK,
ITER_OUT_OF_BOUNDS,
PATH_ENDS_WITH_EXT,
REDUNDANT_AS_STR,
WAKER_CLONE_WAKE,
UNNECESSARY_FALLIBLE_CONVERSIONS,
JOIN_ABSOLUTE_PATHS,
RESULT_FILTER_MAP,
ITER_FILTER_IS_SOME,
ITER_FILTER_IS_OK,
MANUAL_IS_VARIANT_AND,
STR_SPLIT_AT_NEWLINE,
OPTION_AS_REF_CLONED,
UNNECESSARY_RESULT_MAP_OR_ELSE,
MANUAL_C_STR_LITERALS,
UNNECESSARY_GET_THEN_CHECK,
UNNECESSARY_FIRST_THEN_CHECK,
NEEDLESS_CHARACTER_ITERATION,
MANUAL_INSPECT,
UNNECESSARY_MIN_OR_MAX,
NEEDLESS_AS_BYTES,
MAP_ALL_ANY_IDENTITY,
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
UNNECESSARY_MAP_OR,
DOUBLE_ENDED_ITERATOR_LAST,
USELESS_NONZERO_NEW_UNCHECKED,
MANUAL_REPEAT_N,
SLICED_STRING_AS_BYTES,
RETURN_AND_THEN,
UNBUFFERED_BYTES,
MANUAL_CONTAINS,
IO_OTHER_ERROR,
SWAP_WITH_TEMPORARY,
IP_CONSTANT,
]);
/// Extracts a method call name, args, and `Span` of the method name.
/// This ensures that neither the receiver nor any of the arguments
/// come from expansion.
pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> {
if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind
&& !args.iter().any(|e| e.span.from_expansion())
&& !receiver.span.from_expansion()
{
Some((path.ident.name, receiver, args, path.ident.span, call_span))
} else {
None
}
}
impl<'tcx> LateLintPass<'tcx> for Methods {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if expr.span.from_expansion() {
return;
}
self.check_methods(cx, expr);
match expr.kind {
ExprKind::Call(func, args) => {
from_iter_instead_of_collect::check(cx, expr, args, func);
unnecessary_fallible_conversions::check_function(cx, expr, func);
manual_c_str_literals::check(cx, expr, func, args, self.msrv);
useless_nonzero_new_unchecked::check(cx, expr, func, args, self.msrv);
io_other_error::check(cx, expr, func, args, self.msrv);
swap_with_temporary::check(cx, expr, func, args);
ip_constant::check(cx, expr, func, args);
},
ExprKind::MethodCall(method_call, receiver, args, _) => {
let method_span = method_call.ident.span;
or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args);
expect_fun_call::check(
cx,
&self.format_args,
expr,
method_span,
method_call.ident.name,
receiver,
args,
);
clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
single_char_add_str::check(cx, expr, receiver, args);
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
},
ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
let mut info = BinaryExprInfo {
expr,
chain: lhs,
other: rhs,
eq: op.node == hir::BinOpKind::Eq,
};
lint_binary_expr_with_method_call(cx, &mut info);
},
_ => (),
}
}
#[allow(clippy::too_many_lines)]
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
if impl_item.span.in_external_macro(cx.sess().source_map()) {
return;
}
let name = impl_item.ident.name;
let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
let item = cx.tcx.hir_expect_item(parent);
let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind {
let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity();
let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig);
let first_arg_ty_opt = method_sig.inputs().iter().next().copied();
// if this impl block implements a trait, lint in trait definition instead
if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) {
// check missing trait implementations
for method_config in &TRAIT_METHODS {
if name == method_config.method_name
&& sig.decl.inputs.len() == method_config.param_count
&& method_config.output_type.matches(&sig.decl.output)
// in case there is no first arg, since we already have checked the number of arguments
// it's should be always true
&& first_arg_ty_opt.is_none_or(|first_arg_ty| method_config
.self_kind.matches(cx, self_ty, first_arg_ty)
)
&& fn_header_equals(method_config.fn_header, sig.header)
&& method_config.lifetime_param_cond(impl_item)
{
span_lint_and_help(
cx,
SHOULD_IMPLEMENT_TRAIT,
impl_item.span,
format!(
"method `{}` can be confused for the standard trait method `{}::{}`",
method_config.method_name, method_config.trait_name, method_config.method_name
),
None,
format!(
"consider implementing the trait `{}` or choosing a less ambiguous method name",
method_config.trait_name
),
);
}
}
}
if sig.decl.implicit_self.has_implicit_self()
&& !(self.avoid_breaking_exported_api
&& cx.effective_visibilities.is_exported(impl_item.owner_id.def_id))
&& let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir_body(id)).next()
&& let Some(first_arg_ty) = first_arg_ty_opt
{
wrong_self_convention::check(
cx,
name,
self_ty,
first_arg_ty,
first_arg.pat.span,
implements_trait,
false,
);
}
}
// if this impl block implements a trait, lint in trait definition instead
if implements_trait {
return;
}
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.owner_id);
if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
return;
}
if name == sym::new && ret_ty != self_ty {
span_lint(
cx,
NEW_RET_NO_SELF,
impl_item.span,
"methods called `new` usually return `Self`",
);
}
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if item.span.in_external_macro(cx.tcx.sess.source_map()) {
return;
}
if let TraitItemKind::Fn(ref sig, _) = item.kind
&& sig.decl.implicit_self.has_implicit_self()
&& let Some(first_arg_hir_ty) = sig.decl.inputs.first()
&& let Some(&first_arg_ty) = cx
.tcx
.fn_sig(item.owner_id)
.instantiate_identity()
.inputs()
.skip_binder()
.first()
{
let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty();
wrong_self_convention::check(
cx,
item.ident.name,
self_ty,
first_arg_ty,
first_arg_hir_ty.span,
false,
true,
);
}
if item.ident.name == sym::new
&& let TraitItemKind::Fn(_, _) = item.kind
&& let ret_ty = return_ty(cx, item.owner_id)
&& let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty()
&& !ret_ty.contains(self_ty)
{
span_lint(
cx,
NEW_RET_NO_SELF,
item.span,
"methods called `new` usually return `Self`",
);
}
}
}
impl Methods {
#[allow(clippy::too_many_lines)]
fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// Handle method calls whose receiver and arguments may not come from expansion
if let Some((name, recv, args, span, call_span)) = method_call(expr) {
match (name, args) {
(
sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub,
[_arg],
) => {
zst_offset::check(cx, expr, recv);
},
(sym::all, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, true);
match method_call(recv) {
Some((sym::cloned, recv2, [], _, _)) => {
iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::NeedlessMove(arg),
false,
);
},
Some((sym::map, _, [map_arg], _, map_call_span)) => {
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all");
},
_ => {},
}
},
(sym::and_then, [arg]) => {
let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg);
let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg);
if !biom_option_linted && !biom_result_linted {
let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
if !ule_and_linted {
return_and_then::check(cx, expr, recv, arg);
}
}
},
(sym::any, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, false);
match method_call(recv) {
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::NeedlessMove(arg),
false,
),
Some((sym::chars, recv, _, _, _))
if let ExprKind::Closure(arg) = arg.kind
&& let body = cx.tcx.hir_body(arg.body)
&& let [param] = body.params =>
{
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), self.msrv);
},
Some((sym::map, _, [map_arg], _, map_call_span)) => {
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any");
},
Some((sym::iter, iter_recv, ..)) => {
manual_contains::check(cx, expr, iter_recv, arg);
},
_ => {},
}
},
(sym::arg, [arg]) => {
suspicious_command_arg_space::check(cx, recv, arg, span);
},
(sym::as_deref | sym::as_deref_mut, []) => {
needless_option_as_deref::check(cx, expr, recv, name);
},
(sym::as_bytes, []) => {
if let Some((sym::as_str, recv, [], as_str_span, _)) = method_call(recv) {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
}
sliced_string_as_bytes::check(cx, expr, recv);
},
(sym::as_mut | sym::as_ref, []) => useless_asref::check(cx, expr, name, recv),
(sym::as_ptr, []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, self.msrv),
(sym::assume_init, []) => uninit_assumed_init::check(cx, expr, recv),
(sym::bytes, []) => unbuffered_bytes::check(cx, expr, recv),
(sym::cloned, []) => {
cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv);
option_as_ref_cloned::check(cx, recv, span);
},
(sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => {
needless_collect::check(cx, span, expr, recv, call_span);
match method_call(recv) {
Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => {
iter_cloned_collect::check(cx, name, expr, recv2);
},
Some((sym::map, m_recv, [m_arg], m_ident_span, _)) => {
map_collect_result_unit::check(cx, expr, m_recv, m_arg);
format_collect::check(cx, expr, m_arg, m_ident_span);
},
Some((sym::take, take_self_arg, [take_arg], _, _)) => {
if self.msrv.meets(cx, msrvs::STR_REPEAT) {
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
}
},
Some((sym::drain, recv, args, ..)) => {
drain_collect::check(cx, args, expr, recv);
},
_ => {},
}
},
(sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
Some((sym::cloned, recv2, [], _, _)) => {
iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false);
},
Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => {
iter_count::check(cx, expr, recv2, name2);
},
Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg),
Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg),
Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {},
},
(sym::min | sym::max, [arg]) => {
unnecessary_min_or_max::check(cx, expr, name, recv, arg);
},
(sym::drain, ..) => {
if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.parent_hir_node(expr.hir_id)
&& matches!(kind, StmtKind::Semi(_))
&& args.len() <= 1
{
clear_with_drain::check(cx, expr, recv, span, args.first());
} else if let [arg] = args {
iter_with_drain::check(cx, expr, recv, span, arg);
}
},
(sym::ends_with, [arg]) => {
if let ExprKind::MethodCall(.., span) = expr.kind {
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv);
}
path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles);
},
(sym::expect, [_]) => {
match method_call(recv) {
Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv),
Some((sym::err, recv, [], err_span, _)) => {
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
},
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
(sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
(sym::extend, [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
extend_with_drain::check(cx, expr, recv, arg);
},
(sym::filter, [arg]) => {
if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) {
// if `arg` has side-effect, the semantic will change
iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::FixClosure(name, arg),
false,
);
}
if self.msrv.meets(cx, msrvs::ITER_FLATTEN) {
// use the sourcemap to get the span of the closure
iter_filter::check(cx, expr, arg, span);
}
},
(sym::find, [arg]) => {
if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) {
// if `arg` has side-effect, the semantic will change
iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::FixClosure(name, arg),
false,
);
}
},
(sym::filter_map, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
unnecessary_filter_map::check(cx, expr, arg, name);
filter_map_bool_then::check(cx, expr, arg, call_span);
filter_map_identity::check(cx, expr, arg, span);
},
(sym::find_map, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
unnecessary_filter_map::check(cx, expr, arg, name);
},
(sym::flat_map, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
flat_map_identity::check(cx, expr, arg, span);
flat_map_option::check(cx, expr, arg, span);
},
(sym::flatten, []) => match method_call(recv) {
Some((sym::map, recv, [map_arg], map_span, _)) => {
map_flatten::check(cx, expr, recv, map_arg, map_span);
},
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::LaterCloned,
true,
),
_ => {},
},
(sym::fold, [init, acc]) => {
manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv);
unnecessary_fold::check(cx, expr, init, acc, span);
},
(sym::for_each, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
match method_call(recv) {
Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::NeedlessMove(arg),
false,
),
_ => {},
}
},
(sym::get, [arg]) => {
get_first::check(cx, expr, recv, arg);
get_last_with_len::check(cx, expr, recv, arg);
},
(sym::get_or_insert_with, [arg]) => {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert");
},
(sym::hash, [arg]) => {
unit_hash::check(cx, expr, recv, arg);
},
(sym::is_empty, []) => {
match method_call(recv) {
Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) => {
needless_as_bytes::check(cx, prev_method, name, prev_recv, expr.span);
},
Some((sym::as_str, recv, [], as_str_span, _)) => {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
},
_ => {},
}
is_empty::check(cx, expr, recv);
},
(sym::is_file, []) => filetype_is_file::check(cx, expr, recv),
(sym::is_digit, [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
(sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false),
(sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true),
(sym::iter | sym::iter_mut | sym::into_iter, []) => {
iter_on_single_or_empty_collections::check(cx, expr, name, recv);
},
(sym::join, [join_arg]) => {
if let Some((sym::collect, _, _, span, _)) = method_call(recv) {
unnecessary_join::check(cx, expr, recv, join_arg, span);
} else {
join_absolute_paths::check(cx, recv, join_arg, expr.span);
}
},
(sym::last, []) => {
if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::LaterCloned,
false,
);
}
double_ended_iterator_last::check(cx, expr, recv, call_span);
},
(sym::len, []) => {
if let Some((prev_method @ (sym::as_bytes | sym::bytes), prev_recv, [], _, _)) = method_call(recv) {
needless_as_bytes::check(cx, prev_method, sym::len, prev_recv, expr.span);
}
},
(sym::lock, []) => {
mut_mutex_lock::check(cx, expr, recv, span);
},
(name @ (sym::map | sym::map_err), [m_arg]) => {
if name == sym::map {
unused_enumerate_index::check(cx, expr, recv, m_arg);
map_clone::check(cx, expr, recv, m_arg, self.msrv);
map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span);
manual_is_variant_and::check_map(cx, expr);
match method_call(recv) {
Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => {
iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv);
},
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::NeedlessMove(m_arg),
false,
),
_ => {},
}
} else {
map_err_ignore::check(cx, expr, m_arg);
}
if let Some((name, recv2, args, span2, _)) = method_call(recv) {
match (name, args) {
(sym::as_mut, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
(sym::as_ref, []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
(sym::filter, [f_arg]) => {
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
},
(sym::find, [f_arg]) => {
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
},
_ => {},
}
}
map_identity::check(cx, expr, recv, m_arg, name, span);
manual_inspect::check(cx, expr, m_arg, name, span, self.msrv);
},
(sym::map_or, [def, map]) => {
option_map_or_none::check(cx, expr, recv, def, map);
manual_ok_or::check(cx, expr, recv, def, map);
unnecessary_map_or::check(cx, expr, recv, def, map, span, self.msrv);
},
(sym::map_or_else, [def, map]) => {
result_map_or_else_none::check(cx, expr, recv, def, map);
unnecessary_result_map_or_else::check(cx, expr, recv, def, map);
},
(sym::next, []) => {
if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
match (name2, args2) {
(sym::cloned, []) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::LaterCloned,
false,
),
(sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg),
(sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
(sym::iter, []) => iter_next_slice::check(cx, expr, recv2),
(sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
(sym::skip_while, [_]) => skip_while_next::check(cx, expr),
(sym::rev, []) => manual_next_back::check(cx, expr, recv, recv2),
_ => {},
}
}
},
(sym::nth, [n_arg]) => match method_call(recv) {
Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::LaterCloned,
false,
),
Some((iter_method @ (sym::iter | sym::iter_mut), iter_recv, [], iter_span, _)) => {
if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) {
iter_nth_zero::check(cx, expr, recv, n_arg);
}
},
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
},
(sym::ok_or_else, [arg]) => {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or");
},
(sym::open, [_]) => {
open_options::check(cx, expr, recv);
},
(sym::or_else, [arg]) => {
if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
}
},
(sym::push, [arg]) => {
path_buf_push_overwrite::check(cx, expr, arg);
},
(sym::read_to_end, [_]) => {
verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
},
(sym::read_to_string, [_]) => {
verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
},
(sym::read_line, [arg]) => {
read_line_without_trim::check(cx, expr, recv, arg);
},
(sym::repeat, [arg]) => {
repeat_once::check(cx, expr, recv, arg);
},
(name @ (sym::replace | sym::replacen), [arg1, arg2] | [arg1, arg2, _]) => {
no_effect_replace::check(cx, expr, arg1, arg2);
// Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
if self.msrv.meets(cx, msrvs::PATTERN_TRAIT_CHAR_ARRAY)
&& name == sym::replace
&& let Some((sym::replace, ..)) = method_call(recv)
{
collapsible_str_replace::check(cx, expr, arg1, arg2);
}
},
(sym::resize, [count_arg, default_arg]) => {
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
},
(sym::seek, [arg]) => {
if self.msrv.meets(cx, msrvs::SEEK_FROM_CURRENT) {
seek_from_current::check(cx, expr, recv, arg);
}
if self.msrv.meets(cx, msrvs::SEEK_REWIND) {
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
}
},
(sym::skip, [arg]) => {
iter_skip_zero::check(cx, expr, arg);
iter_out_of_bounds::check_skip(cx, expr, recv, arg);
if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::LaterCloned,
false,
);
}
},
(sym::sort, []) => {
stable_sort_primitive::check(cx, expr, recv);
},
(sym::sort_by, [arg]) => {
unnecessary_sort_by::check(cx, expr, recv, arg, false);
},
(sym::sort_unstable_by, [arg]) => {
unnecessary_sort_by::check(cx, expr, recv, arg, true);
},
(sym::split, [arg]) => {
str_split::check(cx, expr, recv, arg);
},
(sym::splitn | sym::rsplitn, [count_arg, pat_arg]) => {
if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
}
},
(sym::splitn_mut | sym::rsplitn_mut, [count_arg, _]) => {
if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
}
},
(sym::step_by, [arg]) => iterator_step_by_zero::check(cx, expr, arg),
(sym::take, [arg]) => {
iter_out_of_bounds::check_take(cx, expr, recv, arg);
manual_repeat_n::check(cx, expr, recv, arg, self.msrv);
if let Some((sym::cloned, recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::LaterCloned,
false,
);
}
},
(sym::take, []) => needless_option_take::check(cx, expr, recv),
(sym::then, [arg]) => {
if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) {
return;
}
unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
},
(sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => {
unnecessary_fallible_conversions::check_method(cx, expr);
},
(sym::to_owned, []) => {
if !suspicious_to_owned::check(cx, expr, recv) {
implicit_clone::check(cx, name, expr, recv);
}
},
(sym::to_os_string | sym::to_path_buf | sym::to_string | sym::to_vec, []) => {
implicit_clone::check(cx, name, expr, recv);
},
(sym::type_id, []) => {
type_id_on_box::check(cx, recv, expr.span);
},
(sym::unwrap, []) => {
match method_call(recv) {
Some((sym::get, recv, [get_arg], _, _)) => {
get_unwrap::check(cx, expr, recv, get_arg, false);
},
Some((sym::get_mut, recv, [get_arg], _, _)) => {
get_unwrap::check(cx, expr, recv, get_arg, true);
},
Some((sym::or, recv, [or_arg], or_span, _)) => {
or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
},
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
(sym::unwrap_or, [u_arg]) => {
match method_call(recv) {
Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => {
manual_saturating_arithmetic::check(
cx,
expr,
lhs,
rhs,
u_arg,
&arith.as_str()[const { "checked_".len() }..],
);
},
Some((sym::map, m_recv, [m_arg], span, _)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
},
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name);
},
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
(sym::unwrap_or_default, []) => {
match method_call(recv) {
Some((sym::map, m_recv, [arg], span, _)) => {
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
},
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(
cx,
expr,
t_recv,
t_arg,
None,
then_method,
sym::unwrap_or_default,
);
},
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
(sym::unwrap_or_else, [u_arg]) => {
match method_call(recv) {
Some((sym::map, recv, [map_arg], _, _))
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(
cx,
expr,
t_recv,
t_arg,
Some(u_arg),
then_method,
sym::unwrap_or_else,
);
},
_ => {
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
(sym::wake, []) => {
waker_clone_wake::check(cx, expr, recv);
},
(sym::write, []) => {
readonly_write_lock::check(cx, expr, recv);
},
(sym::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);
}
},
_ => {},
}
}
// Handle method calls whose receiver and arguments may come from expansion
if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind {
match (path.ident.name, args) {
(sym::expect, [_]) => {
unwrap_expect_used::check(
cx,
expr,
recv,
false,
self.allow_expect_in_consts,
self.allow_expect_in_tests,
unwrap_expect_used::Variant::Expect,
);
},
(sym::expect_err, [_]) => {
unwrap_expect_used::check(
cx,
expr,
recv,
true,
self.allow_expect_in_consts,
self.allow_expect_in_tests,
unwrap_expect_used::Variant::Expect,
);
},
(sym::unwrap, []) => {
unwrap_expect_used::check(
cx,
expr,
recv,
false,
self.allow_unwrap_in_consts,
self.allow_unwrap_in_tests,
unwrap_expect_used::Variant::Unwrap,
);
},
(sym::unwrap_err, []) => {
unwrap_expect_used::check(
cx,
expr,
recv,
true,
self.allow_unwrap_in_consts,
self.allow_unwrap_in_tests,
unwrap_expect_used::Variant::Unwrap,
);
},
_ => {},
}
}
}
}
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, is_some: bool) {
match method_call(recv) {
Some((name @ (sym::find | sym::position | sym::rposition), f_recv, [arg], span, _)) => {
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
},
Some((sym::get, f_recv, [arg], _, _)) => {
unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some);
},
Some((sym::first, f_recv, [], _, _)) => {
unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some);
},
_ => {},
}
}
/// Used for `lint_binary_expr_with_method_call`.
#[derive(Copy, Clone)]
struct BinaryExprInfo<'a> {
expr: &'a Expr<'a>,
chain: &'a Expr<'a>,
other: &'a Expr<'a>,
eq: bool,
}
/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
macro_rules! lint_with_both_lhs_and_rhs {
($func:expr, $cx:expr, $info:ident) => {
if !$func($cx, $info) {
::std::mem::swap(&mut $info.chain, &mut $info.other);
if $func($cx, $info) {
return;
}
}
};
}
lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
}
const FN_HEADER: hir::FnHeader = hir::FnHeader {
safety: hir::HeaderSafety::Normal(hir::Safety::Safe),
constness: hir::Constness::NotConst,
asyncness: hir::IsAsync::NotAsync,
abi: ExternAbi::Rust,
};
struct ShouldImplTraitCase {
trait_name: &'static str,
method_name: Symbol,
param_count: usize,
fn_header: hir::FnHeader,
// implicit self kind expected (none, self, &self, ...)
self_kind: SelfKind,
// checks against the output type
output_type: OutType,
// certain methods with explicit lifetimes can't implement the equivalent trait method
lint_explicit_lifetime: bool,
}
impl ShouldImplTraitCase {
const fn new(
trait_name: &'static str,
method_name: Symbol,
param_count: usize,
fn_header: hir::FnHeader,
self_kind: SelfKind,
output_type: OutType,
lint_explicit_lifetime: bool,
) -> ShouldImplTraitCase {
ShouldImplTraitCase {
trait_name,
method_name,
param_count,
fn_header,
self_kind,
output_type,
lint_explicit_lifetime,
}
}
fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
self.lint_explicit_lifetime
|| !impl_item.generics.params.iter().any(|p| {
matches!(
p.kind,
hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
}
)
})
}
}
#[rustfmt::skip]
const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true),
ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true),
ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true),
ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true),
ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true),
ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true),
ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true),
ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false),
ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
];
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum SelfKind {
Value,
Ref,
RefMut,
No, // When we want the first argument type to be different than `Self`
}
impl SelfKind {
fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
if ty == parent_ty {
true
} else if let Some(boxed_ty) = ty.boxed_ty() {
boxed_ty == parent_ty
} else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
if let ty::Adt(_, args) = ty.kind() {
args.types().next() == Some(parent_ty)
} else {
false
}
} else {
false
}
}
fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
if let ty::Ref(_, t, m) = *ty.kind() {
return m == mutability && t == parent_ty;
}
let trait_sym = match mutability {
hir::Mutability::Not => sym::AsRef,
hir::Mutability::Mut => sym::AsMut,
};
let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else {
return false;
};
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
}
fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
!matches_value(cx, parent_ty, ty)
&& !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
&& !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
}
match self {
Self::Value => matches_value(cx, parent_ty, ty),
Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
Self::No => matches_none(cx, parent_ty, ty),
}
}
#[must_use]
fn description(self) -> &'static str {
match self {
Self::Value => "`self` by value",
Self::Ref => "`self` by reference",
Self::RefMut => "`self` by mutable reference",
Self::No => "no `self`",
}
}
}
#[derive(Clone, Copy)]
enum OutType {
Unit,
Bool,
Any,
Ref,
}
impl OutType {
fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
match (self, ty) {
(Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
(Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
(Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
(Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
(Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)),
_ => false,
}
}
}
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness
}