Skip to content

Commit 68b4498

Browse files
committed
Auto merge of #8365 - Alexendoo:explicit-write-suggestion, r=camsteffen
Add `explicit_write` suggestions for `write!`s with format args changelog: Add [`explicit_write`] suggestions for `write!`s with format args Fixes #4542 ```rust writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); ``` Now suggests: ``` error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:36:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` ``` --------- r? `@camsteffen` (again, sorry 😛) for the `FormatArgsExpn` change Before this change `inputs_span` returned a span pointing to just `1` in ```rust macro_rules! one { () => { 1 }; } `writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();` ``` And the `source_callsite` of that span didn't include the format string, it was just `one!()`
2 parents 29cc0d8 + 144b4a5 commit 68b4498

15 files changed

+173
-83
lines changed

clippy_lints/src/explicit_write.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::macros::FormatArgsExpn;
3+
use clippy_utils::source::snippet_with_applicability;
34
use clippy_utils::{is_expn_of, match_function_call, paths};
45
use if_chain::if_chain;
56
use rustc_errors::Applicability;
@@ -79,28 +80,22 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
7980
"print".into(),
8081
)
8182
};
82-
let msg = format!("use of `{}.unwrap()`", used);
83-
if let [write_output] = *format_args.format_string_parts {
84-
let mut write_output = write_output.to_string();
85-
if write_output.ends_with('\n') {
86-
write_output.pop();
87-
}
88-
89-
let sugg = format!("{}{}!(\"{}\")", prefix, sugg_mac, write_output.escape_default());
90-
span_lint_and_sugg(
91-
cx,
92-
EXPLICIT_WRITE,
93-
expr.span,
94-
&msg,
95-
"try this",
96-
sugg,
97-
Applicability::MachineApplicable
98-
);
99-
} else {
100-
// We don't have a proper suggestion
101-
let help = format!("consider using `{}{}!` instead", prefix, sugg_mac);
102-
span_lint_and_help(cx, EXPLICIT_WRITE, expr.span, &msg, None, &help);
103-
}
83+
let mut applicability = Applicability::MachineApplicable;
84+
let inputs_snippet = snippet_with_applicability(
85+
cx,
86+
format_args.inputs_span(),
87+
"..",
88+
&mut applicability,
89+
);
90+
span_lint_and_sugg(
91+
cx,
92+
EXPLICIT_WRITE,
93+
expr.span,
94+
&format!("use of `{}.unwrap()`", used),
95+
"try this",
96+
format!("{}{}!({})", prefix, sugg_mac, inputs_snippet),
97+
applicability,
98+
)
10499
}
105100
}
106101
}

clippy_utils/src/macros.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_hir::intravisit::Visitor;
99
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
1010
use rustc_lint::LateContext;
1111
use rustc_span::def_id::DefId;
12-
use rustc_span::hygiene::{MacroKind, SyntaxContext};
12+
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
1313
use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
1414
use std::ops::ControlFlow;
1515

@@ -306,6 +306,7 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
306306
}
307307

308308
/// A parsed `format_args!` expansion
309+
#[derive(Debug)]
309310
pub struct FormatArgsExpn<'tcx> {
310311
/// Span of the first argument, the format string
311312
pub format_string_span: Span,
@@ -465,11 +466,13 @@ impl<'tcx> FormatArgsExpn<'tcx> {
465466
.collect()
466467
}
467468

468-
/// Span of all inputs
469+
/// Source callsite span of all inputs
469470
pub fn inputs_span(&self) -> Span {
470471
match *self.value_args {
471472
[] => self.format_string_span,
472-
[.., last] => self.format_string_span.to(last.span),
473+
[.., last] => self
474+
.format_string_span
475+
.to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
473476
}
474477
}
475478
}

tests/ui/expect_fun_call.fixed

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55

66
/// Checks implementation of the `EXPECT_FUN_CALL` lint
77

8+
macro_rules! one {
9+
() => {
10+
1
11+
};
12+
}
13+
814
fn main() {
915
struct Foo;
1016

@@ -31,6 +37,9 @@ fn main() {
3137
let with_none_and_as_str: Option<i32> = None;
3238
with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
3339

40+
let with_none_and_format_with_macro: Option<i32> = None;
41+
with_none_and_format_with_macro.unwrap_or_else(|| panic!("Error {}: fake error", one!()));
42+
3443
let with_ok: Result<(), ()> = Ok(());
3544
with_ok.expect("error");
3645

tests/ui/expect_fun_call.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55

66
/// Checks implementation of the `EXPECT_FUN_CALL` lint
77
8+
macro_rules! one {
9+
() => {
10+
1
11+
};
12+
}
13+
814
fn main() {
915
struct Foo;
1016

@@ -31,6 +37,9 @@ fn main() {
3137
let with_none_and_as_str: Option<i32> = None;
3238
with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
3339

40+
let with_none_and_format_with_macro: Option<i32> = None;
41+
with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
42+
3443
let with_ok: Result<(), ()> = Ok(());
3544
with_ok.expect("error");
3645

tests/ui/expect_fun_call.stderr

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,82 @@
11
error: use of `expect` followed by a function call
2-
--> $DIR/expect_fun_call.rs:29:26
2+
--> $DIR/expect_fun_call.rs:35:26
33
|
44
LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code));
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
66
|
77
= note: `-D clippy::expect-fun-call` implied by `-D warnings`
88

99
error: use of `expect` followed by a function call
10-
--> $DIR/expect_fun_call.rs:32:26
10+
--> $DIR/expect_fun_call.rs:38:26
1111
|
1212
LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
1414

1515
error: use of `expect` followed by a function call
16-
--> $DIR/expect_fun_call.rs:42:25
16+
--> $DIR/expect_fun_call.rs:41:37
17+
|
18+
LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
20+
21+
error: use of `expect` followed by a function call
22+
--> $DIR/expect_fun_call.rs:51:25
1723
|
1824
LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code));
1925
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
2026

2127
error: use of `expect` followed by a function call
22-
--> $DIR/expect_fun_call.rs:45:25
28+
--> $DIR/expect_fun_call.rs:54:25
2329
|
2430
LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
2531
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
2632

2733
error: use of `expect` followed by a function call
28-
--> $DIR/expect_fun_call.rs:57:17
34+
--> $DIR/expect_fun_call.rs:66:17
2935
|
3036
LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref());
3137
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
3238

3339
error: use of `expect` followed by a function call
34-
--> $DIR/expect_fun_call.rs:78:21
40+
--> $DIR/expect_fun_call.rs:87:21
3541
|
3642
LL | Some("foo").expect(&get_string());
3743
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
3844

3945
error: use of `expect` followed by a function call
40-
--> $DIR/expect_fun_call.rs:79:21
46+
--> $DIR/expect_fun_call.rs:88:21
4147
|
4248
LL | Some("foo").expect(get_string().as_ref());
4349
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
4450

4551
error: use of `expect` followed by a function call
46-
--> $DIR/expect_fun_call.rs:80:21
52+
--> $DIR/expect_fun_call.rs:89:21
4753
|
4854
LL | Some("foo").expect(get_string().as_str());
4955
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
5056

5157
error: use of `expect` followed by a function call
52-
--> $DIR/expect_fun_call.rs:82:21
58+
--> $DIR/expect_fun_call.rs:91:21
5359
|
5460
LL | Some("foo").expect(get_static_str());
5561
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
5662

5763
error: use of `expect` followed by a function call
58-
--> $DIR/expect_fun_call.rs:83:21
64+
--> $DIR/expect_fun_call.rs:92:21
5965
|
6066
LL | Some("foo").expect(get_non_static_str(&0));
6167
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
6268

6369
error: use of `expect` followed by a function call
64-
--> $DIR/expect_fun_call.rs:87:16
70+
--> $DIR/expect_fun_call.rs:96:16
6571
|
6672
LL | Some(true).expect(&format!("key {}, {}", 1, 2));
6773
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
6874

6975
error: use of `expect` followed by a function call
70-
--> $DIR/expect_fun_call.rs:93:17
76+
--> $DIR/expect_fun_call.rs:102:17
7177
|
7278
LL | opt_ref.expect(&format!("{:?}", opt_ref));
7379
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
7480

75-
error: aborting due to 12 previous errors
81+
error: aborting due to 13 previous errors
7682

tests/ui/explicit_write.fixed

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ fn stderr() -> String {
1010
String::new()
1111
}
1212

13+
macro_rules! one {
14+
() => {
15+
1
16+
};
17+
}
18+
1319
fn main() {
1420
// these should warn
1521
{
@@ -24,6 +30,12 @@ fn main() {
2430
// including newlines
2531
println!("test\ntest");
2632
eprintln!("test\ntest");
33+
34+
let value = 1;
35+
eprintln!("with {}", value);
36+
eprintln!("with {} {}", 2, value);
37+
eprintln!("with {value}");
38+
eprintln!("macro arg {}", one!());
2739
}
2840
// these should not warn, different destination
2941
{

tests/ui/explicit_write.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ fn stderr() -> String {
1010
String::new()
1111
}
1212

13+
macro_rules! one {
14+
() => {
15+
1
16+
};
17+
}
18+
1319
fn main() {
1420
// these should warn
1521
{
@@ -24,6 +30,12 @@ fn main() {
2430
// including newlines
2531
writeln!(std::io::stdout(), "test\ntest").unwrap();
2632
writeln!(std::io::stderr(), "test\ntest").unwrap();
33+
34+
let value = 1;
35+
writeln!(std::io::stderr(), "with {}", value).unwrap();
36+
writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
37+
writeln!(std::io::stderr(), "with {value}").unwrap();
38+
writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
2739
}
2840
// these should not warn, different destination
2941
{

tests/ui/explicit_write.stderr

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,76 @@
11
error: use of `write!(stdout(), ...).unwrap()`
2-
--> $DIR/explicit_write.rs:17:9
2+
--> $DIR/explicit_write.rs:23:9
33
|
44
LL | write!(std::io::stdout(), "test").unwrap();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
66
|
77
= note: `-D clippy::explicit-write` implied by `-D warnings`
88

99
error: use of `write!(stderr(), ...).unwrap()`
10-
--> $DIR/explicit_write.rs:18:9
10+
--> $DIR/explicit_write.rs:24:9
1111
|
1212
LL | write!(std::io::stderr(), "test").unwrap();
1313
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
1414

1515
error: use of `writeln!(stdout(), ...).unwrap()`
16-
--> $DIR/explicit_write.rs:19:9
16+
--> $DIR/explicit_write.rs:25:9
1717
|
1818
LL | writeln!(std::io::stdout(), "test").unwrap();
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
2020

2121
error: use of `writeln!(stderr(), ...).unwrap()`
22-
--> $DIR/explicit_write.rs:20:9
22+
--> $DIR/explicit_write.rs:26:9
2323
|
2424
LL | writeln!(std::io::stderr(), "test").unwrap();
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
2626

2727
error: use of `stdout().write_fmt(...).unwrap()`
28-
--> $DIR/explicit_write.rs:21:9
28+
--> $DIR/explicit_write.rs:27:9
2929
|
3030
LL | std::io::stdout().write_fmt(format_args!("test")).unwrap();
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
3232

3333
error: use of `stderr().write_fmt(...).unwrap()`
34-
--> $DIR/explicit_write.rs:22:9
34+
--> $DIR/explicit_write.rs:28:9
3535
|
3636
LL | std::io::stderr().write_fmt(format_args!("test")).unwrap();
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
3838

3939
error: use of `writeln!(stdout(), ...).unwrap()`
40-
--> $DIR/explicit_write.rs:25:9
40+
--> $DIR/explicit_write.rs:31:9
4141
|
4242
LL | writeln!(std::io::stdout(), "test/ntest").unwrap();
4343
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
4444

4545
error: use of `writeln!(stderr(), ...).unwrap()`
46-
--> $DIR/explicit_write.rs:26:9
46+
--> $DIR/explicit_write.rs:32:9
4747
|
4848
LL | writeln!(std::io::stderr(), "test/ntest").unwrap();
4949
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
5050

51-
error: aborting due to 8 previous errors
51+
error: use of `writeln!(stderr(), ...).unwrap()`
52+
--> $DIR/explicit_write.rs:35:9
53+
|
54+
LL | writeln!(std::io::stderr(), "with {}", value).unwrap();
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)`
56+
57+
error: use of `writeln!(stderr(), ...).unwrap()`
58+
--> $DIR/explicit_write.rs:36:9
59+
|
60+
LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)`
62+
63+
error: use of `writeln!(stderr(), ...).unwrap()`
64+
--> $DIR/explicit_write.rs:37:9
65+
|
66+
LL | writeln!(std::io::stderr(), "with {value}").unwrap();
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")`
68+
69+
error: use of `writeln!(stderr(), ...).unwrap()`
70+
--> $DIR/explicit_write.rs:38:9
71+
|
72+
LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
73+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())`
74+
75+
error: aborting due to 12 previous errors
5276

tests/ui/explicit_write_non_rustfix.rs

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/ui/explicit_write_non_rustfix.stderr

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)