]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_builtin_macros/src/assert.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / compiler / rustc_builtin_macros / src / assert.rs
CommitLineData
923072b8
FG
1mod context;
2
a2a8927a 3use crate::edition_panic::use_panic_2021;
353b0b11 4use crate::errors;
74b04a01 5use rustc_ast::ptr::P;
fc512014 6use rustc_ast::token;
add651ee 7use rustc_ast::token::Delimiter;
fc512014 8use rustc_ast::tokenstream::{DelimSpan, TokenStream};
add651ee 9use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp};
74b04a01 10use rustc_ast_pretty::pprust;
353b0b11 11use rustc_errors::PResult;
c620b35d 12use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
60c5eb7d 13use rustc_parse::parser::Parser;
f9f354fc 14use rustc_span::symbol::{sym, Ident, Symbol};
dfeec247 15use rustc_span::{Span, DUMMY_SP};
9ffffee4 16use thin_vec::thin_vec;
0531ce1d 17
e8be2606 18pub(crate) fn expand_assert<'cx>(
9fa01778 19 cx: &'cx mut ExtCtxt<'_>,
5869c6ff 20 span: Span,
e1599b0c 21 tts: TokenStream,
c620b35d 22) -> MacroExpanderResult<'cx> {
5869c6ff 23 let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
0731742a 24 Ok(assert) => assert,
c0240ec0 25 Err(err) => {
c620b35d
FG
26 let guar = err.emit();
27 return ExpandResult::Ready(DummyResult::any(span, guar));
0531ce1d 28 }
0531ce1d
XL
29 };
30
e1599b0c
XL
31 // `core::panic` and `std::panic` are different macros, so we use call-site
32 // context to pick up whichever is currently in scope.
923072b8 33 let call_site_span = cx.with_call_site_ctxt(span);
fc512014 34
923072b8
FG
35 let panic_path = || {
36 if use_panic_2021(span) {
5869c6ff
XL
37 // On edition 2021, we always call `$crate::panic::panic_2021!()`.
38 Path {
923072b8 39 span: call_site_span,
5869c6ff
XL
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 // Before edition 2021, we call `panic!()` unqualified,
49 // such that it calls either `std::panic!()` or `core::panic!()`.
923072b8
FG
50 Path::from_ident(Ident::new(sym::panic, call_site_span))
51 }
52 };
53
54 // Simply uses the user provided message instead of generating custom outputs
55 let expr = if let Some(tokens) = custom_message {
56 let then = cx.expr(
57 call_site_span,
f2b60f7d 58 ExprKind::MacCall(P(MacCall {
923072b8 59 path: panic_path(),
487cf647
FG
60 args: P(DelimArgs {
61 dspan: DelimSpan::from_single(call_site_span),
add651ee 62 delim: Delimiter::Parenthesis,
fc512014 63 tokens,
487cf647 64 }),
f2b60f7d 65 })),
923072b8
FG
66 );
67 expr_if_not(cx, call_site_span, cond_expr, then, None)
68 }
69 // If `generic_assert` is enabled, generates rich captured outputs
70 //
71 // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
add651ee 72 else if cx.ecfg.features.generic_assert {
923072b8
FG
73 context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
74 }
75 // If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
76 // string
77 else {
fc512014
XL
78 // Pass our own message directly to $crate::panicking::panic(),
79 // because it might contain `{` and `}` that should always be
80 // passed literally.
923072b8
FG
81 let then = cx.expr_call_global(
82 call_site_span,
fc512014 83 cx.std_path(&[sym::panicking, sym::panic]),
9ffffee4 84 thin_vec![cx.expr_str(
fc512014 85 DUMMY_SP,
dfeec247
XL
86 Symbol::intern(&format!(
87 "assertion failed: {}",
ed00b5ec 88 pprust::expr_to_string(&cond_expr)
dfeec247 89 )),
fc512014 90 )],
923072b8
FG
91 );
92 expr_if_not(cx, call_site_span, cond_expr, then, None)
0531ce1d 93 };
923072b8 94
c620b35d 95 ExpandResult::Ready(MacEager::expr(expr))
0531ce1d 96}
0731742a
XL
97
98struct Assert {
923072b8 99 cond_expr: P<Expr>,
0731742a
XL
100 custom_message: Option<TokenStream>,
101}
102
923072b8
FG
103// if !{ ... } { ... } else { ... }
104fn 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
e8be2606 114fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
e1599b0c 115 let mut parser = cx.new_parser_from_tts(stream);
0731742a
XL
116
117 if parser.token == token::Eof {
c0240ec0 118 return Err(cx.dcx().create_err(errors::AssertRequiresBoolean { span: sp }));
0731742a
XL
119 }
120
48663c56
XL
121 let cond_expr = parser.parse_expr()?;
122
123 // Some crates use the `assert!` macro in the following form (note extra semicolon):
124 //
125 // assert!(
126 // my_function();
127 // );
128 //
74b04a01 129 // Emit an error about semicolon and suggest removing it.
48663c56 130 if parser.token == token::Semi {
c0240ec0 131 cx.dcx().emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span });
48663c56
XL
132 parser.bump();
133 }
134
135 // Some crates use the `assert!` macro in the following form (note missing comma before
136 // message):
137 //
138 // assert!(true "error message");
139 //
74b04a01 140 // Emit an error and suggest inserting a comma.
dfeec247
XL
141 let custom_message =
142 if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
353b0b11 143 let comma = parser.prev_token.span.shrink_to_hi();
c0240ec0 144 cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
48663c56 145
dfeec247
XL
146 parse_custom_message(&mut parser)
147 } else if parser.eat(&token::Comma) {
148 parse_custom_message(&mut parser)
149 } else {
150 None
151 };
48663c56
XL
152
153 if parser.token != token::Eof {
e8be2606 154 parser.unexpected()?;
48663c56
XL
155 }
156
157 Ok(Assert { cond_expr, custom_message })
158}
159
416331ca 160fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
48663c56 161 let ts = parser.parse_tokens();
dfeec247 162 if !ts.is_empty() { Some(ts) } else { None }
0731742a 163}