@@ -3,9 +3,9 @@ mod context;
3
3
use crate :: edition_panic:: use_panic_2021;
4
4
use crate :: errors;
5
5
use rustc_ast:: ptr:: P ;
6
- use rustc_ast:: token;
7
6
use rustc_ast:: token:: Delimiter ;
8
7
use rustc_ast:: tokenstream:: { DelimSpan , TokenStream } ;
8
+ use rustc_ast:: { self as ast, token} ;
9
9
use rustc_ast:: { DelimArgs , Expr , ExprKind , MacCall , Path , PathSegment , UnOp } ;
10
10
use rustc_ast_pretty:: pprust;
11
11
use rustc_errors:: PResult ;
@@ -20,7 +20,7 @@ pub fn expand_assert<'cx>(
20
20
span : Span ,
21
21
tts : TokenStream ,
22
22
) -> MacroExpanderResult < ' cx > {
23
- let Assert { cond_expr, custom_message } = match parse_assert ( cx, span, tts) {
23
+ let Assert { cond_expr, inner_cond_expr , custom_message } = match parse_assert ( cx, span, tts) {
24
24
Ok ( assert) => assert,
25
25
Err ( err) => {
26
26
let guar = err. emit ( ) ;
@@ -70,7 +70,9 @@ pub fn expand_assert<'cx>(
70
70
//
71
71
// FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
72
72
else if cx. ecfg . features . generic_assert {
73
- context:: Context :: new ( cx, call_site_span) . build ( cond_expr, panic_path ( ) )
73
+ // FIXME(estebank): we use the condition the user passed without coercing to `bool` when
74
+ // `generic_assert` is enabled, but we could use `cond_expr` instead.
75
+ context:: Context :: new ( cx, call_site_span) . build ( inner_cond_expr, panic_path ( ) )
74
76
}
75
77
// If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
76
78
// string
@@ -85,7 +87,7 @@ pub fn expand_assert<'cx>(
85
87
DUMMY_SP ,
86
88
Symbol :: intern( & format!(
87
89
"assertion failed: {}" ,
88
- pprust:: expr_to_string( & cond_expr )
90
+ pprust:: expr_to_string( & inner_cond_expr )
89
91
) ) ,
90
92
) ] ,
91
93
) ;
@@ -95,8 +97,12 @@ pub fn expand_assert<'cx>(
95
97
ExpandResult :: Ready ( MacEager :: expr ( expr) )
96
98
}
97
99
100
+ // `assert!($cond_expr, $custom_message)`
98
101
struct Assert {
102
+ // `{ let assert_macro: bool = $cond_expr; assert_macro }`
99
103
cond_expr : P < Expr > ,
104
+ // We keep the condition without the `bool` coercion for the panic message.
105
+ inner_cond_expr : P < Expr > ,
100
106
custom_message : Option < TokenStream > ,
101
107
}
102
108
@@ -118,7 +124,7 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
118
124
return Err ( cx. dcx ( ) . create_err ( errors:: AssertRequiresBoolean { span : sp } ) ) ;
119
125
}
120
126
121
- let cond_expr = parser. parse_expr ( ) ?;
127
+ let inner_cond_expr = parser. parse_expr ( ) ?;
122
128
123
129
// Some crates use the `assert!` macro in the following form (note extra semicolon):
124
130
//
@@ -154,7 +160,54 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
154
160
return parser. unexpected ( ) ;
155
161
}
156
162
157
- Ok ( Assert { cond_expr, custom_message } )
163
+ let cond_expr = expand_cond ( cx, parser, inner_cond_expr. clone ( ) ) ;
164
+ Ok ( Assert { cond_expr, inner_cond_expr, custom_message } )
165
+ }
166
+
167
+ fn expand_cond ( cx : & ExtCtxt < ' _ > , parser : Parser < ' _ > , cond_expr : P < Expr > ) -> P < Expr > {
168
+ let span = cx. with_call_site_ctxt ( cond_expr. span ) ;
169
+ // Coerce the expression to `bool` for more accurate errors. If `assert!` is passed an
170
+ // expression that isn't `bool`, the type error will point at only the expression and not the
171
+ // entire macro call. If a non-`bool` is passed that doesn't implement `trait Not`, we won't
172
+ // talk about traits, we'll just state the appropriate type error.
173
+ // `let assert_macro: bool = $expr;`
174
+ let ident = Ident :: new ( sym:: assert_macro, span) ;
175
+ let local = P ( ast:: Local {
176
+ ty : Some ( P ( ast:: Ty {
177
+ kind : ast:: TyKind :: Path ( None , ast:: Path :: from_ident ( Ident :: new ( sym:: bool, span) ) ) ,
178
+ id : ast:: DUMMY_NODE_ID ,
179
+ span,
180
+ tokens : None ,
181
+ } ) ) ,
182
+ pat : parser. mk_pat_ident ( span, ast:: BindingAnnotation :: NONE , ident) ,
183
+ kind : ast:: LocalKind :: Init ( cond_expr) ,
184
+ id : ast:: DUMMY_NODE_ID ,
185
+ span,
186
+ colon_sp : None ,
187
+ attrs : Default :: default ( ) ,
188
+ tokens : None ,
189
+ } ) ;
190
+ // `{ let assert_macro: bool = $expr; assert_macro }`
191
+ parser. mk_expr (
192
+ span,
193
+ ast:: ExprKind :: Block (
194
+ parser. mk_block (
195
+ thin_vec ! [
196
+ parser. mk_stmt( span, ast:: StmtKind :: Let ( local) ) ,
197
+ parser. mk_stmt(
198
+ span,
199
+ ast:: StmtKind :: Expr ( parser. mk_expr(
200
+ span,
201
+ ast:: ExprKind :: Path ( None , ast:: Path :: from_ident( ident) )
202
+ ) ) ,
203
+ ) ,
204
+ ] ,
205
+ ast:: BlockCheckMode :: Default ,
206
+ span,
207
+ ) ,
208
+ None ,
209
+ ) ,
210
+ )
158
211
}
159
212
160
213
fn parse_custom_message ( parser : & mut Parser < ' _ > ) -> Option < TokenStream > {
0 commit comments