1 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
3 use rustc_ast
::ast
::{self, *}
;
5 use rustc_ast
::token
::{self, TokenKind}
;
6 use rustc_ast
::tokenstream
::{DelimSpan, TokenStream, TokenTree}
;
7 use rustc_ast_pretty
::pprust
;
8 use rustc_expand
::base
::*;
9 use rustc_parse
::parser
::Parser
;
10 use rustc_span
::symbol
::{sym, 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
, sp
, tts
) {
22 return DummyResult
::any(sp
);
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(sp
);
29 let tokens
= custom_message
.unwrap_or_else(|| {
30 TokenStream
::from(TokenTree
::token(
33 Symbol
::intern(&format
!(
34 "assertion failed: {}",
35 pprust
::expr_to_string(&cond_expr
).escape_debug()
42 let args
= P(MacArgs
::Delimited(DelimSpan
::from_single(sp
), MacDelimiter
::Parenthesis
, tokens
));
43 let panic_call
= MacCall
{
44 path
: Path
::from_ident(Ident
::new(sym
::panic
, sp
)),
46 prior_type_ascription
: None
,
48 let if_expr
= cx
.expr_if(
50 cx
.expr(sp
, ExprKind
::Unary(UnOp
::Not
, cond_expr
)),
51 cx
.expr(sp
, ExprKind
::MacCall(panic_call
)),
54 MacEager
::expr(if_expr
)
58 cond_expr
: P
<ast
::Expr
>,
59 custom_message
: Option
<TokenStream
>,
66 ) -> Result
<Assert
, DiagnosticBuilder
<'a
>> {
67 let mut parser
= cx
.new_parser_from_tts(stream
);
69 if parser
.token
== token
::Eof
{
70 let mut err
= cx
.struct_span_err(sp
, "macro requires a boolean expression as an argument");
71 err
.span_label(sp
, "boolean expression required");
75 let cond_expr
= parser
.parse_expr()?
;
77 // Some crates use the `assert!` macro in the following form (note extra semicolon):
83 // Emit an error about semicolon and suggest removing it.
84 if parser
.token
== token
::Semi
{
85 let mut err
= cx
.struct_span_err(sp
, "macro requires an expression as an argument");
88 "try removing semicolon",
90 Applicability
::MaybeIncorrect
,
97 // Some crates use the `assert!` macro in the following form (note missing comma before
100 // assert!(true "error message");
102 // Emit an error and suggest inserting a comma.
104 if let token
::Literal(token
::Lit { kind: token::Str, .. }
) = parser
.token
.kind
{
105 let mut err
= cx
.struct_span_err(parser
.token
.span
, "unexpected string literal");
106 let comma_span
= parser
.prev_token
.span
.shrink_to_hi();
107 err
.span_suggestion_short(
109 "try adding a comma",
111 Applicability
::MaybeIncorrect
,
115 parse_custom_message(&mut parser
)
116 } else if parser
.eat(&token
::Comma
) {
117 parse_custom_message(&mut parser
)
122 if parser
.token
!= token
::Eof
{
123 parser
.expect_one_of(&[], &[])?
;
127 Ok(Assert { cond_expr, custom_message }
)
130 fn parse_custom_message(parser
: &mut Parser
<'_
>) -> Option
<TokenStream
> {
131 let ts
= parser
.parse_tokens();
132 if !ts
.is_empty() { Some(ts) }
else { None }