@@ -3,7 +3,7 @@ use rustc_ast::token::{self, Delimiter, Token};
33use rustc_ast:: tokenstream:: { DelimSpan , Spacing , TokenStream , TokenTree } ;
44use rustc_ast_pretty:: pprust:: token_to_string;
55use rustc_data_structures:: fx:: FxHashMap ;
6- use rustc_errors:: { PErr , PResult } ;
6+ use rustc_errors:: { Diagnostic , PErr , PResult } ;
77use rustc_span:: Span ;
88
99pub ( super ) struct TokenTreesReader < ' a > {
@@ -104,22 +104,7 @@ impl<'a> TokenTreesReader<'a> {
104104 }
105105
106106 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) ;
123108 }
124109 err
125110 }
@@ -157,15 +142,11 @@ impl<'a> TokenTreesReader<'a> {
157142 //only add braces
158143 if let ( Delimiter :: Brace , Delimiter :: Brace ) = ( open_brace, open_delim) {
159144 self . matching_block_spans . push ( ( open_brace_span, close_brace_span) ) ;
160- }
161145
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
167147 self . matching_delim_spans . push ( ( open_brace, open_brace_span, close_brace_span) ) ;
168148 }
149+
169150 // Move past the closing delimiter.
170151 self . token = self . string_reader . next_token ( ) . 0 ;
171152 }
@@ -183,15 +164,12 @@ impl<'a> TokenTreesReader<'a> {
183164 if let Some ( & ( _, sp) ) = self . open_braces . last ( ) {
184165 unclosed_delimiter = Some ( sp) ;
185166 } ;
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) ;
195173 }
196174 }
197175 let ( tok, _) = self . open_braces . pop ( ) . unwrap ( ) ;
@@ -236,23 +214,68 @@ impl<'a> TokenTreesReader<'a> {
236214 let mut err =
237215 self . string_reader . sess . span_diagnostic . struct_span_err ( self . token . span , & msg) ;
238216
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+ }
248258 }
249- } else {
250- err. span_label ( parent. 0 , "this opening brace..." ) ;
251- err. span_label ( parent. 1 , "...matches this closing brace" ) ;
252259 }
253260 }
254261
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+ }
257280 }
258281}
0 commit comments