rustc_builtin_macros/
assert.rs1mod context;
2
3use rustc_ast::token::Delimiter;
4use rustc_ast::tokenstream::TokenStream;
5use rustc_ast::{Expr, ExprKind, Path, UnOp, token};
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::diagnostics;
15use crate::edition_panic::use_panic_2021;
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_err();
26 return ExpandResult::Ready(DummyResult::any(span, guar));
27 }
28 };
29
30 let call_site_span = cx.with_call_site_ctxt(span);
33
34 let panic_path = || {
35 if use_panic_2021(span) {
36 cx.path(call_site_span, cx.std_path(&[sym::panic, sym::panic_2021]))
38 } else {
39 Path::from_ident(Ident::new(sym::panic, call_site_span))
42 }
43 };
44
45 let expr = if let Some(tokens) = custom_message {
47 let then = cx.expr_macro_call(
48 call_site_span,
49 cx.macro_call(call_site_span, panic_path(), Delimiter::Parenthesis, tokens),
50 );
51 expr_if_not(cx, call_site_span, cond_expr, then, None)
52 }
53 else if cx.ecfg.features.generic_assert() {
57 context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
58 }
59 else {
62 let then = cx.expr_call_global(
66 call_site_span,
67 cx.std_path(&[sym::panicking, sym::panic]),
68 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_str(DUMMY_SP,
Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("assertion failed: {0}",
pprust::expr_to_string(&cond_expr)))
}))));
vec
}thin_vec![cx.expr_str(
69 DUMMY_SP,
70 Symbol::intern(&format!(
71 "assertion failed: {}",
72 pprust::expr_to_string(&cond_expr)
73 )),
74 )],
75 );
76 expr_if_not(cx, call_site_span, cond_expr, then, None)
77 };
78
79 ExpandResult::Ready(MacEager::expr(expr))
80}
81
82struct Assert {
83 cond_expr: Box<Expr>,
84 custom_message: Option<TokenStream>,
85}
86
87fn expr_if_not(
89 cx: &ExtCtxt<'_>,
90 span: Span,
91 cond: Box<Expr>,
92 then: Box<Expr>,
93 els: Option<Box<Expr>>,
94) -> Box<Expr> {
95 cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
96}
97
98fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
99 let mut parser = cx.new_parser_from_tts(stream);
100
101 if parser.token == token::Eof {
102 return Err(cx.dcx().create_err(diagnostics::AssertRequiresBoolean { span: sp }));
103 }
104
105 let cond_expr = parser.parse_expr()?;
106
107 if parser.token == token::Semi {
115 cx.dcx()
116 .emit_err(diagnostics::AssertRequiresExpression { span: sp, token: parser.token.span });
117 parser.bump();
118 }
119
120 let custom_message =
127 if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
128 let comma = parser.prev_token.span.shrink_to_hi();
129 cx.dcx().emit_err(diagnostics::AssertMissingComma { span: parser.token.span, comma });
130
131 parse_custom_message(&mut parser)
132 } else if parser.eat(::rustc_parse::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Comma,
token_type: ::rustc_parse::parser::token_type::TokenType::Comma,
}exp!(Comma)) {
133 parse_custom_message(&mut parser)
134 } else {
135 None
136 };
137
138 if parser.token != token::Eof {
139 parser.unexpected()?;
140 }
141
142 Ok(Assert { cond_expr, custom_message })
143}
144
145fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
146 let ts = parser.parse_tokens();
147 if !ts.is_empty() { Some(ts) } else { None }
148}