Skip to content

Commit 547b3ad

Browse files
authored
Rollup merge of #82113 - m-ou-se:panic-format-lint, r=estebank
Improve non_fmt_panic lint. This change: - fixes the span used by this lint in the case the panic argument is a single macro expansion (e.g. `panic!(a!())`); - adds a suggestion for `panic!(format!(..))` to remove `format!()` instead of adding `"{}", ` or using `panic_any` like it does now; and - fixes the incorrect suggestion to replace `panic![123]` by `panic_any(123]`. Fixes #82109. Fixes #82110. Fixes #82111. Example output: ``` warning: panic message is not a string literal --> src/main.rs:8:12 | 8 | panic!(format!("error: {}", "oh no")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(non_fmt_panic)]` on by default = note: this is no longer accepted in Rust 2021 = note: the panic!() macro supports formatting, so there's no need for the format!() macro here help: remove the `format!(..)` macro call | 8 | panic!("error: {}", "oh no"); | -- -- ``` r? `@estebank`
2 parents 18d1284 + ad93f48 commit 547b3ad

File tree

5 files changed

+142
-13
lines changed

5 files changed

+142
-13
lines changed

compiler/rustc_lint/src/non_fmt_panic.rs

+64-9
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,65 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
6969

7070
let (span, panic) = panic_call(cx, f);
7171

72-
cx.struct_span_lint(NON_FMT_PANIC, arg.span, |lint| {
72+
// Find the span of the argument to `panic!()`, before expansion in the
73+
// case of `panic!(some_macro!())`.
74+
// We don't use source_callsite(), because this `panic!(..)` might itself
75+
// be expanded from another macro, in which case we want to stop at that
76+
// expansion.
77+
let mut arg_span = arg.span;
78+
let mut arg_macro = None;
79+
while !span.contains(arg_span) {
80+
let expn = arg_span.ctxt().outer_expn_data();
81+
if expn.is_root() {
82+
break;
83+
}
84+
arg_macro = expn.macro_def_id;
85+
arg_span = expn.call_site;
86+
}
87+
88+
cx.struct_span_lint(NON_FMT_PANIC, arg_span, |lint| {
7389
let mut l = lint.build("panic message is not a string literal");
7490
l.note("this is no longer accepted in Rust 2021");
75-
if span.contains(arg.span) {
91+
if !span.contains(arg_span) {
92+
// No clue where this argument is coming from.
93+
l.emit();
94+
return;
95+
}
96+
if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
97+
// A case of `panic!(format!(..))`.
98+
l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here");
99+
if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
100+
l.multipart_suggestion(
101+
"remove the `format!(..)` macro call",
102+
vec![
103+
(arg_span.until(open.shrink_to_hi()), "".into()),
104+
(close.until(arg_span.shrink_to_hi()), "".into()),
105+
],
106+
Applicability::MachineApplicable,
107+
);
108+
}
109+
} else {
76110
l.span_suggestion_verbose(
77-
arg.span.shrink_to_lo(),
111+
arg_span.shrink_to_lo(),
78112
"add a \"{}\" format string to Display the message",
79113
"\"{}\", ".into(),
80114
Applicability::MaybeIncorrect,
81115
);
82116
if panic == sym::std_panic_macro {
83-
l.span_suggestion_verbose(
84-
span.until(arg.span),
85-
"or use std::panic::panic_any instead",
86-
"std::panic::panic_any(".into(),
87-
Applicability::MachineApplicable,
88-
);
117+
if let Some((open, close, del)) = find_delimiters(cx, span) {
118+
l.multipart_suggestion(
119+
"or use std::panic::panic_any instead",
120+
if del == '(' {
121+
vec![(span.until(open), "std::panic::panic_any".into())]
122+
} else {
123+
vec![
124+
(span.until(open.shrink_to_hi()), "std::panic::panic_any(".into()),
125+
(close, ")".into()),
126+
]
127+
},
128+
Applicability::MachineApplicable,
129+
);
130+
}
89131
}
90132
}
91133
l.emit();
@@ -175,6 +217,19 @@ fn check_panic_str<'tcx>(
175217
}
176218
}
177219

220+
/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`,
221+
/// and the type of (opening) delimiter used.
222+
fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> {
223+
let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
224+
let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?;
225+
let close = snippet.rfind(|c| ")]}".contains(c))?;
226+
Some((
227+
span.from_inner(InnerSpan { start: open, end: open + 1 }),
228+
span.from_inner(InnerSpan { start: close, end: close + 1 }),
229+
open_ch,
230+
))
231+
}
232+
178233
fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) {
179234
let mut expn = f.span.ctxt().outer_expn_data();
180235

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ symbols! {
560560
format_args,
561561
format_args_capture,
562562
format_args_nl,
563+
format_macro,
563564
freeze,
564565
freg,
565566
frem_fast,

library/alloc/src/macros.rs

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ macro_rules! vec {
107107
/// ```
108108
#[macro_export]
109109
#[stable(feature = "rust1", since = "1.0.0")]
110+
#[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")]
110111
macro_rules! format {
111112
($($arg:tt)*) => {{
112113
let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));

src/test/ui/non-fmt-panic.rs

+11
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ fn main() {
2929
fancy_panic::fancy_panic!(S);
3030
//~^ WARN panic message is not a string literal
3131

32+
macro_rules! a {
33+
() => { 123 };
34+
}
35+
36+
panic!(a!()); //~ WARN panic message is not a string literal
37+
38+
panic!(format!("{}", 1)); //~ WARN panic message is not a string literal
39+
40+
panic![123]; //~ WARN panic message is not a string literal
41+
panic!{123}; //~ WARN panic message is not a string literal
42+
3243
// Check that the lint only triggers for std::panic and core::panic,
3344
// not any panic macro:
3445
macro_rules! panic {

src/test/ui/non-fmt-panic.stderr

+65-4
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ LL | panic!("{}", C);
9393
help: or use std::panic::panic_any instead
9494
|
9595
LL | std::panic::panic_any(C);
96-
| ^^^^^^^^^^^^^^^^^^^^^^
96+
| ^^^^^^^^^^^^^^^^^^^^^
9797

9898
warning: panic message is not a string literal
9999
--> $DIR/non-fmt-panic.rs:20:12
@@ -109,7 +109,7 @@ LL | panic!("{}", S);
109109
help: or use std::panic::panic_any instead
110110
|
111111
LL | std::panic::panic_any(S);
112-
| ^^^^^^^^^^^^^^^^^^^^^^
112+
| ^^^^^^^^^^^^^^^^^^^^^
113113

114114
warning: panic message is not a string literal
115115
--> $DIR/non-fmt-panic.rs:21:17
@@ -125,7 +125,7 @@ LL | std::panic!("{}", 123);
125125
help: or use std::panic::panic_any instead
126126
|
127127
LL | std::panic::panic_any(123);
128-
| ^^^^^^^^^^^^^^^^^^^^^^
128+
| ^^^^^^^^^^^^^^^^^^^^^
129129

130130
warning: panic message is not a string literal
131131
--> $DIR/non-fmt-panic.rs:22:18
@@ -183,5 +183,66 @@ LL | fancy_panic::fancy_panic!(S);
183183
|
184184
= note: this is no longer accepted in Rust 2021
185185

186-
warning: 14 warnings emitted
186+
warning: panic message is not a string literal
187+
--> $DIR/non-fmt-panic.rs:36:12
188+
|
189+
LL | panic!(a!());
190+
| ^^^^
191+
|
192+
= note: this is no longer accepted in Rust 2021
193+
help: add a "{}" format string to Display the message
194+
|
195+
LL | panic!("{}", a!());
196+
| ^^^^^
197+
help: or use std::panic::panic_any instead
198+
|
199+
LL | std::panic::panic_any(a!());
200+
| ^^^^^^^^^^^^^^^^^^^^^
201+
202+
warning: panic message is not a string literal
203+
--> $DIR/non-fmt-panic.rs:38:12
204+
|
205+
LL | panic!(format!("{}", 1));
206+
| ^^^^^^^^^^^^^^^^
207+
|
208+
= note: this is no longer accepted in Rust 2021
209+
= note: the panic!() macro supports formatting, so there's no need for the format!() macro here
210+
help: remove the `format!(..)` macro call
211+
|
212+
LL | panic!("{}", 1);
213+
| -- --
214+
215+
warning: panic message is not a string literal
216+
--> $DIR/non-fmt-panic.rs:40:12
217+
|
218+
LL | panic![123];
219+
| ^^^
220+
|
221+
= note: this is no longer accepted in Rust 2021
222+
help: add a "{}" format string to Display the message
223+
|
224+
LL | panic!["{}", 123];
225+
| ^^^^^
226+
help: or use std::panic::panic_any instead
227+
|
228+
LL | std::panic::panic_any(123);
229+
| ^^^^^^^^^^^^^^^^^^^^^^ ^
230+
231+
warning: panic message is not a string literal
232+
--> $DIR/non-fmt-panic.rs:41:12
233+
|
234+
LL | panic!{123};
235+
| ^^^
236+
|
237+
= note: this is no longer accepted in Rust 2021
238+
help: add a "{}" format string to Display the message
239+
|
240+
LL | panic!{"{}", 123};
241+
| ^^^^^
242+
help: or use std::panic::panic_any instead
243+
|
244+
LL | std::panic::panic_any(123);
245+
| ^^^^^^^^^^^^^^^^^^^^^^ ^
246+
247+
warning: 18 warnings emitted
187248

0 commit comments

Comments
 (0)