@@ -11,6 +11,7 @@ use rustc_ast_pretty::pprust;
11
11
use rustc_errors:: PResult ;
12
12
use rustc_expand:: base:: { DummyResult , ExpandResult , ExtCtxt , MacEager , MacroExpanderResult } ;
13
13
use rustc_parse:: parser:: Parser ;
14
+ use rustc_span:: edition:: Edition ;
14
15
use rustc_span:: symbol:: { sym, Ident , Symbol } ;
15
16
use rustc_span:: { Span , DUMMY_SP } ;
16
17
use thin_vec:: thin_vec;
@@ -172,45 +173,54 @@ fn expand_cond(cx: &ExtCtxt<'_>, parser: Parser<'_>, cond_expr: P<Expr>) -> P<Ex
172
173
// talk about traits, we'll just state the appropriate type error.
173
174
// `let assert_macro: bool = $expr;`
174
175
let ident = Ident :: new ( sym:: assert_macro, DUMMY_SP ) ;
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, DUMMY_SP ) ) ) ,
178
- id : ast:: DUMMY_NODE_ID ,
179
- span : DUMMY_SP ,
180
- tokens : None ,
181
- } ) ) ,
182
- pat : parser. mk_pat_ident ( DUMMY_SP , 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,
176
+
177
+ let expr = if use_assert_2024 ( span) {
178
+ // `{ let assert_macro: bool = $expr; assert_macro }`
179
+ cond_expr
180
+ } else {
181
+ // In <=2021, we allow anything that can be negated to `bool`, not just `bool`s. We use the
182
+ // "double not" trick to coerce the expression to `bool`. We still assign it to a new `bool`
183
+ // binding so that in the case of a type that implements `Not` but doesn't return `bool`,
184
+ // like `i32`, we still point at the condition and not at the whole macro.
185
+ // `{ let assert_macro: bool = !!$expr; assert_macro }`
186
+ let not = |expr| parser. mk_expr ( span, ast:: ExprKind :: Unary ( ast:: UnOp :: Not , expr) ) ;
187
+ not ( not ( cond_expr) )
188
+ } ;
189
+ let block = thin_vec ! [
190
+ cx. stmt_let_ty(
191
+ DUMMY_SP ,
192
+ false ,
193
+ ident,
194
+ Some ( cx. ty_ident( span, Ident :: new( sym:: bool , DUMMY_SP ) ) ) ,
195
+ expr,
196
+ ) ,
197
+ parser. mk_stmt(
198
+ span,
199
+ ast:: StmtKind :: Expr (
200
+ parser. mk_expr( span, ast:: ExprKind :: Path ( None , ast:: Path :: from_ident( ident) ) )
207
201
) ,
208
- None ,
209
202
) ,
203
+ ] ;
204
+ parser. mk_expr (
205
+ span,
206
+ ast:: ExprKind :: Block ( parser. mk_block ( block, ast:: BlockCheckMode :: Default , span) , None ) ,
210
207
)
211
208
}
212
209
213
210
fn parse_custom_message ( parser : & mut Parser < ' _ > ) -> Option < TokenStream > {
214
211
let ts = parser. parse_tokens ( ) ;
215
212
if !ts. is_empty ( ) { Some ( ts) } else { None }
216
213
}
214
+
215
+ pub fn use_assert_2024 ( mut span : Span ) -> bool {
216
+ // To determine the edition, we check the first span up the expansion
217
+ // stack that isn't internal.
218
+ loop {
219
+ let expn = span. ctxt ( ) . outer_expn_data ( ) ;
220
+ if let Some ( _features) = expn. allow_internal_unstable {
221
+ span = expn. call_site ;
222
+ continue ;
223
+ }
224
+ break expn. edition >= Edition :: Edition2024 ;
225
+ }
226
+ }
0 commit comments