1use rustc_ast::token::{self, Delimiter, IdentIsRaw};
2use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
3use rustc_ast::{
4 BinOpKind, BorrowKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, ItemKind, MacCall, MethodCall,
5 Mutability, Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind,
6};
7use rustc_ast_pretty::pprust;
8use rustc_data_structures::fx::FxHashSet;
9use rustc_expand::base::ExtCtxt;
10use rustc_span::{Ident, Span, Symbol, sym};
11use thin_vec::{ThinVec, thin_vec};
12
13pub(super) struct Context<'cx, 'a> {
14 best_case_captures: Vec<Stmt>,
19 capture_decls: Vec<Capture>,
21 cx: &'cx ExtCtxt<'a>,
22 fmt_string: String,
24 is_consumed: bool,
27 local_bind_decls: Vec<Stmt>,
29 paths: FxHashSet<Ident>,
36 span: Span,
37}
38
39impl<'cx, 'a> Context<'cx, 'a> {
40 pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {
41 Self {
42 best_case_captures: <_>::default(),
43 capture_decls: <_>::default(),
44 cx,
45 fmt_string: <_>::default(),
46 is_consumed: true,
47 local_bind_decls: <_>::default(),
48 paths: <_>::default(),
49 span,
50 }
51 }
52
53 pub(super) fn build(mut self, mut cond_expr: Box<Expr>, panic_path: Path) -> Box<Expr> {
73 let expr_str = pprust::expr_to_string(&cond_expr);
74 self.manage_cond_expr(&mut cond_expr);
75 let initial_imports = self.build_initial_imports();
76 let panic = self.build_panic(&expr_str, panic_path);
77 let cond_expr_with_unlikely = self.build_unlikely(cond_expr);
78
79 let Self { best_case_captures, capture_decls, cx, local_bind_decls, span, .. } = self;
80
81 let mut assert_then_stmts = ThinVec::with_capacity(2);
82 assert_then_stmts.extend(best_case_captures);
83 assert_then_stmts.push(self.cx.stmt_expr(panic));
84 let assert_then = self.cx.block(span, assert_then_stmts);
85
86 let mut stmts = ThinVec::with_capacity(4);
87 stmts.push(initial_imports);
88 stmts.extend(capture_decls.into_iter().map(|c| c.decl));
89 stmts.extend(local_bind_decls);
90 stmts.push(
91 cx.stmt_expr(cx.expr(span, ExprKind::If(cond_expr_with_unlikely, assert_then, None))),
92 );
93 cx.expr_block(cx.block(span, stmts))
94 }
95
96 fn build_initial_imports(&self) -> Stmt {
100 let nested_tree = |this: &Self, sym| {
101 (
102 UseTree {
103 prefix: this.cx.path(this.span, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Ident::with_dummy_span(sym)]))vec![Ident::with_dummy_span(sym)]),
104 kind: UseTreeKind::Simple(None),
105 },
106 DUMMY_NODE_ID,
107 )
108 };
109 self.cx.stmt_item(
110 self.span,
111 self.cx.item(
112 self.span,
113 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(self.cx.attr_nested_word(sym::allow, sym::unused_imports,
self.span));
vec
}thin_vec![self.cx.attr_nested_word(sym::allow, sym::unused_imports, self.span)],
114 ItemKind::Use(UseTree {
115 prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
116 kind: UseTreeKind::Nested {
117 items: {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(nested_tree(self, sym::TryCaptureGeneric));
vec.push(nested_tree(self, sym::TryCapturePrintable));
vec
}thin_vec![
118 nested_tree(self, sym::TryCaptureGeneric),
119 nested_tree(self, sym::TryCapturePrintable),
120 ],
121 span: self.span,
122 },
123 }),
124 ),
125 )
126 }
127
128 fn build_unlikely(&self, cond_expr: Box<Expr>) -> Box<Expr> {
130 let unlikely_path = self.cx.std_path(&[sym::intrinsics, sym::unlikely]);
131 self.cx.expr_call(
132 self.span,
133 self.cx.expr_path(self.cx.path(self.span, unlikely_path)),
134 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(self.cx.expr(self.span, ExprKind::Unary(UnOp::Not, cond_expr)));
vec
}thin_vec![self.cx.expr(self.span, ExprKind::Unary(UnOp::Not, cond_expr))],
135 )
136 }
137
138 fn build_panic(&self, expr_str: &str, panic_path: Path) -> Box<Expr> {
146 let escaped_expr_str = escape_to_fmt(expr_str);
147 let initial = [
148 TokenTree::token_joint(
149 token::Literal(token::Lit {
150 kind: token::LitKind::Str,
151 symbol: Symbol::intern(&if self.fmt_string.is_empty() {
152 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Assertion failed: {0}",
escaped_expr_str))
})format!("Assertion failed: {escaped_expr_str}")
153 } else {
154 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Assertion failed: {1}\nWith captures:\n{0}",
&self.fmt_string, escaped_expr_str))
})format!(
155 "Assertion failed: {escaped_expr_str}\nWith captures:\n{}",
156 &self.fmt_string
157 )
158 }),
159 suffix: None,
160 }),
161 self.span,
162 ),
163 TokenTree::token_alone(token::Comma, self.span),
164 ];
165 let captures = self.capture_decls.iter().flat_map(|cap| {
166 [
167 TokenTree::token_joint(
168 token::Ident(cap.ident.name, IdentIsRaw::No),
169 cap.ident.span,
170 ),
171 TokenTree::token_alone(token::Comma, self.span),
172 ]
173 });
174 self.cx.expr(
175 self.span,
176 ExprKind::MacCall(Box::new(MacCall {
177 path: panic_path,
178 args: Box::new(DelimArgs {
179 dspan: DelimSpan::from_single(self.span),
180 delim: Delimiter::Parenthesis,
181 tokens: initial.into_iter().chain(captures).collect::<TokenStream>(),
182 }),
183 })),
184 )
185 }
186
187 fn manage_cond_expr(&mut self, expr: &mut Box<Expr>) {
191 match &mut expr.kind {
192 ExprKind::AddrOf(_, mutability, local_expr) => {
193 self.with_is_consumed_management(#[allow(non_exhaustive_omitted_patterns)] match mutability {
Mutability::Mut => true,
_ => false,
}matches!(mutability, Mutability::Mut), |this| {
194 this.manage_cond_expr(local_expr)
195 });
196 }
197 ExprKind::Array(local_exprs) => {
198 for local_expr in local_exprs {
199 self.manage_cond_expr(local_expr);
200 }
201 }
202 ExprKind::Binary(op, lhs, rhs) => {
203 self.with_is_consumed_management(
204 #[allow(non_exhaustive_omitted_patterns)] match op.node {
BinOpKind::Add | BinOpKind::And | BinOpKind::BitAnd | BinOpKind::BitOr |
BinOpKind::BitXor | BinOpKind::Div | BinOpKind::Mul | BinOpKind::Or |
BinOpKind::Rem | BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub =>
true,
_ => false,
}matches!(
205 op.node,
206 BinOpKind::Add
207 | BinOpKind::And
208 | BinOpKind::BitAnd
209 | BinOpKind::BitOr
210 | BinOpKind::BitXor
211 | BinOpKind::Div
212 | BinOpKind::Mul
213 | BinOpKind::Or
214 | BinOpKind::Rem
215 | BinOpKind::Shl
216 | BinOpKind::Shr
217 | BinOpKind::Sub
218 ),
219 |this| {
220 this.manage_cond_expr(lhs);
221 this.manage_cond_expr(rhs);
222 },
223 );
224 }
225 ExprKind::Call(_, local_exprs) => {
226 for local_expr in local_exprs {
227 self.manage_cond_expr(local_expr);
228 }
229 }
230 ExprKind::Cast(local_expr, _) => {
231 self.manage_cond_expr(local_expr);
232 }
233 ExprKind::If(local_expr, _, _) => {
234 self.manage_cond_expr(local_expr);
235 }
236 ExprKind::Index(prefix, suffix, _) => {
237 self.manage_cond_expr(prefix);
238 self.manage_cond_expr(suffix);
239 }
240 ExprKind::Let(_, local_expr, _, _) => {
241 self.manage_cond_expr(local_expr);
242 }
243 ExprKind::Match(local_expr, ..) => {
244 self.manage_cond_expr(local_expr);
245 }
246 ExprKind::MethodCall(call) => {
247 for arg in &mut call.args {
248 self.manage_cond_expr(arg);
249 }
250 }
251 ExprKind::Path(_, Path { segments, .. }) if let [path_segment] = &segments[..] => {
252 let path_ident = path_segment.ident;
253 self.manage_initial_capture(expr, path_ident);
254 }
255 ExprKind::Paren(local_expr) => {
256 self.manage_cond_expr(local_expr);
257 }
258 ExprKind::Range(prefix, suffix, _) => {
259 if let Some(elem) = prefix {
260 self.manage_cond_expr(elem);
261 }
262 if let Some(elem) = suffix {
263 self.manage_cond_expr(elem);
264 }
265 }
266 ExprKind::Repeat(local_expr, elem) => {
267 self.manage_cond_expr(local_expr);
268 self.manage_cond_expr(&mut elem.value);
269 }
270 ExprKind::Struct(elem) => {
271 for field in &mut elem.fields {
272 self.manage_cond_expr(&mut field.expr);
273 }
274 if let StructRest::Base(local_expr) = &mut elem.rest {
275 self.manage_cond_expr(local_expr);
276 }
277 }
278 ExprKind::Tup(local_exprs) => {
279 for local_expr in local_exprs {
280 self.manage_cond_expr(local_expr);
281 }
282 }
283 ExprKind::Unary(un_op, local_expr) => {
284 self.with_is_consumed_management(#[allow(non_exhaustive_omitted_patterns)] match un_op {
UnOp::Neg | UnOp::Not => true,
_ => false,
}matches!(un_op, UnOp::Neg | UnOp::Not), |this| {
285 this.manage_cond_expr(local_expr)
286 });
287 }
288 ExprKind::Assign(_, _, _)
293 | ExprKind::AssignOp(_, _, _)
294 | ExprKind::Gen(_, _, _, _)
295 | ExprKind::Await(_, _)
296 | ExprKind::Use(_, _)
297 | ExprKind::Block(_, _)
298 | ExprKind::Break(_, _)
299 | ExprKind::Closure(_)
300 | ExprKind::ConstBlock(_)
301 | ExprKind::Continue(_)
302 | ExprKind::Dummy
303 | ExprKind::Err(_)
304 | ExprKind::Field(_, _)
305 | ExprKind::ForLoop { .. }
306 | ExprKind::FormatArgs(_)
307 | ExprKind::IncludedBytes(..)
308 | ExprKind::InlineAsm(_)
309 | ExprKind::Lit(_)
310 | ExprKind::Loop(_, _, _)
311 | ExprKind::MacCall(_)
312 | ExprKind::OffsetOf(_, _)
313 | ExprKind::Path(_, _)
314 | ExprKind::Ret(_)
315 | ExprKind::Try(_)
316 | ExprKind::TryBlock(_, _)
317 | ExprKind::Type(_, _)
318 | ExprKind::Underscore
319 | ExprKind::While(_, _, _)
320 | ExprKind::Yeet(_)
321 | ExprKind::Become(_)
322 | ExprKind::Yield(_)
323 | ExprKind::UnsafeBinderCast(..) => {}
324 }
325 }
326
327 fn manage_initial_capture(&mut self, expr: &mut Box<Expr>, path_ident: Ident) {
332 if self.paths.contains(&path_ident) {
333 return;
334 } else {
335 self.fmt_string.push_str(" ");
336 self.fmt_string.push_str(path_ident.as_str());
337 self.fmt_string.push_str(" = {:?}\n");
338 let _ = self.paths.insert(path_ident);
339 }
340 let curr_capture_idx = self.capture_decls.len();
341 let capture_string = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("__capture{0}", curr_capture_idx))
})format!("__capture{curr_capture_idx}");
342 let ident = Ident::new(Symbol::intern(&capture_string), self.span);
343 let init_std_path = self.cx.std_path(&[sym::asserting, sym::Capture, sym::new]);
344 let init = self.cx.expr_call(
345 self.span,
346 self.cx.expr_path(self.cx.path(self.span, init_std_path)),
347 ThinVec::new(),
348 );
349 let capture = Capture { decl: self.cx.stmt_let(self.span, true, ident, init), ident };
350 self.capture_decls.push(capture);
351 self.manage_try_capture(ident, curr_capture_idx, expr);
352 }
353
354 fn manage_try_capture(
361 &mut self,
362 capture: Ident,
363 curr_capture_idx: usize,
364 expr: &mut Box<Expr>,
365 ) {
366 let local_bind_string = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("__local_bind{0}",
curr_capture_idx))
})format!("__local_bind{curr_capture_idx}");
367 let local_bind = Ident::new(Symbol::intern(&local_bind_string), self.span);
368 self.local_bind_decls.push(self.cx.stmt_let(
369 self.span,
370 false,
371 local_bind,
372 self.cx.expr_addr_of(self.span, expr.clone()),
373 ));
374 let wrapper = self.cx.expr_call(
375 self.span,
376 self.cx.expr_path(
377 self.cx.path(self.span, self.cx.std_path(&[sym::asserting, sym::Wrapper])),
378 ),
379 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(self.cx.expr_path(Path::from_ident(local_bind)));
vec
}thin_vec![self.cx.expr_path(Path::from_ident(local_bind))],
380 );
381 let try_capture_call = self
382 .cx
383 .stmt_expr(expr_method_call(
384 self.cx,
385 PathSegment {
386 args: None,
387 id: DUMMY_NODE_ID,
388 ident: Ident::new(sym::try_capture, self.span),
389 },
390 expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)),
391 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(expr_addr_of_mut(self.cx, self.span,
self.cx.expr_path(Path::from_ident(capture))));
vec
}thin_vec![expr_addr_of_mut(
392 self.cx,
393 self.span,
394 self.cx.expr_path(Path::from_ident(capture)),
395 )],
396 self.span,
397 ))
398 .add_trailing_semicolon();
399 let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind));
400 let rslt = if self.is_consumed {
401 let ret = self.cx.stmt_expr(local_bind_path);
402 self.cx.expr_block(self.cx.block(self.span, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(try_capture_call);
vec.push(ret);
vec
}thin_vec![try_capture_call, ret]))
403 } else {
404 self.best_case_captures.push(try_capture_call);
405 local_bind_path
406 };
407 *expr = self.cx.expr_deref(self.span, rslt);
408 }
409
410 fn with_is_consumed_management(&mut self, curr_is_consumed: bool, f: impl FnOnce(&mut Self)) {
413 let prev_is_consumed = self.is_consumed;
414 self.is_consumed = curr_is_consumed;
415 f(self);
416 self.is_consumed = prev_is_consumed;
417 }
418}
419
420#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Capture {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "Capture",
"decl", &self.decl, "ident", &&self.ident)
}
}Debug)]
422struct Capture {
423 decl: Stmt,
427 ident: Ident,
431}
432
433fn escape_to_fmt(s: &str) -> String {
435 let mut rslt = String::with_capacity(s.len());
436 for c in s.chars() {
437 rslt.extend(c.escape_debug());
438 match c {
439 '{' | '}' => rslt.push(c),
440 _ => {}
441 }
442 }
443 rslt
444}
445
446fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: Box<Expr>) -> Box<Expr> {
447 cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e))
448}
449
450fn expr_method_call(
451 cx: &ExtCtxt<'_>,
452 seg: PathSegment,
453 receiver: Box<Expr>,
454 args: ThinVec<Box<Expr>>,
455 span: Span,
456) -> Box<Expr> {
457 cx.expr(span, ExprKind::MethodCall(Box::new(MethodCall { seg, receiver, args, span })))
458}
459
460fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: Box<Expr>) -> Box<Expr> {
461 cx.expr(sp, ExprKind::Paren(e))
462}