Skip to content

Commit a428ab1

Browse files
committed
Improve suggestion for panic!(format!(..)).
1 parent 1abc1e2 commit a428ab1

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

compiler/rustc_lint/src/non_fmt_panic.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,38 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
7272
// Find the span of the argument to `panic!()`, before expansion in the
7373
// case of `panic!(some_macro!())`.
7474
let mut arg_span = arg.span;
75+
let mut arg_macro = None;
7576
while !span.contains(arg_span) {
7677
let expn = arg_span.ctxt().outer_expn_data();
7778
if expn.is_root() {
7879
break;
7980
}
81+
arg_macro = expn.macro_def_id;
8082
arg_span = expn.call_site;
8183
}
8284

8385
cx.struct_span_lint(NON_FMT_PANIC, arg_span, |lint| {
8486
let mut l = lint.build("panic message is not a string literal");
8587
l.note("this is no longer accepted in Rust 2021");
86-
if span.contains(arg_span) {
88+
if !span.contains(arg_span) {
89+
// No clue where this argument is coming from.
90+
l.emit();
91+
return;
92+
}
93+
if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
94+
// A case of `panic!(format!(..))`.
95+
l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here");
96+
if let Some(inner) = find_inner_span(cx, arg_span) {
97+
l.multipart_suggestion(
98+
"remove the `format!(..)` macro call",
99+
vec![
100+
(arg_span.until(inner), "".into()),
101+
(inner.between(arg_span.shrink_to_hi()), "".into()),
102+
],
103+
Applicability::MachineApplicable,
104+
);
105+
}
106+
} else {
87107
l.span_suggestion_verbose(
88108
arg_span.shrink_to_lo(),
89109
"add a \"{}\" format string to Display the message",
@@ -186,6 +206,15 @@ fn check_panic_str<'tcx>(
186206
}
187207
}
188208

209+
/// Given the span of `some_macro!(args)`, gives the span of `args`.
210+
fn find_inner_span<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<Span> {
211+
let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
212+
Some(span.from_inner(InnerSpan {
213+
start: snippet.find(&['(', '{', '['][..])? + 1,
214+
end: snippet.rfind(&[')', '}', ']'][..])?,
215+
}))
216+
}
217+
189218
fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) {
190219
let mut expn = f.span.ctxt().outer_expn_data();
191220

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ symbols! {
554554
format_args,
555555
format_args_capture,
556556
format_args_nl,
557+
format_macro,
557558
freeze,
558559
freg,
559560
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+
#[rustc_diagnostic_item = "format_macro"]
110111
macro_rules! format {
111112
($($arg:tt)*) => {{
112113
let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));

0 commit comments

Comments
 (0)