1 use crate::{LateContext, LateLintPass, LintContext}
;
3 use rustc_errors
::{pluralize, Applicability}
;
6 use rustc_parse_format
::{ParseMode, Parser, Piece}
;
7 use rustc_span
::{sym, InnerSpan}
;
10 /// The `non_fmt_panic` lint detects `panic!("..")` with `{` or `}` in the string literal
11 /// when it is not used as a format string.
23 /// `panic!("{}")` panics with the message `"{}"`, as a `panic!()` invocation
24 /// with a single argument does not use `format_args!()`.
25 /// A future edition of Rust will interpret this string as format string,
26 /// which would break this.
29 "detect braces in single-argument panic!() invocations",
30 report_in_external_macro
33 declare_lint_pass
!(PanicFmt
=> [NON_FMT_PANIC
]);
35 impl<'tcx
> LateLintPass
<'tcx
> for PanicFmt
{
36 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx hir
::Expr
<'tcx
>) {
37 if let hir
::ExprKind
::Call(f
, [arg
]) = &expr
.kind
{
38 if let &ty
::FnDef(def_id
, _
) = cx
.typeck_results().expr_ty(f
).kind() {
39 if Some(def_id
) == cx
.tcx
.lang_items().begin_panic_fn()
40 || Some(def_id
) == cx
.tcx
.lang_items().panic_fn()
42 check_panic(cx
, f
, arg
);
49 fn check_panic
<'tcx
>(cx
: &LateContext
<'tcx
>, f
: &'tcx hir
::Expr
<'tcx
>, arg
: &'tcx hir
::Expr
<'tcx
>) {
50 if let hir
::ExprKind
::Lit(lit
) = &arg
.kind
{
51 if let ast
::LitKind
::Str(sym
, _
) = lit
.node
{
52 let mut expn
= f
.span
.ctxt().outer_expn_data();
53 if let Some(id
) = expn
.macro_def_id
{
54 if cx
.tcx
.is_diagnostic_item(sym
::std_panic_macro
, id
)
55 || cx
.tcx
.is_diagnostic_item(sym
::core_panic_macro
, id
)
57 let fmt
= sym
.as_str();
58 if !fmt
.contains(&['{', '}'
][..]) {
62 let fmt_span
= arg
.span
.source_callsite();
64 let (snippet
, style
) =
65 match cx
.sess().parse_sess
.source_map().span_to_snippet(fmt_span
) {
67 // Count the number of `#`s between the `r` and `"`.
68 let style
= snippet
.strip_prefix('r'
).and_then(|s
| s
.find('
"'));
69 (Some(snippet), style)
71 Err(_) => (None, None),
75 Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format);
77 (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count();
79 // Unwrap another level of macro expansion if this panic!()
80 // was expanded from assert!() or debug_assert!().
81 for &assert in &[sym::assert_macro, sym::debug_assert_macro] {
82 let parent = expn.call_site.ctxt().outer_expn_data();
85 .map_or(false, |id| cx.tcx.is_diagnostic_item(assert, id))
91 if n_arguments > 0 && fmt_parser.errors.is_empty() {
92 let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
94 v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(),
96 cx.struct_span_lint(NON_FMT_PANIC, arg_spans, |lint| {
97 let mut l = lint.build(match n_arguments {
98 1 => "panic message contains an unused formatting placeholder
",
99 _ => "panic message contains unused formatting placeholders
",
101 l.note("this message is not used
as a format string when given without arguments
, but will be
in a future Rust edition
");
102 if expn.call_site.contains(arg.span) {
104 arg.span.shrink_to_hi(),
105 &format!("add the missing argument{}
", pluralize!(n_arguments)),
107 Applicability::HasPlaceholders,
110 arg.span.shrink_to_lo(),
111 "or add a
\"{}
\" format string to
use the message literally
",
113 Applicability::MachineApplicable,
119 let brace_spans: Option<Vec<_>> = snippet
120 .filter(|s| s.starts_with('"'
) || s
.starts_with("r#"))
123 .filter(|&(_
, c
)| c
== '{' || c == '}'
)
125 fmt_span
.from_inner(InnerSpan { start: i, end: i + 1 }
)
129 let msg
= match &brace_spans
{
130 Some(v
) if v
.len() == 1 => "panic message contains a brace",
131 _
=> "panic message contains braces",
133 cx
.struct_span_lint(NON_FMT_PANIC
, brace_spans
.unwrap_or(vec
![expn
.call_site
]), |lint
| {
134 let mut l
= lint
.build(msg
);
135 l
.note("this message is not used as a format string, but will be in a future Rust edition");
136 if expn
.call_site
.contains(arg
.span
) {
138 arg
.span
.shrink_to_lo(),
139 "add a \"{}\" format string to use the message literally",
141 Applicability
::MachineApplicable
,