about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
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 /compiler/rustc_lint/src
parent1abc1e2a43c116a87bd430fa14a86e67617b861a (diff)
Improve suggestion for panic!(format!(..)).
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs31
1 files changed, 30 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();