1 use crate::edition_panic
::use_panic_2021
;
4 use rustc_ast
::tokenstream
::{DelimSpan, TokenStream}
;
5 use rustc_ast
::{self as ast, *}
;
6 use rustc_ast_pretty
::pprust
;
7 use rustc_errors
::{Applicability, PResult}
;
8 use rustc_expand
::base
::*;
9 use rustc_parse
::parser
::Parser
;
10 use rustc_span
::symbol
::{sym, Ident, Symbol}
;
11 use rustc_span
::{Span, DUMMY_SP}
;
13 pub fn expand_assert
<'cx
>(
14 cx
: &'cx
mut ExtCtxt
<'_
>,
17 ) -> Box
<dyn MacResult
+ 'cx
> {
18 let Assert { cond_expr, custom_message }
= match parse_assert(cx
, span
, tts
) {
22 return DummyResult
::any(span
);
26 // `core::panic` and `std::panic` are different macros, so we use call-site
27 // context to pick up whichever is currently in scope.
28 let sp
= cx
.with_call_site_ctxt(span
);
30 let panic_call
= if let Some(tokens
) = custom_message
{
31 let path
= if use_panic_2021(span
) {
32 // On edition 2021, we always call `$crate::panic::panic_2021!()`.
36 .std_path(&[sym
::panic
, sym
::panic_2021
])
38 .map(|ident
| PathSegment
::from_ident(ident
))
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
))
47 // Pass the custom message to panic!().
50 ExprKind
::MacCall(MacCall
{
52 args
: P(MacArgs
::Delimited(
53 DelimSpan
::from_single(sp
),
54 MacDelimiter
::Parenthesis
,
57 prior_type_ascription
: None
,
61 // Pass our own message directly to $crate::panicking::panic(),
62 // because it might contain `{` and `}` that should always be
66 cx
.std_path(&[sym
::panicking
, sym
::panic
]),
69 Symbol
::intern(&format
!(
70 "assertion failed: {}",
71 pprust
::expr_to_string(&cond_expr
).escape_debug()
77 cx
.expr_if(sp
, cx
.expr(sp
, ExprKind
::Unary(UnOp
::Not
, cond_expr
)), panic_call
, None
);
78 MacEager
::expr(if_expr
)
82 cond_expr
: P
<ast
::Expr
>,
83 custom_message
: Option
<TokenStream
>,
86 fn parse_assert
<'a
>(cx
: &mut ExtCtxt
<'a
>, sp
: Span
, stream
: TokenStream
) -> PResult
<'a
, Assert
> {
87 let mut parser
= cx
.new_parser_from_tts(stream
);
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");
95 let cond_expr
= parser
.parse_expr()?
;
97 // Some crates use the `assert!` macro in the following form (note extra semicolon):
103 // Emit an error about semicolon and suggest removing it.
104 if parser
.token
== token
::Semi
{
105 let mut err
= cx
.struct_span_err(sp
, "macro requires an expression as an argument");
108 "try removing semicolon",
110 Applicability
::MaybeIncorrect
,
117 // Some crates use the `assert!` macro in the following form (note missing comma before
120 // assert!(true "error message");
122 // Emit an error and suggest inserting a comma.
124 if let token
::Literal(token
::Lit { kind: token::Str, .. }
) = parser
.token
.kind
{
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();
127 err
.span_suggestion_short(
129 "try adding a comma",
131 Applicability
::MaybeIncorrect
,
135 parse_custom_message(&mut parser
)
136 } else if parser
.eat(&token
::Comma
) {
137 parse_custom_message(&mut parser
)
142 if parser
.token
!= token
::Eof
{
143 return parser
.unexpected();
146 Ok(Assert { cond_expr, custom_message }
)
149 fn parse_custom_message(parser
: &mut Parser
<'_
>) -> Option
<TokenStream
> {
150 let ts
= parser
.parse_tokens();
151 if !ts
.is_empty() { Some(ts) }
else { None }