mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-17 05:25:37 +03:00
0fb01d219c
Replace them with equivalents of `Span::{def_site,call_site}` from proc macro API.
The new API is much less error prone and doesn't rely on macros having default transparency.
139 lines
4.0 KiB
Rust
139 lines
4.0 KiB
Rust
use errors::{Applicability, DiagnosticBuilder};
|
|
|
|
use syntax::ast::{self, *};
|
|
use syntax::ext::base::*;
|
|
use syntax::parse::token::{self, TokenKind};
|
|
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>(
|
|
cx: &'cx mut ExtCtxt<'_>,
|
|
sp: Span,
|
|
tts: &[TokenTree],
|
|
) -> 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);
|
|
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,
|
|
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,
|
|
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,
|
|
tts: &[TokenTree]
|
|
) -> Result<Assert, DiagnosticBuilder<'a>> {
|
|
let mut parser = cx.new_parser_from_tts(tts);
|
|
|
|
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);
|
|
}
|
|
|
|
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,
|
|
"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.
|
|
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");
|
|
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!();
|
|
}
|
|
|
|
Ok(Assert { cond_expr, custom_message })
|
|
}
|
|
|
|
fn parse_custom_message(parser: &mut Parser<'_>) -> Option<TokenStream> {
|
|
let ts = parser.parse_tokens();
|
|
if !ts.is_empty() {
|
|
Some(ts)
|
|
} else {
|
|
None
|
|
}
|
|
}
|