1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::macros
::{find_format_arg_expr, find_format_args, root_macro_call_first_node}
;
3 use clippy_utils
::source
::{snippet_opt, snippet_with_context}
;
4 use clippy_utils
::sugg
::Sugg
;
5 use rustc_ast
::{FormatArgsPiece, FormatOptions, FormatTrait}
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{Expr, ExprKind}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
10 use rustc_session
::declare_lint_pass
;
11 use rustc_span
::{sym, Span}
;
13 declare_clippy_lint
! {
15 /// Checks for the use of `format!("string literal with no
16 /// argument")` and `format!("{}", foo)` where `foo` is a string.
18 /// ### Why is this bad?
19 /// There is no point of doing that. `format!("foo")` can
20 /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
21 /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
22 /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
28 /// format!("{}", foo);
36 #[clippy::version = "pre 1.29.0"]
39 "useless use of `format!`"
42 declare_lint_pass
!(UselessFormat
=> [USELESS_FORMAT
]);
44 impl<'tcx
> LateLintPass
<'tcx
> for UselessFormat
{
45 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
46 if let Some(macro_call
) = root_macro_call_first_node(cx
, expr
)
47 && cx
.tcx
.is_diagnostic_item(sym
::format_macro
, macro_call
.def_id
)
48 && let Some(format_args
) = find_format_args(cx
, expr
, macro_call
.expn
)
50 let mut applicability
= Applicability
::MachineApplicable
;
51 let call_site
= macro_call
.span
;
53 match (format_args
.arguments
.all_args(), &format_args
.template
[..]) {
54 ([], []) => span_useless_format_empty(cx
, call_site
, "String::new()".to_owned(), applicability
),
56 // Simulate macro expansion, converting {{ and }} to { and }.
57 let Some(snippet
) = snippet_opt(cx
, format_args
.span
) else {
60 let s_expand
= snippet
.replace("{{", "{").replace("}}", "}");
61 let sugg
= format
!("{s_expand}.to_string()");
62 span_useless_format(cx
, call_site
, sugg
, applicability
);
65 if let Ok(value
) = find_format_arg_expr(expr
, arg
)
66 && let FormatArgsPiece
::Placeholder(placeholder
) = piece
67 && placeholder
.format_trait
== FormatTrait
::Display
68 && placeholder
.format_options
== FormatOptions
::default()
69 && match cx
.typeck_results().expr_ty(value
).peel_refs().kind() {
70 ty
::Adt(adt
, _
) => Some(adt
.did()) == cx
.tcx
.lang_items().string(),
75 let is_new_string
= match value
.kind
{
76 ExprKind
::Binary(..) => true,
77 ExprKind
::MethodCall(path
, ..) => path
.ident
.name
== sym
::to_string
,
80 let sugg
= if is_new_string
{
81 snippet_with_context(cx
, value
.span
, call_site
.ctxt(), "..", &mut applicability
)
85 let sugg
= Sugg
::hir_with_context(cx
, value
, call_site
.ctxt(), "<arg>", &mut applicability
);
86 format
!("{}.to_string()", sugg
.maybe_par())
88 span_useless_format(cx
, call_site
, sugg
, applicability
);
97 fn span_useless_format_empty(cx
: &LateContext
<'_
>, span
: Span
, sugg
: String
, applicability
: Applicability
) {
102 "useless use of `format!`",
103 "consider using `String::new()`",
109 fn span_useless_format(cx
: &LateContext
<'_
>, span
: Span
, sugg
: String
, applicability
: Applicability
) {
114 "useless use of `format!`",
115 "consider using `.to_string()`",