about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_lint/Cargo.toml1
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs66
-rw-r--r--src/test/ui/non-fmt-panic.fixed30
-rw-r--r--src/test/ui/non-fmt-panic.rs30
-rw-r--r--src/test/ui/non-fmt-panic.stderr193
6 files changed, 288 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f9c52200aa6..cfa02daab7e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3933,6 +3933,7 @@ dependencies = [
  "rustc_feature",
  "rustc_hir",
  "rustc_index",
+ "rustc_infer",
  "rustc_middle",
  "rustc_parse_format",
  "rustc_serialize",
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index 75a031a86b6..8294d5878fa 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -22,3 +22,4 @@ rustc_session = { path = "../rustc_session" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_parse_format = { path = "../rustc_parse_format" }
+rustc_infer = { path = "../rustc_infer" }
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index ee66a948dd9..33a6edafa26 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -2,12 +2,15 @@ use crate::{LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
 use rustc_errors::{pluralize, Applicability};
 use rustc_hir as hir;
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
+use rustc_middle::ty::subst::InternalSubsts;
 use rustc_parse_format::{ParseMode, Parser, Piece};
 use rustc_session::lint::FutureIncompatibilityReason;
 use rustc_span::edition::Edition;
 use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol};
+use rustc_trait_selection::infer::InferCtxtExt;
 
 declare_lint! {
     /// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first
@@ -99,7 +102,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
 
     cx.struct_span_lint(NON_FMT_PANICS, arg_span, |lint| {
         let mut l = lint.build("panic message is not a string literal");
-        l.note("this usage of panic!() is deprecated; it will be a hard error in Rust 2021");
+        l.note(&format!("this usage of {}!() is deprecated; it will be a hard error in Rust 2021", symbol_str));
         l.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>");
         if !is_arg_inside_call(arg_span, span) {
             // No clue where this argument is coming from.
@@ -129,20 +132,57 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 ty.ty_adt_def(),
                 Some(ty_def) if cx.tcx.is_diagnostic_item(sym::string_type, ty_def.did),
             );
-            l.span_suggestion_verbose(
-                arg_span.shrink_to_lo(),
-                "add a \"{}\" format string to Display the message",
-                "\"{}\", ".into(),
-                if is_str {
-                    Applicability::MachineApplicable
-                } else {
-                    Applicability::MaybeIncorrect
-                },
-            );
-            if !is_str && panic == sym::std_panic_macro {
+
+            let (suggest_display, suggest_debug) = cx.tcx.infer_ctxt().enter(|infcx| {
+                let display = is_str || cx.tcx.get_diagnostic_item(sym::display_trait).map(|t| {
+                    infcx.type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env).may_apply()
+                }) == Some(true);
+                let debug = !display && cx.tcx.get_diagnostic_item(sym::debug_trait).map(|t| {
+                    infcx.type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env).may_apply()
+                }) == Some(true);
+                (display, debug)
+            });
+
+            let suggest_panic_any = !is_str && panic == sym::std_panic_macro;
+
+            let fmt_applicability = if suggest_panic_any {
+                // If we can use panic_any, use that as the MachineApplicable suggestion.
+                Applicability::MaybeIncorrect
+            } else {
+                // If we don't suggest panic_any, using a format string is our best bet.
+                Applicability::MachineApplicable
+            };
+
+            if suggest_display {
+                l.span_suggestion_verbose(
+                    arg_span.shrink_to_lo(),
+                    "add a \"{}\" format string to Display the message",
+                    "\"{}\", ".into(),
+                    fmt_applicability,
+                );
+            } else if suggest_debug {
+                l.span_suggestion_verbose(
+                    arg_span.shrink_to_lo(),
+                    &format!(
+                        "add a \"{{:?}}\" format string to use the Debug implementation of `{}`",
+                        ty,
+                    ),
+                    "\"{:?}\", ".into(),
+                    fmt_applicability,
+                );
+            }
+
+            if suggest_panic_any {
                 if let Some((open, close, del)) = find_delimiters(cx, span) {
                     l.multipart_suggestion(
-                        "or use std::panic::panic_any instead",
+                        &format!(
+                            "{}use std::panic::panic_any instead",
+                            if suggest_display || suggest_debug {
+                                "or "
+                            } else {
+                                ""
+                            },
+                        ),
                         if del == '(' {
                             vec![(span.until(open), "std::panic::panic_any".into())]
                         } else {
diff --git a/src/test/ui/non-fmt-panic.fixed b/src/test/ui/non-fmt-panic.fixed
index c85e1887d96..d226f4129aa 100644
--- a/src/test/ui/non-fmt-panic.fixed
+++ b/src/test/ui/non-fmt-panic.fixed
@@ -17,11 +17,16 @@ fn main() {
     //~^ WARN panic message contains unused formatting placeholders
     assert!(false, "{}", S);
     //~^ WARN panic message is not a string literal
+    assert!(false, "{}", 123);
+    //~^ WARN panic message is not a string literal
+    assert!(false, "{:?}", Some(123));
+    //~^ WARN panic message is not a string literal
     debug_assert!(false, "{}", "{{}} bla"); //~ WARN panic message contains braces
     panic!("{}", C); //~ WARN panic message is not a string literal
     panic!("{}", S); //~ WARN panic message is not a string literal
     std::panic::panic_any(123); //~ WARN panic message is not a string literal
     core::panic!("{}", &*"abc"); //~ WARN panic message is not a string literal
+    std::panic::panic_any(Some(123)); //~ WARN panic message is not a string literal
     panic!("{}", concat!("{", "}")); //~ WARN panic message contains an unused formatting placeholder
     panic!("{}", concat!("{", "{")); //~ WARN panic message contains braces
 
@@ -51,4 +56,29 @@ fn main() {
     }
     panic!("{}"); // OK
     panic!(S); // OK
+
+    a(1);
+    b(1);
+    c(1);
+    d(1);
+}
+
+fn a<T: Send + 'static>(v: T) {
+    std::panic::panic_any(v); //~ WARN panic message is not a string literal
+    assert!(false, v); //~ WARN panic message is not a string literal
+}
+
+fn b<T: std::fmt::Debug + Send + 'static>(v: T) {
+    std::panic::panic_any(v); //~ WARN panic message is not a string literal
+    assert!(false, "{:?}", v); //~ WARN panic message is not a string literal
+}
+
+fn c<T: std::fmt::Display + Send + 'static>(v: T) {
+    std::panic::panic_any(v); //~ WARN panic message is not a string literal
+    assert!(false, "{}", v); //~ WARN panic message is not a string literal
+}
+
+fn d<T: std::fmt::Display + std::fmt::Debug + Send + 'static>(v: T) {
+    std::panic::panic_any(v); //~ WARN panic message is not a string literal
+    assert!(false, "{}", v); //~ WARN panic message is not a string literal
 }
diff --git a/src/test/ui/non-fmt-panic.rs b/src/test/ui/non-fmt-panic.rs
index 020bcf00a01..2ffd7638ae0 100644
--- a/src/test/ui/non-fmt-panic.rs
+++ b/src/test/ui/non-fmt-panic.rs
@@ -17,11 +17,16 @@ fn main() {
     //~^ WARN panic message contains unused formatting placeholders
     assert!(false, S);
     //~^ WARN panic message is not a string literal
+    assert!(false, 123);
+    //~^ WARN panic message is not a string literal
+    assert!(false, Some(123));
+    //~^ WARN panic message is not a string literal
     debug_assert!(false, "{{}} bla"); //~ WARN panic message contains braces
     panic!(C); //~ WARN panic message is not a string literal
     panic!(S); //~ WARN panic message is not a string literal
     std::panic!(123); //~ WARN panic message is not a string literal
     core::panic!(&*"abc"); //~ WARN panic message is not a string literal
+    panic!(Some(123)); //~ WARN panic message is not a string literal
     panic!(concat!("{", "}")); //~ WARN panic message contains an unused formatting placeholder
     panic!(concat!("{", "{")); //~ WARN panic message contains braces
 
@@ -51,4 +56,29 @@ fn main() {
     }
     panic!("{}"); // OK
     panic!(S); // OK
+
+    a(1);
+    b(1);
+    c(1);
+    d(1);
+}
+
+fn a<T: Send + 'static>(v: T) {
+    panic!(v); //~ WARN panic message is not a string literal
+    assert!(false, v); //~ WARN panic message is not a string literal
+}
+
+fn b<T: std::fmt::Debug + Send + 'static>(v: T) {
+    panic!(v); //~ WARN panic message is not a string literal
+    assert!(false, v); //~ WARN panic message is not a string literal
+}
+
+fn c<T: std::fmt::Display + Send + 'static>(v: T) {
+    panic!(v); //~ WARN panic message is not a string literal
+    assert!(false, v); //~ WARN panic message is not a string literal
+}
+
+fn d<T: std::fmt::Display + std::fmt::Debug + Send + 'static>(v: T) {
+    panic!(v); //~ WARN panic message is not a string literal
+    assert!(false, v); //~ WARN panic message is not a string literal
 }
diff --git a/src/test/ui/non-fmt-panic.stderr b/src/test/ui/non-fmt-panic.stderr
index 513ffd37dc3..b62cc378aa5 100644
--- a/src/test/ui/non-fmt-panic.stderr
+++ b/src/test/ui/non-fmt-panic.stderr
@@ -61,15 +61,41 @@ warning: panic message is not a string literal
 LL |     assert!(false, S);
    |                    ^
    |
-   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
 help: add a "{}" format string to Display the message
    |
 LL |     assert!(false, "{}", S);
    |                    +++++
 
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:20:20
+   |
+LL |     assert!(false, 123);
+   |                    ^^^
+   |
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+   |
+LL |     assert!(false, "{}", 123);
+   |                    +++++
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:22:20
+   |
+LL |     assert!(false, Some(123));
+   |                    ^^^^^^^^^
+   |
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{:?}" format string to use the Debug implementation of `Option<i32>`
+   |
+LL |     assert!(false, "{:?}", Some(123));
+   |                    +++++++
+
 warning: panic message contains braces
-  --> $DIR/non-fmt-panic.rs:20:27
+  --> $DIR/non-fmt-panic.rs:24:27
    |
 LL |     debug_assert!(false, "{{}} bla");
    |                           ^^^^
@@ -81,7 +107,7 @@ LL |     debug_assert!(false, "{}", "{{}} bla");
    |                          +++++
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:21:12
+  --> $DIR/non-fmt-panic.rs:25:12
    |
 LL |     panic!(C);
    |            ^
@@ -94,7 +120,7 @@ LL |     panic!("{}", C);
    |            +++++
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:22:12
+  --> $DIR/non-fmt-panic.rs:26:12
    |
 LL |     panic!(S);
    |            ^
@@ -107,12 +133,12 @@ LL |     panic!("{}", S);
    |            +++++
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:23:17
+  --> $DIR/non-fmt-panic.rs:27:17
    |
 LL |     std::panic!(123);
    |                 ^^^
    |
-   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: this usage of std::panic!() is deprecated; it will be a hard error in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
 help: add a "{}" format string to Display the message
    |
@@ -124,20 +150,37 @@ LL |     std::panic::panic_any(123);
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:24:18
+  --> $DIR/non-fmt-panic.rs:28:18
    |
 LL |     core::panic!(&*"abc");
    |                  ^^^^^^^
    |
-   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: this usage of core::panic!() is deprecated; it will be a hard error in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
 help: add a "{}" format string to Display the message
    |
 LL |     core::panic!("{}", &*"abc");
    |                  +++++
 
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:29:12
+   |
+LL |     panic!(Some(123));
+   |            ^^^^^^^^^
+   |
+   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{:?}" format string to use the Debug implementation of `Option<i32>`
+   |
+LL |     panic!("{:?}", Some(123));
+   |            +++++++
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(Some(123));
+   |     ~~~~~~~~~~~~~~~~~~~~~
+
 warning: panic message contains an unused formatting placeholder
-  --> $DIR/non-fmt-panic.rs:25:12
+  --> $DIR/non-fmt-panic.rs:30:12
    |
 LL |     panic!(concat!("{", "}"));
    |            ^^^^^^^^^^^^^^^^^
@@ -153,7 +196,7 @@ LL |     panic!("{}", concat!("{", "}"));
    |            +++++
 
 warning: panic message contains braces
-  --> $DIR/non-fmt-panic.rs:26:5
+  --> $DIR/non-fmt-panic.rs:31:5
    |
 LL |     panic!(concat!("{", "{"));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -165,7 +208,7 @@ LL |     panic!("{}", concat!("{", "{"));
    |            +++++
 
 warning: panic message contains an unused formatting placeholder
-  --> $DIR/non-fmt-panic.rs:28:37
+  --> $DIR/non-fmt-panic.rs:33:37
    |
 LL |     fancy_panic::fancy_panic!("test {} 123");
    |                                     ^^
@@ -173,7 +216,7 @@ LL |     fancy_panic::fancy_panic!("test {} 123");
    = note: this message is not used as a format string when given without arguments, but will be in Rust 2021
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:38:12
+  --> $DIR/non-fmt-panic.rs:43:12
    |
 LL |     panic!(a!());
    |            ^^^^
@@ -190,7 +233,7 @@ LL |     std::panic::panic_any(a!());
    |     ~~~~~~~~~~~~~~~~~~~~~
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:40:12
+  --> $DIR/non-fmt-panic.rs:45:12
    |
 LL |     panic!(format!("{}", 1));
    |            ^^^^^^^^^^^^^^^^
@@ -205,12 +248,12 @@ LL +     panic!("{}", 1);
    | 
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:41:20
+  --> $DIR/non-fmt-panic.rs:46:20
    |
 LL |     assert!(false, format!("{}", 1));
    |                    ^^^^^^^^^^^^^^^^
    |
-   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
    = note: the assert!() macro supports formatting, so there's no need for the format!() macro here
 help: remove the `format!(..)` macro call
@@ -220,12 +263,12 @@ LL +     assert!(false, "{}", 1);
    | 
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:42:26
+  --> $DIR/non-fmt-panic.rs:47:26
    |
 LL |     debug_assert!(false, format!("{}", 1));
    |                          ^^^^^^^^^^^^^^^^
    |
-   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: this usage of debug_assert!() is deprecated; it will be a hard error in Rust 2021
    = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
    = note: the debug_assert!() macro supports formatting, so there's no need for the format!() macro here
 help: remove the `format!(..)` macro call
@@ -235,7 +278,7 @@ LL +     debug_assert!(false, "{}", 1);
    | 
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:44:12
+  --> $DIR/non-fmt-panic.rs:49:12
    |
 LL |     panic![123];
    |            ^^^
@@ -252,7 +295,7 @@ LL |     std::panic::panic_any(123);
    |     ~~~~~~~~~~~~~~~~~~~~~~   ~
 
 warning: panic message is not a string literal
-  --> $DIR/non-fmt-panic.rs:45:12
+  --> $DIR/non-fmt-panic.rs:50:12
    |
 LL |     panic!{123};
    |            ^^^
@@ -268,5 +311,115 @@ help: or use std::panic::panic_any instead
 LL |     std::panic::panic_any(123);
    |     ~~~~~~~~~~~~~~~~~~~~~~   ~
 
-warning: 19 warnings emitted
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:67:12
+   |
+LL |     panic!(v);
+   |     ------ ^
+   |     |
+   |     help: use std::panic::panic_any instead: `std::panic::panic_any`
+   |
+   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:68:20
+   |
+LL |     assert!(false, v);
+   |                    ^
+   |
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:72:12
+   |
+LL |     panic!(v);
+   |            ^
+   |
+   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{:?}" format string to use the Debug implementation of `T`
+   |
+LL |     panic!("{:?}", v);
+   |            +++++++
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(v);
+   |     ~~~~~~~~~~~~~~~~~~~~~
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:73:20
+   |
+LL |     assert!(false, v);
+   |                    ^
+   |
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{:?}" format string to use the Debug implementation of `T`
+   |
+LL |     assert!(false, "{:?}", v);
+   |                    +++++++
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:77:12
+   |
+LL |     panic!(v);
+   |            ^
+   |
+   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+   |
+LL |     panic!("{}", v);
+   |            +++++
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(v);
+   |     ~~~~~~~~~~~~~~~~~~~~~
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:78:20
+   |
+LL |     assert!(false, v);
+   |                    ^
+   |
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+   |
+LL |     assert!(false, "{}", v);
+   |                    +++++
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:82:12
+   |
+LL |     panic!(v);
+   |            ^
+   |
+   = note: this usage of panic!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+   |
+LL |     panic!("{}", v);
+   |            +++++
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(v);
+   |     ~~~~~~~~~~~~~~~~~~~~~
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:83:20
+   |
+LL |     assert!(false, v);
+   |                    ^
+   |
+   = note: this usage of assert!() is deprecated; it will be a hard error in Rust 2021
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+   |
+LL |     assert!(false, "{}", v);
+   |                    +++++
+
+warning: 30 warnings emitted