2022-06-02 12:00:04 +00:00
|
|
|
mod context;
|
|
|
|
|
2022-01-21 22:04:06 +00:00
|
|
|
use crate::edition_panic::use_panic_2021;
|
2023-04-08 19:37:41 +00:00
|
|
|
use crate::errors;
|
2020-02-29 17:37:32 +00:00
|
|
|
use rustc_ast::ptr::P;
|
2020-10-18 20:30:16 +00:00
|
|
|
use rustc_ast::token;
|
2023-08-01 23:56:26 +00:00
|
|
|
use rustc_ast::token::Delimiter;
|
2020-10-18 20:30:16 +00:00
|
|
|
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
|
2023-08-01 23:56:26 +00:00
|
|
|
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp};
|
2020-01-11 16:02:46 +00:00
|
|
|
use rustc_ast_pretty::pprust;
|
2023-04-08 19:37:41 +00:00
|
|
|
use rustc_errors::PResult;
|
2022-06-02 12:00:04 +00:00
|
|
|
use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult};
|
2019-10-15 20:48:13 +00:00
|
|
|
use rustc_parse::parser::Parser;
|
2020-04-19 11:00:18 +00:00
|
|
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
2019-12-31 17:15:40 +00:00
|
|
|
use rustc_span::{Span, DUMMY_SP};
|
2022-11-23 00:55:16 +00:00
|
|
|
use thin_vec::thin_vec;
|
2018-03-07 07:13:15 +00:00
|
|
|
|
|
|
|
pub fn expand_assert<'cx>(
|
2019-02-04 12:49:54 +00:00
|
|
|
cx: &'cx mut ExtCtxt<'_>,
|
2021-01-24 13:15:15 +00:00
|
|
|
span: Span,
|
2019-08-31 17:08:06 +00:00
|
|
|
tts: TokenStream,
|
2018-07-12 09:58:16 +00:00
|
|
|
) -> Box<dyn MacResult + 'cx> {
|
2021-01-24 13:15:15 +00:00
|
|
|
let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
|
2018-12-04 19:10:32 +00:00
|
|
|
Ok(assert) => assert,
|
|
|
|
Err(mut err) => {
|
|
|
|
err.emit();
|
2021-01-24 13:15:15 +00:00
|
|
|
return DummyResult::any(span);
|
2018-03-07 07:13:15 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-14 20:17:11 +00:00
|
|
|
// `core::panic` and `std::panic` are different macros, so we use call-site
|
|
|
|
// context to pick up whichever is currently in scope.
|
2022-06-02 12:00:04 +00:00
|
|
|
let call_site_span = cx.with_call_site_ctxt(span);
|
2020-10-18 20:30:16 +00:00
|
|
|
|
2022-06-02 12:00:04 +00:00
|
|
|
let panic_path = || {
|
|
|
|
if use_panic_2021(span) {
|
2021-02-01 22:08:22 +00:00
|
|
|
// On edition 2021, we always call `$crate::panic::panic_2021!()`.
|
2021-01-09 19:53:24 +00:00
|
|
|
Path {
|
2022-06-02 12:00:04 +00:00
|
|
|
span: call_site_span,
|
2021-01-09 19:53:24 +00:00
|
|
|
segments: cx
|
2021-02-01 22:08:22 +00:00
|
|
|
.std_path(&[sym::panic, sym::panic_2021])
|
2021-01-09 19:53:24 +00:00
|
|
|
.into_iter()
|
|
|
|
.map(|ident| PathSegment::from_ident(ident))
|
|
|
|
.collect(),
|
|
|
|
tokens: None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Before edition 2021, we call `panic!()` unqualified,
|
|
|
|
// such that it calls either `std::panic!()` or `core::panic!()`.
|
2022-06-02 12:00:04 +00:00
|
|
|
Path::from_ident(Ident::new(sym::panic, call_site_span))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Simply uses the user provided message instead of generating custom outputs
|
|
|
|
let expr = if let Some(tokens) = custom_message {
|
|
|
|
let then = cx.expr(
|
|
|
|
call_site_span,
|
2022-08-12 02:20:10 +00:00
|
|
|
ExprKind::MacCall(P(MacCall {
|
2022-06-02 12:00:04 +00:00
|
|
|
path: panic_path(),
|
2022-11-18 00:24:21 +00:00
|
|
|
args: P(DelimArgs {
|
|
|
|
dspan: DelimSpan::from_single(call_site_span),
|
2023-08-01 23:56:26 +00:00
|
|
|
delim: Delimiter::Parenthesis,
|
2020-10-19 19:14:05 +00:00
|
|
|
tokens,
|
2022-11-18 00:24:21 +00:00
|
|
|
}),
|
2022-08-12 02:20:10 +00:00
|
|
|
})),
|
2022-06-02 12:00:04 +00:00
|
|
|
);
|
|
|
|
expr_if_not(cx, call_site_span, cond_expr, then, None)
|
|
|
|
}
|
|
|
|
// If `generic_assert` is enabled, generates rich captured outputs
|
|
|
|
//
|
|
|
|
// FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
|
2023-08-09 12:28:00 +00:00
|
|
|
else if cx.ecfg.features.generic_assert {
|
2022-06-02 12:00:04 +00:00
|
|
|
context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
|
|
|
|
}
|
|
|
|
// If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
|
|
|
|
// string
|
|
|
|
else {
|
2020-10-19 19:14:05 +00:00
|
|
|
// Pass our own message directly to $crate::panicking::panic(),
|
|
|
|
// because it might contain `{` and `}` that should always be
|
|
|
|
// passed literally.
|
2022-06-02 12:00:04 +00:00
|
|
|
let then = cx.expr_call_global(
|
|
|
|
call_site_span,
|
2020-10-19 19:14:05 +00:00
|
|
|
cx.std_path(&[sym::panicking, sym::panic]),
|
2022-11-23 00:55:16 +00:00
|
|
|
thin_vec![cx.expr_str(
|
2020-10-19 19:14:05 +00:00
|
|
|
DUMMY_SP,
|
|
|
|
Symbol::intern(&format!(
|
|
|
|
"assertion failed: {}",
|
2023-10-09 02:32:17 +00:00
|
|
|
pprust::expr_to_string(&cond_expr)
|
2020-10-19 19:14:05 +00:00
|
|
|
)),
|
|
|
|
)],
|
2022-06-02 12:00:04 +00:00
|
|
|
);
|
|
|
|
expr_if_not(cx, call_site_span, cond_expr, then, None)
|
2018-03-07 07:13:15 +00:00
|
|
|
};
|
2022-06-02 12:00:04 +00:00
|
|
|
|
|
|
|
MacEager::expr(expr)
|
2018-03-07 07:13:15 +00:00
|
|
|
}
|
2018-12-04 19:10:32 +00:00
|
|
|
|
|
|
|
struct Assert {
|
2022-06-02 12:00:04 +00:00
|
|
|
cond_expr: P<Expr>,
|
2018-12-04 19:10:32 +00:00
|
|
|
custom_message: Option<TokenStream>,
|
|
|
|
}
|
|
|
|
|
2022-06-02 12:00:04 +00:00
|
|
|
// if !{ ... } { ... } else { ... }
|
|
|
|
fn expr_if_not(
|
|
|
|
cx: &ExtCtxt<'_>,
|
|
|
|
span: Span,
|
|
|
|
cond: P<Expr>,
|
|
|
|
then: P<Expr>,
|
|
|
|
els: Option<P<Expr>>,
|
|
|
|
) -> P<Expr> {
|
|
|
|
cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
|
|
|
|
}
|
|
|
|
|
2022-01-27 09:44:25 +00:00
|
|
|
fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
|
2019-08-31 17:08:06 +00:00
|
|
|
let mut parser = cx.new_parser_from_tts(stream);
|
2018-12-04 19:10:32 +00:00
|
|
|
|
|
|
|
if parser.token == token::Eof {
|
2023-12-18 09:54:03 +00:00
|
|
|
return Err(cx.dcx().create_err(errors::AssertRequiresBoolean { span: sp }));
|
2018-12-04 19:10:32 +00:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
// );
|
|
|
|
//
|
2020-02-28 13:10:33 +00:00
|
|
|
// Emit an error about semicolon and suggest removing it.
|
2019-04-24 22:44:28 +00:00
|
|
|
if parser.token == token::Semi {
|
2023-12-18 09:54:03 +00:00
|
|
|
cx.dcx().emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span });
|
2019-04-24 22:44:28 +00:00
|
|
|
parser.bump();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some crates use the `assert!` macro in the following form (note missing comma before
|
|
|
|
// message):
|
|
|
|
//
|
|
|
|
// assert!(true "error message");
|
|
|
|
//
|
2020-02-28 13:10:33 +00:00
|
|
|
// Emit an error and suggest inserting a comma.
|
2019-06-05 11:17:56 +00:00
|
|
|
let custom_message =
|
|
|
|
if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
|
2023-04-08 19:37:41 +00:00
|
|
|
let comma = parser.prev_token.span.shrink_to_hi();
|
2023-12-18 09:54:03 +00:00
|
|
|
cx.dcx().emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
|
2019-04-24 22:44:28 +00:00
|
|
|
|
|
|
|
parse_custom_message(&mut parser)
|
|
|
|
} else if parser.eat(&token::Comma) {
|
|
|
|
parse_custom_message(&mut parser)
|
|
|
|
} else {
|
|
|
|
None
|
2019-04-17 11:01:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if parser.token != token::Eof {
|
2020-10-05 21:21:03 +00:00
|
|
|
return parser.unexpected();
|
2019-04-17 11:01:57 +00:00
|
|
|
}
|
|
|
|
|
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 }
|
2018-12-04 19:10:32 +00:00
|
|
|
}
|