@@ -3,9 +3,9 @@ mod context;
33use crate :: edition_panic:: use_panic_2021;
44use crate :: errors;
55use rustc_ast:: ptr:: P ;
6- use rustc_ast:: token;
76use rustc_ast:: token:: Delimiter ;
87use rustc_ast:: tokenstream:: { DelimSpan , TokenStream } ;
8+ use rustc_ast:: { self as ast, token} ;
99use rustc_ast:: { DelimArgs , Expr , ExprKind , MacCall , Path , PathSegment , UnOp } ;
1010use rustc_ast_pretty:: pprust;
1111use rustc_errors:: PResult ;
@@ -20,7 +20,7 @@ pub fn expand_assert<'cx>(
2020 span : Span ,
2121 tts : TokenStream ,
2222) -> 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) {
2424 Ok ( assert) => assert,
2525 Err ( err) => {
2626 let guar = err. emit ( ) ;
@@ -70,7 +70,9 @@ pub fn expand_assert<'cx>(
7070 //
7171 // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
7272 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 ( ) )
7476 }
7577 // If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
7678 // string
@@ -85,7 +87,7 @@ pub fn expand_assert<'cx>(
8587 DUMMY_SP ,
8688 Symbol :: intern( & format!(
8789 "assertion failed: {}" ,
88- pprust:: expr_to_string( & cond_expr )
90+ pprust:: expr_to_string( & inner_cond_expr )
8991 ) ) ,
9092 ) ] ,
9193 ) ;
@@ -95,8 +97,12 @@ pub fn expand_assert<'cx>(
9597 ExpandResult :: Ready ( MacEager :: expr ( expr) )
9698}
9799
100+ // `assert!($cond_expr, $custom_message)`
98101struct Assert {
102+ // `{ let assert_macro: bool = $cond_expr; assert_macro }`
99103 cond_expr : P < Expr > ,
104+ // We keep the condition without the `bool` coercion for the panic message.
105+ inner_cond_expr : P < Expr > ,
100106 custom_message : Option < TokenStream > ,
101107}
102108
@@ -118,7 +124,7 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
118124 return Err ( cx. dcx ( ) . create_err ( errors:: AssertRequiresBoolean { span : sp } ) ) ;
119125 }
120126
121- let cond_expr = parser. parse_expr ( ) ?;
127+ let inner_cond_expr = parser. parse_expr ( ) ?;
122128
123129 // Some crates use the `assert!` macro in the following form (note extra semicolon):
124130 //
@@ -154,7 +160,54 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
154160 return parser. unexpected ( ) ;
155161 }
156162
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+ )
158211}
159212
160213fn parse_custom_message ( parser : & mut Parser < ' _ > ) -> Option < TokenStream > {
0 commit comments