|
1 |
| -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; |
| 1 | +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; |
2 | 2 | use clippy_utils::source::{snippet, snippet_with_applicability};
|
3 | 3 | use clippy_utils::ty::is_type_lang_item;
|
4 | 4 | use clippy_utils::{
|
@@ -438,27 +438,85 @@ declare_clippy_lint! {
|
438 | 438 |
|
439 | 439 | declare_lint_pass!(StringToString => [STRING_TO_STRING]);
|
440 | 440 |
|
| 441 | +fn is_parent_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<rustc_span::Span> { |
| 442 | + if let Some(parent_expr) = get_parent_expr(cx, expr) |
| 443 | + && let ExprKind::MethodCall(name, ..) = parent_expr.kind |
| 444 | + && name.ident.name == sym::map |
| 445 | + && let Some(caller_def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) |
| 446 | + && (clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Result) |
| 447 | + || clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Option) |
| 448 | + || clippy_utils::is_diag_trait_item(cx, caller_def_id, sym::Iterator)) |
| 449 | + { |
| 450 | + Some(parent_expr.span) |
| 451 | + } else { |
| 452 | + None |
| 453 | + } |
| 454 | +} |
| 455 | + |
| 456 | +fn is_called_from_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<rustc_span::Span> { |
| 457 | + let parent = get_parent_expr(cx, expr)?; |
| 458 | + |
| 459 | + if matches!(parent.kind, ExprKind::Closure(_)) { |
| 460 | + is_parent_map_like(cx, parent) |
| 461 | + } else { |
| 462 | + None |
| 463 | + } |
| 464 | +} |
| 465 | + |
| 466 | +fn suggest_cloned_string_to_string(cx: &LateContext<'_>, span: rustc_span::Span) { |
| 467 | + span_lint_and_help( |
| 468 | + cx, |
| 469 | + STRING_TO_STRING, |
| 470 | + span, |
| 471 | + "`to_string()` called on a `String`", |
| 472 | + None, |
| 473 | + "consider using `.cloned()`", |
| 474 | + ); |
| 475 | +} |
| 476 | + |
441 | 477 | impl<'tcx> LateLintPass<'tcx> for StringToString {
|
442 | 478 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
443 | 479 | if expr.span.from_expansion() {
|
444 | 480 | return;
|
445 | 481 | }
|
446 | 482 |
|
447 |
| - if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind |
448 |
| - && path.ident.name == sym::to_string |
449 |
| - && let ty = cx.typeck_results().expr_ty(self_arg) |
450 |
| - && is_type_lang_item(cx, ty, LangItem::String) |
451 |
| - { |
452 |
| - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] |
453 |
| - span_lint_and_then( |
454 |
| - cx, |
455 |
| - STRING_TO_STRING, |
456 |
| - expr.span, |
457 |
| - "`to_string()` called on a `String`", |
458 |
| - |diag| { |
459 |
| - diag.help("consider using `.clone()`"); |
460 |
| - }, |
461 |
| - ); |
| 483 | + match &expr.kind { |
| 484 | + ExprKind::MethodCall(path, self_arg, [], _) => { |
| 485 | + if path.ident.name == sym::to_string |
| 486 | + && let ty = cx.typeck_results().expr_ty(self_arg) |
| 487 | + && is_type_lang_item(cx, ty.peel_refs(), LangItem::String) |
| 488 | + { |
| 489 | + if let Some(parent_span) = is_called_from_map_like(cx, expr) { |
| 490 | + suggest_cloned_string_to_string(cx, parent_span); |
| 491 | + } else { |
| 492 | + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] |
| 493 | + span_lint_and_then( |
| 494 | + cx, |
| 495 | + STRING_TO_STRING, |
| 496 | + expr.span, |
| 497 | + "`to_string()` called on a `String`", |
| 498 | + |diag| { |
| 499 | + diag.help("consider using `.clone()`"); |
| 500 | + }, |
| 501 | + ); |
| 502 | + } |
| 503 | + } |
| 504 | + }, |
| 505 | + ExprKind::Path(QPath::TypeRelative(ty, segment)) => { |
| 506 | + if segment.ident.name == sym::to_string |
| 507 | + && let rustc_hir::TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind |
| 508 | + && let rustc_hir::def::Res::Def(_, def_id) = path.res |
| 509 | + && cx |
| 510 | + .tcx |
| 511 | + .lang_items() |
| 512 | + .get(LangItem::String) |
| 513 | + .is_some_and(|lang_id| lang_id == def_id) |
| 514 | + && let Some(parent_span) = is_parent_map_like(cx, expr) |
| 515 | + { |
| 516 | + suggest_cloned_string_to_string(cx, parent_span); |
| 517 | + } |
| 518 | + }, |
| 519 | + _ => {}, |
462 | 520 | }
|
463 | 521 | }
|
464 | 522 | }
|
|
0 commit comments