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