]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
Auto merge of #99422 - Dylan-DPC:rollup-htjofm6, r=Dylan-DPC
[rust.git] / src / tools / clippy / clippy_lints / src / methods / manual_str_repeat.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
3 use clippy_utils::sugg::Sugg;
4 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
5 use clippy_utils::{is_expr_path_def_path, paths};
6 use if_chain::if_chain;
7 use rustc_ast::LitKind;
8 use rustc_errors::Applicability;
9 use rustc_hir::{Expr, ExprKind, LangItem};
10 use rustc_lint::LateContext;
11 use rustc_middle::ty::{self, Ty};
12 use rustc_span::symbol::sym;
13 use std::borrow::Cow;
14
15 use super::MANUAL_STR_REPEAT;
16
17 enum RepeatKind {
18     String,
19     Char(char),
20 }
21
22 fn get_ty_param(ty: Ty<'_>) -> Option<Ty<'_>> {
23     if let ty::Adt(_, subs) = ty.kind() {
24         subs.types().next()
25     } else {
26         None
27     }
28 }
29
30 fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
31     if let ExprKind::Lit(lit) = &e.kind {
32         match lit.node {
33             LitKind::Str(..) => Some(RepeatKind::String),
34             LitKind::Char(c) => Some(RepeatKind::Char(c)),
35             _ => None,
36         }
37     } else {
38         let ty = cx.typeck_results().expr_ty(e);
39         if is_type_diagnostic_item(cx, ty, sym::String)
40             || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
41             || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str))
42         {
43             Some(RepeatKind::String)
44         } else {
45             let ty = ty.peel_refs();
46             (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String)
47         }
48     }
49 }
50
51 pub(super) fn check(
52     cx: &LateContext<'_>,
53     collect_expr: &Expr<'_>,
54     take_expr: &Expr<'_>,
55     take_self_arg: &Expr<'_>,
56     take_arg: &Expr<'_>,
57 ) {
58     if_chain! {
59         if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
60         if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT);
61         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String);
62         if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
63         if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
64         if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
65         if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id);
66         if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id);
67         if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg);
68         let ctxt = collect_expr.span.ctxt();
69         if ctxt == take_expr.span.ctxt();
70         if ctxt == take_self_arg.span.ctxt();
71         then {
72             let mut app = Applicability::MachineApplicable;
73             let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0;
74
75             let val_str = match repeat_kind {
76                 RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return,
77                 RepeatKind::Char('\'') => r#""'""#.into(),
78                 RepeatKind::Char('"') => r#""\"""#.into(),
79                 RepeatKind::Char(_) =>
80                     match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) {
81                         Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])),
82                         s @ Cow::Borrowed(_) => s,
83                     },
84                 RepeatKind::String =>
85                     Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(),
86             };
87
88             span_lint_and_sugg(
89                 cx,
90                 MANUAL_STR_REPEAT,
91                 collect_expr.span,
92                 "manual implementation of `str::repeat` using iterators",
93                 "try this",
94                 format!("{}.repeat({})", val_str, count_snip),
95                 app
96             )
97         }
98     }
99 }