mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 11:48:30 +00:00
Auto merge of #97665 - c410-f3r:assert-compiler, r=oli-obk
[RFC 2011] Minimal initial implementation Tracking issue: #44838 Third step of #96496 Implementation has ~290 LOC with the bare minimum to be in a functional state. Currently only searches for binary operations to mimic what `assert_eq!` and `assert_ne!` already do. r? `@oli-obk`
This commit is contained in:
commit
ca983054e1
@ -1,44 +1,299 @@
|
|||||||
use rustc_ast::{ptr::P, Expr, Path};
|
use crate::assert::expr_if_not;
|
||||||
|
use rustc_ast::{
|
||||||
|
attr,
|
||||||
|
ptr::P,
|
||||||
|
token,
|
||||||
|
tokenstream::{DelimSpan, TokenStream, TokenTree},
|
||||||
|
BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path,
|
||||||
|
PathSegment, Stmt, UseTree, UseTreeKind, DUMMY_NODE_ID,
|
||||||
|
};
|
||||||
|
use rustc_ast_pretty::pprust;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_expand::base::ExtCtxt;
|
use rustc_expand::base::ExtCtxt;
|
||||||
use rustc_span::Span;
|
use rustc_span::{
|
||||||
|
symbol::{sym, Ident, Symbol},
|
||||||
|
Span,
|
||||||
|
};
|
||||||
|
|
||||||
pub(super) struct Context<'cx, 'a> {
|
pub(super) struct Context<'cx, 'a> {
|
||||||
|
// Top-level `let captureN = Capture::new()` statements
|
||||||
|
capture_decls: Vec<Capture>,
|
||||||
cx: &'cx ExtCtxt<'a>,
|
cx: &'cx ExtCtxt<'a>,
|
||||||
|
// Formatting string used for debugging
|
||||||
|
fmt_string: String,
|
||||||
|
// Top-level `let __local_bindN = &expr` statements
|
||||||
|
local_bind_decls: Vec<Stmt>,
|
||||||
|
// Used to avoid capturing duplicated paths
|
||||||
|
//
|
||||||
|
// ```rust
|
||||||
|
// let a = 1i32;
|
||||||
|
// assert!(add(a, a) == 3);
|
||||||
|
// ```
|
||||||
|
paths: FxHashSet<Ident>,
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'cx, 'a> Context<'cx, 'a> {
|
impl<'cx, 'a> Context<'cx, 'a> {
|
||||||
pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {
|
pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {
|
||||||
Self { cx, span }
|
Self {
|
||||||
|
capture_decls: <_>::default(),
|
||||||
|
cx,
|
||||||
|
fmt_string: <_>::default(),
|
||||||
|
local_bind_decls: <_>::default(),
|
||||||
|
paths: <_>::default(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the whole `assert!` expression.
|
/// Builds the whole `assert!` expression. For example, `let elem = 1; assert!(elem == 1);` expands to:
|
||||||
///
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let elem = 1;
|
||||||
/// {
|
/// {
|
||||||
/// use ::core::asserting::{ ... };
|
/// #[allow(unused_imports)]
|
||||||
///
|
/// use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};
|
||||||
/// let mut __capture0 = Capture::new();
|
/// let mut __capture0 = ::core::asserting::Capture::new();
|
||||||
/// ...
|
/// let __local_bind0 = &elem;
|
||||||
/// ...
|
/// if !(
|
||||||
/// ...
|
/// *{
|
||||||
///
|
/// (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
|
||||||
/// if !{
|
/// __local_bind0
|
||||||
/// ...
|
/// } == 1
|
||||||
/// ...
|
/// ) {
|
||||||
/// ...
|
/// panic!("Assertion failed: elem == 1\nWith captures:\n elem = {}", __capture0)
|
||||||
/// } {
|
/// }
|
||||||
/// panic!(
|
|
||||||
/// "Assertion failed: ... \n With expansion: ...",
|
|
||||||
/// __capture0,
|
|
||||||
/// ...
|
|
||||||
/// ...
|
|
||||||
/// ...
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// }
|
/// }
|
||||||
pub(super) fn build(self, _cond_expr: P<Expr>, _panic_path: Path) -> P<Expr> {
|
/// ```
|
||||||
let Self { cx, span, .. } = self;
|
pub(super) fn build(mut self, mut cond_expr: P<Expr>, panic_path: Path) -> P<Expr> {
|
||||||
let stmts = Vec::new();
|
let expr_str = pprust::expr_to_string(&cond_expr);
|
||||||
|
self.manage_cond_expr(&mut cond_expr);
|
||||||
|
let initial_imports = self.build_initial_imports();
|
||||||
|
let panic = self.build_panic(&expr_str, panic_path);
|
||||||
|
|
||||||
|
let Self { capture_decls, cx, local_bind_decls, span, .. } = self;
|
||||||
|
|
||||||
|
let mut stmts = Vec::with_capacity(4);
|
||||||
|
stmts.push(initial_imports);
|
||||||
|
stmts.extend(capture_decls.into_iter().map(|c| c.decl));
|
||||||
|
stmts.extend(local_bind_decls);
|
||||||
|
stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None)));
|
||||||
cx.expr_block(cx.block(span, stmts))
|
cx.expr_block(cx.block(span, stmts))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initial **trait** imports
|
||||||
|
///
|
||||||
|
/// use ::core::asserting::{ ... };
|
||||||
|
fn build_initial_imports(&self) -> Stmt {
|
||||||
|
let nested_tree = |this: &Self, sym| {
|
||||||
|
(
|
||||||
|
UseTree {
|
||||||
|
prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]),
|
||||||
|
kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),
|
||||||
|
span: this.span,
|
||||||
|
},
|
||||||
|
DUMMY_NODE_ID,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
self.cx.stmt_item(
|
||||||
|
self.span,
|
||||||
|
self.cx.item(
|
||||||
|
self.span,
|
||||||
|
Ident::empty(),
|
||||||
|
vec![self.cx.attribute(attr::mk_list_item(
|
||||||
|
Ident::new(sym::allow, self.span),
|
||||||
|
vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],
|
||||||
|
))],
|
||||||
|
ItemKind::Use(UseTree {
|
||||||
|
prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
|
||||||
|
kind: UseTreeKind::Nested(vec![
|
||||||
|
nested_tree(self, sym::TryCaptureGeneric),
|
||||||
|
nested_tree(self, sym::TryCapturePrintable),
|
||||||
|
]),
|
||||||
|
span: self.span,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The necessary custom `panic!(...)` expression.
|
||||||
|
///
|
||||||
|
/// panic!(
|
||||||
|
/// "Assertion failed: ... \n With expansion: ...",
|
||||||
|
/// __capture0,
|
||||||
|
/// ...
|
||||||
|
/// );
|
||||||
|
fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
|
||||||
|
let escaped_expr_str = escape_to_fmt(expr_str);
|
||||||
|
let initial = [
|
||||||
|
TokenTree::token(
|
||||||
|
token::Literal(token::Lit {
|
||||||
|
kind: token::LitKind::Str,
|
||||||
|
symbol: Symbol::intern(&if self.fmt_string.is_empty() {
|
||||||
|
format!("Assertion failed: {escaped_expr_str}")
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"Assertion failed: {escaped_expr_str}\nWith captures:\n{}",
|
||||||
|
&self.fmt_string
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
suffix: None,
|
||||||
|
}),
|
||||||
|
self.span,
|
||||||
|
),
|
||||||
|
TokenTree::token(token::Comma, self.span),
|
||||||
|
];
|
||||||
|
let captures = self.capture_decls.iter().flat_map(|cap| {
|
||||||
|
[
|
||||||
|
TokenTree::token(token::Ident(cap.ident.name, false), cap.ident.span),
|
||||||
|
TokenTree::token(token::Comma, self.span),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
self.cx.expr(
|
||||||
|
self.span,
|
||||||
|
ExprKind::MacCall(MacCall {
|
||||||
|
path: panic_path,
|
||||||
|
args: P(MacArgs::Delimited(
|
||||||
|
DelimSpan::from_single(self.span),
|
||||||
|
MacDelimiter::Parenthesis,
|
||||||
|
initial.into_iter().chain(captures).collect::<TokenStream>(),
|
||||||
|
)),
|
||||||
|
prior_type_ascription: None,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursive function called until `cond_expr` and `fmt_str` are fully modified.
|
||||||
|
///
|
||||||
|
/// See [Self::manage_initial_capture] and [Self::manage_try_capture]
|
||||||
|
fn manage_cond_expr(&mut self, expr: &mut P<Expr>) {
|
||||||
|
match (*expr).kind {
|
||||||
|
ExprKind::Binary(_, ref mut lhs, ref mut rhs) => {
|
||||||
|
self.manage_cond_expr(lhs);
|
||||||
|
self.manage_cond_expr(rhs);
|
||||||
|
}
|
||||||
|
ExprKind::Path(_, Path { ref segments, .. }) if let &[ref path_segment] = &segments[..] => {
|
||||||
|
let path_ident = path_segment.ident;
|
||||||
|
self.manage_initial_capture(expr, path_ident);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes the top-level declarations and modifies `expr` to try capturing variables.
|
||||||
|
///
|
||||||
|
/// `fmt_str`, the formatting string used for debugging, is constructed to show possible
|
||||||
|
/// captured variables.
|
||||||
|
fn manage_initial_capture(&mut self, expr: &mut P<Expr>, path_ident: Ident) {
|
||||||
|
if self.paths.contains(&path_ident) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.fmt_string.push_str(" ");
|
||||||
|
self.fmt_string.push_str(path_ident.as_str());
|
||||||
|
self.fmt_string.push_str(" = {:?}\n");
|
||||||
|
let _ = self.paths.insert(path_ident);
|
||||||
|
}
|
||||||
|
let curr_capture_idx = self.capture_decls.len();
|
||||||
|
let capture_string = format!("__capture{curr_capture_idx}");
|
||||||
|
let ident = Ident::new(Symbol::intern(&capture_string), self.span);
|
||||||
|
let init_std_path = self.cx.std_path(&[sym::asserting, sym::Capture, sym::new]);
|
||||||
|
let init = self.cx.expr_call(
|
||||||
|
self.span,
|
||||||
|
self.cx.expr_path(self.cx.path(self.span, init_std_path)),
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
let capture = Capture { decl: self.cx.stmt_let(self.span, true, ident, init), ident };
|
||||||
|
self.capture_decls.push(capture);
|
||||||
|
self.manage_try_capture(ident, curr_capture_idx, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to copy `__local_bindN` into `__captureN`.
|
||||||
|
///
|
||||||
|
/// *{
|
||||||
|
/// (&Wrapper(__local_bindN)).try_capture(&mut __captureN);
|
||||||
|
/// __local_bindN
|
||||||
|
/// }
|
||||||
|
fn manage_try_capture(&mut self, capture: Ident, curr_capture_idx: usize, expr: &mut P<Expr>) {
|
||||||
|
let local_bind_string = format!("__local_bind{curr_capture_idx}");
|
||||||
|
let local_bind = Ident::new(Symbol::intern(&local_bind_string), self.span);
|
||||||
|
self.local_bind_decls.push(self.cx.stmt_let(
|
||||||
|
self.span,
|
||||||
|
false,
|
||||||
|
local_bind,
|
||||||
|
self.cx.expr_addr_of(self.span, expr.clone()),
|
||||||
|
));
|
||||||
|
let wrapper = self.cx.expr_call(
|
||||||
|
self.span,
|
||||||
|
self.cx.expr_path(
|
||||||
|
self.cx.path(self.span, self.cx.std_path(&[sym::asserting, sym::Wrapper])),
|
||||||
|
),
|
||||||
|
vec![self.cx.expr_path(Path::from_ident(local_bind))],
|
||||||
|
);
|
||||||
|
let try_capture_call = self
|
||||||
|
.cx
|
||||||
|
.stmt_expr(expr_method_call(
|
||||||
|
self.cx,
|
||||||
|
PathSegment {
|
||||||
|
args: None,
|
||||||
|
id: DUMMY_NODE_ID,
|
||||||
|
ident: Ident::new(sym::try_capture, self.span),
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)),
|
||||||
|
expr_addr_of_mut(
|
||||||
|
self.cx,
|
||||||
|
self.span,
|
||||||
|
self.cx.expr_path(Path::from_ident(capture)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
self.span,
|
||||||
|
))
|
||||||
|
.add_trailing_semicolon();
|
||||||
|
let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind));
|
||||||
|
let ret = self.cx.stmt_expr(local_bind_path);
|
||||||
|
let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret]));
|
||||||
|
*expr = self.cx.expr_deref(self.span, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a captured element.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Capture {
|
||||||
|
// Generated indexed `Capture` statement.
|
||||||
|
//
|
||||||
|
// `let __capture{} = Capture::new();`
|
||||||
|
decl: Stmt,
|
||||||
|
// The name of the generated indexed `Capture` variable.
|
||||||
|
//
|
||||||
|
// `__capture{}`
|
||||||
|
ident: Ident,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Escapes to use as a formatting string.
|
||||||
|
fn escape_to_fmt(s: &str) -> String {
|
||||||
|
let mut rslt = String::with_capacity(s.len());
|
||||||
|
for c in s.chars() {
|
||||||
|
rslt.extend(c.escape_debug());
|
||||||
|
match c {
|
||||||
|
'{' | '}' => rslt.push(c),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rslt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: P<Expr>) -> P<Expr> {
|
||||||
|
cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_method_call(
|
||||||
|
cx: &ExtCtxt<'_>,
|
||||||
|
path: PathSegment,
|
||||||
|
args: Vec<P<Expr>>,
|
||||||
|
span: Span,
|
||||||
|
) -> P<Expr> {
|
||||||
|
cx.expr(span, ExprKind::MethodCall(path, args, span))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: P<Expr>) -> P<Expr> {
|
||||||
|
cx.expr(sp, ExprKind::Paren(e))
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#![feature(array_windows)]
|
#![feature(array_windows)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
#![feature(decl_macro)]
|
#![feature(decl_macro)]
|
||||||
|
#![feature(if_let_guard)]
|
||||||
#![feature(is_sorted)]
|
#![feature(is_sorted)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
|
@ -157,6 +157,7 @@ symbols! {
|
|||||||
C,
|
C,
|
||||||
CStr,
|
CStr,
|
||||||
CString,
|
CString,
|
||||||
|
Capture,
|
||||||
Center,
|
Center,
|
||||||
Clone,
|
Clone,
|
||||||
Continue,
|
Continue,
|
||||||
@ -267,6 +268,8 @@ symbols! {
|
|||||||
ToOwned,
|
ToOwned,
|
||||||
ToString,
|
ToString,
|
||||||
Try,
|
Try,
|
||||||
|
TryCaptureGeneric,
|
||||||
|
TryCapturePrintable,
|
||||||
TryFrom,
|
TryFrom,
|
||||||
TryInto,
|
TryInto,
|
||||||
Ty,
|
Ty,
|
||||||
@ -276,6 +279,7 @@ symbols! {
|
|||||||
UnsafeArg,
|
UnsafeArg,
|
||||||
Vec,
|
Vec,
|
||||||
VecDeque,
|
VecDeque,
|
||||||
|
Wrapper,
|
||||||
Yield,
|
Yield,
|
||||||
_DECLS,
|
_DECLS,
|
||||||
_Self,
|
_Self,
|
||||||
@ -358,6 +362,7 @@ symbols! {
|
|||||||
assert_receiver_is_total_eq,
|
assert_receiver_is_total_eq,
|
||||||
assert_uninit_valid,
|
assert_uninit_valid,
|
||||||
assert_zero_valid,
|
assert_zero_valid,
|
||||||
|
asserting,
|
||||||
associated_const_equality,
|
associated_const_equality,
|
||||||
associated_consts,
|
associated_consts,
|
||||||
associated_type_bounds,
|
associated_type_bounds,
|
||||||
@ -1436,6 +1441,7 @@ symbols! {
|
|||||||
truncf32,
|
truncf32,
|
||||||
truncf64,
|
truncf64,
|
||||||
try_blocks,
|
try_blocks,
|
||||||
|
try_capture,
|
||||||
try_from,
|
try_from,
|
||||||
try_into,
|
try_into,
|
||||||
try_trait_v2,
|
try_trait_v2,
|
||||||
@ -1498,6 +1504,7 @@ symbols! {
|
|||||||
unsized_tuple_coercion,
|
unsized_tuple_coercion,
|
||||||
unstable,
|
unstable,
|
||||||
untagged_unions,
|
untagged_unions,
|
||||||
|
unused_imports,
|
||||||
unused_qualifications,
|
unused_qualifications,
|
||||||
unwind,
|
unwind,
|
||||||
unwind_attributes,
|
unwind_attributes,
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// revisions: with-generic-asset without-generic-asset
|
||||||
|
// [with-generic-asset] compile-flags: --cfg feature="generic_assert"
|
||||||
|
|
||||||
// Ensure assert macro does not ignore trailing garbage.
|
// Ensure assert macro does not ignore trailing garbage.
|
||||||
//
|
//
|
||||||
// See https://github.com/rust-lang/rust/issues/60024 for details.
|
// See https://github.com/rust-lang/rust/issues/60024 for details.
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
error: expected one of `,`, `.`, `?`, or an operator, found `some`
|
error: expected one of `,`, `.`, `?`, or an operator, found `some`
|
||||||
--> $DIR/assert-trailing-junk.rs:6:18
|
--> $DIR/assert-trailing-junk.rs:9:18
|
||||||
|
|
|
|
||||||
LL | assert!(true some extra junk, "whatever");
|
LL | assert!(true some extra junk, "whatever");
|
||||||
| ^^^^ expected one of `,`, `.`, `?`, or an operator
|
| ^^^^ expected one of `,`, `.`, `?`, or an operator
|
||||||
|
|
||||||
error: expected one of `,`, `.`, `?`, or an operator, found `some`
|
error: expected one of `,`, `.`, `?`, or an operator, found `some`
|
||||||
--> $DIR/assert-trailing-junk.rs:9:18
|
--> $DIR/assert-trailing-junk.rs:12:18
|
||||||
|
|
|
|
||||||
LL | assert!(true some extra junk);
|
LL | assert!(true some extra junk);
|
||||||
| ^^^^ expected one of `,`, `.`, `?`, or an operator
|
| ^^^^ expected one of `,`, `.`, `?`, or an operator
|
||||||
|
|
||||||
error: no rules expected the token `blah`
|
error: no rules expected the token `blah`
|
||||||
--> $DIR/assert-trailing-junk.rs:12:30
|
--> $DIR/assert-trailing-junk.rs:15:30
|
||||||
|
|
|
|
||||||
LL | assert!(true, "whatever" blah);
|
LL | assert!(true, "whatever" blah);
|
||||||
| -^^^^ no rules expected this token in macro call
|
| -^^^^ no rules expected this token in macro call
|
||||||
@ -19,7 +19,7 @@ LL | assert!(true, "whatever" blah);
|
|||||||
| help: missing comma here
|
| help: missing comma here
|
||||||
|
|
||||||
error: unexpected string literal
|
error: unexpected string literal
|
||||||
--> $DIR/assert-trailing-junk.rs:15:18
|
--> $DIR/assert-trailing-junk.rs:18:18
|
||||||
|
|
|
|
||||||
LL | assert!(true "whatever" blah);
|
LL | assert!(true "whatever" blah);
|
||||||
| -^^^^^^^^^^
|
| -^^^^^^^^^^
|
||||||
@ -27,7 +27,7 @@ LL | assert!(true "whatever" blah);
|
|||||||
| help: try adding a comma
|
| help: try adding a comma
|
||||||
|
|
||||||
error: no rules expected the token `blah`
|
error: no rules expected the token `blah`
|
||||||
--> $DIR/assert-trailing-junk.rs:15:29
|
--> $DIR/assert-trailing-junk.rs:18:29
|
||||||
|
|
|
|
||||||
LL | assert!(true "whatever" blah);
|
LL | assert!(true "whatever" blah);
|
||||||
| -^^^^ no rules expected this token in macro call
|
| -^^^^ no rules expected this token in macro call
|
||||||
@ -35,7 +35,7 @@ LL | assert!(true "whatever" blah);
|
|||||||
| help: missing comma here
|
| help: missing comma here
|
||||||
|
|
||||||
error: macro requires an expression as an argument
|
error: macro requires an expression as an argument
|
||||||
--> $DIR/assert-trailing-junk.rs:19:5
|
--> $DIR/assert-trailing-junk.rs:22:5
|
||||||
|
|
|
|
||||||
LL | assert!(true;);
|
LL | assert!(true;);
|
||||||
| ^^^^^^^^^^^^-^
|
| ^^^^^^^^^^^^-^
|
||||||
@ -43,7 +43,7 @@ LL | assert!(true;);
|
|||||||
| help: try removing semicolon
|
| help: try removing semicolon
|
||||||
|
|
||||||
error: unexpected string literal
|
error: unexpected string literal
|
||||||
--> $DIR/assert-trailing-junk.rs:22:27
|
--> $DIR/assert-trailing-junk.rs:25:27
|
||||||
|
|
|
|
||||||
LL | assert!(false || true "error message");
|
LL | assert!(false || true "error message");
|
||||||
| -^^^^^^^^^^^^^^^
|
| -^^^^^^^^^^^^^^^
|
@ -0,0 +1,54 @@
|
|||||||
|
error: expected one of `,`, `.`, `?`, or an operator, found `some`
|
||||||
|
--> $DIR/assert-trailing-junk.rs:9:18
|
||||||
|
|
|
||||||
|
LL | assert!(true some extra junk, "whatever");
|
||||||
|
| ^^^^ expected one of `,`, `.`, `?`, or an operator
|
||||||
|
|
||||||
|
error: expected one of `,`, `.`, `?`, or an operator, found `some`
|
||||||
|
--> $DIR/assert-trailing-junk.rs:12:18
|
||||||
|
|
|
||||||
|
LL | assert!(true some extra junk);
|
||||||
|
| ^^^^ expected one of `,`, `.`, `?`, or an operator
|
||||||
|
|
||||||
|
error: no rules expected the token `blah`
|
||||||
|
--> $DIR/assert-trailing-junk.rs:15:30
|
||||||
|
|
|
||||||
|
LL | assert!(true, "whatever" blah);
|
||||||
|
| -^^^^ no rules expected this token in macro call
|
||||||
|
| |
|
||||||
|
| help: missing comma here
|
||||||
|
|
||||||
|
error: unexpected string literal
|
||||||
|
--> $DIR/assert-trailing-junk.rs:18:18
|
||||||
|
|
|
||||||
|
LL | assert!(true "whatever" blah);
|
||||||
|
| -^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| help: try adding a comma
|
||||||
|
|
||||||
|
error: no rules expected the token `blah`
|
||||||
|
--> $DIR/assert-trailing-junk.rs:18:29
|
||||||
|
|
|
||||||
|
LL | assert!(true "whatever" blah);
|
||||||
|
| -^^^^ no rules expected this token in macro call
|
||||||
|
| |
|
||||||
|
| help: missing comma here
|
||||||
|
|
||||||
|
error: macro requires an expression as an argument
|
||||||
|
--> $DIR/assert-trailing-junk.rs:22:5
|
||||||
|
|
|
||||||
|
LL | assert!(true;);
|
||||||
|
| ^^^^^^^^^^^^-^
|
||||||
|
| |
|
||||||
|
| help: try removing semicolon
|
||||||
|
|
||||||
|
error: unexpected string literal
|
||||||
|
--> $DIR/assert-trailing-junk.rs:25:27
|
||||||
|
|
|
||||||
|
LL | assert!(false || true "error message");
|
||||||
|
| -^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| help: try adding a comma
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
@ -1,3 +1,6 @@
|
|||||||
|
// revisions: with-generic-asset without-generic-asset
|
||||||
|
// [with-generic-asset] compile-flags: --cfg feature="generic_assert"
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
assert!(); //~ ERROR requires a boolean expression
|
assert!(); //~ ERROR requires a boolean expression
|
||||||
assert!(struct); //~ ERROR expected expression
|
assert!(struct); //~ ERROR expected expression
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
error: macro requires a boolean expression as an argument
|
error: macro requires a boolean expression as an argument
|
||||||
--> $DIR/assert.rs:2:5
|
--> $DIR/assert.rs:5:5
|
||||||
|
|
|
|
||||||
LL | assert!();
|
LL | assert!();
|
||||||
| ^^^^^^^^^ boolean expression required
|
| ^^^^^^^^^ boolean expression required
|
||||||
|
|
||||||
error: expected expression, found keyword `struct`
|
error: expected expression, found keyword `struct`
|
||||||
--> $DIR/assert.rs:3:13
|
--> $DIR/assert.rs:6:13
|
||||||
|
|
|
|
||||||
LL | assert!(struct);
|
LL | assert!(struct);
|
||||||
| ^^^^^^ expected expression
|
| ^^^^^^ expected expression
|
||||||
|
|
||||||
error: macro requires a boolean expression as an argument
|
error: macro requires a boolean expression as an argument
|
||||||
--> $DIR/assert.rs:4:5
|
--> $DIR/assert.rs:7:5
|
||||||
|
|
|
|
||||||
LL | debug_assert!();
|
LL | debug_assert!();
|
||||||
| ^^^^^^^^^^^^^^^ boolean expression required
|
| ^^^^^^^^^^^^^^^ boolean expression required
|
||||||
@ -19,7 +19,7 @@ LL | debug_assert!();
|
|||||||
= note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: expected expression, found keyword `struct`
|
error: expected expression, found keyword `struct`
|
||||||
--> $DIR/assert.rs:5:19
|
--> $DIR/assert.rs:8:19
|
||||||
|
|
|
|
||||||
LL | debug_assert!(struct);
|
LL | debug_assert!(struct);
|
||||||
| ^^^^^^ expected expression
|
| ^^^^^^ expected expression
|
28
src/test/ui/macros/assert.without-generic-asset.stderr
Normal file
28
src/test/ui/macros/assert.without-generic-asset.stderr
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
error: macro requires a boolean expression as an argument
|
||||||
|
--> $DIR/assert.rs:5:5
|
||||||
|
|
|
||||||
|
LL | assert!();
|
||||||
|
| ^^^^^^^^^ boolean expression required
|
||||||
|
|
||||||
|
error: expected expression, found keyword `struct`
|
||||||
|
--> $DIR/assert.rs:6:13
|
||||||
|
|
|
||||||
|
LL | assert!(struct);
|
||||||
|
| ^^^^^^ expected expression
|
||||||
|
|
||||||
|
error: macro requires a boolean expression as an argument
|
||||||
|
--> $DIR/assert.rs:7:5
|
||||||
|
|
|
||||||
|
LL | debug_assert!();
|
||||||
|
| ^^^^^^^^^^^^^^^ boolean expression required
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: expected expression, found keyword `struct`
|
||||||
|
--> $DIR/assert.rs:8:19
|
||||||
|
|
|
||||||
|
LL | debug_assert!(struct);
|
||||||
|
| ^^^^^^ expected expression
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
@ -0,0 +1,143 @@
|
|||||||
|
// edition:2021
|
||||||
|
// ignore-tidy-linelength
|
||||||
|
// only-x86_64
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![allow(path_statements, unused_allocation)]
|
||||||
|
#![feature(box_syntax, core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
|
||||||
|
macro_rules! test {
|
||||||
|
(
|
||||||
|
let mut $elem_ident:ident = $elem_expr:expr;
|
||||||
|
[ $($assert:tt)* ] => $msg:literal
|
||||||
|
) => {
|
||||||
|
{
|
||||||
|
#[allow(unused_assignments, unused_mut, unused_variables)]
|
||||||
|
let rslt = std::panic::catch_unwind(|| {
|
||||||
|
let mut $elem_ident = $elem_expr;
|
||||||
|
assert!($($assert)*);
|
||||||
|
});
|
||||||
|
let err = rslt.unwrap_err();
|
||||||
|
if let Some(elem) = err.downcast_ref::<String>() {
|
||||||
|
assert_eq!(elem, &$msg);
|
||||||
|
}
|
||||||
|
else if let Some(elem) = err.downcast_ref::<&str>() {
|
||||||
|
assert_eq!(elem, &$msg);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("assert!( ... ) should return a string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! tests {
|
||||||
|
(
|
||||||
|
let mut $elem_ident:ident = $elem_expr:expr;
|
||||||
|
|
||||||
|
$(
|
||||||
|
[ $($elem_assert:tt)* ] => $elem_msg:literal
|
||||||
|
)+
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
test!(
|
||||||
|
let mut $elem_ident = $elem_expr;
|
||||||
|
[ $($elem_assert)* ] => $elem_msg
|
||||||
|
);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO: Foo = Foo { bar: 1 };
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
struct Foo {
|
||||||
|
bar: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// ***** Allowed *****
|
||||||
|
|
||||||
|
tests!(
|
||||||
|
let mut elem = 1i32;
|
||||||
|
|
||||||
|
// binary
|
||||||
|
[ elem + 1 == 3 ] => "Assertion failed: elem + 1 == 3\nWith captures:\n elem = 1\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// ***** Disallowed *****
|
||||||
|
|
||||||
|
tests!(
|
||||||
|
let mut elem = 1i32;
|
||||||
|
|
||||||
|
// assign
|
||||||
|
[ { let local = elem; local } == 3 ] => "Assertion failed: { let local = elem; local } == 3"
|
||||||
|
|
||||||
|
// assign op
|
||||||
|
[ { elem += 1; elem } == 3 ] => "Assertion failed: { elem += 1; elem } == 3"
|
||||||
|
|
||||||
|
// async
|
||||||
|
[ { let _ = async { elem }; elem } == 3 ] => "Assertion failed: { let _ = async { elem }; elem } == 3"
|
||||||
|
|
||||||
|
// await
|
||||||
|
|
||||||
|
// block
|
||||||
|
[ { elem } == 3 ] => "Assertion failed: { elem } == 3"
|
||||||
|
|
||||||
|
// box
|
||||||
|
[ box elem == box 3 ] => "Assertion failed: box elem == box 3"
|
||||||
|
|
||||||
|
// break
|
||||||
|
[ loop { break elem; } == 3 ] => "Assertion failed: loop { break elem; } == 3"
|
||||||
|
|
||||||
|
// closure
|
||||||
|
[(|| elem)() == 3 ] => "Assertion failed: (|| elem)() == 3"
|
||||||
|
|
||||||
|
// const block
|
||||||
|
|
||||||
|
// continue
|
||||||
|
|
||||||
|
// err
|
||||||
|
|
||||||
|
// field
|
||||||
|
[ FOO.bar == 3 ] => "Assertion failed: FOO.bar == 3"
|
||||||
|
|
||||||
|
// for loop
|
||||||
|
[ { for _ in 0..elem { elem; } elem } == 3 ] => "Assertion failed: { for _ in 0..elem { elem; } elem } == 3"
|
||||||
|
|
||||||
|
// if
|
||||||
|
[ if true { elem } else { elem } == 3 ] => "Assertion failed: if true { elem } else { elem } == 3"
|
||||||
|
|
||||||
|
// inline asm
|
||||||
|
|
||||||
|
// let
|
||||||
|
[ if let true = true { elem } else { elem } == 3 ] => "Assertion failed: if let true = true { elem } else { elem } == 3"
|
||||||
|
|
||||||
|
// lit
|
||||||
|
|
||||||
|
// loop
|
||||||
|
[ loop { elem; break elem; } == 3 ] => "Assertion failed: loop { elem; break elem; } == 3"
|
||||||
|
|
||||||
|
// mac call
|
||||||
|
|
||||||
|
// match
|
||||||
|
[ match elem { _ => elem } == 3 ] => "Assertion failed: match elem { _ => elem, } == 3"
|
||||||
|
|
||||||
|
// ret
|
||||||
|
[ (|| { return elem; })() == 3 ] => "Assertion failed: (|| { return elem; })() == 3"
|
||||||
|
|
||||||
|
// try
|
||||||
|
[ (|| { Some(Some(elem)?) })() == Some(3) ] => "Assertion failed: (|| { Some(Some(elem)?) })() == Some(3)"
|
||||||
|
|
||||||
|
// try block
|
||||||
|
|
||||||
|
// underscore
|
||||||
|
|
||||||
|
// while
|
||||||
|
[ { while false { elem; break; } elem } == 3 ] => "Assertion failed: { while false { elem; break; } elem } == 3"
|
||||||
|
|
||||||
|
// yeet
|
||||||
|
|
||||||
|
// yield
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// aux-build:common.rs
|
||||||
|
// ignore-tidy-linelength
|
||||||
|
// only-x86_64
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
|
||||||
|
extern crate common;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
struct CopyNoDebug(i32);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct NoCopyDebug(i32);
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct NoCopyNoDebug(i32);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Has Copy but does not have Debug
|
||||||
|
common::test!(
|
||||||
|
let mut copy_no_debug = CopyNoDebug(1);
|
||||||
|
[ copy_no_debug == CopyNoDebug(3) ] => "Assertion failed: copy_no_debug == CopyNoDebug(3)\nWith captures:\n copy_no_debug = N/A\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Does not have Copy but has Debug
|
||||||
|
common::test!(
|
||||||
|
let mut no_copy_debug = NoCopyDebug(1);
|
||||||
|
[ no_copy_debug == NoCopyDebug(3) ] => "Assertion failed: no_copy_debug == NoCopyDebug(3)\nWith captures:\n no_copy_debug = N/A\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Does not have Copy and does not have Debug
|
||||||
|
common::test!(
|
||||||
|
let mut no_copy_no_debug = NoCopyNoDebug(1);
|
||||||
|
[ no_copy_no_debug == NoCopyNoDebug(3) ] => "Assertion failed: no_copy_no_debug == NoCopyNoDebug(3)\nWith captures:\n no_copy_no_debug = N/A\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Unevaluated (Expression short-circuited)
|
||||||
|
common::test!(
|
||||||
|
let mut elem = true;
|
||||||
|
[ false && elem ] => "Assertion failed: false && elem\nWith captures:\n elem = N/A\n"
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// compile-flags: --test
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
|
||||||
|
#[should_panic(expected = "Custom user message")]
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
assert!(1 == 3, "Custom user message");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
// aux-build:common.rs
|
||||||
|
// only-x86_64
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
|
||||||
|
extern crate common;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
common::test!(
|
||||||
|
let mut _nothing = ();
|
||||||
|
[ 1 == 3 ] => "Assertion failed: 1 == 3"
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! test {
|
||||||
|
(
|
||||||
|
let mut $elem_ident:ident = $elem_expr:expr;
|
||||||
|
[ $($assert:tt)* ] => $msg:literal
|
||||||
|
) => {
|
||||||
|
{
|
||||||
|
#[allow(unused_assignments, unused_mut, unused_variables)]
|
||||||
|
let rslt = std::panic::catch_unwind(|| {
|
||||||
|
let mut $elem_ident = $elem_expr;
|
||||||
|
assert!($($assert)*);
|
||||||
|
});
|
||||||
|
let err = rslt.unwrap_err();
|
||||||
|
if let Some(elem) = err.downcast_ref::<String>() {
|
||||||
|
assert_eq!(elem, &$msg);
|
||||||
|
}
|
||||||
|
else if let Some(elem) = err.downcast_ref::<&str>() {
|
||||||
|
assert_eq!(elem, &$msg);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("assert!( ... ) should return a string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
// check-pass
|
||||||
|
// compile-flags: -Z unpretty=expanded
|
||||||
|
|
||||||
|
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let elem = 1i32;
|
||||||
|
assert!(elem == 1);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
#![feature(prelude_import)]
|
||||||
|
#![no_std]
|
||||||
|
// check-pass
|
||||||
|
// compile-flags: -Z unpretty=expanded
|
||||||
|
|
||||||
|
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
#[prelude_import]
|
||||||
|
use ::std::prelude::rust_2015::*;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let elem = 1i32;
|
||||||
|
{
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};
|
||||||
|
let mut __capture0 = ::core::asserting::Capture::new();
|
||||||
|
let __local_bind0 = &elem;
|
||||||
|
if !(*{
|
||||||
|
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
|
||||||
|
__local_bind0
|
||||||
|
} == 1) {
|
||||||
|
{
|
||||||
|
::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ",
|
||||||
|
"\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
// compile-flags: --test
|
// compile-flags: --test
|
||||||
|
// ignore-tidy-linelength
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
// `generic_assert` is completely unimplemented and doesn't generate any logic, thus the
|
|
||||||
// reason why this test currently passes
|
|
||||||
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
|
||||||
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
@ -16,10 +15,11 @@ impl Debug for CopyDebug {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[should_panic(expected = "Assertion failed: copy_debug == CopyDebug(3)\nWith captures:\n copy_debug = With great power comes great electricity bills\n")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let _copy_debug = CopyDebug(1);
|
let copy_debug = CopyDebug(1);
|
||||||
assert!(_copy_debug == CopyDebug(3));
|
assert!(copy_debug == CopyDebug(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user