1 use rustc_errors::{Applicability, DiagnosticBuilder};
3 use rustc_expand::base::*;
4 use rustc_parse::parser::Parser;
5 use rustc_span::symbol::{sym, Symbol};
6 use rustc_span::{Span, DUMMY_SP};
7 use syntax::ast::{self, *};
8 use syntax::print::pprust;
10 use syntax::token::{self, TokenKind};
11 use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
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 = Mac {
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::Mac(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 // Warn about semicolon and suggest removing it. Eventually, this should be turned into an
85 if parser.token == token::Semi {
86 let mut err = cx.struct_span_warn(sp, "macro requires an expression as an argument");
89 "try removing semicolon",
91 Applicability::MaybeIncorrect,
93 err.note("this is going to be an error in the future");
99 // Some crates use the `assert!` macro in the following form (note missing comma before
102 // assert!(true "error message");
104 // Parse this as an actual message, and suggest inserting a comma. Eventually, this should be
105 // turned into an error.
107 if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
108 let mut err = cx.struct_span_warn(parser.token.span, "unexpected string literal");
109 let comma_span = cx.source_map().next_point(parser.prev_span);
110 err.span_suggestion_short(
112 "try adding a comma",
114 Applicability::MaybeIncorrect,
116 err.note("this is going to be an error in the future");
119 parse_custom_message(&mut parser)
120 } else if parser.eat(&token::Comma) {
121 parse_custom_message(&mut parser)
126 if parser.token != token::Eof {
127 parser.expect_one_of(&[], &[])?;
131 Ok(Assert { cond_expr, custom_message })
134 fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
135 let ts = parser.parse_tokens();
136 if !ts.is_empty() { Some(ts) } else { None }