]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_builtin_macros/src/assert.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / assert.rs
CommitLineData
a2a8927a 1use crate::edition_panic::use_panic_2021;
74b04a01 2use rustc_ast::ptr::P;
fc512014
XL
3use rustc_ast::token;
4use rustc_ast::tokenstream::{DelimSpan, TokenStream};
3dfed10e 5use rustc_ast::{self as ast, *};
74b04a01 6use rustc_ast_pretty::pprust;
5e7ed085 7use rustc_errors::{Applicability, PResult};
dfeec247 8use rustc_expand::base::*;
60c5eb7d 9use rustc_parse::parser::Parser;
f9f354fc 10use rustc_span::symbol::{sym, Ident, Symbol};
dfeec247 11use rustc_span::{Span, DUMMY_SP};
0531ce1d
XL
12
13pub fn expand_assert<'cx>(
9fa01778 14 cx: &'cx mut ExtCtxt<'_>,
5869c6ff 15 span: Span,
e1599b0c 16 tts: TokenStream,
8faf50e0 17) -> Box<dyn MacResult + 'cx> {
5869c6ff 18 let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
0731742a
XL
19 Ok(assert) => assert,
20 Err(mut err) => {
21 err.emit();
5869c6ff 22 return DummyResult::any(span);
0531ce1d 23 }
0531ce1d
XL
24 };
25
e1599b0c
XL
26 // `core::panic` and `std::panic` are different macros, so we use call-site
27 // context to pick up whichever is currently in scope.
5869c6ff 28 let sp = cx.with_call_site_ctxt(span);
fc512014
XL
29
30 let panic_call = if let Some(tokens) = custom_message {
dc3f5686 31 let path = if use_panic_2021(span) {
5869c6ff
XL
32 // On edition 2021, we always call `$crate::panic::panic_2021!()`.
33 Path {
34 span: sp,
35 segments: cx
36 .std_path(&[sym::panic, sym::panic_2021])
37 .into_iter()
38 .map(|ident| PathSegment::from_ident(ident))
39 .collect(),
40 tokens: None,
41 }
42 } else {
43 // Before edition 2021, we call `panic!()` unqualified,
44 // such that it calls either `std::panic!()` or `core::panic!()`.
45 Path::from_ident(Ident::new(sym::panic, sp))
46 };
fc512014
XL
47 // Pass the custom message to panic!().
48 cx.expr(
49 sp,
50 ExprKind::MacCall(MacCall {
5869c6ff 51 path,
fc512014
XL
52 args: P(MacArgs::Delimited(
53 DelimSpan::from_single(sp),
54 MacDelimiter::Parenthesis,
55 tokens,
56 )),
57 prior_type_ascription: None,
58 }),
59 )
60 } else {
61 // Pass our own message directly to $crate::panicking::panic(),
62 // because it might contain `{` and `}` that should always be
63 // passed literally.
64 cx.expr_call_global(
65 sp,
66 cx.std_path(&[sym::panicking, sym::panic]),
67 vec![cx.expr_str(
68 DUMMY_SP,
dfeec247
XL
69 Symbol::intern(&format!(
70 "assertion failed: {}",
71 pprust::expr_to_string(&cond_expr).escape_debug()
72 )),
fc512014
XL
73 )],
74 )
0531ce1d 75 };
fc512014
XL
76 let if_expr =
77 cx.expr_if(sp, cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)), panic_call, None);
0531ce1d
XL
78 MacEager::expr(if_expr)
79}
0731742a
XL
80
81struct Assert {
82 cond_expr: P<ast::Expr>,
83 custom_message: Option<TokenStream>,
84}
85
5e7ed085 86fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
e1599b0c 87 let mut parser = cx.new_parser_from_tts(stream);
0731742a
XL
88
89 if parser.token == token::Eof {
90 let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
91 err.span_label(sp, "boolean expression required");
92 return Err(err);
93 }
94
48663c56
XL
95 let cond_expr = parser.parse_expr()?;
96
97 // Some crates use the `assert!` macro in the following form (note extra semicolon):
98 //
99 // assert!(
100 // my_function();
101 // );
102 //
74b04a01 103 // Emit an error about semicolon and suggest removing it.
48663c56 104 if parser.token == token::Semi {
74b04a01 105 let mut err = cx.struct_span_err(sp, "macro requires an expression as an argument");
48663c56 106 err.span_suggestion(
dc9dc135 107 parser.token.span,
48663c56
XL
108 "try removing semicolon",
109 String::new(),
dfeec247 110 Applicability::MaybeIncorrect,
48663c56 111 );
48663c56
XL
112 err.emit();
113
114 parser.bump();
115 }
116
117 // Some crates use the `assert!` macro in the following form (note missing comma before
118 // message):
119 //
120 // assert!(true "error message");
121 //
74b04a01 122 // Emit an error and suggest inserting a comma.
dfeec247
XL
123 let custom_message =
124 if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
74b04a01
XL
125 let mut err = cx.struct_span_err(parser.token.span, "unexpected string literal");
126 let comma_span = parser.prev_token.span.shrink_to_hi();
dfeec247
XL
127 err.span_suggestion_short(
128 comma_span,
129 "try adding a comma",
130 ", ".to_string(),
131 Applicability::MaybeIncorrect,
132 );
dfeec247 133 err.emit();
48663c56 134
dfeec247
XL
135 parse_custom_message(&mut parser)
136 } else if parser.eat(&token::Comma) {
137 parse_custom_message(&mut parser)
138 } else {
139 None
140 };
48663c56
XL
141
142 if parser.token != token::Eof {
29967ef6 143 return parser.unexpected();
48663c56
XL
144 }
145
146 Ok(Assert { cond_expr, custom_message })
147}
148
416331ca 149fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
48663c56 150 let ts = parser.parse_tokens();
dfeec247 151 if !ts.is_empty() { Some(ts) } else { None }
0731742a 152}