1+ mod context;
2+
13use crate :: edition_panic:: use_panic_2021;
24use rustc_ast:: ptr:: P ;
35use rustc_ast:: token;
46use rustc_ast:: tokenstream:: { DelimSpan , TokenStream } ;
5- use rustc_ast:: { self as ast , * } ;
7+ use rustc_ast:: { Expr , ExprKind , MacArgs , MacCall , MacDelimiter , Path , PathSegment , UnOp } ;
68use rustc_ast_pretty:: pprust;
79use rustc_errors:: { Applicability , PResult } ;
8- use rustc_expand:: base:: * ;
10+ use rustc_expand:: base:: { DummyResult , ExtCtxt , MacEager , MacResult } ;
911use rustc_parse:: parser:: Parser ;
1012use rustc_span:: symbol:: { sym, Ident , Symbol } ;
1113use rustc_span:: { Span , DUMMY_SP } ;
@@ -15,7 +17,7 @@ pub fn expand_assert<'cx>(
1517 span : Span ,
1618 tts : TokenStream ,
1719) -> Box < dyn MacResult + ' cx > {
18- let Assert { cond_expr, custom_message } = match parse_assert ( cx, span, tts) {
20+ let Assert { cond_expr, custom_message } = match Assert :: parse ( cx, span, tts) {
1921 Ok ( assert) => assert,
2022 Err ( mut err) => {
2123 err. emit ( ) ;
@@ -25,13 +27,13 @@ pub fn expand_assert<'cx>(
2527
2628 // `core::panic` and `std::panic` are different macros, so we use call-site
2729 // context to pick up whichever is currently in scope.
28- let sp = cx. with_call_site_ctxt ( span) ;
30+ let call_site_span = cx. with_call_site_ctxt ( span) ;
2931
30- let panic_call = if let Some ( tokens ) = custom_message {
31- let path = if use_panic_2021 ( span) {
32+ let panic_path = || {
33+ if use_panic_2021 ( span) {
3234 // On edition 2021, we always call `$crate::panic::panic_2021!()`.
3335 Path {
34- span : sp ,
36+ span : call_site_span ,
3537 segments : cx
3638 . std_path ( & [ sym:: panic, sym:: panic_2021] )
3739 . into_iter ( )
@@ -42,27 +44,39 @@ pub fn expand_assert<'cx>(
4244 } else {
4345 // Before edition 2021, we call `panic!()` unqualified,
4446 // such that it calls either `std::panic!()` or `core::panic!()`.
45- Path :: from_ident ( Ident :: new ( sym:: panic, sp) )
46- } ;
47- // Pass the custom message to panic!().
48- cx. expr (
49- sp,
47+ Path :: from_ident ( Ident :: new ( sym:: panic, call_site_span) )
48+ }
49+ } ;
50+
51+ // Simply uses the user provided message instead of generating custom outputs
52+ let expr = if let Some ( tokens) = custom_message {
53+ let then = cx. expr (
54+ call_site_span,
5055 ExprKind :: MacCall ( MacCall {
51- path,
56+ path : panic_path ( ) ,
5257 args : P ( MacArgs :: Delimited (
53- DelimSpan :: from_single ( sp ) ,
58+ DelimSpan :: from_single ( call_site_span ) ,
5459 MacDelimiter :: Parenthesis ,
5560 tokens,
5661 ) ) ,
5762 prior_type_ascription : None ,
5863 } ) ,
59- )
60- } else {
64+ ) ;
65+ expr_if_not ( cx, call_site_span, cond_expr, then, None )
66+ }
67+ // If `generic_assert` is enabled, generates rich captured outputs
68+ //
69+ // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
70+ else if let Some ( features) = cx. ecfg . features && features. generic_assert {
71+ context:: Context :: new ( cx, call_site_span) . build ( cond_expr, panic_path ( ) )
72+ }
73+ // If `generic_assert` is not enabled, outputs "assertion failed: ..."
74+ else {
6175 // Pass our own message directly to $crate::panicking::panic(),
6276 // because it might contain `{` and `}` that should always be
6377 // passed literally.
64- cx. expr_call_global (
65- sp ,
78+ let then = cx. expr_call_global (
79+ call_site_span ,
6680 cx. std_path ( & [ sym:: panicking, sym:: panic] ) ,
6781 vec ! [ cx. expr_str(
6882 DUMMY_SP ,
@@ -71,82 +85,96 @@ pub fn expand_assert<'cx>(
7185 pprust:: expr_to_string( & cond_expr) . escape_debug( )
7286 ) ) ,
7387 ) ] ,
74- )
88+ ) ;
89+ expr_if_not ( cx, call_site_span, cond_expr, then, None )
7590 } ;
76- let if_expr =
77- cx. expr_if ( sp, cx. expr ( sp, ExprKind :: Unary ( UnOp :: Not , cond_expr) ) , panic_call, None ) ;
78- MacEager :: expr ( if_expr)
91+
92+ MacEager :: expr ( expr)
7993}
8094
8195struct Assert {
82- cond_expr : P < ast :: Expr > ,
96+ cond_expr : P < Expr > ,
8397 custom_message : Option < TokenStream > ,
8498}
8599
86- fn parse_assert < ' a > ( cx : & mut ExtCtxt < ' a > , sp : Span , stream : TokenStream ) -> PResult < ' a , Assert > {
87- let mut parser = cx. new_parser_from_tts ( stream) ;
100+ impl Assert {
101+ fn parse < ' a > ( cx : & mut ExtCtxt < ' a > , sp : Span , stream : TokenStream ) -> PResult < ' a , Assert > {
102+ let mut parser = cx. new_parser_from_tts ( stream) ;
88103
89- if parser. token == token:: Eof {
90- let mut err = cx. struct_span_err ( sp, "macro requires a boolean expression as an argument" ) ;
91- err. span_label ( sp, "boolean expression required" ) ;
92- return Err ( err) ;
93- }
94-
95- let cond_expr = parser. parse_expr ( ) ?;
96-
97- // Some crates use the `assert!` macro in the following form (note extra semicolon):
98- //
99- // assert!(
100- // my_function();
101- // );
102- //
103- // Emit an error about semicolon and suggest removing it.
104- if parser. token == token:: Semi {
105- let mut err = cx. struct_span_err ( sp, "macro requires an expression as an argument" ) ;
106- err. span_suggestion (
107- parser. token . span ,
108- "try removing semicolon" ,
109- String :: new ( ) ,
110- Applicability :: MaybeIncorrect ,
111- ) ;
112- err. emit ( ) ;
104+ if parser. token == token:: Eof {
105+ let mut err =
106+ cx. struct_span_err ( sp, "macro requires a boolean expression as an argument" ) ;
107+ err. span_label ( sp, "boolean expression required" ) ;
108+ return Err ( err) ;
109+ }
113110
114- parser. bump ( ) ;
115- }
111+ let cond_expr = parser. parse_expr ( ) ?;
116112
117- // Some crates use the `assert!` macro in the following form (note missing comma before
118- // message):
119- //
120- // assert!(true "error message");
121- //
122- // Emit an error and suggest inserting a comma.
123- let custom_message =
124- if let token:: Literal ( token:: Lit { kind : token:: Str , .. } ) = parser. token . kind {
125- let mut err = cx. struct_span_err ( parser. token . span , "unexpected string literal" ) ;
126- let comma_span = parser. prev_token . span . shrink_to_hi ( ) ;
127- err. span_suggestion_short (
128- comma_span,
129- "try adding a comma" ,
130- ", " . to_string ( ) ,
113+ // Some crates use the `assert!` macro in the following form (note extra semicolon):
114+ //
115+ // assert!(
116+ // my_function();
117+ // );
118+ //
119+ // Emit an error about semicolon and suggest removing it.
120+ if parser. token == token:: Semi {
121+ let mut err = cx. struct_span_err ( sp, "macro requires an expression as an argument" ) ;
122+ err. span_suggestion (
123+ parser. token . span ,
124+ "try removing semicolon" ,
125+ String :: new ( ) ,
131126 Applicability :: MaybeIncorrect ,
132127 ) ;
133128 err. emit ( ) ;
134129
135- parse_custom_message ( & mut parser)
136- } else if parser. eat ( & token:: Comma ) {
137- parse_custom_message ( & mut parser)
138- } else {
139- None
140- } ;
130+ parser. bump ( ) ;
131+ }
132+
133+ // Some crates use the `assert!` macro in the following form (note missing comma before
134+ // message):
135+ //
136+ // assert!(true "error message");
137+ //
138+ // Emit an error and suggest inserting a comma.
139+ let custom_message =
140+ if let token:: Literal ( token:: Lit { kind : token:: Str , .. } ) = parser. token . kind {
141+ let mut err = cx. struct_span_err ( parser. token . span , "unexpected string literal" ) ;
142+ let comma_span = parser. prev_token . span . shrink_to_hi ( ) ;
143+ err. span_suggestion_short (
144+ comma_span,
145+ "try adding a comma" ,
146+ ", " . to_string ( ) ,
147+ Applicability :: MaybeIncorrect ,
148+ ) ;
149+ err. emit ( ) ;
141150
142- if parser. token != token:: Eof {
143- return parser. unexpected ( ) ;
151+ Self :: parse_custom_message ( & mut parser)
152+ } else if parser. eat ( & token:: Comma ) {
153+ Self :: parse_custom_message ( & mut parser)
154+ } else {
155+ None
156+ } ;
157+
158+ if parser. token != token:: Eof {
159+ return parser. unexpected ( ) ;
160+ }
161+
162+ Ok ( Assert { cond_expr, custom_message } )
144163 }
145164
146- Ok ( Assert { cond_expr, custom_message } )
165+ fn parse_custom_message ( parser : & mut Parser < ' _ > ) -> Option < TokenStream > {
166+ let ts = parser. parse_tokens ( ) ;
167+ if !ts. is_empty ( ) { Some ( ts) } else { None }
168+ }
147169}
148170
149- fn parse_custom_message ( parser : & mut Parser < ' _ > ) -> Option < TokenStream > {
150- let ts = parser. parse_tokens ( ) ;
151- if !ts. is_empty ( ) { Some ( ts) } else { None }
171+ // if !{ ... } { ... } else { ... }
172+ fn expr_if_not (
173+ cx : & ExtCtxt < ' _ > ,
174+ span : Span ,
175+ cond : P < Expr > ,
176+ then : P < Expr > ,
177+ els : Option < P < Expr > > ,
178+ ) -> P < Expr > {
179+ cx. expr_if ( span, cx. expr ( span, ExprKind :: Unary ( UnOp :: Not , cond) ) , then, els)
152180}
0 commit comments