Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 74285eb

Browse files
committedNov 20, 2020
Auto merge of #78088 - fusion-engineering-forks:panic-fmt-lint, r=estebank
Add lint for panic!("{}") This adds a lint that warns about `panic!("{}")`. `panic!(msg)` invocations with a single argument use their argument as panic payload literally, without using it as a format string. The same holds for `assert!(expr, msg)`. This lints checks if `msg` is a string literal (after expansion), and warns in case it contained braces. It suggests to insert `"{}", ` to use the message literally, or to add arguments to use it as a format string. ![image](https://user-images.githubusercontent.com/783247/96643867-79eb1080-1328-11eb-8d4e-a5586837c70a.png) This lint is also a good starting point for adding warnings about `panic!(not_a_string)` later, once [`panic_any()`](#74622) becomes a stable alternative.
2 parents 4ec27e4 + a125ef2 commit 74285eb

File tree

22 files changed

+362
-165
lines changed

22 files changed

+362
-165
lines changed
 

‎Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3831,6 +3831,7 @@ dependencies = [
38313831
"rustc_hir",
38323832
"rustc_index",
38333833
"rustc_middle",
3834+
"rustc_parse_format",
38343835
"rustc_session",
38353836
"rustc_span",
38363837
"rustc_target",

‎compiler/rustc_builtin_macros/src/assert.rs

+30-22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use rustc_errors::{Applicability, DiagnosticBuilder};
22

33
use rustc_ast::ptr::P;
4-
use rustc_ast::token::{self, TokenKind};
5-
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
4+
use rustc_ast::token;
5+
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
66
use rustc_ast::{self as ast, *};
77
use rustc_ast_pretty::pprust;
88
use rustc_expand::base::*;
@@ -26,31 +26,39 @@ pub fn expand_assert<'cx>(
2626
// `core::panic` and `std::panic` are different macros, so we use call-site
2727
// context to pick up whichever is currently in scope.
2828
let sp = cx.with_call_site_ctxt(sp);
29-
let tokens = custom_message.unwrap_or_else(|| {
30-
TokenStream::from(TokenTree::token(
31-
TokenKind::lit(
32-
token::Str,
29+
30+
let panic_call = if let Some(tokens) = custom_message {
31+
// Pass the custom message to panic!().
32+
cx.expr(
33+
sp,
34+
ExprKind::MacCall(MacCall {
35+
path: Path::from_ident(Ident::new(sym::panic, sp)),
36+
args: P(MacArgs::Delimited(
37+
DelimSpan::from_single(sp),
38+
MacDelimiter::Parenthesis,
39+
tokens,
40+
)),
41+
prior_type_ascription: None,
42+
}),
43+
)
44+
} else {
45+
// Pass our own message directly to $crate::panicking::panic(),
46+
// because it might contain `{` and `}` that should always be
47+
// passed literally.
48+
cx.expr_call_global(
49+
sp,
50+
cx.std_path(&[sym::panicking, sym::panic]),
51+
vec![cx.expr_str(
52+
DUMMY_SP,
3353
Symbol::intern(&format!(
3454
"assertion failed: {}",
3555
pprust::expr_to_string(&cond_expr).escape_debug()
3656
)),
37-
None,
38-
),
39-
DUMMY_SP,
40-
))
41-
});
42-
let args = P(MacArgs::Delimited(DelimSpan::from_single(sp), MacDelimiter::Parenthesis, tokens));
43-
let panic_call = MacCall {
44-
path: Path::from_ident(Ident::new(sym::panic, sp)),
45-
args,
46-
prior_type_ascription: None,
57+
)],
58+
)
4759
};
48-
let if_expr = cx.expr_if(
49-
sp,
50-
cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)),
51-
cx.expr(sp, ExprKind::MacCall(panic_call)),
52-
None,
53-
);
60+
let if_expr =
61+
cx.expr_if(sp, cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)), panic_call, None);
5462
MacEager::expr(if_expr)
5563
}
5664

‎compiler/rustc_expand/src/mbe/macro_rules.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
11731173
mbe::TokenTree::MetaVar(_, name) => format!("${}", name),
11741174
mbe::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind),
11751175
_ => panic!(
1176-
"unexpected mbe::TokenTree::{{Sequence or Delimited}} \
1176+
"{}",
1177+
"unexpected mbe::TokenTree::{Sequence or Delimited} \
11771178
in follow set checker"
11781179
),
11791180
}

‎compiler/rustc_lint/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ rustc_feature = { path = "../rustc_feature" }
2020
rustc_index = { path = "../rustc_index" }
2121
rustc_session = { path = "../rustc_session" }
2222
rustc_trait_selection = { path = "../rustc_trait_selection" }
23+
rustc_parse_format = { path = "../rustc_parse_format" }

‎compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mod levels;
5555
mod methods;
5656
mod non_ascii_idents;
5757
mod nonstandard_style;
58+
mod panic_fmt;
5859
mod passes;
5960
mod redundant_semicolon;
6061
mod traits;
@@ -80,6 +81,7 @@ use internal::*;
8081
use methods::*;
8182
use non_ascii_idents::*;
8283
use nonstandard_style::*;
84+
use panic_fmt::PanicFmt;
8385
use redundant_semicolon::*;
8486
use traits::*;
8587
use types::*;
@@ -166,6 +168,7 @@ macro_rules! late_lint_passes {
166168
ClashingExternDeclarations: ClashingExternDeclarations::new(),
167169
DropTraitConstraints: DropTraitConstraints,
168170
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
171+
PanicFmt: PanicFmt,
169172
]
170173
);
171174
};

‎compiler/rustc_lint/src/panic_fmt.rs

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use crate::{LateContext, LateLintPass, LintContext};
2+
use rustc_ast as ast;
3+
use rustc_errors::{pluralize, Applicability};
4+
use rustc_hir as hir;
5+
use rustc_middle::ty;
6+
use rustc_parse_format::{ParseMode, Parser, Piece};
7+
use rustc_span::{sym, InnerSpan};
8+
9+
declare_lint! {
10+
/// The `panic_fmt` lint detects `panic!("..")` with `{` or `}` in the string literal.
11+
///
12+
/// ### Example
13+
///
14+
/// ```rust,no_run
15+
/// panic!("{}");
16+
/// ```
17+
///
18+
/// {{produces}}
19+
///
20+
/// ### Explanation
21+
///
22+
/// `panic!("{}")` panics with the message `"{}"`, as a `panic!()` invocation
23+
/// with a single argument does not use `format_args!()`.
24+
/// A future edition of Rust will interpret this string as format string,
25+
/// which would break this.
26+
PANIC_FMT,
27+
Warn,
28+
"detect braces in single-argument panic!() invocations",
29+
report_in_external_macro
30+
}
31+
32+
declare_lint_pass!(PanicFmt => [PANIC_FMT]);
33+
34+
impl<'tcx> LateLintPass<'tcx> for PanicFmt {
35+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
36+
if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
37+
if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
38+
if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
39+
|| Some(def_id) == cx.tcx.lang_items().panic_fn()
40+
{
41+
check_panic(cx, f, arg);
42+
}
43+
}
44+
}
45+
}
46+
}
47+
48+
fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) {
49+
if let hir::ExprKind::Lit(lit) = &arg.kind {
50+
if let ast::LitKind::Str(sym, _) = lit.node {
51+
let mut expn = f.span.ctxt().outer_expn_data();
52+
if let Some(id) = expn.macro_def_id {
53+
if cx.tcx.is_diagnostic_item(sym::std_panic_macro, id)
54+
|| cx.tcx.is_diagnostic_item(sym::core_panic_macro, id)
55+
{
56+
let fmt = sym.as_str();
57+
if !fmt.contains(&['{', '}'][..]) {
58+
return;
59+
}
60+
61+
let fmt_span = arg.span.source_callsite();
62+
63+
let (snippet, style) =
64+
match cx.sess().parse_sess.source_map().span_to_snippet(fmt_span) {
65+
Ok(snippet) => {
66+
// Count the number of `#`s between the `r` and `"`.
67+
let style = snippet.strip_prefix('r').and_then(|s| s.find('"'));
68+
(Some(snippet), style)
69+
}
70+
Err(_) => (None, None),
71+
};
72+
73+
let mut fmt_parser =
74+
Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format);
75+
let n_arguments =
76+
(&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count();
77+
78+
// Unwrap another level of macro expansion if this panic!()
79+
// was expanded from assert!() or debug_assert!().
80+
for &assert in &[sym::assert_macro, sym::debug_assert_macro] {
81+
let parent = expn.call_site.ctxt().outer_expn_data();
82+
if parent
83+
.macro_def_id
84+
.map_or(false, |id| cx.tcx.is_diagnostic_item(assert, id))
85+
{
86+
expn = parent;
87+
}
88+
}
89+
90+
if n_arguments > 0 && fmt_parser.errors.is_empty() {
91+
let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
92+
[] => vec![fmt_span],
93+
v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(),
94+
};
95+
cx.struct_span_lint(PANIC_FMT, arg_spans, |lint| {
96+
let mut l = lint.build(match n_arguments {
97+
1 => "panic message contains an unused formatting placeholder",
98+
_ => "panic message contains unused formatting placeholders",
99+
});
100+
l.note("this message is not used as a format string when given without arguments, but will be in a future Rust edition");
101+
if expn.call_site.contains(arg.span) {
102+
l.span_suggestion(
103+
arg.span.shrink_to_hi(),
104+
&format!("add the missing argument{}", pluralize!(n_arguments)),
105+
", ...".into(),
106+
Applicability::HasPlaceholders,
107+
);
108+
l.span_suggestion(
109+
arg.span.shrink_to_lo(),
110+
"or add a \"{}\" format string to use the message literally",
111+
"\"{}\", ".into(),
112+
Applicability::MachineApplicable,
113+
);
114+
}
115+
l.emit();
116+
});
117+
} else {
118+
let brace_spans: Option<Vec<_>> = snippet
119+
.filter(|s| s.starts_with('"') || s.starts_with("r#"))
120+
.map(|s| {
121+
s.char_indices()
122+
.filter(|&(_, c)| c == '{' || c == '}')
123+
.map(|(i, _)| {
124+
fmt_span.from_inner(InnerSpan { start: i, end: i + 1 })
125+
})
126+
.collect()
127+
});
128+
let msg = match &brace_spans {
129+
Some(v) if v.len() == 1 => "panic message contains a brace",
130+
_ => "panic message contains braces",
131+
};
132+
cx.struct_span_lint(PANIC_FMT, brace_spans.unwrap_or(vec![expn.call_site]), |lint| {
133+
let mut l = lint.build(msg);
134+
l.note("this message is not used as a format string, but will be in a future Rust edition");
135+
if expn.call_site.contains(arg.span) {
136+
l.span_suggestion(
137+
arg.span.shrink_to_lo(),
138+
"add a \"{}\" format string to use the message literally",
139+
"\"{}\", ".into(),
140+
Applicability::MachineApplicable,
141+
);
142+
}
143+
l.emit();
144+
});
145+
}
146+
}
147+
}
148+
}
149+
}
150+
}

‎compiler/rustc_passes/src/diagnostic_items.rs

+4
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap<Symbol, DefId> {
113113
}
114114
}
115115

116+
for m in tcx.hir().krate().exported_macros {
117+
collector.observe_item(m.attrs, m.hir_id);
118+
}
119+
116120
collector.items
117121
}
118122

‎compiler/rustc_span/src/symbol.rs

+5
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ symbols! {
267267
asm,
268268
assert,
269269
assert_inhabited,
270+
assert_macro,
270271
assert_receiver_is_total_eq,
271272
assert_uninit_valid,
272273
assert_zero_valid,
@@ -393,6 +394,7 @@ symbols! {
393394
copysignf64,
394395
core,
395396
core_intrinsics,
397+
core_panic_macro,
396398
cosf32,
397399
cosf64,
398400
crate_id,
@@ -416,6 +418,7 @@ symbols! {
416418
dead_code,
417419
dealloc,
418420
debug,
421+
debug_assert_macro,
419422
debug_assertions,
420423
debug_struct,
421424
debug_trait,
@@ -789,6 +792,7 @@ symbols! {
789792
panic_runtime,
790793
panic_str,
791794
panic_unwind,
795+
panicking,
792796
param_attrs,
793797
parent_trait,
794798
partial_cmp,
@@ -1064,6 +1068,7 @@ symbols! {
10641068
staticlib,
10651069
std,
10661070
std_inject,
1071+
std_panic_macro,
10671072
stmt,
10681073
stmt_expr_attributes,
10691074
stop_after_dataflow,

‎library/core/src/macros/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#[macro_export]
33
#[allow_internal_unstable(core_panic, const_caller_location)]
44
#[stable(feature = "core", since = "1.6.0")]
5+
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "core_panic_macro")]
56
macro_rules! panic {
67
() => (
78
$crate::panic!("explicit panic")
@@ -162,6 +163,7 @@ macro_rules! assert_ne {
162163
/// ```
163164
#[macro_export]
164165
#[stable(feature = "rust1", since = "1.0.0")]
166+
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "debug_assert_macro")]
165167
macro_rules! debug_assert {
166168
($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert!($($arg)*); })
167169
}
@@ -1215,6 +1217,8 @@ pub(crate) mod builtin {
12151217
#[stable(feature = "rust1", since = "1.0.0")]
12161218
#[rustc_builtin_macro]
12171219
#[macro_export]
1220+
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "assert_macro")]
1221+
#[allow_internal_unstable(core_panic)]
12181222
macro_rules! assert {
12191223
($cond:expr $(,)?) => {{ /* compiler built-in */ }};
12201224
($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }};

‎library/core/tests/nonzero.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ fn test_match_option_string() {
8585
let five = "Five".to_string();
8686
match Some(five) {
8787
Some(s) => assert_eq!(s, "Five"),
88-
None => panic!("unexpected None while matching on Some(String { ... })"),
88+
None => panic!("{}", "unexpected None while matching on Some(String { ... })"),
8989
}
9090
}
9191

‎library/std/src/macros.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#[macro_export]
99
#[stable(feature = "rust1", since = "1.0.0")]
1010
#[allow_internal_unstable(libstd_sys_internals)]
11+
#[cfg_attr(not(any(bootstrap, test)), rustc_diagnostic_item = "std_panic_macro")]
1112
macro_rules! panic {
1213
() => ({ $crate::panic!("explicit panic") });
1314
($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) });

‎src/test/mir-opt/inst_combine_deref.do_not_miscompile.InstCombine.diff

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
let mut _8: bool; // in scope 0 at $DIR/inst_combine_deref.rs:60:5: 60:23
1111
let mut _9: bool; // in scope 0 at $DIR/inst_combine_deref.rs:60:13: 60:21
1212
let mut _10: i32; // in scope 0 at $DIR/inst_combine_deref.rs:60:13: 60:15
13-
let mut _11: !; // in scope 0 at $SRC_DIR/std/src/macros.rs:LL:COL
13+
let mut _11: !; // in scope 0 at $DIR/inst_combine_deref.rs:60:5: 60:23
1414
scope 1 {
1515
debug x => _1; // in scope 1 at $DIR/inst_combine_deref.rs:55:9: 55:10
1616
let _2: i32; // in scope 1 at $DIR/inst_combine_deref.rs:56:9: 56:10
@@ -69,11 +69,11 @@
6969
}
7070

7171
bb2: {
72-
StorageLive(_11); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
73-
begin_panic::<&str>(const "assertion failed: *y == 99"); // scope 4 at $SRC_DIR/std/src/macros.rs:LL:COL
72+
StorageLive(_11); // scope 4 at $DIR/inst_combine_deref.rs:60:5: 60:23
73+
core::panicking::panic(const "assertion failed: *y == 99"); // scope 4 at $DIR/inst_combine_deref.rs:60:5: 60:23
7474
// mir::Constant
75-
// + span: $SRC_DIR/std/src/macros.rs:LL:COL
76-
// + literal: Const { ty: fn(&str) -> ! {std::rt::begin_panic::<&str>}, val: Value(Scalar(<ZST>)) }
75+
// + span: $DIR/inst_combine_deref.rs:60:5: 60:23
76+
// + literal: Const { ty: fn(&'static str) -> ! {core::panicking::panic}, val: Value(Scalar(<ZST>)) }
7777
// ty::Const
7878
// + ty: &str
7979
// + val: Value(Slice { data: Allocation { bytes: [97, 115, 115, 101, 114, 116, 105, 111, 110, 32, 102, 97, 105, 108, 101, 100, 58, 32, 42, 121, 32, 61, 61, 32, 57, 57], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [67108863], len: Size { raw: 26 } }, size: Size { raw: 26 }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 26 })

‎src/test/ui/auxiliary/fancy-panic.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#[macro_export]
2+
macro_rules! fancy_panic {
3+
($msg:expr) => {
4+
panic!($msg)
5+
};
6+
}

‎src/test/ui/fmt/format-args-capture.rs

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ fn panic_with_single_argument_does_not_get_formatted() {
3131
// RFC #2795 suggests that this may need to change so that captured arguments are formatted.
3232
// For stability reasons this will need to part of an edition change.
3333

34+
#[allow(panic_fmt)]
3435
let msg = std::panic::catch_unwind(|| {
3536
panic!("{foo}");
3637
}).unwrap_err();

‎src/test/ui/macros/macro-comma-behavior-rpass.rs

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ fn writeln_1arg() {
5757
//
5858
// (Example: Issue #48042)
5959
#[test]
60+
#[allow(panic_fmt)]
6061
fn to_format_or_not_to_format() {
6162
// ("{}" is the easiest string to test because if this gets
6263
// sent to format_args!, it'll simply fail to compile.

‎src/test/ui/panic-brace.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// build-pass (FIXME(62277): should be check-pass)
2+
// aux-build:fancy-panic.rs
3+
4+
extern crate fancy_panic;
5+
6+
const C: &str = "abc {}";
7+
static S: &str = "{bla}";
8+
9+
#[allow(unreachable_code)]
10+
fn main() {
11+
panic!("here's a brace: {"); //~ WARN panic message contains a brace
12+
std::panic!("another one: }"); //~ WARN panic message contains a brace
13+
core::panic!("Hello {}"); //~ WARN panic message contains an unused formatting placeholder
14+
assert!(false, "{:03x} {test} bla");
15+
//~^ WARN panic message contains unused formatting placeholders
16+
debug_assert!(false, "{{}} bla"); //~ WARN panic message contains braces
17+
panic!(C); // No warning (yet)
18+
panic!(S); // No warning (yet)
19+
panic!(concat!("{", "}")); //~ WARN panic message contains an unused formatting placeholder
20+
panic!(concat!("{", "{")); //~ WARN panic message contains braces
21+
22+
fancy_panic::fancy_panic!("test {} 123");
23+
//~^ WARN panic message contains an unused formatting placeholder
24+
25+
// Check that the lint only triggers for std::panic and core::panic,
26+
// not any panic macro:
27+
macro_rules! panic {
28+
($e:expr) => ();
29+
}
30+
panic!("{}"); // OK
31+
}

‎src/test/ui/panic-brace.stderr

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
warning: panic message contains a brace
2+
--> $DIR/panic-brace.rs:11:29
3+
|
4+
LL | panic!("here's a brace: {");
5+
| ^
6+
|
7+
= note: `#[warn(panic_fmt)]` on by default
8+
= note: this message is not used as a format string, but will be in a future Rust edition
9+
help: add a "{}" format string to use the message literally
10+
|
11+
LL | panic!("{}", "here's a brace: {");
12+
| ^^^^^
13+
14+
warning: panic message contains a brace
15+
--> $DIR/panic-brace.rs:12:31
16+
|
17+
LL | std::panic!("another one: }");
18+
| ^
19+
|
20+
= note: this message is not used as a format string, but will be in a future Rust edition
21+
help: add a "{}" format string to use the message literally
22+
|
23+
LL | std::panic!("{}", "another one: }");
24+
| ^^^^^
25+
26+
warning: panic message contains an unused formatting placeholder
27+
--> $DIR/panic-brace.rs:13:25
28+
|
29+
LL | core::panic!("Hello {}");
30+
| ^^
31+
|
32+
= note: this message is not used as a format string when given without arguments, but will be in a future Rust edition
33+
help: add the missing argument
34+
|
35+
LL | core::panic!("Hello {}", ...);
36+
| ^^^^^
37+
help: or add a "{}" format string to use the message literally
38+
|
39+
LL | core::panic!("{}", "Hello {}");
40+
| ^^^^^
41+
42+
warning: panic message contains unused formatting placeholders
43+
--> $DIR/panic-brace.rs:14:21
44+
|
45+
LL | assert!(false, "{:03x} {test} bla");
46+
| ^^^^^^ ^^^^^^
47+
|
48+
= note: this message is not used as a format string when given without arguments, but will be in a future Rust edition
49+
help: add the missing arguments
50+
|
51+
LL | assert!(false, "{:03x} {test} bla", ...);
52+
| ^^^^^
53+
help: or add a "{}" format string to use the message literally
54+
|
55+
LL | assert!(false, "{}", "{:03x} {test} bla");
56+
| ^^^^^
57+
58+
warning: panic message contains braces
59+
--> $DIR/panic-brace.rs:16:27
60+
|
61+
LL | debug_assert!(false, "{{}} bla");
62+
| ^^^^
63+
|
64+
= note: this message is not used as a format string, but will be in a future Rust edition
65+
help: add a "{}" format string to use the message literally
66+
|
67+
LL | debug_assert!(false, "{}", "{{}} bla");
68+
| ^^^^^
69+
70+
warning: panic message contains an unused formatting placeholder
71+
--> $DIR/panic-brace.rs:19:12
72+
|
73+
LL | panic!(concat!("{", "}"));
74+
| ^^^^^^^^^^^^^^^^^
75+
|
76+
= note: this message is not used as a format string when given without arguments, but will be in a future Rust edition
77+
help: add the missing argument
78+
|
79+
LL | panic!(concat!("{", "}"), ...);
80+
| ^^^^^
81+
help: or add a "{}" format string to use the message literally
82+
|
83+
LL | panic!("{}", concat!("{", "}"));
84+
| ^^^^^
85+
86+
warning: panic message contains braces
87+
--> $DIR/panic-brace.rs:20:5
88+
|
89+
LL | panic!(concat!("{", "{"));
90+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
91+
|
92+
= note: this message is not used as a format string, but will be in a future Rust edition
93+
help: add a "{}" format string to use the message literally
94+
|
95+
LL | panic!("{}", concat!("{", "{"));
96+
| ^^^^^
97+
98+
warning: panic message contains an unused formatting placeholder
99+
--> $DIR/panic-brace.rs:22:37
100+
|
101+
LL | fancy_panic::fancy_panic!("test {} 123");
102+
| ^^
103+
|
104+
= note: this message is not used as a format string when given without arguments, but will be in a future Rust edition
105+
106+
warning: 8 warnings emitted
107+

‎src/tools/clippy/clippy_lints/src/assertions_on_constants.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,11 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
129129
if let ExprKind::Block(ref block, _) = arms[0].body.kind;
130130
if block.stmts.is_empty();
131131
if let Some(block_expr) = &block.expr;
132-
if let ExprKind::Block(ref inner_block, _) = block_expr.kind;
133-
if let Some(begin_panic_call) = &inner_block.expr;
132+
// inner block is optional. unwarp it if it exists, or use the expression as is otherwise.
133+
if let Some(begin_panic_call) = match block_expr.kind {
134+
ExprKind::Block(ref inner_block, _) => &inner_block.expr,
135+
_ => &block.expr,
136+
};
134137
// function call
135138
if let Some(args) = match_panic_call(cx, begin_panic_call);
136139
if args.len() == 1;

‎src/tools/clippy/clippy_lints/src/lib.rs

-3
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
788788
&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
789789
&panic_in_result_fn::PANIC_IN_RESULT_FN,
790790
&panic_unimplemented::PANIC,
791-
&panic_unimplemented::PANIC_PARAMS,
792791
&panic_unimplemented::TODO,
793792
&panic_unimplemented::UNIMPLEMENTED,
794793
&panic_unimplemented::UNREACHABLE,
@@ -1499,7 +1498,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14991498
LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
15001499
LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
15011500
LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
1502-
LintId::of(&panic_unimplemented::PANIC_PARAMS),
15031501
LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
15041502
LintId::of(&precedence::PRECEDENCE),
15051503
LintId::of(&ptr::CMP_NULL),
@@ -1666,7 +1664,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16661664
LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
16671665
LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
16681666
LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
1669-
LintId::of(&panic_unimplemented::PANIC_PARAMS),
16701667
LintId::of(&ptr::CMP_NULL),
16711668
LintId::of(&ptr::PTR_ARG),
16721669
LintId::of(&ptr_eq::PTR_EQ),

‎src/tools/clippy/clippy_lints/src/panic_unimplemented.rs

+4-42
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,10 @@
1-
use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, span_lint};
1+
use crate::utils::{is_expn_of, match_panic_call, span_lint};
22
use if_chain::if_chain;
3-
use rustc_ast::ast::LitKind;
4-
use rustc_hir::{Expr, ExprKind};
3+
use rustc_hir::Expr;
54
use rustc_lint::{LateContext, LateLintPass};
65
use rustc_session::{declare_lint_pass, declare_tool_lint};
76
use rustc_span::Span;
87

9-
declare_clippy_lint! {
10-
/// **What it does:** Checks for missing parameters in `panic!`.
11-
///
12-
/// **Why is this bad?** Contrary to the `format!` family of macros, there are
13-
/// two forms of `panic!`: if there are no parameters given, the first argument
14-
/// is not a format string and used literally. So while `format!("{}")` will
15-
/// fail to compile, `panic!("{}")` will not.
16-
///
17-
/// **Known problems:** None.
18-
///
19-
/// **Example:**
20-
/// ```no_run
21-
/// panic!("This `panic!` is probably missing a parameter there: {}");
22-
/// ```
23-
pub PANIC_PARAMS,
24-
style,
25-
"missing parameters in `panic!` calls"
26-
}
27-
288
declare_clippy_lint! {
299
/// **What it does:** Checks for usage of `panic!`.
3010
///
@@ -89,11 +69,11 @@ declare_clippy_lint! {
8969
"`unreachable!` should not be present in production code"
9070
}
9171

92-
declare_lint_pass!(PanicUnimplemented => [PANIC_PARAMS, UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
72+
declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
9373

9474
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
9575
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
96-
if let Some(params) = match_panic_call(cx, expr) {
76+
if let Some(_) = match_panic_call(cx, expr) {
9777
let span = get_outer_span(expr);
9878
if is_expn_of(expr.span, "unimplemented").is_some() {
9979
span_lint(
@@ -113,7 +93,6 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
11393
);
11494
} else if is_expn_of(expr.span, "panic").is_some() {
11595
span_lint(cx, PANIC, span, "`panic` should not be present in production code");
116-
match_panic(params, expr, cx);
11796
}
11897
}
11998
}
@@ -132,20 +111,3 @@ fn get_outer_span(expr: &Expr<'_>) -> Span {
132111
}
133112
}
134113
}
135-
136-
fn match_panic(params: &[Expr<'_>], expr: &Expr<'_>, cx: &LateContext<'_>) {
137-
if_chain! {
138-
if let ExprKind::Lit(ref lit) = params[0].kind;
139-
if is_direct_expn_of(expr.span, "panic").is_some();
140-
if let LitKind::Str(ref string, _) = lit.node;
141-
let string = string.as_str().replace("{{", "").replace("}}", "");
142-
if let Some(par) = string.find('{');
143-
if string[par..].contains('}');
144-
if params[0].span.source_callee().is_none();
145-
if params[0].span.lo() != params[0].span.hi();
146-
then {
147-
span_lint(cx, PANIC_PARAMS, params[0].span,
148-
"you probably are missing some parameter in your format string");
149-
}
150-
}
151-
}

‎src/tools/clippy/tests/ui/panic.rs

-61
This file was deleted.

‎src/tools/clippy/tests/ui/panic.stderr

-28
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.