rust/src/libsyntax_ext/assert.rs

139 lines
4.0 KiB
Rust
Raw Normal View History

2019-04-24 22:44:28 +00:00
use errors::{Applicability, DiagnosticBuilder};
2019-02-04 12:49:54 +00:00
use syntax::ast::{self, *};
use syntax::ext::base::*;
use syntax::parse::token::{self, TokenKind};
2019-04-24 22:44:28 +00:00
use syntax::parse::parser::Parser;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::symbol::{sym, Symbol};
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax_pos::{Span, DUMMY_SP};
pub fn expand_assert<'cx>(
2019-02-04 12:49:54 +00:00
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> Box<dyn MacResult + 'cx> {
let Assert { cond_expr, custom_message } = match parse_assert(cx, sp, tts) {
Ok(assert) => assert,
Err(mut err) => {
err.emit();
return DummyResult::any(sp);
}
};
let sp = cx.with_legacy_ctxt(sp);
2019-08-14 23:13:53 +00:00
let panic_call = Mac {
path: Path::from_ident(Ident::new(sym::panic, sp)),
tts: custom_message.unwrap_or_else(|| {
TokenStream::from(TokenTree::token(
TokenKind::lit(token::Str, Symbol::intern(&format!(
"assertion failed: {}",
pprust::expr_to_string(&cond_expr).escape_debug()
)), None),
DUMMY_SP,
))
}).into(),
delim: MacDelimiter::Parenthesis,
2019-08-14 23:13:53 +00:00
span: sp,
prior_type_ascription: None,
};
let if_expr = cx.expr_if(
sp,
cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)),
cx.expr(
sp,
2019-08-14 23:13:53 +00:00
ExprKind::Mac(panic_call),
),
None,
);
MacEager::expr(if_expr)
}
struct Assert {
cond_expr: P<ast::Expr>,
custom_message: Option<TokenStream>,
}
fn parse_assert<'a>(
cx: &mut ExtCtxt<'a>,
sp: Span,
stream: TokenStream
) -> Result<Assert, DiagnosticBuilder<'a>> {
let mut parser = cx.new_parser_from_tts(stream);
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 22:44:28 +00:00
let cond_expr = parser.parse_expr()?;
// Some crates use the `assert!` macro in the following form (note extra semicolon):
//
// assert!(
// my_function();
// );
//
// Warn about semicolon and suggest removing it. Eventually, this should be turned into an
// error.
if parser.token == token::Semi {
let mut err = cx.struct_span_warn(sp, "macro requires an expression as an argument");
err.span_suggestion(
parser.token.span,
2019-04-24 22:44:28 +00:00
"try removing semicolon",
String::new(),
Applicability::MaybeIncorrect
);
err.note("this is going to be an error in the future");
err.emit();
parser.bump();
}
// Some crates use the `assert!` macro in the following form (note missing comma before
// message):
//
// assert!(true "error message");
//
// Parse this as an actual message, and suggest inserting a comma. Eventually, this should be
// turned into an error.
2019-06-05 11:17:56 +00:00
let custom_message = if let token::Literal(token::Lit { kind: token::Str, .. })
= parser.token.kind {
let mut err = cx.struct_span_warn(parser.token.span, "unexpected string literal");
2019-04-24 22:44:28 +00:00
let comma_span = cx.source_map().next_point(parser.prev_span);
err.span_suggestion_short(
comma_span,
"try adding a comma",
", ".to_string(),
Applicability::MaybeIncorrect
);
err.note("this is going to be an error in the future");
err.emit();
parse_custom_message(&mut parser)
} else if parser.eat(&token::Comma) {
parse_custom_message(&mut parser)
} else {
None
};
if parser.token != token::Eof {
parser.expect_one_of(&[], &[])?;
unreachable!();
}
2019-04-24 22:44:28 +00:00
Ok(Assert { cond_expr, custom_message })
}
2019-06-21 21:49:03 +00:00
fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
2019-04-24 22:44:28 +00:00
let ts = parser.parse_tokens();
if !ts.is_empty() {
Some(ts)
} else {
None
}
}