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