@@ -11,6 +11,7 @@ use rustc_ast_pretty::pprust;
1111use rustc_errors:: PResult ;
1212use rustc_expand:: base:: { DummyResult , ExpandResult , ExtCtxt , MacEager , MacroExpanderResult } ;
1313use rustc_parse:: parser:: Parser ;
14+ use rustc_span:: edition:: Edition ;
1415use rustc_span:: symbol:: { sym, Ident , Symbol } ;
1516use rustc_span:: { Span , DUMMY_SP } ;
1617use thin_vec:: thin_vec;
@@ -172,45 +173,54 @@ fn expand_cond(cx: &ExtCtxt<'_>, parser: Parser<'_>, cond_expr: P<Expr>) -> P<Ex
172173 // talk about traits, we'll just state the appropriate type error.
173174 // `let assert_macro: bool = $expr;`
174175 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) ) )
207201 ) ,
208- None ,
209202 ) ,
203+ ] ;
204+ parser. mk_expr (
205+ span,
206+ ast:: ExprKind :: Block ( parser. mk_block ( block, ast:: BlockCheckMode :: Default , span) , None ) ,
210207 )
211208}
212209
213210fn parse_custom_message ( parser : & mut Parser < ' _ > ) -> Option < TokenStream > {
214211 let ts = parser. parse_tokens ( ) ;
215212 if !ts. is_empty ( ) { Some ( ts) } else { None }
216213}
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