Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1c2c236

Browse files
committedJun 13, 2022
[RFC 2011] Minimal initial implementation
1 parent 083721a commit 1c2c236

17 files changed

+665
-41
lines changed
 
Lines changed: 281 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,299 @@
1-
use rustc_ast::{ptr::P, Expr, Path};
1+
use crate::assert::expr_if_not;
2+
use rustc_ast::{
3+
attr,
4+
ptr::P,
5+
token,
6+
tokenstream::{DelimSpan, TokenStream, TokenTree},
7+
BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path,
8+
PathSegment, Stmt, UseTree, UseTreeKind, DUMMY_NODE_ID,
9+
};
10+
use rustc_ast_pretty::pprust;
11+
use rustc_data_structures::fx::FxHashSet;
212
use rustc_expand::base::ExtCtxt;
3-
use rustc_span::Span;
13+
use rustc_span::{
14+
symbol::{sym, Ident, Symbol},
15+
Span,
16+
};
417

518
pub(super) struct Context<'cx, 'a> {
19+
// Top-level `let captureN = Capture::new()` statements
20+
capture_decls: Vec<Capture>,
621
cx: &'cx ExtCtxt<'a>,
22+
// Formatting string used for debugging
23+
fmt_string: String,
24+
// Top-level `let __local_bindN = &expr` statements
25+
local_bind_decls: Vec<Stmt>,
26+
// Used to avoid capturing duplicated paths
27+
//
28+
// ```rust
29+
// let a = 1i32;
30+
// assert!(add(a, a) == 3);
31+
// ```
32+
paths: FxHashSet<Ident>,
733
span: Span,
834
}
935

1036
impl<'cx, 'a> Context<'cx, 'a> {
1137
pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {
12-
Self { cx, span }
38+
Self {
39+
capture_decls: <_>::default(),
40+
cx,
41+
fmt_string: <_>::default(),
42+
local_bind_decls: <_>::default(),
43+
paths: <_>::default(),
44+
span,
45+
}
1346
}
1447

15-
/// Builds the whole `assert!` expression.
48+
/// Builds the whole `assert!` expression. For example, `let elem = 1; assert!(elem == 1);` expands to:
1649
///
50+
/// ```rust
51+
/// let elem = 1;
1752
/// {
18-
/// use ::core::asserting::{ ... };
53+
/// #[allow(unused_imports)]
54+
/// use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};
55+
/// let mut __capture0 = ::core::asserting::Capture::new();
56+
/// let __local_bind0 = &elem;
57+
/// if !(
58+
/// *{
59+
/// (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
60+
/// __local_bind0
61+
/// } == 1
62+
/// ) {
63+
/// panic!("Assertion failed: elem == 1\nWith captures:\n elem = {}", __capture0)
64+
/// }
65+
/// }
66+
/// ```
67+
pub(super) fn build(mut self, mut cond_expr: P<Expr>, panic_path: Path) -> P<Expr> {
68+
let expr_str = pprust::expr_to_string(&cond_expr);
69+
self.manage_cond_expr(&mut cond_expr);
70+
let initial_imports = self.build_initial_imports();
71+
let panic = self.build_panic(&expr_str, panic_path);
72+
73+
let Self { capture_decls, cx, local_bind_decls, span, .. } = self;
74+
75+
let mut stmts = Vec::with_capacity(4);
76+
stmts.push(initial_imports);
77+
stmts.extend(capture_decls.into_iter().map(|c| c.decl));
78+
stmts.extend(local_bind_decls);
79+
stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None)));
80+
cx.expr_block(cx.block(span, stmts))
81+
}
82+
83+
/// Initial **trait** imports
84+
///
85+
/// use ::core::asserting::{ ... };
86+
fn build_initial_imports(&self) -> Stmt {
87+
let nested_tree = |this: &Self, sym| {
88+
(
89+
UseTree {
90+
prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]),
91+
kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),
92+
span: this.span,
93+
},
94+
DUMMY_NODE_ID,
95+
)
96+
};
97+
self.cx.stmt_item(
98+
self.span,
99+
self.cx.item(
100+
self.span,
101+
Ident::empty(),
102+
vec![self.cx.attribute(attr::mk_list_item(
103+
Ident::new(sym::allow, self.span),
104+
vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],
105+
))],
106+
ItemKind::Use(UseTree {
107+
prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
108+
kind: UseTreeKind::Nested(vec![
109+
nested_tree(self, sym::TryCaptureGeneric),
110+
nested_tree(self, sym::TryCapturePrintable),
111+
]),
112+
span: self.span,
113+
}),
114+
),
115+
)
116+
}
117+
118+
/// The necessary custom `panic!(...)` expression.
119+
///
120+
/// panic!(
121+
/// "Assertion failed: ... \n With expansion: ...",
122+
/// __capture0,
123+
/// ...
124+
/// );
125+
fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
126+
let escaped_expr_str = escape_to_fmt(expr_str);
127+
let initial = [
128+
TokenTree::token(
129+
token::Literal(token::Lit {
130+
kind: token::LitKind::Str,
131+
symbol: Symbol::intern(&if self.fmt_string.is_empty() {
132+
format!("Assertion failed: {escaped_expr_str}")
133+
} else {
134+
format!(
135+
"Assertion failed: {escaped_expr_str}\nWith captures:\n{}",
136+
&self.fmt_string
137+
)
138+
}),
139+
suffix: None,
140+
}),
141+
self.span,
142+
),
143+
TokenTree::token(token::Comma, self.span),
144+
];
145+
let captures = self.capture_decls.iter().flat_map(|cap| {
146+
[
147+
TokenTree::token(token::Ident(cap.ident.name, false), cap.ident.span),
148+
TokenTree::token(token::Comma, self.span),
149+
]
150+
});
151+
self.cx.expr(
152+
self.span,
153+
ExprKind::MacCall(MacCall {
154+
path: panic_path,
155+
args: P(MacArgs::Delimited(
156+
DelimSpan::from_single(self.span),
157+
MacDelimiter::Parenthesis,
158+
initial.into_iter().chain(captures).collect::<TokenStream>(),
159+
)),
160+
prior_type_ascription: None,
161+
}),
162+
)
163+
}
164+
165+
/// Recursive function called until `cond_expr` and `fmt_str` are fully modified.
166+
///
167+
/// See [Self::manage_initial_capture] and [Self::manage_try_capture]
168+
fn manage_cond_expr(&mut self, expr: &mut P<Expr>) {
169+
match (*expr).kind {
170+
ExprKind::Binary(_, ref mut lhs, ref mut rhs) => {
171+
self.manage_cond_expr(lhs);
172+
self.manage_cond_expr(rhs);
173+
}
174+
ExprKind::Path(_, Path { ref segments, .. }) if let &[ref path_segment] = &segments[..] => {
175+
let path_ident = path_segment.ident;
176+
self.manage_initial_capture(expr, path_ident);
177+
}
178+
_ => {}
179+
}
180+
}
181+
182+
/// Pushes the top-level declarations and modifies `expr` to try capturing variables.
19183
///
20-
/// let mut __capture0 = Capture::new();
21-
/// ...
22-
/// ...
23-
/// ...
184+
/// `fmt_str`, the formatting string used for debugging, is constructed to show possible
185+
/// captured variables.
186+
fn manage_initial_capture(&mut self, expr: &mut P<Expr>, path_ident: Ident) {
187+
if self.paths.contains(&path_ident) {
188+
return;
189+
} else {
190+
self.fmt_string.push_str(" ");
191+
self.fmt_string.push_str(path_ident.as_str());
192+
self.fmt_string.push_str(" = {:?}\n");
193+
let _ = self.paths.insert(path_ident);
194+
}
195+
let curr_capture_idx = self.capture_decls.len();
196+
let capture_string = format!("__capture{curr_capture_idx}");
197+
let ident = Ident::new(Symbol::intern(&capture_string), self.span);
198+
let init_std_path = self.cx.std_path(&[sym::asserting, sym::Capture, sym::new]);
199+
let init = self.cx.expr_call(
200+
self.span,
201+
self.cx.expr_path(self.cx.path(self.span, init_std_path)),
202+
vec![],
203+
);
204+
let capture = Capture { decl: self.cx.stmt_let(self.span, true, ident, init), ident };
205+
self.capture_decls.push(capture);
206+
self.manage_try_capture(ident, curr_capture_idx, expr);
207+
}
208+
209+
/// Tries to copy `__local_bindN` into `__captureN`.
24210
///
25-
/// if !{
26-
/// ...
27-
/// ...
28-
/// ...
29-
/// } {
30-
/// panic!(
31-
/// "Assertion failed: ... \n With expansion: ...",
32-
/// __capture0,
33-
/// ...
34-
/// ...
35-
/// ...
36-
/// );
37-
/// }
211+
/// *{
212+
/// (&Wrapper(__local_bindN)).try_capture(&mut __captureN);
213+
/// __local_bindN
38214
/// }
39-
pub(super) fn build(self, _cond_expr: P<Expr>, _panic_path: Path) -> P<Expr> {
40-
let Self { cx, span, .. } = self;
41-
let stmts = Vec::new();
42-
cx.expr_block(cx.block(span, stmts))
215+
fn manage_try_capture(&mut self, capture: Ident, curr_capture_idx: usize, expr: &mut P<Expr>) {
216+
let local_bind_string = format!("__local_bind{curr_capture_idx}");
217+
let local_bind = Ident::new(Symbol::intern(&local_bind_string), self.span);
218+
self.local_bind_decls.push(self.cx.stmt_let(
219+
self.span,
220+
false,
221+
local_bind,
222+
self.cx.expr_addr_of(self.span, expr.clone()),
223+
));
224+
let wrapper = self.cx.expr_call(
225+
self.span,
226+
self.cx.expr_path(
227+
self.cx.path(self.span, self.cx.std_path(&[sym::asserting, sym::Wrapper])),
228+
),
229+
vec![self.cx.expr_path(Path::from_ident(local_bind))],
230+
);
231+
let try_capture_call = self
232+
.cx
233+
.stmt_expr(expr_method_call(
234+
self.cx,
235+
PathSegment {
236+
args: None,
237+
id: DUMMY_NODE_ID,
238+
ident: Ident::new(sym::try_capture, self.span),
239+
},
240+
vec![
241+
expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)),
242+
expr_addr_of_mut(
243+
self.cx,
244+
self.span,
245+
self.cx.expr_path(Path::from_ident(capture)),
246+
),
247+
],
248+
self.span,
249+
))
250+
.add_trailing_semicolon();
251+
let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind));
252+
let ret = self.cx.stmt_expr(local_bind_path);
253+
let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret]));
254+
*expr = self.cx.expr_deref(self.span, block);
255+
}
256+
}
257+
258+
/// Information about a captured element.
259+
#[derive(Debug)]
260+
struct Capture {
261+
// Generated indexed `Capture` statement.
262+
//
263+
// `let __capture{} = Capture::new();`
264+
decl: Stmt,
265+
// The name of the generated indexed `Capture` variable.
266+
//
267+
// `__capture{}`
268+
ident: Ident,
269+
}
270+
271+
/// Escapes to use as a formatting string.
272+
fn escape_to_fmt(s: &str) -> String {
273+
let mut rslt = String::with_capacity(s.len());
274+
for c in s.chars() {
275+
rslt.extend(c.escape_debug());
276+
match c {
277+
'{' | '}' => rslt.push(c),
278+
_ => {}
279+
}
43280
}
281+
rslt
282+
}
283+
284+
fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: P<Expr>) -> P<Expr> {
285+
cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e))
286+
}
287+
288+
fn expr_method_call(
289+
cx: &ExtCtxt<'_>,
290+
path: PathSegment,
291+
args: Vec<P<Expr>>,
292+
span: Span,
293+
) -> P<Expr> {
294+
cx.expr(span, ExprKind::MethodCall(path, args, span))
295+
}
296+
297+
fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: P<Expr>) -> P<Expr> {
298+
cx.expr(sp, ExprKind::Paren(e))
44299
}

‎compiler/rustc_builtin_macros/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![feature(array_windows)]
77
#![feature(box_patterns)]
88
#![feature(decl_macro)]
9+
#![feature(if_let_guard)]
910
#![feature(is_sorted)]
1011
#![feature(let_chains)]
1112
#![feature(let_else)]

‎compiler/rustc_span/src/symbol.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ symbols! {
156156
C,
157157
CStr,
158158
CString,
159+
Capture,
159160
Center,
160161
Clone,
161162
Continue,
@@ -263,6 +264,8 @@ symbols! {
263264
ToOwned,
264265
ToString,
265266
Try,
267+
TryCaptureGeneric,
268+
TryCapturePrintable,
266269
TryFrom,
267270
TryInto,
268271
Ty,
@@ -272,6 +275,7 @@ symbols! {
272275
UnsafeArg,
273276
Vec,
274277
VecDeque,
278+
Wrapper,
275279
Yield,
276280
_DECLS,
277281
_Self,
@@ -354,6 +358,7 @@ symbols! {
354358
assert_receiver_is_total_eq,
355359
assert_uninit_valid,
356360
assert_zero_valid,
361+
asserting,
357362
associated_const_equality,
358363
associated_consts,
359364
associated_type_bounds,
@@ -1432,6 +1437,7 @@ symbols! {
14321437
truncf32,
14331438
truncf64,
14341439
try_blocks,
1440+
try_capture,
14351441
try_from,
14361442
try_into,
14371443
try_trait_v2,
@@ -1494,6 +1500,7 @@ symbols! {
14941500
unsized_tuple_coercion,
14951501
unstable,
14961502
untagged_unions,
1503+
unused_imports,
14971504
unused_qualifications,
14981505
unwind,
14991506
unwind_attributes,

‎src/test/ui/macros/assert-trailing-junk.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// revisions: with-generic-asset without-generic-asset
2+
// [with-generic-asset] compile-flags: --cfg feature="generic_assert"
3+
14
// Ensure assert macro does not ignore trailing garbage.
25
//
36
// See https://github.com/rust-lang/rust/issues/60024 for details.

‎src/test/ui/macros/assert-trailing-junk.stderr renamed to ‎src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,49 @@
11
error: expected one of `,`, `.`, `?`, or an operator, found `some`
2-
--> $DIR/assert-trailing-junk.rs:6:18
2+
--> $DIR/assert-trailing-junk.rs:9:18
33
|
44
LL | assert!(true some extra junk, "whatever");
55
| ^^^^ expected one of `,`, `.`, `?`, or an operator
66

77
error: expected one of `,`, `.`, `?`, or an operator, found `some`
8-
--> $DIR/assert-trailing-junk.rs:9:18
8+
--> $DIR/assert-trailing-junk.rs:12:18
99
|
1010
LL | assert!(true some extra junk);
1111
| ^^^^ expected one of `,`, `.`, `?`, or an operator
1212

1313
error: no rules expected the token `blah`
14-
--> $DIR/assert-trailing-junk.rs:12:30
14+
--> $DIR/assert-trailing-junk.rs:15:30
1515
|
1616
LL | assert!(true, "whatever" blah);
1717
| -^^^^ no rules expected this token in macro call
1818
| |
1919
| help: missing comma here
2020

2121
error: unexpected string literal
22-
--> $DIR/assert-trailing-junk.rs:15:18
22+
--> $DIR/assert-trailing-junk.rs:18:18
2323
|
2424
LL | assert!(true "whatever" blah);
2525
| -^^^^^^^^^^
2626
| |
2727
| help: try adding a comma
2828

2929
error: no rules expected the token `blah`
30-
--> $DIR/assert-trailing-junk.rs:15:29
30+
--> $DIR/assert-trailing-junk.rs:18:29
3131
|
3232
LL | assert!(true "whatever" blah);
3333
| -^^^^ no rules expected this token in macro call
3434
| |
3535
| help: missing comma here
3636

3737
error: macro requires an expression as an argument
38-
--> $DIR/assert-trailing-junk.rs:19:5
38+
--> $DIR/assert-trailing-junk.rs:22:5
3939
|
4040
LL | assert!(true;);
4141
| ^^^^^^^^^^^^-^
4242
| |
4343
| help: try removing semicolon
4444

4545
error: unexpected string literal
46-
--> $DIR/assert-trailing-junk.rs:22:27
46+
--> $DIR/assert-trailing-junk.rs:25:27
4747
|
4848
LL | assert!(false || true "error message");
4949
| -^^^^^^^^^^^^^^^
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
error: expected one of `,`, `.`, `?`, or an operator, found `some`
2+
--> $DIR/assert-trailing-junk.rs:9:18
3+
|
4+
LL | assert!(true some extra junk, "whatever");
5+
| ^^^^ expected one of `,`, `.`, `?`, or an operator
6+
7+
error: expected one of `,`, `.`, `?`, or an operator, found `some`
8+
--> $DIR/assert-trailing-junk.rs:12:18
9+
|
10+
LL | assert!(true some extra junk);
11+
| ^^^^ expected one of `,`, `.`, `?`, or an operator
12+
13+
error: no rules expected the token `blah`
14+
--> $DIR/assert-trailing-junk.rs:15:30
15+
|
16+
LL | assert!(true, "whatever" blah);
17+
| -^^^^ no rules expected this token in macro call
18+
| |
19+
| help: missing comma here
20+
21+
error: unexpected string literal
22+
--> $DIR/assert-trailing-junk.rs:18:18
23+
|
24+
LL | assert!(true "whatever" blah);
25+
| -^^^^^^^^^^
26+
| |
27+
| help: try adding a comma
28+
29+
error: no rules expected the token `blah`
30+
--> $DIR/assert-trailing-junk.rs:18:29
31+
|
32+
LL | assert!(true "whatever" blah);
33+
| -^^^^ no rules expected this token in macro call
34+
| |
35+
| help: missing comma here
36+
37+
error: macro requires an expression as an argument
38+
--> $DIR/assert-trailing-junk.rs:22:5
39+
|
40+
LL | assert!(true;);
41+
| ^^^^^^^^^^^^-^
42+
| |
43+
| help: try removing semicolon
44+
45+
error: unexpected string literal
46+
--> $DIR/assert-trailing-junk.rs:25:27
47+
|
48+
LL | assert!(false || true "error message");
49+
| -^^^^^^^^^^^^^^^
50+
| |
51+
| help: try adding a comma
52+
53+
error: aborting due to 7 previous errors
54+

‎src/test/ui/macros/assert.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// revisions: with-generic-asset without-generic-asset
2+
// [with-generic-asset] compile-flags: --cfg feature="generic_assert"
3+
14
fn main() {
25
assert!(); //~ ERROR requires a boolean expression
36
assert!(struct); //~ ERROR expected expression

‎src/test/ui/macros/assert.stderr renamed to ‎src/test/ui/macros/assert.with-generic-asset.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
error: macro requires a boolean expression as an argument
2-
--> $DIR/assert.rs:2:5
2+
--> $DIR/assert.rs:5:5
33
|
44
LL | assert!();
55
| ^^^^^^^^^ boolean expression required
66

77
error: expected expression, found keyword `struct`
8-
--> $DIR/assert.rs:3:13
8+
--> $DIR/assert.rs:6:13
99
|
1010
LL | assert!(struct);
1111
| ^^^^^^ expected expression
1212

1313
error: macro requires a boolean expression as an argument
14-
--> $DIR/assert.rs:4:5
14+
--> $DIR/assert.rs:7:5
1515
|
1616
LL | debug_assert!();
1717
| ^^^^^^^^^^^^^^^ boolean expression required
1818
|
1919
= note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
2020

2121
error: expected expression, found keyword `struct`
22-
--> $DIR/assert.rs:5:19
22+
--> $DIR/assert.rs:8:19
2323
|
2424
LL | debug_assert!(struct);
2525
| ^^^^^^ expected expression
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: macro requires a boolean expression as an argument
2+
--> $DIR/assert.rs:5:5
3+
|
4+
LL | assert!();
5+
| ^^^^^^^^^ boolean expression required
6+
7+
error: expected expression, found keyword `struct`
8+
--> $DIR/assert.rs:6:13
9+
|
10+
LL | assert!(struct);
11+
| ^^^^^^ expected expression
12+
13+
error: macro requires a boolean expression as an argument
14+
--> $DIR/assert.rs:7:5
15+
|
16+
LL | debug_assert!();
17+
| ^^^^^^^^^^^^^^^ boolean expression required
18+
|
19+
= note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
20+
21+
error: expected expression, found keyword `struct`
22+
--> $DIR/assert.rs:8:19
23+
|
24+
LL | debug_assert!(struct);
25+
| ^^^^^^ expected expression
26+
27+
error: aborting due to 4 previous errors
28+
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// edition:2021
2+
// ignore-tidy-linelength
3+
// run-pass
4+
5+
#![allow(path_statements, unused_allocation)]
6+
#![feature(box_syntax, core_intrinsics, generic_assert, generic_assert_internals)]
7+
8+
macro_rules! test {
9+
(
10+
let mut $elem_ident:ident = $elem_expr:expr;
11+
[ $($assert:tt)* ] => $msg:literal
12+
) => {
13+
{
14+
#[allow(unused_assignments, unused_mut, unused_variables)]
15+
let rslt = std::panic::catch_unwind(|| {
16+
let mut $elem_ident = $elem_expr;
17+
assert!($($assert)*);
18+
});
19+
let err = rslt.unwrap_err();
20+
if let Some(elem) = err.downcast_ref::<String>() {
21+
assert_eq!(elem, &$msg);
22+
}
23+
else if let Some(elem) = err.downcast_ref::<&str>() {
24+
assert_eq!(elem, &$msg);
25+
}
26+
else {
27+
panic!("assert!( ... ) should return a string");
28+
}
29+
}
30+
}
31+
}
32+
33+
macro_rules! tests {
34+
(
35+
let mut $elem_ident:ident = $elem_expr:expr;
36+
37+
$(
38+
[ $($elem_assert:tt)* ] => $elem_msg:literal
39+
)+
40+
) => {
41+
$(
42+
test!(
43+
let mut $elem_ident = $elem_expr;
44+
[ $($elem_assert)* ] => $elem_msg
45+
);
46+
)+
47+
}
48+
}
49+
50+
const FOO: Foo = Foo { bar: 1 };
51+
52+
#[derive(Clone, Copy, Debug, PartialEq)]
53+
struct Foo {
54+
bar: i32
55+
}
56+
57+
fn main() {
58+
// ***** Allowed *****
59+
60+
tests!(
61+
let mut elem = 1i32;
62+
63+
// binary
64+
[ elem + 1 == 3 ] => "Assertion failed: elem + 1 == 3\nWith captures:\n elem = 1\n"
65+
);
66+
67+
// ***** Disallowed *****
68+
69+
tests!(
70+
let mut elem = 1i32;
71+
72+
// assign
73+
[ { let local = elem; local } == 3 ] => "Assertion failed: { let local = elem; local } == 3"
74+
75+
// assign op
76+
[ { elem += 1; elem } == 3 ] => "Assertion failed: { elem += 1; elem } == 3"
77+
78+
// async
79+
[ { let _ = async { elem }; elem } == 3 ] => "Assertion failed: { let _ = async { elem }; elem } == 3"
80+
81+
// await
82+
83+
// block
84+
[ { elem } == 3 ] => "Assertion failed: { elem } == 3"
85+
86+
// box
87+
[ box elem == box 3 ] => "Assertion failed: box elem == box 3"
88+
89+
// break
90+
[ loop { break elem; } == 3 ] => "Assertion failed: loop { break elem; } == 3"
91+
92+
// closure
93+
[(|| elem)() == 3 ] => "Assertion failed: (|| elem)() == 3"
94+
95+
// const block
96+
97+
// continue
98+
99+
// err
100+
101+
// field
102+
[ FOO.bar == 3 ] => "Assertion failed: FOO.bar == 3"
103+
104+
// for loop
105+
[ { for _ in 0..elem { elem; } elem } == 3 ] => "Assertion failed: { for _ in 0..elem { elem; } elem } == 3"
106+
107+
// if
108+
[ if true { elem } else { elem } == 3 ] => "Assertion failed: if true { elem } else { elem } == 3"
109+
110+
// inline asm
111+
112+
// let
113+
[ if let true = true { elem } else { elem } == 3 ] => "Assertion failed: if let true = true { elem } else { elem } == 3"
114+
115+
// lit
116+
117+
// loop
118+
[ loop { elem; break elem; } == 3 ] => "Assertion failed: loop { elem; break elem; } == 3"
119+
120+
// mac call
121+
122+
// match
123+
[ match elem { _ => elem } == 3 ] => "Assertion failed: match elem { _ => elem, } == 3"
124+
125+
// ret
126+
[ (|| { return elem; })() == 3 ] => "Assertion failed: (|| { return elem; })() == 3"
127+
128+
// try
129+
[ (|| { Some(Some(elem)?) })() == Some(3) ] => "Assertion failed: (|| { Some(Some(elem)?) })() == Some(3)"
130+
131+
// try block
132+
133+
// underscore
134+
135+
// while
136+
[ { while false { elem; break; } elem } == 3 ] => "Assertion failed: { while false { elem; break; } elem } == 3"
137+
138+
// yeet
139+
140+
// yield
141+
);
142+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// aux-build:common.rs
2+
// ignore-tidy-linelength
3+
// run-pass
4+
5+
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
6+
7+
extern crate common;
8+
9+
#[derive(Clone, Copy, PartialEq)]
10+
struct CopyNoDebug(i32);
11+
12+
#[derive(Debug, PartialEq)]
13+
struct NoCopyDebug(i32);
14+
15+
#[derive(PartialEq)]
16+
struct NoCopyNoDebug(i32);
17+
18+
fn main() {
19+
// Has Copy but does not have Debug
20+
common::test!(
21+
let mut copy_no_debug = CopyNoDebug(1);
22+
[ copy_no_debug == CopyNoDebug(3) ] => "Assertion failed: copy_no_debug == CopyNoDebug(3)\nWith captures:\n copy_no_debug = N/A\n"
23+
);
24+
25+
// Does not have Copy but has Debug
26+
common::test!(
27+
let mut no_copy_debug = NoCopyDebug(1);
28+
[ no_copy_debug == NoCopyDebug(3) ] => "Assertion failed: no_copy_debug == NoCopyDebug(3)\nWith captures:\n no_copy_debug = N/A\n"
29+
);
30+
31+
// Does not have Copy and does not have Debug
32+
common::test!(
33+
let mut no_copy_no_debug = NoCopyNoDebug(1);
34+
[ no_copy_no_debug == NoCopyNoDebug(3) ] => "Assertion failed: no_copy_no_debug == NoCopyNoDebug(3)\nWith captures:\n no_copy_no_debug = N/A\n"
35+
);
36+
37+
// Unevaluated (Expression short-circuited)
38+
common::test!(
39+
let mut elem = true;
40+
[ false && elem ] => "Assertion failed: false && elem\nWith captures:\n elem = N/A\n"
41+
);
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-flags: --test
2+
// run-pass
3+
4+
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
5+
6+
#[should_panic(expected = "Custom user message")]
7+
#[test]
8+
fn test() {
9+
assert!(1 == 3, "Custom user message");
10+
}
11+
12+
fn main() {
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// aux-build:common.rs
2+
// run-pass
3+
4+
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
5+
6+
extern crate common;
7+
8+
fn main() {
9+
common::test!(
10+
let mut _nothing = ();
11+
[ 1 == 3 ] => "Assertion failed: 1 == 3"
12+
);
13+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#[macro_export]
2+
macro_rules! test {
3+
(
4+
let mut $elem_ident:ident = $elem_expr:expr;
5+
[ $($assert:tt)* ] => $msg:literal
6+
) => {
7+
{
8+
#[allow(unused_assignments, unused_mut, unused_variables)]
9+
let rslt = std::panic::catch_unwind(|| {
10+
let mut $elem_ident = $elem_expr;
11+
assert!($($assert)*);
12+
});
13+
let err = rslt.unwrap_err();
14+
if let Some(elem) = err.downcast_ref::<String>() {
15+
assert_eq!(elem, &$msg);
16+
}
17+
else if let Some(elem) = err.downcast_ref::<&str>() {
18+
assert_eq!(elem, &$msg);
19+
}
20+
else {
21+
panic!("assert!( ... ) should return a string");
22+
}
23+
}
24+
}
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// check-pass
2+
// compile-flags: -Z unpretty=expanded
3+
4+
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
5+
6+
fn main() {
7+
let elem = 1i32;
8+
assert!(elem == 1);
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![feature(prelude_import)]
2+
#![no_std]
3+
// check-pass
4+
// compile-flags: -Z unpretty=expanded
5+
6+
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
7+
#[prelude_import]
8+
use ::std::prelude::rust_2015::*;
9+
#[macro_use]
10+
extern crate std;
11+
12+
fn main() {
13+
let elem = 1i32;
14+
{
15+
#[allow(unused_imports)]
16+
use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};
17+
let mut __capture0 = ::core::asserting::Capture::new();
18+
let __local_bind0 = &elem;
19+
if !(*{
20+
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
21+
__local_bind0
22+
} == 1) {
23+
{
24+
::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ",
25+
"\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
26+
}
27+
}
28+
};
29+
}

‎src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// compile-flags: --test
2+
// ignore-tidy-linelength
23
// run-pass
34

4-
// `generic_assert` is completely unimplemented and doesn't generate any logic, thus the
5-
// reason why this test currently passes
65
#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
76

87
use std::fmt::{Debug, Formatter};
@@ -16,10 +15,11 @@ impl Debug for CopyDebug {
1615
}
1716
}
1817

18+
#[should_panic(expected = "Assertion failed: copy_debug == CopyDebug(3)\nWith captures:\n copy_debug = With great power comes great electricity bills\n")]
1919
#[test]
2020
fn test() {
21-
let _copy_debug = CopyDebug(1);
22-
assert!(_copy_debug == CopyDebug(3));
21+
let copy_debug = CopyDebug(1);
22+
assert!(copy_debug == CopyDebug(3));
2323
}
2424

2525
fn main() {

0 commit comments

Comments
 (0)
Please sign in to comment.