rustc_builtin_macros/
assert.rs
1mod context;
2
3use rustc_ast::ptr::P;
4use rustc_ast::token::Delimiter;
5use rustc_ast::tokenstream::{DelimSpan, TokenStream};
6use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token};
7use rustc_ast_pretty::pprust;
8use rustc_errors::PResult;
9use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
10use rustc_parse::exp;
11use rustc_parse::parser::Parser;
12use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
13use thin_vec::thin_vec;
14
15use crate::edition_panic::use_panic_2021;
16use crate::errors;
17
18pub(crate) fn expand_assert<'cx>(
19 cx: &'cx mut ExtCtxt<'_>,
20 span: Span,
21 tts: TokenStream,
22) -> MacroExpanderResult<'cx> {
23 let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
24 Ok(assert) => assert,
25 Err(err) => {
26 let guar = err.emit();
27 return ExpandResult::Ready(DummyResult::any(span, guar));
28 }
29 };
30
31 let call_site_span = cx.with_call_site_ctxt(span);
34
35 let panic_path = || {
36 if use_panic_2021(span) {
37 Path {
39 span: call_site_span,
40 segments: cx
41 .std_path(&[sym::panic, sym::panic_2021])
42 .into_iter()
43 .map(|ident| PathSegment::from_ident(ident))
44 .collect(),
45 tokens: None,
46 }
47 } else {
48 Path::from_ident(Ident::new(sym::panic, call_site_span))
51 }
52 };
53
54 let expr = if let Some(tokens) = custom_message {
56 let then = cx.expr(
57 call_site_span,
58 ExprKind::MacCall(P(MacCall {
59 path: panic_path(),
60 args: P(DelimArgs {
61 dspan: DelimSpan::from_single(call_site_span),
62 delim: Delimiter::Parenthesis,
63 tokens,
64 }),
65 })),
66 );
67 expr_if_not(cx, call_site_span, cond_expr, then, None)
68 }
69 else if cx.ecfg.features.generic_assert() {
73 context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
74 }
75 else {
78 let then = cx.expr_call_global(
82 call_site_span,
83 cx.std_path(&[sym::panicking, sym::panic]),
84 thin_vec![cx.expr_str(
85 DUMMY_SP,
86 Symbol::intern(&format!(
87 "assertion failed: {}",
88 pprust::expr_to_string(&cond_expr)
89 )),
90 )],
91 );
92 expr_if_not(cx, call_site_span, cond_expr, then, None)
93 };
94
95 ExpandResult::Ready(MacEager::expr(expr))
96}
97
98struct Assert {
99 cond_expr: P<Expr>,
100 custom_message: Option<TokenStream>,
101}
102
103fn expr_if_not(
105 cx: &ExtCtxt<'_>,
106 span: Span,
107 cond: P<Expr>,
108 then: P<Expr>,
109 els: Option<P<Expr>>,
110) -> P<Expr> {
111 cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
112}
113
114fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
115 let mut parser = cx.new_parser_from_tts(stream);
116
117 if parser.token == token::Eof {
118 return Err(cx.dcx().create_err(errors::AssertRequiresBoolean { span: sp }));
119 }
120
121 let cond_expr = parser.parse_expr()?;
122
123 if parser.token == token::Semi {
131 cx.dcx().emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span });
132 parser.bump();
133 }
134
135 let custom_message =
142 if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
143 let comma = parser.prev_token.span.shrink_to_hi();
144 cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
145
146 parse_custom_message(&mut parser)
147 } else if parser.eat(exp!(Comma)) {
148 parse_custom_message(&mut parser)
149 } else {
150 None
151 };
152
153 if parser.token != token::Eof {
154 parser.unexpected()?;
155 }
156
157 Ok(Assert { cond_expr, custom_message })
158}
159
160fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
161 let ts = parser.parse_tokens();
162 if !ts.is_empty() { Some(ts) } else { None }
163}