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