about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMara Bos <m-ou.se@m-ou.se>2021-02-14 18:52:47 +0100
committerMara Bos <m-ou.se@m-ou.se>2021-02-14 18:52:47 +0100
commita428ab17abc310c17ca13eeb6f74b3c5bddff940 (patch)
tree6a5ef88b0890d44fc2286d9bbf58d5952e7da575
parent1abc1e2a43c116a87bd430fa14a86e67617b861a (diff)
downloadrust-a428ab17abc310c17ca13eeb6f74b3c5bddff940.tar.gz
rust-a428ab17abc310c17ca13eeb6f74b3c5bddff940.zip
Improve suggestion for panic!(format!(..)).
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs31
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/alloc/src/macros.rs1
3 files changed, 32 insertions, 1 deletions
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 0c5456cf619..7432f476d7c 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -72,18 +72,38 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
     // Find the span of the argument to `panic!()`, before expansion in the
     // case of `panic!(some_macro!())`.
     let mut arg_span = arg.span;
+    let mut arg_macro = None;
     while !span.contains(arg_span) {
         let expn = arg_span.ctxt().outer_expn_data();
         if expn.is_root() {
             break;
         }
+        arg_macro = expn.macro_def_id;
         arg_span = expn.call_site;
     }
 
     cx.struct_span_lint(NON_FMT_PANIC, arg_span, |lint| {
         let mut l = lint.build("panic message is not a string literal");
         l.note("this is no longer accepted in Rust 2021");
-        if span.contains(arg_span) {
+        if !span.contains(arg_span) {
+            // No clue where this argument is coming from.
+            l.emit();
+            return;
+        }
+        if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
+            // A case of `panic!(format!(..))`.
+            l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here");
+            if let Some(inner) = find_inner_span(cx, arg_span) {
+                l.multipart_suggestion(
+                    "remove the `format!(..)` macro call",
+                    vec![
+                        (arg_span.until(inner), "".into()),
+                        (inner.between(arg_span.shrink_to_hi()), "".into()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            }
+        } else {
             l.span_suggestion_verbose(
                 arg_span.shrink_to_lo(),
                 "add a \"{}\" format string to Display the message",
@@ -186,6 +206,15 @@ fn check_panic_str<'tcx>(
     }
 }
 
+/// Given the span of `some_macro!(args)`, gives the span of `args`.
+fn find_inner_span<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<Span> {
+    let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
+    Some(span.from_inner(InnerSpan {
+        start: snippet.find(&['(', '{', '['][..])? + 1,
+        end: snippet.rfind(&[')', '}', ']'][..])?,
+    }))
+}
+
 fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) {
     let mut expn = f.span.ctxt().outer_expn_data();
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 20e4f7262ac..831e82559ad 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -554,6 +554,7 @@ symbols! {
         format_args,
         format_args_capture,
         format_args_nl,
+        format_macro,
         freeze,
         freg,
         frem_fast,
diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs
index a64a8b32ad7..88a6cec3a83 100644
--- a/library/alloc/src/macros.rs
+++ b/library/alloc/src/macros.rs
@@ -107,6 +107,7 @@ macro_rules! vec {
 /// ```
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_diagnostic_item = "format_macro"]
 macro_rules! format {
     ($($arg:tt)*) => {{
         let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));