]>
Commit | Line | Data |
---|---|---|
923072b8 FG |
1 | mod context; |
2 | ||
a2a8927a | 3 | use crate::edition_panic::use_panic_2021; |
353b0b11 | 4 | use crate::errors; |
74b04a01 | 5 | use rustc_ast::ptr::P; |
fc512014 | 6 | use rustc_ast::token; |
add651ee | 7 | use rustc_ast::token::Delimiter; |
fc512014 | 8 | use rustc_ast::tokenstream::{DelimSpan, TokenStream}; |
add651ee | 9 | use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp}; |
74b04a01 | 10 | use rustc_ast_pretty::pprust; |
353b0b11 | 11 | use rustc_errors::PResult; |
923072b8 | 12 | use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult}; |
60c5eb7d | 13 | use rustc_parse::parser::Parser; |
f9f354fc | 14 | use rustc_span::symbol::{sym, Ident, Symbol}; |
dfeec247 | 15 | use rustc_span::{Span, DUMMY_SP}; |
9ffffee4 | 16 | use thin_vec::thin_vec; |
0531ce1d XL |
17 | |
18 | pub fn expand_assert<'cx>( | |
9fa01778 | 19 | cx: &'cx mut ExtCtxt<'_>, |
5869c6ff | 20 | span: Span, |
e1599b0c | 21 | tts: TokenStream, |
8faf50e0 | 22 | ) -> Box<dyn MacResult + 'cx> { |
5869c6ff | 23 | let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) { |
0731742a XL |
24 | Ok(assert) => assert, |
25 | Err(mut err) => { | |
26 | err.emit(); | |
5869c6ff | 27 | return DummyResult::any(span); |
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 FG |
94 | |
95 | MacEager::expr(expr) | |
0531ce1d | 96 | } |
0731742a XL |
97 | |
98 | struct Assert { | |
923072b8 | 99 | cond_expr: P<Expr>, |
0731742a XL |
100 | custom_message: Option<TokenStream>, |
101 | } | |
102 | ||
923072b8 FG |
103 | // if !{ ... } { ... } else { ... } |
104 | fn 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 | ||
5e7ed085 | 114 | fn parse_assert<'a>(cx: &mut 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 { | |
353b0b11 | 118 | return Err(cx.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 { |
353b0b11 | 131 | cx.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 FG |
143 | let comma = parser.prev_token.span.shrink_to_hi(); |
144 | cx.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 { | |
29967ef6 | 154 | return parser.unexpected(); |
48663c56 XL |
155 | } |
156 | ||
157 | Ok(Assert { cond_expr, custom_message }) | |
158 | } | |
159 | ||
416331ca | 160 | fn 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 | } |