]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
Rollup merge of #105123 - BlackHoleFox:fixing-the-macos-deployment, r=oli-obk
[rust.git] / src / tools / clippy / clippy_lints / src / unnecessary_owned_empty_strings.rs
1 use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item};
2 use clippy_utils::{match_def_path, paths};
3 use if_chain::if_chain;
4 use rustc_ast::ast::LitKind;
5 use rustc_errors::Applicability;
6 use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
7 use rustc_lint::{LateContext, LateLintPass};
8 use rustc_middle::ty;
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10
11 declare_clippy_lint! {
12     /// ### What it does
13     ///
14     /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
15     ///
16     /// ### Why is this bad?
17     ///
18     /// This results in longer and less readable code
19     ///
20     /// ### Example
21     /// ```rust
22     /// vec!["1", "2", "3"].join(&String::new());
23     /// ```
24     /// Use instead:
25     /// ```rust
26     /// vec!["1", "2", "3"].join("");
27     /// ```
28     #[clippy::version = "1.62.0"]
29     pub UNNECESSARY_OWNED_EMPTY_STRINGS,
30     style,
31     "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
32 }
33 declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]);
34
35 impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
36     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
37         if_chain! {
38             if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
39             if let ExprKind::Call(fun, args) = inner_expr.kind;
40             if let ExprKind::Path(ref qpath) = fun.kind;
41             if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
42             if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
43             if inner_str.is_str();
44             then {
45                 if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
46                      span_lint_and_sugg(
47                             cx,
48                             UNNECESSARY_OWNED_EMPTY_STRINGS,
49                             expr.span,
50                             "usage of `&String::new()` for a function expecting a `&str` argument",
51                             "try",
52                             "\"\"".to_owned(),
53                             Applicability::MachineApplicable,
54                         );
55                 } else {
56                     if_chain! {
57                         if Some(fun_def_id) == cx.tcx.lang_items().from_fn();
58                         if let [.., last_arg] = args;
59                         if let ExprKind::Lit(spanned) = &last_arg.kind;
60                         if let LitKind::Str(symbol, _) = spanned.node;
61                         if symbol.is_empty();
62                         let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
63                         if is_type_lang_item(cx, inner_expr_type, LangItem::String);
64                         then {
65                             span_lint_and_sugg(
66                                 cx,
67                                 UNNECESSARY_OWNED_EMPTY_STRINGS,
68                                 expr.span,
69                                 "usage of `&String::from(\"\")` for a function expecting a `&str` argument",
70                                 "try",
71                                 "\"\"".to_owned(),
72                                 Applicability::MachineApplicable,
73                             );
74                         }
75                     }
76                 }
77             }
78         }
79     }
80 }