|
1 |
| -use crate::methods::{single_char_insert_string, single_char_push_string}; |
2 |
| -use rustc_hir as hir; |
| 1 | +use super::SINGLE_CHAR_ADD_STR; |
| 2 | +use clippy_utils::diagnostics::span_lint_and_sugg; |
| 3 | +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; |
| 4 | +use rustc_ast::BorrowKind; |
| 5 | +use rustc_errors::Applicability; |
| 6 | +use rustc_hir::{Expr, ExprKind}; |
3 | 7 | use rustc_lint::LateContext;
|
| 8 | +use rustc_middle::ty; |
4 | 9 | use rustc_span::sym;
|
5 | 10 |
|
6 |
| -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { |
| 11 | +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { |
7 | 12 | if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
8 |
| - match cx.tcx.get_diagnostic_name(fn_def_id) { |
9 |
| - Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), |
10 |
| - Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), |
11 |
| - _ => {}, |
| 13 | + let mut applicability = Applicability::MachineApplicable; |
| 14 | + let (short_name, arg, extra) = match cx.tcx.get_diagnostic_name(fn_def_id) { |
| 15 | + Some(sym::string_insert_str) => ( |
| 16 | + "insert", |
| 17 | + &args[1], |
| 18 | + Some(|applicability| { |
| 19 | + format!( |
| 20 | + "{}, ", |
| 21 | + snippet_with_applicability(cx, args[0].span, "..", applicability) |
| 22 | + ) |
| 23 | + }), |
| 24 | + ), |
| 25 | + Some(sym::string_push_str) => ("push", &args[0], None), |
| 26 | + _ => return, |
| 27 | + }; |
| 28 | + |
| 29 | + if let Some(extension_string) = str_literal_to_char_literal(cx, arg, &mut applicability, false) { |
| 30 | + let base_string_snippet = |
| 31 | + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); |
| 32 | + span_lint_and_sugg( |
| 33 | + cx, |
| 34 | + SINGLE_CHAR_ADD_STR, |
| 35 | + expr.span, |
| 36 | + format!("calling `{short_name}_str()` using a single-character string literal"), |
| 37 | + format!("consider using `{short_name}` with a character literal"), |
| 38 | + format!( |
| 39 | + "{base_string_snippet}.{short_name}({}{extension_string})", |
| 40 | + extra.map_or(String::new(), |f| f(&mut applicability)) |
| 41 | + ), |
| 42 | + applicability, |
| 43 | + ); |
| 44 | + } else if let ExprKind::AddrOf(BorrowKind::Ref, _, inner) = arg.kind |
| 45 | + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = inner.kind |
| 46 | + && path_segment.ident.name == sym::to_string |
| 47 | + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) |
| 48 | + { |
| 49 | + let base_string_snippet = |
| 50 | + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); |
| 51 | + let extension_string = match ( |
| 52 | + snippet_with_applicability(cx, method_arg.span.source_callsite(), "_", &mut applicability), |
| 53 | + is_ref_char(cx, method_arg), |
| 54 | + ) { |
| 55 | + (snippet, false) => snippet, |
| 56 | + (snippet, true) => format!("*{snippet}").into(), |
| 57 | + }; |
| 58 | + span_lint_and_sugg( |
| 59 | + cx, |
| 60 | + SINGLE_CHAR_ADD_STR, |
| 61 | + expr.span, |
| 62 | + format!("calling `{short_name}_str()` using a single-character converted to string"), |
| 63 | + format!("consider using `{short_name}` without `to_string()`"), |
| 64 | + format!( |
| 65 | + "{base_string_snippet}.{short_name}({}{extension_string})", |
| 66 | + extra.map_or(String::new(), |f| f(&mut applicability)) |
| 67 | + ), |
| 68 | + applicability, |
| 69 | + ); |
12 | 70 | }
|
13 | 71 | }
|
14 | 72 | }
|
| 73 | + |
| 74 | +fn is_ref_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
| 75 | + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::Ref(_, ty, _) if ty.is_char()) |
| 76 | +} |
| 77 | + |
| 78 | +fn is_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
| 79 | + cx.typeck_results().expr_ty(expr).is_char() |
| 80 | +} |
0 commit comments