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