1
+ mod context;
2
+
1
3
use crate :: edition_panic:: use_panic_2021;
2
4
use rustc_ast:: ptr:: P ;
3
5
use rustc_ast:: token;
4
6
use rustc_ast:: tokenstream:: { DelimSpan , TokenStream } ;
5
- use rustc_ast:: { self as ast , * } ;
7
+ use rustc_ast:: { Expr , ExprKind , MacArgs , MacCall , MacDelimiter , Path , PathSegment , UnOp } ;
6
8
use rustc_ast_pretty:: pprust;
7
9
use rustc_errors:: { Applicability , PResult } ;
8
- use rustc_expand:: base:: * ;
10
+ use rustc_expand:: base:: { DummyResult , ExtCtxt , MacEager , MacResult } ;
9
11
use rustc_parse:: parser:: Parser ;
10
12
use rustc_span:: symbol:: { sym, Ident , Symbol } ;
11
13
use rustc_span:: { Span , DUMMY_SP } ;
@@ -15,7 +17,7 @@ pub fn expand_assert<'cx>(
15
17
span : Span ,
16
18
tts : TokenStream ,
17
19
) -> 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) {
19
21
Ok ( assert) => assert,
20
22
Err ( mut err) => {
21
23
err. emit ( ) ;
@@ -25,13 +27,13 @@ pub fn expand_assert<'cx>(
25
27
26
28
// `core::panic` and `std::panic` are different macros, so we use call-site
27
29
// 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) ;
29
31
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) {
32
34
// On edition 2021, we always call `$crate::panic::panic_2021!()`.
33
35
Path {
34
- span : sp ,
36
+ span : call_site_span ,
35
37
segments : cx
36
38
. std_path ( & [ sym:: panic, sym:: panic_2021] )
37
39
. into_iter ( )
@@ -42,27 +44,39 @@ pub fn expand_assert<'cx>(
42
44
} else {
43
45
// Before edition 2021, we call `panic!()` unqualified,
44
46
// 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,
50
55
ExprKind :: MacCall ( MacCall {
51
- path,
56
+ path : panic_path ( ) ,
52
57
args : P ( MacArgs :: Delimited (
53
- DelimSpan :: from_single ( sp ) ,
58
+ DelimSpan :: from_single ( call_site_span ) ,
54
59
MacDelimiter :: Parenthesis ,
55
60
tokens,
56
61
) ) ,
57
62
prior_type_ascription : None ,
58
63
} ) ,
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 {
61
75
// Pass our own message directly to $crate::panicking::panic(),
62
76
// because it might contain `{` and `}` that should always be
63
77
// passed literally.
64
- cx. expr_call_global (
65
- sp ,
78
+ let then = cx. expr_call_global (
79
+ call_site_span ,
66
80
cx. std_path ( & [ sym:: panicking, sym:: panic] ) ,
67
81
vec ! [ cx. expr_str(
68
82
DUMMY_SP ,
@@ -71,82 +85,96 @@ pub fn expand_assert<'cx>(
71
85
pprust:: expr_to_string( & cond_expr) . escape_debug( )
72
86
) ) ,
73
87
) ] ,
74
- )
88
+ ) ;
89
+ expr_if_not ( cx, call_site_span, cond_expr, then, None )
75
90
} ;
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)
79
93
}
80
94
81
95
struct Assert {
82
- cond_expr : P < ast :: Expr > ,
96
+ cond_expr : P < Expr > ,
83
97
custom_message : Option < TokenStream > ,
84
98
}
85
99
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) ;
88
103
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
+ }
113
110
114
- parser. bump ( ) ;
115
- }
111
+ let cond_expr = parser. parse_expr ( ) ?;
116
112
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 ( ) ,
131
126
Applicability :: MaybeIncorrect ,
132
127
) ;
133
128
err. emit ( ) ;
134
129
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 ( ) ;
141
150
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 } )
144
163
}
145
164
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
+ }
147
169
}
148
170
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)
152
180
}
0 commit comments