mod bind_instead_of_map;
mod bytes_nth;
+mod clone_on_copy;
mod clone_on_ref_ptr;
+mod expect_fun_call;
mod expect_used;
mod filetype_is_file;
+mod filter_flat_map;
mod filter_map;
+mod filter_map_flat_map;
mod filter_map_identity;
mod filter_map_map;
mod filter_map_next;
mod filter_next;
+mod flat_map_identity;
mod from_iter_instead_of_collect;
mod get_unwrap;
mod implicit_clone;
mod iter_next_slice;
mod iter_nth;
mod iter_nth_zero;
+mod iter_skip_next;
mod iterator_step_by_zero;
mod manual_saturating_arithmetic;
mod map_collect_result_unit;
+mod map_flatten;
+mod map_unwrap_or;
mod ok_expect;
mod option_as_ref_deref;
+mod option_map_or_none;
mod option_map_unwrap_or;
+mod or_fun_call;
+mod search_is_some;
mod single_char_insert_string;
mod single_char_pattern;
mod single_char_push_string;
mod suspicious_map;
mod uninit_assumed_init;
mod unnecessary_filter_map;
+mod unnecessary_fold;
mod unnecessary_lazy_eval;
mod unwrap_used;
+mod useless_asref;
mod wrong_self_convention;
mod zst_offset;
-use std::borrow::Cow;
-use std::iter;
-
use bind_instead_of_map::BindInsteadOfMap;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{
+ contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls,
+ method_chain_args, paths, return_ty, single_segment_path, SpanlessEq,
+};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_hir::{PatKind, TraitItem, TraitItemKind};
+use rustc_hir::{TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
-use rustc_span::symbol::{sym, Symbol, SymbolStr};
+use rustc_span::symbol::{sym, SymbolStr};
use rustc_typeck::hir_ty_to_ty;
-use crate::utils::eager_or_lazy::is_lazyness_candidate;
-use crate::utils::usage::mutated_variables;
-use crate::utils::{
- contains_return, contains_ty, get_parent_expr, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of,
- is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method,
- match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty,
- single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
- span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth, SpanlessEq,
-};
-
declare_clippy_lint! {
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
///
/// **What it does:** Checks for methods with certain name prefixes and which
/// doesn't match how self is taken. The actual rules are:
///
- /// |Prefix |`self` taken |
- /// |-------|----------------------|
- /// |`as_` |`&self` or `&mut self`|
- /// |`from_`| none |
- /// |`into_`|`self` |
- /// |`is_` |`&self` or none |
- /// |`to_` |`&self` |
+ /// |Prefix |Postfix |`self` taken | `self` type |
+ /// |-------|------------|-----------------------|--------------|
+ /// |`as_` | none |`&self` or `&mut self` | any |
+ /// |`from_`| none | none | any |
+ /// |`into_`| none |`self` | any |
+ /// |`is_` | none |`&self` or none | any |
+ /// |`to_` | `_mut` |`&mut self` | any |
+ /// |`to_` | not `_mut` |`self` | `Copy` |
+ /// |`to_` | not `_mut` |`&self` | not `Copy` |
+ ///
+ /// 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
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()`.
+ /// `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(_)`.
+ /// **Why is this bad?** Readability, this can be written more concisely as:
+ /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
+ /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
- /// # let vec = vec![1];
+ /// let vec = vec![1];
/// vec.iter().find(|x| **x == 0).is_some();
+ ///
+ /// let _ = "hello world".find("world").is_none();
/// ```
/// Could be written as
/// ```rust
- /// # let vec = vec![1];
+ /// let vec = vec![1];
/// vec.iter().any(|x| *x == 0);
+ ///
+ /// let _ = !"hello world".contains("world");
/// ```
pub SEARCH_IS_SOME,
complexity,
- "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`"
+ "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! {
["expect", ..] => expect_used::check(cx, expr, arg_lists[0]),
["unwrap_or", "map"] => option_map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
["unwrap_or_else", "map"] => {
- if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
+ if !map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or");
}
},
- ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
+ ["map_or", ..] => option_map_or_none::check(cx, expr, arg_lists[0]),
["and_then", ..] => {
let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, arg_lists[0]);
let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, arg_lists[0]);
["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]),
["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()),
["map", "find"] => filter_map::check(cx, expr, true),
- ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
- ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
- ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
- ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
- ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
- ["is_some", "position"] => {
- lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
+ ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+ ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+ ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]),
+ ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]),
+ [option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => {
+ search_is_some::check(
+ cx,
+ expr,
+ "find",
+ option_check_method,
+ arg_lists[1],
+ arg_lists[0],
+ method_spans[1],
+ )
},
- ["is_some", "rposition"] => {
- lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
+ [option_check_method, "position"]
+ if "is_some" == *option_check_method || "is_none" == *option_check_method =>
+ {
+ search_is_some::check(
+ cx,
+ expr,
+ "position",
+ option_check_method,
+ arg_lists[1],
+ arg_lists[0],
+ method_spans[1],
+ )
+ },
+ [option_check_method, "rposition"]
+ if "is_some" == *option_check_method || "is_none" == *option_check_method =>
+ {
+ search_is_some::check(
+ cx,
+ expr,
+ "rposition",
+ option_check_method,
+ arg_lists[1],
+ arg_lists[0],
+ method_spans[1],
+ )
},
["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]),
["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"),
["nth", "bytes"] => bytes_nth::check(cx, expr, &arg_lists[1]),
["nth", ..] => iter_nth_zero::check(cx, expr, arg_lists[0]),
["step_by", ..] => iterator_step_by_zero::check(cx, expr, arg_lists[0]),
- ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]),
+ ["next", "skip"] => iter_skip_next::check(cx, expr, arg_lists[1]),
["collect", "cloned"] => iter_cloned_collect::check(cx, expr, arg_lists[1]),
- ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
- ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
- ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
+ ["as_ref"] => useless_asref::check(cx, expr, "as_ref", arg_lists[0]),
+ ["as_mut"] => useless_asref::check(cx, expr, "as_mut", arg_lists[0]),
+ ["fold", ..] => unnecessary_fold::check(cx, expr, arg_lists[0], method_spans[0]),
["filter_map", ..] => {
unnecessary_filter_map::check(cx, expr, arg_lists[0]);
filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
},
- ["count", "map"] => suspicious_map::check(cx, expr),
+ ["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]),
["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr),
["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {
manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..])
}
},
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
- lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
- lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
+ or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
+ expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
if args.len() == 1 && method_call.ident.name == sym::clone {
- lint_clone_on_copy(cx, expr, &args[0], self_ty);
+ clone_on_copy::check(cx, expr, &args[0], self_ty);
clone_on_ref_ptr::check(cx, expr, &args[0]);
}
if args.len() == 1 && method_call.ident.name == sym!(to_string) {
let item = cx.tcx.hir().expect_item(parent);
let self_ty = cx.tcx.type_of(item.def_id);
- // if this impl block implements a trait, lint in trait definition instead
- if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind {
- return;
- }
+ let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
if_chain! {
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
if let Some(first_arg_ty) = first_arg_ty;
then {
- if cx.access_levels.is_exported(impl_item.hir_id()) {
+ // if this impl block implements a trait, lint in trait definition instead
+ if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) {
// check missing trait implementations
for method_config in &TRAIT_METHODS {
if name == method_config.method_name &&
item.vis.node.is_pub(),
self_ty,
first_arg_ty,
- first_arg.pat.span
+ first_arg.pat.span,
+ 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.hir_id());
false,
self_ty,
first_arg_ty,
- first_arg_span
+ first_arg_span,
+ true
);
}
}
extract_msrv_attr!(LateContext);
}
-/// Checks for the `OR_FUN_CALL` lint.
-#[allow(clippy::too_many_lines)]
-fn lint_or_fun_call<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &hir::Expr<'_>,
- method_span: Span,
- name: &str,
- args: &'tcx [hir::Expr<'_>],
-) {
- /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
- fn check_unwrap_or_default(
- cx: &LateContext<'_>,
- name: &str,
- fun: &hir::Expr<'_>,
- self_expr: &hir::Expr<'_>,
- arg: &hir::Expr<'_>,
- or_has_args: bool,
- span: Span,
- ) -> bool {
- if_chain! {
- if !or_has_args;
- if name == "unwrap_or";
- if let hir::ExprKind::Path(ref qpath) = fun.kind;
- let path = &*last_path_segment(qpath).ident.as_str();
- if ["default", "new"].contains(&path);
- let arg_ty = cx.typeck_results().expr_ty(arg);
- if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
- if implements_trait(cx, arg_ty, default_trait_id, &[]);
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- OR_FUN_CALL,
- span,
- &format!("use of `{}` followed by a call to `{}`", name, path),
- "try this",
- format!(
- "{}.unwrap_or_default()",
- snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
- ),
- applicability,
- );
-
- true
- } else {
- false
- }
- }
- }
-
- /// Checks for `*or(foo())`.
- #[allow(clippy::too_many_arguments)]
- fn check_general_case<'tcx>(
- cx: &LateContext<'tcx>,
- name: &str,
- method_span: Span,
- self_expr: &hir::Expr<'_>,
- arg: &'tcx hir::Expr<'_>,
- span: Span,
- // None if lambda is required
- fun_span: Option<Span>,
- ) {
- // (path, fn_has_argument, methods, suffix)
- static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
- (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
- (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
- (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
- (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
- ];
-
- if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
- if path.ident.as_str() == "len" {
- let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
-
- match ty.kind() {
- ty::Slice(_) | ty::Array(_, _) => return,
- _ => (),
- }
-
- if is_type_diagnostic_item(cx, ty, sym::vec_type) {
- return;
- }
- }
- }
-
- if_chain! {
- if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
-
- if is_lazyness_candidate(cx, arg);
- if !contains_return(&arg);
-
- let self_ty = cx.typeck_results().expr_ty(self_expr);
-
- if let Some(&(_, fn_has_arguments, poss, suffix)) =
- KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
-
- if poss.contains(&name);
-
- then {
- let macro_expanded_snipped;
- let sugg: Cow<'_, str> = {
- let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
- (false, Some(fun_span)) => (fun_span, false),
- _ => (arg.span, true),
- };
- let snippet = {
- let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
- if not_macro_argument_snippet == "vec![]" {
- macro_expanded_snipped = snippet(cx, snippet_span, "..");
- match macro_expanded_snipped.strip_prefix("$crate::vec::") {
- Some(stripped) => Cow::from(stripped),
- None => macro_expanded_snipped
- }
- }
- else {
- not_macro_argument_snippet
- }
- };
-
- if use_lambda {
- let l_arg = if fn_has_arguments { "_" } else { "" };
- format!("|{}| {}", l_arg, snippet).into()
- } else {
- snippet
- }
- };
- let span_replace_word = method_span.with_hi(span.hi());
- span_lint_and_sugg(
- cx,
- OR_FUN_CALL,
- span_replace_word,
- &format!("use of `{}` followed by a function call", name),
- "try this",
- format!("{}_{}({})", name, suffix, sugg),
- Applicability::HasPlaceholders,
- );
- }
- }
- }
-
- if args.len() == 2 {
- match args[1].kind {
- hir::ExprKind::Call(ref fun, ref or_args) => {
- let or_has_args = !or_args.is_empty();
- if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
- let fun_span = if or_has_args { None } else { Some(fun.span) };
- check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
- }
- },
- hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
- check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
- },
- _ => {},
- }
- }
-}
-
-/// Checks for the `EXPECT_FUN_CALL` lint.
-#[allow(clippy::too_many_lines)]
-fn lint_expect_fun_call(
- cx: &LateContext<'_>,
- expr: &hir::Expr<'_>,
- method_span: Span,
- name: &str,
- args: &[hir::Expr<'_>],
-) {
- // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
- // `&str`
- fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
- let mut arg_root = arg;
- loop {
- arg_root = match &arg_root.kind {
- hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
- hir::ExprKind::MethodCall(method_name, _, call_args, _) => {
- if call_args.len() == 1
- && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref))
- && {
- let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
- let base_type = arg_type.peel_refs();
- *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type)
- }
- {
- &call_args[0]
- } else {
- break;
- }
- },
- _ => break,
- };
- }
- arg_root
- }
-
- // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
- // converted to string.
- fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
- let arg_ty = cx.typeck_results().expr_ty(arg);
- if is_type_diagnostic_item(cx, arg_ty, sym::string_type) {
- return false;
- }
- if let ty::Ref(_, ty, ..) = arg_ty.kind() {
- if *ty.kind() == ty::Str && can_be_static_str(cx, arg) {
- return false;
- }
- };
- true
- }
-
- // Check if an expression could have type `&'static str`, knowing that it
- // has type `&str` for some lifetime.
- fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
- match arg.kind {
- hir::ExprKind::Lit(_) => true,
- hir::ExprKind::Call(fun, _) => {
- if let hir::ExprKind::Path(ref p) = fun.kind {
- match cx.qpath_res(p, fun.hir_id) {
- hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
- cx.tcx.fn_sig(def_id).output().skip_binder().kind(),
- ty::Ref(ty::ReStatic, ..)
- ),
- _ => false,
- }
- } else {
- false
- }
- },
- hir::ExprKind::MethodCall(..) => {
- cx.typeck_results()
- .type_dependent_def_id(arg.hir_id)
- .map_or(false, |method_id| {
- matches!(
- cx.tcx.fn_sig(method_id).output().skip_binder().kind(),
- ty::Ref(ty::ReStatic, ..)
- )
- })
- },
- hir::ExprKind::Path(ref p) => matches!(
- cx.qpath_res(p, arg.hir_id),
- hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _)
- ),
- _ => false,
- }
- }
-
- fn generate_format_arg_snippet(
- cx: &LateContext<'_>,
- a: &hir::Expr<'_>,
- applicability: &mut Applicability,
- ) -> Vec<String> {
- if_chain! {
- if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind;
- if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
- if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
-
- then {
- format_arg_expr_tup
- .iter()
- .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
- .collect()
- } else {
- unreachable!()
- }
- }
- }
-
- fn is_call(node: &hir::ExprKind<'_>) -> bool {
- match node {
- hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
- is_call(&expr.kind)
- },
- hir::ExprKind::Call(..)
- | hir::ExprKind::MethodCall(..)
- // These variants are debatable or require further examination
- | hir::ExprKind::If(..)
- | hir::ExprKind::Match(..)
- | hir::ExprKind::Block{ .. } => true,
- _ => false,
- }
- }
-
- if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
- return;
- }
-
- let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]);
- let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) {
- "||"
- } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) {
- "|_|"
- } else {
- return;
- };
-
- let arg_root = get_arg_root(cx, &args[1]);
-
- let span_replace_word = method_span.with_hi(expr.span.hi());
-
- let mut applicability = Applicability::MachineApplicable;
-
- //Special handling for `format!` as arg_root
- if_chain! {
- if let hir::ExprKind::Block(block, None) = &arg_root.kind;
- if block.stmts.len() == 1;
- if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
- if let Some(arg_root) = &local.init;
- if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind;
- if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
- if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
- then {
- let fmt_spec = &format_args[0];
- let fmt_args = &format_args[1];
-
- let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
-
- args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
-
- let sugg = args.join(", ");
-
- span_lint_and_sugg(
- cx,
- EXPECT_FUN_CALL,
- span_replace_word,
- &format!("use of `{}` followed by a function call", name),
- "try this",
- format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
- applicability,
- );
-
- return;
- }
- }
-
- let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
- if requires_to_string(cx, arg_root) {
- arg_root_snippet.to_mut().push_str(".to_string()");
- }
-
- span_lint_and_sugg(
- cx,
- EXPECT_FUN_CALL,
- span_replace_word,
- &format!("use of `{}` followed by a function call", name),
- "try this",
- format!(
- "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})",
- closure_args, arg_root_snippet
- ),
- applicability,
- );
-}
-
-/// Checks for the `CLONE_ON_COPY` lint.
-fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
- let ty = cx.typeck_results().expr_ty(expr);
- if let ty::Ref(_, inner, _) = arg_ty.kind() {
- if let ty::Ref(_, innermost, _) = inner.kind() {
- span_lint_and_then(
- cx,
- CLONE_DOUBLE_REF,
- expr.span,
- &format!(
- "using `clone` on a double-reference; \
- this will copy the reference of type `{}` instead of cloning the inner type",
- ty
- ),
- |diag| {
- if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
- let mut ty = innermost;
- let mut n = 0;
- while let ty::Ref(_, inner, _) = ty.kind() {
- ty = inner;
- n += 1;
- }
- let refs: String = iter::repeat('&').take(n + 1).collect();
- let derefs: String = iter::repeat('*').take(n).collect();
- let explicit = format!("<{}{}>::clone({})", refs, ty, snip);
- diag.span_suggestion(
- expr.span,
- "try dereferencing it",
- format!("{}({}{}).clone()", refs, derefs, snip.deref()),
- Applicability::MaybeIncorrect,
- );
- diag.span_suggestion(
- expr.span,
- "or try being explicit if you are sure, that you want to clone a reference",
- explicit,
- Applicability::MaybeIncorrect,
- );
- }
- },
- );
- return; // don't report clone_on_copy
- }
- }
-
- if is_copy(cx, ty) {
- let snip;
- if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
- let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
- match &cx.tcx.hir().get(parent) {
- hir::Node::Expr(parent) => match parent.kind {
- // &*x is a nop, &x.clone() is not
- hir::ExprKind::AddrOf(..) => return,
- // (*x).func() is useless, x.clone().func() can work in case func borrows mutably
- hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => {
- return;
- },
-
- _ => {},
- },
- hir::Node::Stmt(stmt) => {
- if let hir::StmtKind::Local(ref loc) = stmt.kind {
- if let hir::PatKind::Ref(..) = loc.pat.kind {
- // let ref y = *x borrows x, let ref y = x.clone() does not
- return;
- }
- }
- },
- _ => {},
- }
-
- // x.clone() might have dereferenced x, possibly through Deref impls
- if cx.typeck_results().expr_ty(arg) == ty {
- snip = Some(("try removing the `clone` call", format!("{}", snippet)));
- } else {
- let deref_count = cx
- .typeck_results()
- .expr_adjustments(arg)
- .iter()
- .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
- .count();
- let derefs: String = iter::repeat('*').take(deref_count).collect();
- snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
- }
- } else {
- snip = None;
- }
- span_lint_and_then(
- cx,
- CLONE_ON_COPY,
- expr.span,
- &format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
- |diag| {
- if let Some((text, snip)) = snip {
- diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
- }
- },
- );
- }
-}
-
-fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) {
- fn check_fold_with_op(
- cx: &LateContext<'_>,
- expr: &hir::Expr<'_>,
- fold_args: &[hir::Expr<'_>],
- fold_span: Span,
- op: hir::BinOpKind,
- replacement_method_name: &str,
- replacement_has_args: bool,
- ) {
- if_chain! {
- // Extract the body of the closure passed to fold
- if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
- let closure_body = cx.tcx.hir().body(body_id);
- let closure_expr = remove_blocks(&closure_body.value);
-
- // Check if the closure body is of the form `acc <op> some_expr(x)`
- if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
- if bin_op.node == op;
-
- // Extract the names of the two arguments to the closure
- if let [param_a, param_b] = closure_body.params;
- if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind;
- if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind;
-
- if path_to_local_id(left_expr, first_arg_id);
- if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- let sugg = if replacement_has_args {
- format!(
- "{replacement}(|{s}| {r})",
- replacement = replacement_method_name,
- s = second_arg_ident,
- r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
- )
- } else {
- format!(
- "{replacement}()",
- replacement = replacement_method_name,
- )
- };
-
- span_lint_and_sugg(
- cx,
- UNNECESSARY_FOLD,
- fold_span.with_hi(expr.span.hi()),
- // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
- "this `.fold` can be written more succinctly using another method",
- "try",
- sugg,
- applicability,
- );
- }
- }
- }
-
- // Check that this is a call to Iterator::fold rather than just some function called fold
- if !match_trait_method(cx, expr, &paths::ITERATOR) {
- return;
- }
-
- assert!(
- fold_args.len() == 3,
- "Expected fold_args to have three entries - the receiver, the initial value and the closure"
- );
-
- // Check if the first argument to .fold is a suitable literal
- if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
- match lit.node {
- ast::LitKind::Bool(false) => {
- check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
- },
- ast::LitKind::Bool(true) => {
- check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
- },
- ast::LitKind::Int(0, _) => {
- check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
- },
- ast::LitKind::Int(1, _) => {
- check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
- },
- _ => (),
- }
- }
-}
-
-fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
- // lint if caller of skip is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
- if let [caller, n] = skip_args {
- let hint = format!(".nth({})", snippet(cx, n.span, ".."));
- span_lint_and_sugg(
- cx,
- ITER_SKIP_NEXT,
- expr.span.trim_start(caller.span).unwrap(),
- "called `skip(..).next()` on an iterator",
- "use `nth` instead",
- hint,
- Applicability::MachineApplicable,
- );
- }
- }
-}
-
fn derefs_to_slice<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
}
}
-/// lint use of `map().flatten()` for `Iterators` and 'Options'
-fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
- // lint if caller of `.map().flatten()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
- let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
- let is_map_to_option = match map_closure_ty.kind() {
- ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
- let map_closure_sig = match map_closure_ty.kind() {
- ty::Closure(_, substs) => substs.as_closure().sig(),
- _ => map_closure_ty.fn_sig(cx.tcx),
- };
- let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
- is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type)
- },
- _ => false,
- };
-
- let method_to_use = if is_map_to_option {
- // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
- "filter_map"
- } else {
- // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
- "flat_map"
- };
- let func_snippet = snippet(cx, map_args[1].span, "..");
- let hint = format!(".{0}({1})", method_to_use, func_snippet);
- span_lint_and_sugg(
- cx,
- MAP_FLATTEN,
- expr.span.with_lo(map_args[0].span.hi()),
- "called `map(..).flatten()` on an `Iterator`",
- &format!("try using `{}` instead", method_to_use),
- hint,
- Applicability::MachineApplicable,
- );
- }
-
- // lint if caller of `.map().flatten()` is an Option
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
- let func_snippet = snippet(cx, map_args[1].span, "..");
- let hint = format!(".and_then({})", func_snippet);
- span_lint_and_sugg(
- cx,
- MAP_FLATTEN,
- expr.span.with_lo(map_args[0].span.hi()),
- "called `map(..).flatten()` on an `Option`",
- "try using `and_then` instead",
- hint,
- Applicability::MachineApplicable,
- );
- }
-}
-
-const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
-/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
-/// Return true if lint triggered
-fn lint_map_unwrap_or_else<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- map_args: &'tcx [hir::Expr<'_>],
- unwrap_args: &'tcx [hir::Expr<'_>],
- msrv: Option<&RustcVersion>,
-) -> bool {
- if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
- return false;
- }
- // lint if the caller of `map()` is an `Option`
- let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
- let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
-
- if is_option || is_result {
- // Don't make a suggestion that may fail to compile due to mutably borrowing
- // the same variable twice.
- let map_mutated_vars = mutated_variables(&map_args[0], cx);
- let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
- if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
- if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
- return false;
- }
- } else {
- return false;
- }
-
- // lint message
- let msg = if is_option {
- "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \
- `map_or_else(<g>, <f>)` instead"
- } else {
- "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \
- `.map_or_else(<g>, <f>)` instead"
- };
- // get snippets for args to map() and unwrap_or_else()
- let map_snippet = snippet(cx, map_args[1].span, "..");
- let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
- // lint, with note if neither arg is > 1 line and both map() and
- // unwrap_or_else() have the same span
- let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
- let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
- if same_span && !multiline {
- let var_snippet = snippet(cx, map_args[0].span, "..");
- span_lint_and_sugg(
- cx,
- MAP_UNWRAP_OR,
- expr.span,
- msg,
- "try this",
- format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet),
- Applicability::MachineApplicable,
- );
- return true;
- } else if same_span && multiline {
- span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
- return true;
- }
- }
-
- false
-}
-
-/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
-fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) {
- let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type);
- let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type);
-
- // There are two variants of this `map_or` lint:
- // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
- // (2) using `map_or` as a combinator instead of `and_then`
- //
- // (For this lint) we don't care if any other type calls `map_or`
- if !is_option && !is_result {
- return;
- }
-
- let (lint_name, msg, instead, hint) = {
- let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind {
- match_qpath(qpath, &paths::OPTION_NONE)
- } else {
- return;
- };
-
- if !default_arg_is_none {
- // nothing to lint!
- return;
- }
-
- let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind {
- match_qpath(qpath, &paths::OPTION_SOME)
- } else {
- false
- };
-
- if is_option {
- let self_snippet = snippet(cx, map_or_args[0].span, "..");
- let func_snippet = snippet(cx, map_or_args[2].span, "..");
- let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
- `and_then(..)` instead";
- (
- OPTION_MAP_OR_NONE,
- msg,
- "try using `and_then` instead",
- format!("{0}.and_then({1})", self_snippet, func_snippet),
- )
- } else if f_arg_is_some {
- let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
- `ok()` instead";
- let self_snippet = snippet(cx, map_or_args[0].span, "..");
- (
- RESULT_MAP_OR_INTO_OPTION,
- msg,
- "try using `ok` instead",
- format!("{0}.ok()", self_snippet),
- )
- } else {
- // nothing to lint!
- return;
- }
- };
-
- span_lint_and_sugg(
- cx,
- lint_name,
- expr.span,
- msg,
- instead,
- hint,
- Applicability::MachineApplicable,
- );
-}
-
-/// lint use of `filter().flat_map()` for `Iterators`
-fn lint_filter_flat_map<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- _filter_args: &'tcx [hir::Expr<'_>],
- _map_args: &'tcx [hir::Expr<'_>],
-) {
- // lint if caller of `.filter().flat_map()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
- let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
- and filtering by returning `iter::empty()`";
- span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
- }
-}
-
-/// lint use of `filter_map().flat_map()` for `Iterators`
-fn lint_filter_map_flat_map<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- _filter_args: &'tcx [hir::Expr<'_>],
- _map_args: &'tcx [hir::Expr<'_>],
-) {
- // lint if caller of `.filter_map().flat_map()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
- let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
- and filtering by returning `iter::empty()`";
- span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
- }
-}
-
-/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
-fn lint_flat_map_identity<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- flat_map_args: &'tcx [hir::Expr<'_>],
- flat_map_span: Span,
-) {
- if match_trait_method(cx, expr, &paths::ITERATOR) {
- let arg_node = &flat_map_args[1].kind;
-
- let apply_lint = |message: &str| {
- span_lint_and_sugg(
- cx,
- FLAT_MAP_IDENTITY,
- flat_map_span.with_hi(expr.span.hi()),
- message,
- "try",
- "flatten()".to_string(),
- Applicability::MachineApplicable,
- );
- };
-
- if_chain! {
- if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
- let body = cx.tcx.hir().body(*body_id);
-
- if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
- if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
-
- if path.segments.len() == 1;
- if path.segments[0].ident.name == binding_ident.name;
-
- then {
- apply_lint("called `flat_map(|x| x)` on an `Iterator`");
- }
- }
-
- if_chain! {
- if let hir::ExprKind::Path(ref qpath) = arg_node;
-
- if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
- then {
- apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
- }
- }
- }
-}
-
-/// lint searching an Iterator followed by `is_some()`
-/// or calling `find()` on a string followed by `is_some()`
-fn lint_search_is_some<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- search_method: &str,
- search_args: &'tcx [hir::Expr<'_>],
- is_some_args: &'tcx [hir::Expr<'_>],
- method_span: Span,
-) {
- // lint if caller of search is an Iterator
- if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
- let msg = format!(
- "called `is_some()` after searching an `Iterator` with `{}`",
- search_method
- );
- let hint = "this is more succinctly expressed by calling `any()`";
- let search_snippet = snippet(cx, search_args[1].span, "..");
- if search_snippet.lines().count() <= 1 {
- // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
- // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
- let any_search_snippet = if_chain! {
- if search_method == "find";
- if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind;
- let closure_body = cx.tcx.hir().body(body_id);
- if let Some(closure_arg) = closure_body.params.get(0);
- then {
- if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
- Some(search_snippet.replacen('&', "", 1))
- } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind {
- let name = &*ident.name.as_str();
- Some(search_snippet.replace(&format!("*{}", name), name))
- } else {
- None
- }
- } else {
- None
- }
- };
- // add note if not multi-line
- span_lint_and_sugg(
- cx,
- SEARCH_IS_SOME,
- method_span.with_hi(expr.span.hi()),
- &msg,
- "use `any()` instead",
- format!(
- "any({})",
- any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
- ),
- Applicability::MachineApplicable,
- );
- } else {
- span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint);
- }
- }
- // lint if `find()` is called by `String` or `&str`
- else if search_method == "find" {
- let is_string_or_str_slice = |e| {
- let self_ty = cx.typeck_results().expr_ty(e).peel_refs();
- if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
- true
- } else {
- *self_ty.kind() == ty::Str
- }
- };
- if_chain! {
- if is_string_or_str_slice(&search_args[0]);
- if is_string_or_str_slice(&search_args[1]);
- then {
- let msg = "called `is_some()` after calling `find()` on a string";
- let mut applicability = Applicability::MachineApplicable;
- let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
- span_lint_and_sugg(
- cx,
- SEARCH_IS_SOME,
- method_span.with_hi(expr.span.hi()),
- msg,
- "use `contains()` instead",
- format!("contains({})", find_arg),
- applicability,
- );
- }
- }
- }
-}
-
/// Used for `lint_binary_expr_with_method_call`.
#[derive(Copy, Clone)]
struct BinaryExprInfo<'a> {
}
}
-/// Checks for the `USELESS_ASREF` lint.
-fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
- // when we get here, we've already checked that the call name is "as_ref" or "as_mut"
- // check if the call is to the actual `AsRef` or `AsMut` trait
- if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
- // check if the type after `as_ref` or `as_mut` is the same as before
- let recvr = &as_ref_args[0];
- let rcv_ty = cx.typeck_results().expr_ty(recvr);
- let res_ty = cx.typeck_results().expr_ty(expr);
- let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
- let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
- if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
- // allow the `as_ref` or `as_mut` if it is followed by another method call
- if_chain! {
- if let Some(parent) = get_parent_expr(cx, expr);
- if let hir::ExprKind::MethodCall(_, ref span, _, _) = parent.kind;
- if span != &expr.span;
- then {
- return;
- }
- }
-
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- USELESS_ASREF,
- expr.span,
- &format!("this call to `{}` does nothing", call_name),
- "try this",
- snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(),
- applicability,
- );
- }
- }
-}
-
const FN_HEADER: hir::FnHeader = hir::FnHeader {
unsafety: hir::Unsafety::Normal,
constness: hir::Constness::NotConst,
#[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",
+ Self::Value => "`self` by value",
+ Self::Ref => "`self` by reference",
+ Self::RefMut => "`self` by mutable reference",
+ Self::No => "no `self`",
}
}
}