@@ -3,7 +3,7 @@ use rustc_ast::token::{self, Delimiter, Token};
3
3
use rustc_ast:: tokenstream:: { DelimSpan , Spacing , TokenStream , TokenTree } ;
4
4
use rustc_ast_pretty:: pprust:: token_to_string;
5
5
use rustc_data_structures:: fx:: FxHashMap ;
6
- use rustc_errors:: { PErr , PResult } ;
6
+ use rustc_errors:: { Diagnostic , PErr , PResult } ;
7
7
use rustc_span:: Span ;
8
8
9
9
pub ( super ) struct TokenTreesReader < ' a > {
@@ -104,22 +104,7 @@ impl<'a> TokenTreesReader<'a> {
104
104
}
105
105
106
106
if let Some ( ( delim, _) ) = self . open_braces . last ( ) {
107
- if let Some ( ( _, open_sp, close_sp) ) =
108
- self . matching_delim_spans . iter ( ) . find ( |( d, open_sp, close_sp) | {
109
- let sm = self . string_reader . sess . source_map ( ) ;
110
- if let Some ( close_padding) = sm. span_to_margin ( * close_sp) {
111
- if let Some ( open_padding) = sm. span_to_margin ( * open_sp) {
112
- return delim == d && close_padding != open_padding;
113
- }
114
- }
115
- false
116
- } )
117
- // these are in reverse order as they get inserted on close, but
118
- {
119
- // we want the last open/first close
120
- err. span_label ( * open_sp, "this delimiter might not be properly closed..." ) ;
121
- err. span_label ( * close_sp, "...as it matches this but it has different indentation" ) ;
122
- }
107
+ self . report_error_prone_delim_block ( * delim, & mut err) ;
123
108
}
124
109
err
125
110
}
@@ -157,15 +142,11 @@ impl<'a> TokenTreesReader<'a> {
157
142
//only add braces
158
143
if let ( Delimiter :: Brace , Delimiter :: Brace ) = ( open_brace, open_delim) {
159
144
self . matching_block_spans . push ( ( open_brace_span, close_brace_span) ) ;
160
- }
161
145
162
- if self . open_braces . is_empty ( ) {
163
- // Clear up these spans to avoid suggesting them as we've found
164
- // properly matched delimiters so far for an entire block.
165
- self . matching_delim_spans . clear ( ) ;
166
- } else {
146
+ // Add all the matching spans, we will sort by span later
167
147
self . matching_delim_spans . push ( ( open_brace, open_brace_span, close_brace_span) ) ;
168
148
}
149
+
169
150
// Move past the closing delimiter.
170
151
self . token = self . string_reader . next_token ( ) . 0 ;
171
152
}
@@ -183,15 +164,12 @@ impl<'a> TokenTreesReader<'a> {
183
164
if let Some ( & ( _, sp) ) = self . open_braces . last ( ) {
184
165
unclosed_delimiter = Some ( sp) ;
185
166
} ;
186
- let sm = self . string_reader . sess . source_map ( ) ;
187
- if let Some ( current_padding) = sm. span_to_margin ( self . token . span ) {
188
- for ( brace, brace_span) in & self . open_braces {
189
- if let Some ( padding) = sm. span_to_margin ( * brace_span) {
190
- // high likelihood of these two corresponding
191
- if current_padding == padding && brace == & close_delim {
192
- candidate = Some ( * brace_span) ;
193
- }
194
- }
167
+ for ( brace, brace_span) in & self . open_braces {
168
+ if self . same_identation_level ( self . token . span , * brace_span)
169
+ && brace == & close_delim
170
+ {
171
+ // high likelihood of these two corresponding
172
+ candidate = Some ( * brace_span) ;
195
173
}
196
174
}
197
175
let ( tok, _) = self . open_braces . pop ( ) . unwrap ( ) ;
@@ -236,23 +214,68 @@ impl<'a> TokenTreesReader<'a> {
236
214
let mut err =
237
215
self . string_reader . sess . span_diagnostic . struct_span_err ( self . token . span , & msg) ;
238
216
239
- // Braces are added at the end, so the last element is the biggest block
240
- if let Some ( parent) = self . matching_block_spans . last ( ) {
241
- if let Some ( span) = self . last_delim_empty_block_spans . remove ( & delim) {
242
- // Check if the (empty block) is in the last properly closed block
243
- if ( parent. 0 . to ( parent. 1 ) ) . contains ( span) {
244
- err. span_label ( span, "block is empty, you might have not meant to close it" ) ;
245
- } else {
246
- err. span_label ( parent. 0 , "this opening brace..." ) ;
247
- err. span_label ( parent. 1 , "...matches this closing brace" ) ;
217
+ self . report_error_prone_delim_block ( delim, & mut err) ;
218
+ err. span_label ( self . token . span , "unexpected closing delimiter" ) ;
219
+ err
220
+ }
221
+
222
+ fn same_identation_level ( & self , open_sp : Span , close_sp : Span ) -> bool {
223
+ let sm = self . string_reader . sess . source_map ( ) ;
224
+ if let ( Some ( open_padding) , Some ( close_padding) ) =
225
+ ( sm. span_to_margin ( open_sp) , sm. span_to_margin ( close_sp) )
226
+ {
227
+ open_padding == close_padding
228
+ } else {
229
+ false
230
+ }
231
+ }
232
+
233
+ fn report_error_prone_delim_block ( & self , delim : Delimiter , err : & mut Diagnostic ) {
234
+ let mut matched_spans = vec ! [ ] ;
235
+ let mut candidate_span = None ;
236
+
237
+ for & ( d, open_sp, close_sp) in & self . matching_delim_spans {
238
+ if d == delim {
239
+ let block_span = open_sp. with_hi ( close_sp. lo ( ) ) ;
240
+ let same_ident = self . same_identation_level ( open_sp, close_sp) ;
241
+ matched_spans. push ( ( block_span, same_ident) ) ;
242
+ }
243
+ }
244
+
245
+ // sort by `lo`, so the large block spans in the front
246
+ matched_spans. sort_by ( |a, b| a. 0 . lo ( ) . cmp ( & b. 0 . lo ( ) ) ) ;
247
+
248
+ // We use larger block whose identation is well to cover those innert blocks
249
+ // O(N^2) here, but we are on error reporting path, so it is fine
250
+ for i in 0 ..matched_spans. len ( ) {
251
+ let ( block_span, same_ident) = matched_spans[ i] ;
252
+ if same_ident {
253
+ for j in i + 1 ..matched_spans. len ( ) {
254
+ let ( inner_block, innert_same_ident) = matched_spans[ j] ;
255
+ if block_span. contains ( inner_block) && !innert_same_ident {
256
+ matched_spans[ j] = ( inner_block, true ) ;
257
+ }
248
258
}
249
- } else {
250
- err. span_label ( parent. 0 , "this opening brace..." ) ;
251
- err. span_label ( parent. 1 , "...matches this closing brace" ) ;
252
259
}
253
260
}
254
261
255
- err. span_label ( self . token . span , "unexpected closing delimiter" ) ;
256
- err
262
+ // Find the innermost span candidate for final report
263
+ for ( block_span, same_ident) in matched_spans. into_iter ( ) . rev ( ) {
264
+ if !same_ident {
265
+ candidate_span = Some ( block_span) ;
266
+ break ;
267
+ }
268
+ }
269
+
270
+ if let Some ( block_span) = candidate_span {
271
+ err. span_label (
272
+ block_span. shrink_to_lo ( ) ,
273
+ "this delimiter might not be properly closed..." ,
274
+ ) ;
275
+ err. span_label (
276
+ block_span. shrink_to_hi ( ) ,
277
+ "...as it matches this but it has different indentation" ,
278
+ ) ;
279
+ }
257
280
}
258
281
}
0 commit comments