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 use rustc_span::symbol::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 { check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)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 cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id); 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_lang_item(cx, inner_expr_type, LangItem::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 } 82