Skip to content

Commit ebd5841

Browse files
committed
Partially outline code inside the panic! macro
1 parent 15e52b0 commit ebd5841

File tree

6 files changed

+214
-58
lines changed

6 files changed

+214
-58
lines changed

Diff for: library/core/src/panic.rs

+65
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub macro panic_2015 {
4747
#[allow_internal_unstable(core_panic, const_format_args)]
4848
#[rustc_diagnostic_item = "core_panic_2021_macro"]
4949
#[rustc_macro_transparency = "semitransparent"]
50+
#[cfg(any(bootstrap, feature = "panic_immediate_abort"))]
5051
pub macro panic_2021 {
5152
() => (
5253
$crate::panicking::panic("explicit panic")
@@ -62,6 +63,70 @@ pub macro panic_2021 {
6263
}),
6364
}
6465

66+
#[doc(hidden)]
67+
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
68+
#[allow_internal_unstable(
69+
core_panic,
70+
core_intrinsics,
71+
const_dispatch,
72+
const_eval_select,
73+
const_format_args,
74+
panic_args,
75+
rustc_attrs
76+
)]
77+
#[rustc_diagnostic_item = "core_panic_2021_macro"]
78+
#[rustc_macro_transparency = "semitransparent"]
79+
#[cfg(not(any(bootstrap, feature = "panic_immediate_abort")))]
80+
pub macro panic_2021 {
81+
() => ({
82+
// Create a function so that the argument for `track_caller`
83+
// can be moved inside if possible.
84+
#[cold]
85+
#[track_caller]
86+
#[inline(never)]
87+
const fn panic_cold_explicit() -> ! {
88+
$crate::panicking::panic_explicit()
89+
}
90+
panic_cold_explicit();
91+
}),
92+
// Special-case the single-argument case for const_panic.
93+
("{}", $arg:expr $(,)?) => ({
94+
#[cold]
95+
#[track_caller]
96+
#[inline(never)]
97+
#[rustc_do_not_const_check] // Allow the call to `panic_display`.
98+
const fn panic_cold_display<T: $crate::fmt::Display>(arg: &T) -> ! {
99+
$crate::panicking::panic_display(arg)
100+
}
101+
102+
let arg = &$arg;
103+
104+
// Ensure the caller could call `panic_display` itself directly.
105+
// Const checking will ensure only &str is allowed.
106+
if false {
107+
$crate::panicking::panic_display(arg);
108+
}
109+
110+
// Call `panic_display` directly for const eval since `panic_cold_display` can not be
111+
// evaluated as it is marked with `rustc_do_not_const_check`.
112+
113+
// SAFETY: These branches are observably equivalent as `panic_cold_display` is just an
114+
// indirect way of calling `panic_display`.
115+
unsafe {
116+
$crate::intrinsics::const_eval_select(
117+
(arg,),
118+
$crate::panicking::panic_display,
119+
panic_cold_display
120+
);
121+
}
122+
}),
123+
($($t:tt)+) => ({
124+
// Semicolon to prevent temporaries inside the formatting machinery from
125+
// being considered alive in the caller after the panic_fmt call.
126+
$crate::panicking::panic_fmt($crate::const_format_args!($($t)+));
127+
}),
128+
}
129+
65130
#[doc(hidden)]
66131
#[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
67132
#[allow_internal_unstable(core_panic)]

Diff for: library/core/src/panicking.rs

+8
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ pub const fn panic_str(expr: &str) -> ! {
152152
panic_display(&expr);
153153
}
154154

155+
#[track_caller]
156+
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
157+
#[cfg_attr(feature = "panic_immediate_abort", inline)]
158+
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
159+
pub const fn panic_explicit() -> ! {
160+
panic_display(&"explicit panic");
161+
}
162+
155163
#[inline]
156164
#[track_caller]
157165
#[rustc_diagnostic_item = "unreachable_display"] // needed for `non-fmt-panics` lint

Diff for: src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs

+47-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
22
use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
33
use if_chain::if_chain;
4+
use rustc_ast::LitKind;
45
use rustc_hir::intravisit::{walk_expr, Visitor};
56
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
67
use rustc_lint::{LateContext, LateLintPass};
78
use rustc_middle::ty;
89
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
use rustc_span::source_map::Spanned;
911

1012
declare_clippy_lint! {
1113
/// ### What it does
@@ -134,15 +136,56 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
134136
}
135137
}
136138

139+
fn expr_might_diverge(e: &Expr<'_>) -> bool {
140+
match e.kind {
141+
ExprKind::Path(..) => false,
142+
ExprKind::AddrOf(.., e) => expr_might_diverge(e),
143+
ExprKind::If(
144+
&Expr {
145+
kind:
146+
ExprKind::Lit(Spanned {
147+
node: LitKind::Bool(false),
148+
..
149+
}),
150+
..
151+
},
152+
_,
153+
None,
154+
) => false,
155+
_ => true,
156+
}
157+
}
158+
159+
fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool {
160+
match stmt.kind {
161+
StmtKind::Item(..) => false,
162+
StmtKind::Local(Local {
163+
init: Some(e),
164+
els: None,
165+
..
166+
}) => expr_might_diverge(e),
167+
StmtKind::Expr(e) | StmtKind::Semi(e) => expr_might_diverge(e),
168+
_ => true,
169+
}
170+
}
171+
137172
impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
138173
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
139174
match e.kind {
140175
// fix #10776
141176
ExprKind::Block(block, ..) => match (block.stmts, block.expr) {
142-
([], Some(e)) => self.visit_expr(e),
143-
([stmt], None) => match stmt.kind {
144-
StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e),
145-
_ => {},
177+
(stmts, Some(e)) => {
178+
if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) {
179+
self.visit_expr(e)
180+
}
181+
},
182+
([first @ .., stmt], None) => {
183+
if first.iter().all(|stmt| !stmt_might_diverge(stmt)) {
184+
match stmt.kind {
185+
StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e),
186+
_ => {},
187+
}
188+
}
146189
},
147190
_ => {},
148191
},

Diff for: src/tools/clippy/clippy_utils/src/macros.rs

+74-38
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::visitors::{for_each_expr, Descend};
44

55
use arrayvec::ArrayVec;
6+
use hir::{Block, Local, Stmt, StmtKind};
67
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
78
use rustc_data_structures::fx::FxHashMap;
89
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
@@ -106,12 +107,7 @@ pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
106107
macro_def_id: Some(def_id),
107108
call_site: span,
108109
..
109-
} => Some(MacroCall {
110-
def_id,
111-
kind,
112-
expn,
113-
span,
114-
}),
110+
} => Some(MacroCall { def_id, kind, expn, span }),
115111
_ => None,
116112
})
117113
}
@@ -133,11 +129,14 @@ pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) ->
133129

134130
/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
135131
/// macro call, as in [`first_node_in_macro`].
136-
pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
132+
pub fn first_node_macro_backtrace(
133+
cx: &LateContext<'_>,
134+
node: &impl HirNode,
135+
) -> impl Iterator<Item = MacroCall> {
137136
let span = node.span();
138-
first_node_in_macro(cx, node)
139-
.into_iter()
140-
.flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
137+
first_node_in_macro(cx, node).into_iter().flat_map(move |expn| {
138+
macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn)
139+
})
141140
}
142141

143142
/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
@@ -222,17 +221,54 @@ pub enum PanicExpn<'a> {
222221
/// A single argument that implements `Display` - `panic!("{}", object)`
223222
Display(&'a Expr<'a>),
224223
/// Anything else - `panic!("error {}: {}", a, b)`
225-
Format(&'a Expr<'a>),
224+
Format(Option<&'a Expr<'a>>),
226225
}
227226

228227
impl<'a> PanicExpn<'a> {
229228
pub fn parse(expr: &'a Expr<'a>) -> Option<Self> {
230-
let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else {
229+
// Pattern match on the special single-argument case for const_panic in `panic_2021`.
230+
if let ExprKind::Block(
231+
&Block {
232+
stmts:
233+
[
234+
Stmt { kind: StmtKind::Item(..), .. },
235+
Stmt {
236+
kind:
237+
StmtKind::Local(&Local {
238+
init: Some(&Expr { kind: ExprKind::AddrOf(_, _, arg), .. }),
239+
..
240+
}),
241+
..
242+
},
243+
Stmt { kind: StmtKind::Expr(&Expr { kind: ExprKind::If(..), .. }), .. },
244+
..,
245+
],
246+
..
247+
},
248+
..,
249+
) = &expr.kind
250+
{
251+
return Some(Self::Display(arg));
252+
}
253+
254+
let ExprKind::Call(callee, args) = &expr.kind else {
231255
return None;
232256
};
233257
let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else {
234258
return None;
235259
};
260+
let name = path.segments.last().unwrap().ident.as_str();
261+
262+
// These may have no arguments
263+
match name {
264+
"panic_cold_explicit" => return Some(Self::Empty),
265+
"panic_cold" => return Some(Self::Format(None)),
266+
_ => (),
267+
};
268+
269+
let [arg, rest @ ..] = args else {
270+
return None;
271+
};
236272
let result = match path.segments.last().unwrap().ident.as_str() {
237273
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
238274
"panic" | "panic_str" => Self::Str(arg),
@@ -241,8 +277,8 @@ impl<'a> PanicExpn<'a> {
241277
return None;
242278
};
243279
Self::Display(e)
244-
},
245-
"panic_fmt" => Self::Format(arg),
280+
}
281+
"panic_fmt" => Self::Format(Some(arg)),
246282
// Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
247283
// `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
248284
"assert_failed" => {
@@ -254,10 +290,10 @@ impl<'a> PanicExpn<'a> {
254290
// `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
255291
let msg_arg = &rest[2];
256292
match msg_arg.kind {
257-
ExprKind::Call(_, [fmt_arg]) => Self::Format(fmt_arg),
293+
ExprKind::Call(_, [fmt_arg]) => Self::Format(Some(fmt_arg)),
258294
_ => Self::Empty,
259295
}
260-
},
296+
}
261297
_ => return None,
262298
};
263299
Some(result)
@@ -301,7 +337,9 @@ fn find_assert_args_inner<'a, const N: usize>(
301337
let macro_id = expn.expn_data().macro_def_id?;
302338
let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
303339
None => (expr, expn),
304-
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
340+
Some(inner_name) => {
341+
find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?
342+
}
305343
};
306344
let mut args = ArrayVec::new();
307345
let panic_expn = for_each_expr(expr, |e| {
@@ -337,7 +375,9 @@ fn find_assert_within_debug_assert<'a>(
337375
let e_expn = e.span.ctxt().outer_expn();
338376
if e_expn == expn {
339377
ControlFlow::Continue(Descend::Yes)
340-
} else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
378+
} else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id))
379+
== Some(assert_name)
380+
{
341381
ControlFlow::Break((e, e_expn))
342382
} else {
343383
ControlFlow::Continue(Descend::No)
@@ -395,14 +435,20 @@ pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
395435

396436
/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
397437
/// descendant of `expn_id`
398-
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
438+
pub fn find_format_args(
439+
cx: &LateContext<'_>,
440+
start: &Expr<'_>,
441+
expn_id: ExpnId,
442+
callback: impl FnOnce(&FormatArgs),
443+
) {
399444
let format_args_expr = for_each_expr(start, |expr| {
400445
let ctxt = expr.span.ctxt();
401446
if ctxt.outer_expn().is_descendant_of(expn_id) {
402-
if macro_backtrace(expr.span)
403-
.map(|macro_call| cx.tcx.item_name(macro_call.def_id))
404-
.any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
405-
{
447+
if macro_backtrace(expr.span).map(|macro_call| cx.tcx.item_name(macro_call.def_id)).any(
448+
|name| {
449+
matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)
450+
},
451+
) {
406452
ControlFlow::Break(expr)
407453
} else {
408454
ControlFlow::Continue(Descend::Yes)
@@ -425,12 +471,7 @@ pub fn find_format_arg_expr<'hir, 'ast>(
425471
start: &'hir Expr<'hir>,
426472
target: &'ast FormatArgument,
427473
) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> {
428-
let SpanData {
429-
lo,
430-
hi,
431-
ctxt,
432-
parent: _,
433-
} = target.expr.span.data();
474+
let SpanData { lo, hi, ctxt, parent: _ } = target.expr.span.data();
434475

435476
for_each_expr(start, |expr| {
436477
// When incremental compilation is enabled spans gain a parent during AST to HIR lowering,
@@ -456,12 +497,7 @@ pub fn format_placeholder_format_span(placeholder: &FormatPlaceholder) -> Option
456497

457498
// `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
458499
// brace `{...|}`
459-
Some(Span::new(
460-
placeholder.argument.span?.hi(),
461-
base.hi - BytePos(1),
462-
base.ctxt,
463-
base.parent,
464-
))
500+
Some(Span::new(placeholder.argument.span?.hi(), base.hi - BytePos(1), base.ctxt, base.parent))
465501
}
466502

467503
/// Span covering the format string and values
@@ -473,9 +509,9 @@ pub fn format_placeholder_format_span(placeholder: &FormatPlaceholder) -> Option
473509
pub fn format_args_inputs_span(format_args: &FormatArgs) -> Span {
474510
match format_args.arguments.explicit_args() {
475511
[] => format_args.span,
476-
[.., last] => format_args
477-
.span
478-
.to(hygiene::walk_chain(last.expr.span, format_args.span.ctxt())),
512+
[.., last] => {
513+
format_args.span.to(hygiene::walk_chain(last.expr.span, format_args.span.ctxt()))
514+
}
479515
}
480516
}
481517

0 commit comments

Comments
 (0)