rustc_builtin_macros/
assert.rs1mod context;
2
3use rustc_ast::token::{self, Delimiter};
4use rustc_ast::tokenstream::{DelimSpan, TokenStream};
5use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment};
6use rustc_ast_pretty::pprust;
7use rustc_errors::PResult;
8use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
9use rustc_parse::exp;
10use rustc_parse::parser::Parser;
11use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
12use thin_vec::thin_vec;
13
14use crate::edition_panic::use_panic_2021;
15use crate::errors;
16
17pub(crate) fn expand_assert<'cx>(
18 cx: &'cx mut ExtCtxt<'_>,
19 span: Span,
20 tts: TokenStream,
21) -> MacroExpanderResult<'cx> {
22 let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
23 Ok(assert) => assert,
24 Err(err) => {
25 let guar = err.emit();
26 return ExpandResult::Ready(DummyResult::any(span, guar));
27 }
28 };
29
30 let call_site_span = cx.with_call_site_ctxt(cond_expr.span);
33
34 let panic_path = || {
35 if use_panic_2021(span) {
36 Path {
38 span: call_site_span,
39 segments: cx
40 .std_path(&[sym::panic, sym::panic_2021])
41 .into_iter()
42 .map(|ident| PathSegment::from_ident(ident))
43 .collect(),
44 tokens: None,
45 }
46 } else {
47 Path::from_ident(Ident::new(sym::panic, call_site_span))
50 }
51 };
52
53 let expr = if let Some(tokens) = custom_message {
55 let then = cx.expr(
56 call_site_span,
57 ExprKind::MacCall(Box::new(MacCall {
58 path: panic_path(),
59 args: Box::new(DelimArgs {
60 dspan: DelimSpan::from_single(call_site_span),
61 delim: Delimiter::Parenthesis,
62 tokens,
63 }),
64 })),
65 );
66 assert_cond_check(cx, call_site_span, cond_expr, then)
67 }
68 else if cx.ecfg.features.generic_assert() {
72 context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
73 }
74 else {
77 let then = cx.expr_call_global(
81 call_site_span,
82 cx.std_path(&[sym::panicking, sym::panic]),
83 thin_vec![cx.expr_str(
84 DUMMY_SP,
85 Symbol::intern(&format!(
86 "assertion failed: {}",
87 pprust::expr_to_string(&cond_expr)
88 )),
89 )],
90 );
91 assert_cond_check(cx, call_site_span, cond_expr, then)
92 };
93
94 ExpandResult::Ready(MacEager::expr(expr))
95}
96
97struct Assert {
99 cond_expr: Box<Expr>,
100 custom_message: Option<TokenStream>,
101}
102
103fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> {
105 let els = cx.expr_block(cx.block(span, thin_vec![]));
111 let mut arms = thin_vec![];
112 arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els));
113 arms.push(cx.arm(span, cx.pat_wild(span), then));
114
115 cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into()))
118}
119
120fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
121 let mut parser = cx.new_parser_from_tts(stream);
122
123 if parser.token == token::Eof {
124 return Err(cx.dcx().create_err(errors::AssertRequiresBoolean { span: sp }));
125 }
126
127 let cond_expr = parser.parse_expr()?;
128
129 if parser.token == token::Semi {
137 cx.dcx().emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span });
138 parser.bump();
139 }
140
141 let custom_message =
148 if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
149 let comma = parser.prev_token.span.shrink_to_hi();
150 cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
151
152 parse_custom_message(&mut parser)
153 } else if parser.eat(exp!(Comma)) {
154 parse_custom_message(&mut parser)
155 } else {
156 None
157 };
158
159 if parser.token != token::Eof {
160 parser.unexpected()?;
161 }
162
163 Ok(Assert { cond_expr, custom_message })
164}
165
166fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
167 let ts = parser.parse_tokens();
168 if !ts.is_empty() { Some(ts) } else { None }
169}