2020-01-09 04:18:47 -06:00
|
|
|
use rustc_errors::{Applicability, DiagnosticBuilder};
|
2019-02-04 06:49:54 -06:00
|
|
|
|
2020-02-29 11:37:32 -06:00
|
|
|
use rustc_ast::ptr::P;
|
|
|
|
use rustc_ast::token::{self, TokenKind};
|
|
|
|
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
|
2020-04-27 12:56:11 -05:00
|
|
|
use rustc_ast::{self as ast, *};
|
2020-01-11 10:02:46 -06:00
|
|
|
use rustc_ast_pretty::pprust;
|
2019-12-29 08:23:55 -06:00
|
|
|
use rustc_expand::base::*;
|
2019-10-15 15:48:13 -05:00
|
|
|
use rustc_parse::parser::Parser;
|
2020-04-19 06:00:18 -05:00
|
|
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
2019-12-31 11:15:40 -06:00
|
|
|
use rustc_span::{Span, DUMMY_SP};
|
2018-03-07 01:13:15 -06:00
|
|
|
|
|
|
|
pub fn expand_assert<'cx>(
|
2019-02-04 06:49:54 -06:00
|
|
|
cx: &'cx mut ExtCtxt<'_>,
|
2018-03-07 01:13:15 -06:00
|
|
|
sp: Span,
|
2019-08-31 12:08:06 -05:00
|
|
|
tts: TokenStream,
|
2018-07-12 04:58:16 -05:00
|
|
|
) -> Box<dyn MacResult + 'cx> {
|
2018-12-04 13:10:32 -06:00
|
|
|
let Assert { cond_expr, custom_message } = match parse_assert(cx, sp, tts) {
|
|
|
|
Ok(assert) => assert,
|
|
|
|
Err(mut err) => {
|
|
|
|
err.emit();
|
2019-08-13 12:51:54 -05:00
|
|
|
return DummyResult::any(sp);
|
2018-03-07 01:13:15 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-14 15:17:11 -05:00
|
|
|
// `core::panic` and `std::panic` are different macros, so we use call-site
|
|
|
|
// context to pick up whichever is currently in scope.
|
|
|
|
let sp = cx.with_call_site_ctxt(sp);
|
2019-11-30 17:25:32 -06:00
|
|
|
let tokens = custom_message.unwrap_or_else(|| {
|
|
|
|
TokenStream::from(TokenTree::token(
|
2019-12-22 16:42:04 -06:00
|
|
|
TokenKind::lit(
|
|
|
|
token::Str,
|
|
|
|
Symbol::intern(&format!(
|
|
|
|
"assertion failed: {}",
|
|
|
|
pprust::expr_to_string(&cond_expr).escape_debug()
|
|
|
|
)),
|
|
|
|
None,
|
|
|
|
),
|
2019-11-30 17:25:32 -06:00
|
|
|
DUMMY_SP,
|
|
|
|
))
|
|
|
|
});
|
|
|
|
let args = P(MacArgs::Delimited(DelimSpan::from_single(sp), MacDelimiter::Parenthesis, tokens));
|
2020-02-29 10:32:20 -06:00
|
|
|
let panic_call = MacCall {
|
2019-05-21 21:42:23 -05:00
|
|
|
path: Path::from_ident(Ident::new(sym::panic, sp)),
|
2019-11-30 17:25:32 -06:00
|
|
|
args,
|
2019-07-18 20:36:19 -05:00
|
|
|
prior_type_ascription: None,
|
2018-03-07 01:13:15 -06:00
|
|
|
};
|
|
|
|
let if_expr = cx.expr_if(
|
|
|
|
sp,
|
|
|
|
cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)),
|
2020-02-29 10:32:20 -06:00
|
|
|
cx.expr(sp, ExprKind::MacCall(panic_call)),
|
2018-03-07 01:13:15 -06:00
|
|
|
None,
|
|
|
|
);
|
|
|
|
MacEager::expr(if_expr)
|
|
|
|
}
|
2018-12-04 13:10:32 -06:00
|
|
|
|
|
|
|
struct Assert {
|
|
|
|
cond_expr: P<ast::Expr>,
|
|
|
|
custom_message: Option<TokenStream>,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_assert<'a>(
|
|
|
|
cx: &mut ExtCtxt<'a>,
|
|
|
|
sp: Span,
|
2019-12-22 16:42:04 -06:00
|
|
|
stream: TokenStream,
|
2018-12-04 13:10:32 -06:00
|
|
|
) -> Result<Assert, DiagnosticBuilder<'a>> {
|
2019-08-31 12:08:06 -05:00
|
|
|
let mut parser = cx.new_parser_from_tts(stream);
|
2018-12-04 13:10:32 -06:00
|
|
|
|
|
|
|
if parser.token == token::Eof {
|
|
|
|
let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
|
|
|
|
err.span_label(sp, "boolean expression required");
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
|
2019-04-24 17:44:28 -05:00
|
|
|
let cond_expr = parser.parse_expr()?;
|
|
|
|
|
|
|
|
// Some crates use the `assert!` macro in the following form (note extra semicolon):
|
|
|
|
//
|
|
|
|
// assert!(
|
|
|
|
// my_function();
|
|
|
|
// );
|
|
|
|
//
|
2020-02-28 07:10:33 -06:00
|
|
|
// Emit an error about semicolon and suggest removing it.
|
2019-04-24 17:44:28 -05:00
|
|
|
if parser.token == token::Semi {
|
2020-02-28 07:10:33 -06:00
|
|
|
let mut err = cx.struct_span_err(sp, "macro requires an expression as an argument");
|
2019-04-24 17:44:28 -05:00
|
|
|
err.span_suggestion(
|
2019-06-07 05:31:13 -05:00
|
|
|
parser.token.span,
|
2019-04-24 17:44:28 -05:00
|
|
|
"try removing semicolon",
|
|
|
|
String::new(),
|
2019-12-22 16:42:04 -06:00
|
|
|
Applicability::MaybeIncorrect,
|
2019-04-24 17:44:28 -05:00
|
|
|
);
|
|
|
|
err.emit();
|
|
|
|
|
|
|
|
parser.bump();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some crates use the `assert!` macro in the following form (note missing comma before
|
|
|
|
// message):
|
|
|
|
//
|
|
|
|
// assert!(true "error message");
|
|
|
|
//
|
2020-02-28 07:10:33 -06:00
|
|
|
// Emit an error and suggest inserting a comma.
|
2019-12-22 16:42:04 -06:00
|
|
|
let custom_message =
|
|
|
|
if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
|
2020-02-28 07:10:33 -06:00
|
|
|
let mut err = cx.struct_span_err(parser.token.span, "unexpected string literal");
|
2020-02-29 05:56:15 -06:00
|
|
|
let comma_span = parser.prev_token.span.shrink_to_hi();
|
2019-12-22 16:42:04 -06:00
|
|
|
err.span_suggestion_short(
|
|
|
|
comma_span,
|
|
|
|
"try adding a comma",
|
|
|
|
", ".to_string(),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
err.emit();
|
2019-04-24 17:44:28 -05:00
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
parse_custom_message(&mut parser)
|
|
|
|
} else if parser.eat(&token::Comma) {
|
|
|
|
parse_custom_message(&mut parser)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2019-04-17 06:01:57 -05:00
|
|
|
|
|
|
|
if parser.token != token::Eof {
|
|
|
|
parser.expect_one_of(&[], &[])?;
|
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
|
2019-04-24 17:44:28 -05:00
|
|
|
Ok(Assert { cond_expr, custom_message })
|
|
|
|
}
|
|
|
|
|
2019-06-21 16:49:03 -05:00
|
|
|
fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
|
2019-04-24 17:44:28 -05:00
|
|
|
let ts = parser.parse_tokens();
|
2019-12-22 16:42:04 -06:00
|
|
|
if !ts.is_empty() { Some(ts) } else { None }
|
2018-12-04 13:10:32 -06:00
|
|
|
}
|