about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-03-22 19:36:22 +0000
committerbors <bors@rust-lang.org>2021-03-22 19:36:22 +0000
commit029777f029984723bc68cf9fb3fb6d7ee2a7a617 (patch)
treea6ba9aa43c013b92a5961c3899b40f2c4ec4d5f1
parentaca95aac7b070bc8730178bfea8464f52277e616 (diff)
parentb6a2757561167f831939f874735e259e655031a1 (diff)
downloadrust-029777f029984723bc68cf9fb3fb6d7ee2a7a617.tar.gz
rust-029777f029984723bc68cf9fb3fb6d7ee2a7a617.zip
Auto merge of #6896 - TaKO8Ki:refactor-lints-in-methods-module, r=phansch
Refactor lints in methods module

This PR refactors methods lints other than the lints I refactored in https://github.com/rust-lang/rust-clippy/pull/6826 and moves some functions to methods/utils.rs.
Basically, I follow the instruction described in #6680.

**For ease of review, I refactored step by step, keeping each commit small.**

closes https://github.com/rust-lang/rust-clippy/issues/6886
cc: `@phansch,` `@flip1995,` `@Y-Nak`

changelog: Move lints in methods module to their own modules and some function to methods/utils.rs.
-rw-r--r--clippy_lints/src/methods/chars_cmp.rs54
-rw-r--r--clippy_lints/src/methods/chars_cmp_with_unwrap.rs44
-rw-r--r--clippy_lints/src/methods/chars_last_cmp.rs13
-rw-r--r--clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs13
-rw-r--r--clippy_lints/src/methods/chars_next_cmp.rs8
-rw-r--r--clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs8
-rw-r--r--clippy_lints/src/methods/clone_on_copy.rs10
-rw-r--r--clippy_lints/src/methods/clone_on_ref_ptr.rs8
-rw-r--r--clippy_lints/src/methods/filter_flat_map.rs7
-rw-r--r--clippy_lints/src/methods/filter_map_flat_map.rs7
-rw-r--r--clippy_lints/src/methods/filter_map_map.rs7
-rw-r--r--clippy_lints/src/methods/from_iter_instead_of_collect.rs12
-rw-r--r--clippy_lints/src/methods/get_unwrap.rs2
-rw-r--r--clippy_lints/src/methods/inefficient_to_string.rs11
-rw-r--r--clippy_lints/src/methods/into_iter_on_ref.rs49
-rw-r--r--clippy_lints/src/methods/iter_cloned_collect.rs2
-rw-r--r--clippy_lints/src/methods/iter_count.rs2
-rw-r--r--clippy_lints/src/methods/iter_next_slice.rs14
-rw-r--r--clippy_lints/src/methods/iter_nth.rs2
-rw-r--r--clippy_lints/src/methods/iter_skip_next.rs3
-rw-r--r--clippy_lints/src/methods/mod.rs254
-rw-r--r--clippy_lints/src/methods/single_char_add_str.rs14
-rw-r--r--clippy_lints/src/methods/single_char_insert_string.rs2
-rw-r--r--clippy_lints/src/methods/single_char_pattern.rs38
-rw-r--r--clippy_lints/src/methods/single_char_push_string.rs2
-rw-r--r--clippy_lints/src/methods/string_extend_chars.rs57
-rw-r--r--clippy_lints/src/methods/utils.rs78
27 files changed, 388 insertions, 333 deletions
diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs
new file mode 100644
index 00000000000..c668fe52781
--- /dev/null
+++ b/clippy_lints/src/methods/chars_cmp.rs
@@ -0,0 +1,54 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{method_chain_args, single_segment_path};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_lint::Lint;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    info: &crate::methods::BinaryExprInfo<'_>,
+    chain_methods: &[&str],
+    lint: &'static Lint,
+    suggest: &str,
+) -> bool {
+    if_chain! {
+        if let Some(args) = method_chain_args(info.chain, chain_methods);
+        if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
+        if arg_char.len() == 1;
+        if let hir::ExprKind::Path(ref qpath) = fun.kind;
+        if let Some(segment) = single_segment_path(qpath);
+        if segment.ident.name == sym::Some;
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
+
+            if *self_ty.kind() != ty::Str {
+                return false;
+            }
+
+            span_lint_and_sugg(
+                cx,
+                lint,
+                info.expr.span,
+                &format!("you should use the `{}` method", suggest),
+                "like this",
+                format!("{}{}.{}({})",
+                        if info.eq { "" } else { "!" },
+                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+                        suggest,
+                        snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
+                applicability,
+            );
+
+            return true;
+        }
+    }
+
+    false
+}
diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
new file mode 100644
index 00000000000..4275857757f
--- /dev/null
+++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
@@ -0,0 +1,44 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_lint::Lint;
+
+/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    info: &crate::methods::BinaryExprInfo<'_>,
+    chain_methods: &[&str],
+    lint: &'static Lint,
+    suggest: &str,
+) -> bool {
+    if_chain! {
+        if let Some(args) = method_chain_args(info.chain, chain_methods);
+        if let hir::ExprKind::Lit(ref lit) = info.other.kind;
+        if let ast::LitKind::Char(c) = lit.node;
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                lint,
+                info.expr.span,
+                &format!("you should use the `{}` method", suggest),
+                "like this",
+                format!("{}{}.{}('{}')",
+                        if info.eq { "" } else { "!" },
+                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+                        suggest,
+                        c),
+                applicability,
+            );
+
+            true
+        } else {
+            false
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/chars_last_cmp.rs b/clippy_lints/src/methods/chars_last_cmp.rs
new file mode 100644
index 00000000000..07bbc5ca1bf
--- /dev/null
+++ b/clippy_lints/src/methods/chars_last_cmp.rs
@@ -0,0 +1,13 @@
+use crate::methods::chars_cmp;
+use rustc_lint::LateContext;
+
+use super::CHARS_LAST_CMP;
+
+/// Checks for the `CHARS_LAST_CMP` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
+        true
+    } else {
+        chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
+    }
+}
diff --git a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs
new file mode 100644
index 00000000000..c29ee0ec8c8
--- /dev/null
+++ b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs
@@ -0,0 +1,13 @@
+use crate::methods::chars_cmp_with_unwrap;
+use rustc_lint::LateContext;
+
+use super::CHARS_LAST_CMP;
+
+/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
+        true
+    } else {
+        chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
+    }
+}
diff --git a/clippy_lints/src/methods/chars_next_cmp.rs b/clippy_lints/src/methods/chars_next_cmp.rs
new file mode 100644
index 00000000000..a6701d8830e
--- /dev/null
+++ b/clippy_lints/src/methods/chars_next_cmp.rs
@@ -0,0 +1,8 @@
+use rustc_lint::LateContext;
+
+use super::CHARS_NEXT_CMP;
+
+/// Checks for the `CHARS_NEXT_CMP` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
+}
diff --git a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs
new file mode 100644
index 00000000000..28ede28e935
--- /dev/null
+++ b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs
@@ -0,0 +1,8 @@
+use rustc_lint::LateContext;
+
+use super::CHARS_NEXT_CMP;
+
+/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
+}
diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs
index 949152cf789..edb6649b87b 100644
--- a/clippy_lints/src/methods/clone_on_copy.rs
+++ b/clippy_lints/src/methods/clone_on_copy.rs
@@ -4,14 +4,20 @@ use clippy_utils::ty::is_copy;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty;
+use rustc_span::symbol::{sym, Symbol};
 use std::iter;
 
 use super::CLONE_DOUBLE_REF;
 use super::CLONE_ON_COPY;
 
 /// Checks for the `CLONE_ON_COPY` lint.
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+    if !(args.len() == 1 && method_name == sym::clone) {
+        return;
+    }
+    let arg = &args[0];
+    let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
     let ty = cx.typeck_results().expr_ty(expr);
     if let ty::Ref(_, inner, _) = arg_ty.kind() {
         if let ty::Ref(_, innermost, _) = inner.kind() {
diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs
index 03c949d5c31..6417bc81304 100644
--- a/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -6,11 +6,15 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{sym, Symbol};
 
 use super::CLONE_ON_REF_PTR;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+    if !(args.len() == 1 && method_name == sym::clone) {
+        return;
+    }
+    let arg = &args[0];
     let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
 
     if let ty::Adt(_, subst) = obj_ty.kind() {
diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs
index 2f3a3c55ab5..1588eec8882 100644
--- a/clippy_lints/src/methods/filter_flat_map.rs
+++ b/clippy_lints/src/methods/filter_flat_map.rs
@@ -7,12 +7,7 @@ use rustc_span::sym;
 use super::FILTER_MAP;
 
 /// lint use of `filter().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    _filter_args: &'tcx [hir::Expr<'_>],
-    _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
     // lint if caller of `.filter().flat_map()` is an Iterator
     if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs
index b1a4dc98eb8..741b1e7e361 100644
--- a/clippy_lints/src/methods/filter_map_flat_map.rs
+++ b/clippy_lints/src/methods/filter_map_flat_map.rs
@@ -7,12 +7,7 @@ use rustc_span::sym;
 use super::FILTER_MAP;
 
 /// lint use of `filter_map().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    _filter_args: &'tcx [hir::Expr<'_>],
-    _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
     // lint if caller of `.filter_map().flat_map()` is an Iterator
     if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs
index 0b0a8b1dd3b..713bbf25837 100644
--- a/clippy_lints/src/methods/filter_map_map.rs
+++ b/clippy_lints/src/methods/filter_map_map.rs
@@ -7,12 +7,7 @@ use rustc_span::sym;
 use super::FILTER_MAP;
 
 /// lint use of `filter_map().map()` for `Iterators`
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    _filter_args: &'tcx [hir::Expr<'_>],
-    _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
     // lint if caller of `.filter_map().map()` is an Iterator
     if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter_map(..).map(..)` on an `Iterator`";
diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 2095a60e44b..15cf5674313 100644
--- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -1,20 +1,22 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, paths, sugg};
+use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::ExprKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::Ty;
 use rustc_span::sym;
 
 use super::FROM_ITER_INSTEAD_OF_COLLECT;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
-    let ty = cx.typeck_results().expr_ty(expr);
-    let arg_ty = cx.typeck_results().expr_ty(&args[0]);
-
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
     if_chain! {
+        if let hir::ExprKind::Path(path) = func_kind;
+        if match_qpath(path, &["from_iter"]);
+        let ty = cx.typeck_results().expr_ty(expr);
+        let arg_ty = cx.typeck_results().expr_ty(&args[0]);
         if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
         if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 
diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs
index b122a0a0b89..b3a9743c614 100644
--- a/clippy_lints/src/methods/get_unwrap.rs
+++ b/clippy_lints/src/methods/get_unwrap.rs
@@ -1,4 +1,4 @@
-use crate::methods::derefs_to_slice;
+use super::utils::derefs_to_slice;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_type_diagnostic_item, match_type};
diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs
index d10a540c24e..950ec62c9fe 100644
--- a/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/clippy_lints/src/methods/inefficient_to_string.rs
@@ -1,4 +1,3 @@
-use super::INEFFICIENT_TO_STRING;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
@@ -8,14 +7,18 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::sym;
+use rustc_span::symbol::{sym, Symbol};
+
+use super::INEFFICIENT_TO_STRING;
 
 /// Checks for the `INEFFICIENT_TO_STRING` lint
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
+pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
     if_chain! {
+        if args.len() == 1 && method_name == sym!(to_string);
         if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
         if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
         if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
+        let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
         let self_ty = substs.type_at(0);
         let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
         if deref_count >= 1;
@@ -32,7 +35,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr
                         self_ty, deref_self_ty
                     ));
                     let mut applicability = Applicability::MachineApplicable;
-                    let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
+                    let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
                     diag.span_suggestion(
                         expr.span,
                         "try dereferencing the receiver",
diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs
index cfe749cf361..da13b4ba37a 100644
--- a/clippy_lints/src/methods/into_iter_on_ref.rs
+++ b/clippy_lints/src/methods/into_iter_on_ref.rs
@@ -1,32 +1,43 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
 use clippy_utils::ty::has_iter_method;
-use clippy_utils::{match_trait_method, paths};
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Symbol};
 
 use super::INTO_ITER_ON_REF;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) {
-    if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) {
-        return;
-    }
-    if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) {
-        span_lint_and_sugg(
-            cx,
-            INTO_ITER_ON_REF,
-            method_span,
-            &format!(
-                "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
-                method_name, kind,
-            ),
-            "call directly",
-            method_name.to_string(),
-            Applicability::MachineApplicable,
-        );
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    method_span: Span,
+    method_name: Symbol,
+    args: &[hir::Expr<'_>],
+) {
+    let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
+    if_chain! {
+        if let ty::Ref(..) = self_ty.kind();
+        if method_name == sym::into_iter;
+        if is_trait_method(cx, expr, sym::IntoIterator);
+        if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
+        then {
+            span_lint_and_sugg(
+                cx,
+                INTO_ITER_ON_REF,
+                method_span,
+                &format!(
+                    "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
+                    method_name, kind,
+                ),
+                "call directly",
+                method_name.to_string(),
+                Applicability::MachineApplicable,
+            );
+        }
     }
 }
 
diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs
index cd575c9cb02..848f47e39f6 100644
--- a/clippy_lints/src/methods/iter_cloned_collect.rs
+++ b/clippy_lints/src/methods/iter_cloned_collect.rs
@@ -1,4 +1,4 @@
-use crate::methods::derefs_to_slice;
+use crate::methods::utils::derefs_to_slice;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs
index fd3d53816a1..e394a8fe819 100644
--- a/clippy_lints/src/methods/iter_count.rs
+++ b/clippy_lints/src/methods/iter_count.rs
@@ -1,4 +1,4 @@
-use crate::methods::derefs_to_slice;
+use super::utils::derefs_to_slice;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::paths;
 use clippy_utils::source::snippet_with_applicability;
diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs
index feebf8b8209..e9b37b6f2bd 100644
--- a/clippy_lints/src/methods/iter_next_slice.rs
+++ b/clippy_lints/src/methods/iter_next_slice.rs
@@ -1,4 +1,4 @@
-use crate::methods::derefs_to_slice;
+use super::utils::derefs_to_slice;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -47,12 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
                 );
             }
         }
-    } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type)
-        || matches!(
-            &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
-            ty::Array(_, _)
-        )
-    {
+    } else if is_vec_or_array(cx, caller_expr) {
         // caller is a Vec or an Array
         let mut applicability = Applicability::MachineApplicable;
         span_lint_and_sugg(
@@ -69,3 +64,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
         );
     }
 }
+
+fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
+    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type)
+        || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
+}
diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs
index 2619793e1e2..c46af427b3c 100644
--- a/clippy_lints/src/methods/iter_nth.rs
+++ b/clippy_lints/src/methods/iter_nth.rs
@@ -1,4 +1,4 @@
-use crate::methods::derefs_to_slice;
+use super::utils::derefs_to_slice;
 use crate::methods::iter_nth_zero;
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::is_type_diagnostic_item;
diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs
index ea01860b456..b1d398876d3 100644
--- a/clippy_lints/src/methods/iter_skip_next.rs
+++ b/clippy_lints/src/methods/iter_skip_next.rs
@@ -12,14 +12,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir
     // lint if caller of skip is an Iterator
     if is_trait_method(cx, expr, sym::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,
+                format!(".nth({})", snippet(cx, n.span, "..")),
                 Applicability::MachineApplicable,
             );
         }
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 5be28354769..b37d744269e 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -1,5 +1,11 @@
 mod bind_instead_of_map;
 mod bytes_nth;
+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 clone_on_copy;
 mod clone_on_ref_ptr;
 mod expect_fun_call;
@@ -36,6 +42,7 @@ mod option_map_or_none;
 mod option_map_unwrap_or;
 mod or_fun_call;
 mod search_is_some;
+mod single_char_add_str;
 mod single_char_insert_string;
 mod single_char_pattern;
 mod single_char_push_string;
@@ -48,23 +55,21 @@ mod unnecessary_fold;
 mod unnecessary_lazy_eval;
 mod unwrap_used;
 mod useless_asref;
+mod utils;
 mod wrong_self_convention;
 mod zst_offset;
 
 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::diagnostics::{span_lint, span_lint_and_help};
 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,
+    contains_return, get_trait_def_id, in_macro, iter_input_pats, match_qpath, method_calls, paths, return_ty,
+    SpanlessEq,
 };
 use if_chain::if_chain;
-use rustc_ast::ast;
-use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::{TraitItem, TraitItemKind};
-use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 use rustc_semver::RustcVersion;
@@ -1718,11 +1723,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]),
             ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]),
             ["map", "filter"] => filter_map::check(cx, expr, false),
-            ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+            ["map", "filter_map"] => filter_map_map::check(cx, expr),
             ["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"] => 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", "filter"] => filter_flat_map::check(cx, expr),
+            ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr),
             ["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 => {
@@ -1809,46 +1814,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
 
         match expr.kind {
             hir::ExprKind::Call(ref func, ref args) => {
-                if let hir::ExprKind::Path(path) = &func.kind {
-                    if match_qpath(path, &["from_iter"]) {
-                        from_iter_instead_of_collect::check(cx, expr, args);
-                    }
-                }
+                from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
             },
             hir::ExprKind::MethodCall(ref method_call, ref method_span, ref 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 {
-                    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) {
-                    inefficient_to_string::check(cx, expr, &args[0], self_ty);
-                }
-
-                if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
-                    if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
-                        single_char_push_string::check(cx, expr, args);
-                    } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
-                        single_char_insert_string::check(cx, expr, args);
-                    }
-                }
-
-                match self_ty.kind() {
-                    ty::Ref(_, ty, _) if *ty.kind() == ty::Str => {
-                        for &(method, pos) in &PATTERN_METHODS {
-                            if method_call.ident.name.as_str() == method && args.len() > pos {
-                                single_char_pattern::check(cx, expr, &args[pos]);
-                            }
-                        }
-                    },
-                    ty::Ref(..) if method_call.ident.name == sym::into_iter => {
-                        into_iter_on_ref::check(cx, expr, self_ty, *method_span);
-                    },
-                    _ => (),
-                }
+                clone_on_copy::check(cx, expr, method_call.ident.name, args);
+                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
+                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
+                single_char_add_str::check(cx, expr, args);
+                into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
+                single_char_pattern::check(cx, expr, method_call.ident.name, args);
             },
             hir::ExprKind::Binary(op, ref lhs, ref rhs)
                 if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne =>
@@ -2015,47 +1991,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
 
     extract_msrv_attr!(LateContext);
 }
-
-fn derefs_to_slice<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'tcx>,
-    ty: Ty<'tcx>,
-) -> Option<&'tcx hir::Expr<'tcx>> {
-    fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
-        match ty.kind() {
-            ty::Slice(_) => true,
-            ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
-            ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
-            ty::Array(_, size) => size
-                .try_eval_usize(cx.tcx, cx.param_env)
-                .map_or(false, |size| size < 32),
-            ty::Ref(_, inner, _) => may_slice(cx, inner),
-            _ => false,
-        }
-    }
-
-    if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
-        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
-            Some(&args[0])
-        } else {
-            None
-        }
-    } else {
-        match ty.kind() {
-            ty::Slice(_) => Some(expr),
-            ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
-            ty::Ref(_, inner, _) => {
-                if may_slice(cx, inner) {
-                    Some(expr)
-                } else {
-                    None
-                }
-            },
-            _ => None,
-        }
-    }
-}
-
 /// Used for `lint_binary_expr_with_method_call`.
 #[derive(Copy, Clone)]
 struct BinaryExprInfo<'a> {
@@ -2068,7 +2003,7 @@ struct BinaryExprInfo<'a> {
 /// 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:ident, $cx:expr, $info:ident) => {
+        ($func:expr, $cx:expr, $info:ident) => {
             if !$func($cx, $info) {
                 ::std::mem::swap(&mut $info.chain, &mut $info.other);
                 if $func($cx, $info) {
@@ -2078,145 +2013,10 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr
         };
     }
 
-    lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info);
-    lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info);
-    lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info);
-    lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info);
-}
-
-/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
-fn lint_chars_cmp(
-    cx: &LateContext<'_>,
-    info: &BinaryExprInfo<'_>,
-    chain_methods: &[&str],
-    lint: &'static Lint,
-    suggest: &str,
-) -> bool {
-    if_chain! {
-        if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
-        if arg_char.len() == 1;
-        if let hir::ExprKind::Path(ref qpath) = fun.kind;
-        if let Some(segment) = single_segment_path(qpath);
-        if segment.ident.name == sym::Some;
-        then {
-            let mut applicability = Applicability::MachineApplicable;
-            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
-
-            if *self_ty.kind() != ty::Str {
-                return false;
-            }
-
-            span_lint_and_sugg(
-                cx,
-                lint,
-                info.expr.span,
-                &format!("you should use the `{}` method", suggest),
-                "like this",
-                format!("{}{}.{}({})",
-                        if info.eq { "" } else { "!" },
-                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
-                        suggest,
-                        snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
-                applicability,
-            );
-
-            return true;
-        }
-    }
-
-    false
-}
-
-/// Checks for the `CHARS_NEXT_CMP` lint.
-fn lint_chars_next_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
-}
-
-/// Checks for the `CHARS_LAST_CMP` lint.
-fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
-        true
-    } else {
-        lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
-    }
-}
-
-/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
-fn lint_chars_cmp_with_unwrap<'tcx>(
-    cx: &LateContext<'tcx>,
-    info: &BinaryExprInfo<'_>,
-    chain_methods: &[&str],
-    lint: &'static Lint,
-    suggest: &str,
-) -> bool {
-    if_chain! {
-        if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Lit(ref lit) = info.other.kind;
-        if let ast::LitKind::Char(c) = lit.node;
-        then {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                lint,
-                info.expr.span,
-                &format!("you should use the `{}` method", suggest),
-                "like this",
-                format!("{}{}.{}('{}')",
-                        if info.eq { "" } else { "!" },
-                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
-                        suggest,
-                        c),
-                applicability,
-            );
-
-            true
-        } else {
-            false
-        }
-    }
-}
-
-/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
-fn lint_chars_next_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
-}
-
-/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
-fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
-        true
-    } else {
-        lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
-    }
-}
-
-fn get_hint_if_single_char_arg(
-    cx: &LateContext<'_>,
-    arg: &hir::Expr<'_>,
-    applicability: &mut Applicability,
-) -> Option<String> {
-    if_chain! {
-        if let hir::ExprKind::Lit(lit) = &arg.kind;
-        if let ast::LitKind::Str(r, style) = lit.node;
-        let string = r.as_str();
-        if string.chars().count() == 1;
-        then {
-            let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
-            let ch = if let ast::StrStyle::Raw(nhash) = style {
-                let nhash = nhash as usize;
-                // for raw string: r##"a"##
-                &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
-            } else {
-                // for regular string: "a"
-                &snip[1..(snip.len() - 1)]
-            };
-            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
-            Some(hint)
-        } else {
-            None
-        }
-    }
+    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 {
diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs
new file mode 100644
index 00000000000..9a5fabcf7cd
--- /dev/null
+++ b/clippy_lints/src/methods/single_char_add_str.rs
@@ -0,0 +1,14 @@
+use crate::methods::{single_char_insert_string, single_char_push_string};
+use clippy_utils::{match_def_path, paths};
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+    if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+        if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
+            single_char_push_string::check(cx, expr, args);
+        } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
+            single_char_insert_string::check(cx, expr, args);
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs
index cd87d2c56b7..6cdc954c03b 100644
--- a/clippy_lints/src/methods/single_char_insert_string.rs
+++ b/clippy_lints/src/methods/single_char_insert_string.rs
@@ -1,4 +1,4 @@
-use crate::methods::get_hint_if_single_char_arg;
+use super::utils::get_hint_if_single_char_arg;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs
index 12d6418d700..f4090c7c617 100644
--- a/clippy_lints/src/methods/single_char_pattern.rs
+++ b/clippy_lints/src/methods/single_char_pattern.rs
@@ -1,23 +1,35 @@
-use crate::methods::get_hint_if_single_char_arg;
+use super::utils::get_hint_if_single_char_arg;
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::symbol::Symbol;
 
 use super::SINGLE_CHAR_PATTERN;
 
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
-pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
-    let mut applicability = Applicability::MachineApplicable;
-    if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) {
-        span_lint_and_sugg(
-            cx,
-            SINGLE_CHAR_PATTERN,
-            arg.span,
-            "single-character string constant used as pattern",
-            "try using a `char` instead",
-            hint,
-            applicability,
-        );
+pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+    for &(method, pos) in &crate::methods::PATTERN_METHODS {
+        if_chain! {
+            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
+            if *ty.kind() == ty::Str;
+            if method_name.as_str() == method && args.len() > pos;
+            let arg = &args[pos];
+            let mut applicability = Applicability::MachineApplicable;
+            if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    SINGLE_CHAR_PATTERN,
+                    arg.span,
+                    "single-character string constant used as pattern",
+                    "try using a `char` instead",
+                    hint,
+                    applicability,
+                );
+            }
+        }
     }
 }
diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs
index 882703a987d..0237d39cbdb 100644
--- a/clippy_lints/src/methods/single_char_push_string.rs
+++ b/clippy_lints/src/methods/single_char_push_string.rs
@@ -1,4 +1,4 @@
-use crate::methods::get_hint_if_single_char_arg;
+use super::utils::get_hint_if_single_char_arg;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs
index 52b26a36fe3..5c688ac5621 100644
--- a/clippy_lints/src/methods/string_extend_chars.rs
+++ b/clippy_lints/src/methods/string_extend_chars.rs
@@ -12,34 +12,35 @@ use super::STRING_EXTEND_CHARS;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
-    if is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
-        let arg = &args[1];
-        if let Some(arglists) = method_chain_args(arg, &["chars"]) {
-            let target = &arglists[0][0];
-            let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
-            let ref_str = if *self_ty.kind() == ty::Str {
-                ""
-            } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
-                "&"
-            } else {
-                return;
-            };
+    if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
+        return;
+    }
+    let arg = &args[1];
+    if let Some(arglists) = method_chain_args(arg, &["chars"]) {
+        let target = &arglists[0][0];
+        let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
+        let ref_str = if *self_ty.kind() == ty::Str {
+            ""
+        } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
+            "&"
+        } else {
+            return;
+        };
 
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                STRING_EXTEND_CHARS,
-                expr.span,
-                "calling `.extend(_.chars())`",
-                "try this",
-                format!(
-                    "{}.push_str({}{})",
-                    snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
-                    ref_str,
-                    snippet_with_applicability(cx, target.span, "..", &mut applicability)
-                ),
-                applicability,
-            );
-        }
+        let mut applicability = Applicability::MachineApplicable;
+        span_lint_and_sugg(
+            cx,
+            STRING_EXTEND_CHARS,
+            expr.span,
+            "calling `.extend(_.chars())`",
+            "try this",
+            format!(
+                "{}.push_str({}{})",
+                snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
+                ref_str,
+                snippet_with_applicability(cx, target.span, "..", &mut applicability)
+            ),
+            applicability,
+        );
     }
 }
diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs
new file mode 100644
index 00000000000..8de23e1bc6e
--- /dev/null
+++ b/clippy_lints/src/methods/utils.rs
@@ -0,0 +1,78 @@
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_middle::ty::Ty;
+use rustc_span::symbol::sym;
+
+pub(super) fn derefs_to_slice<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<&'tcx hir::Expr<'tcx>> {
+    fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
+        match ty.kind() {
+            ty::Slice(_) => true,
+            ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
+            ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
+            ty::Array(_, size) => size
+                .try_eval_usize(cx.tcx, cx.param_env)
+                .map_or(false, |size| size < 32),
+            ty::Ref(_, inner, _) => may_slice(cx, inner),
+            _ => false,
+        }
+    }
+
+    if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
+        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
+            Some(&args[0])
+        } else {
+            None
+        }
+    } else {
+        match ty.kind() {
+            ty::Slice(_) => Some(expr),
+            ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
+            ty::Ref(_, inner, _) => {
+                if may_slice(cx, inner) {
+                    Some(expr)
+                } else {
+                    None
+                }
+            },
+            _ => None,
+        }
+    }
+}
+
+pub(super) fn get_hint_if_single_char_arg(
+    cx: &LateContext<'_>,
+    arg: &hir::Expr<'_>,
+    applicability: &mut Applicability,
+) -> Option<String> {
+    if_chain! {
+        if let hir::ExprKind::Lit(lit) = &arg.kind;
+        if let ast::LitKind::Str(r, style) = lit.node;
+        let string = r.as_str();
+        if string.chars().count() == 1;
+        then {
+            let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
+            let ch = if let ast::StrStyle::Raw(nhash) = style {
+                let nhash = nhash as usize;
+                // for raw string: r##"a"##
+                &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
+            } else {
+                // for regular string: "a"
+                &snip[1..(snip.len() - 1)]
+            };
+            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+            Some(hint)
+        } else {
+            None
+        }
+    }
+}