]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/format.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / format.rs
CommitLineData
136023e0 1use clippy_utils::diagnostics::span_lint_and_sugg;
5099ac24 2use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
136023e0 3use clippy_utils::source::{snippet_opt, snippet_with_applicability};
cdc7bbd5 4use clippy_utils::sugg::Sugg;
f20569fa 5use if_chain::if_chain;
f20569fa 6use rustc_errors::Applicability;
3c0e092e 7use rustc_hir::{Expr, ExprKind};
136023e0
XL
8use rustc_lint::{LateContext, LateLintPass};
9use rustc_middle::ty;
f20569fa 10use rustc_session::{declare_lint_pass, declare_tool_lint};
136023e0 11use rustc_span::symbol::kw;
3c0e092e 12use rustc_span::{sym, BytePos, Span};
f20569fa
XL
13
14declare_clippy_lint! {
94222f64
XL
15 /// ### What it does
16 /// Checks for the use of `format!("string literal with no
f20569fa
XL
17 /// argument")` and `format!("{}", foo)` where `foo` is a string.
18 ///
94222f64
XL
19 /// ### Why is this bad?
20 /// There is no point of doing that. `format!("foo")` can
f20569fa
XL
21 /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
22 /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
23 /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
24 /// if `foo: &str`.
25 ///
94222f64 26 /// ### Examples
f20569fa
XL
27 /// ```rust
28 ///
29 /// // Bad
30 /// let foo = "foo";
31 /// format!("{}", foo);
32 ///
33 /// // Good
34 /// foo.to_owned();
35 /// ```
a2a8927a 36 #[clippy::version = "pre 1.29.0"]
f20569fa
XL
37 pub USELESS_FORMAT,
38 complexity,
39 "useless use of `format!`"
40}
41
42declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
43
44impl<'tcx> LateLintPass<'tcx> for UselessFormat {
45 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
5099ac24
FG
46 let (format_args, call_site) = if_chain! {
47 if let Some(macro_call) = root_macro_call_first_node(cx, expr);
48 if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
49 if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
50 then {
51 (format_args, macro_call.span)
52 } else {
53 return
54 }
f20569fa
XL
55 };
56
136023e0
XL
57 let mut applicability = Applicability::MachineApplicable;
58 if format_args.value_args.is_empty() {
5099ac24
FG
59 match *format_args.format_string_parts {
60 [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
61 [_] => {
62 if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
3c0e092e
XL
63 // Simulate macro expansion, converting {{ and }} to { and }.
64 let s_expand = s_src.replace("{{", "{").replace("}}", "}");
65 let sugg = format!("{}.to_string()", s_expand);
66 span_useless_format(cx, call_site, sugg, applicability);
67 }
5099ac24
FG
68 },
69 [..] => {},
136023e0
XL
70 }
71 } else if let [value] = *format_args.value_args {
72 if_chain! {
5099ac24 73 if format_args.format_string_parts == [kw::Empty];
136023e0 74 if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ee023bcb 75 ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
136023e0
XL
76 ty::Str => true,
77 _ => false,
78 };
3c0e092e 79 if let Some(args) = format_args.args();
5099ac24 80 if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
136023e0
XL
81 then {
82 let is_new_string = match value.kind {
83 ExprKind::Binary(..) => true,
84 ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
85 _ => false,
86 };
3c0e092e
XL
87 let sugg = if format_args.format_string_span.contains(value.span) {
88 // Implicit argument. e.g. `format!("{x}")` span points to `{x}`
89 let spdata = value.span.data();
90 let span = Span::new(
91 spdata.lo + BytePos(1),
92 spdata.hi - BytePos(1),
93 spdata.ctxt,
94 spdata.parent
95 );
96 let snip = snippet_with_applicability(cx, span, "..", &mut applicability);
97 if is_new_string {
98 snip.into()
99 } else {
a2a8927a 100 format!("{snip}.to_string()")
3c0e092e
XL
101 }
102 } else if is_new_string {
136023e0
XL
103 snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
104 } else {
105 let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
106 format!("{}.to_string()", sugg.maybe_par())
107 };
108 span_useless_format(cx, call_site, sugg, applicability);
f20569fa 109 }
f20569fa 110 }
136023e0 111 };
f20569fa 112 }
f20569fa
XL
113}
114
3c0e092e 115fn span_useless_format_empty(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
136023e0
XL
116 span_lint_and_sugg(
117 cx,
118 USELESS_FORMAT,
119 span,
120 "useless use of `format!`",
3c0e092e 121 "consider using `String::new()`",
136023e0
XL
122 sugg,
123 applicability,
124 );
f20569fa
XL
125}
126
3c0e092e
XL
127fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
128 span_lint_and_sugg(
129 cx,
130 USELESS_FORMAT,
131 span,
132 "useless use of `format!`",
133 "consider using `.to_string()`",
134 sugg,
135 applicability,
136 );
f20569fa 137}