1
+ use super :: diagnostics:: report_suspicious_mismatch_block;
2
+ use super :: diagnostics:: same_identation_level;
3
+ use super :: diagnostics:: TokenTreeDiagInfo ;
1
4
use super :: { StringReader , UnmatchedBrace } ;
2
5
use rustc_ast:: token:: { self , Delimiter , Token } ;
3
6
use rustc_ast:: tokenstream:: { DelimSpan , Spacing , TokenStream , TokenTree } ;
4
7
use rustc_ast_pretty:: pprust:: token_to_string;
5
- use rustc_data_structures:: fx:: FxHashMap ;
6
8
use rustc_errors:: { PErr , PResult } ;
7
- use rustc_span:: Span ;
8
9
9
10
pub ( super ) struct TokenTreesReader < ' a > {
10
11
string_reader : StringReader < ' a > ,
11
12
/// The "next" token, which has been obtained from the `StringReader` but
12
13
/// not yet handled by the `TokenTreesReader`.
13
14
token : Token ,
14
- /// Stack of open delimiters and their spans. Used for error message.
15
- open_braces : Vec < ( Delimiter , Span ) > ,
16
- unmatched_braces : Vec < UnmatchedBrace > ,
17
- /// The type and spans for all braces
18
- ///
19
- /// Used only for error recovery when arriving to EOF with mismatched braces.
20
- matching_delim_spans : Vec < ( Delimiter , Span , Span ) > ,
21
- last_unclosed_found_span : Option < Span > ,
22
- /// Collect empty block spans that might have been auto-inserted by editors.
23
- last_delim_empty_block_spans : FxHashMap < Delimiter , Span > ,
24
- /// Collect the spans of braces (Open, Close). Used only
25
- /// for detecting if blocks are empty and only braces.
26
- matching_block_spans : Vec < ( Span , Span ) > ,
15
+ diag_info : TokenTreeDiagInfo ,
27
16
}
28
17
29
18
impl < ' a > TokenTreesReader < ' a > {
@@ -33,15 +22,10 @@ impl<'a> TokenTreesReader<'a> {
33
22
let mut tt_reader = TokenTreesReader {
34
23
string_reader,
35
24
token : Token :: dummy ( ) ,
36
- open_braces : Vec :: new ( ) ,
37
- unmatched_braces : Vec :: new ( ) ,
38
- matching_delim_spans : Vec :: new ( ) ,
39
- last_unclosed_found_span : None ,
40
- last_delim_empty_block_spans : FxHashMap :: default ( ) ,
41
- matching_block_spans : Vec :: new ( ) ,
25
+ diag_info : TokenTreeDiagInfo :: default ( ) ,
42
26
} ;
43
27
let res = tt_reader. parse_token_trees ( /* is_delimited */ false ) ;
44
- ( res, tt_reader. unmatched_braces )
28
+ ( res, tt_reader. diag_info . unmatched_braces )
45
29
}
46
30
47
31
// Parse a stream of tokens into a list of `TokenTree`s.
@@ -92,9 +76,9 @@ impl<'a> TokenTreesReader<'a> {
92
76
fn eof_err ( & mut self ) -> PErr < ' a > {
93
77
let msg = "this file contains an unclosed delimiter" ;
94
78
let mut err = self . string_reader . sess . span_diagnostic . struct_span_err ( self . token . span , msg) ;
95
- for & ( _, sp) in & self . open_braces {
79
+ for & ( _, sp) in & self . diag_info . open_braces {
96
80
err. span_label ( sp, "unclosed delimiter" ) ;
97
- self . unmatched_braces . push ( UnmatchedBrace {
81
+ self . diag_info . unmatched_braces . push ( UnmatchedBrace {
98
82
expected_delim : Delimiter :: Brace ,
99
83
found_delim : None ,
100
84
found_span : self . token . span ,
@@ -103,23 +87,13 @@ impl<'a> TokenTreesReader<'a> {
103
87
} ) ;
104
88
}
105
89
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
- }
90
+ if let Some ( ( delim, _) ) = self . diag_info . open_braces . last ( ) {
91
+ report_suspicious_mismatch_block (
92
+ & mut err,
93
+ & self . diag_info ,
94
+ & self . string_reader . sess . source_map ( ) ,
95
+ * delim,
96
+ )
123
97
}
124
98
err
125
99
}
@@ -128,7 +102,7 @@ impl<'a> TokenTreesReader<'a> {
128
102
// The span for beginning of the delimited section
129
103
let pre_span = self . token . span ;
130
104
131
- self . open_braces . push ( ( open_delim, self . token . span ) ) ;
105
+ self . diag_info . open_braces . push ( ( open_delim, self . token . span ) ) ;
132
106
133
107
// Parse the token trees within the delimiters.
134
108
// We stop at any delimiter so we can try to recover if the user
@@ -137,35 +111,29 @@ impl<'a> TokenTreesReader<'a> {
137
111
138
112
// Expand to cover the entire delimited token tree
139
113
let delim_span = DelimSpan :: from_pair ( pre_span, self . token . span ) ;
114
+ let sm = self . string_reader . sess . source_map ( ) ;
140
115
141
116
match self . token . kind {
142
117
// Correct delimiter.
143
118
token:: CloseDelim ( close_delim) if close_delim == open_delim => {
144
- let ( open_brace, open_brace_span) = self . open_braces . pop ( ) . unwrap ( ) ;
119
+ let ( open_brace, open_brace_span) = self . diag_info . open_braces . pop ( ) . unwrap ( ) ;
145
120
let close_brace_span = self . token . span ;
146
121
147
- if tts. is_empty ( ) {
122
+ if tts. is_empty ( ) && close_delim == Delimiter :: Brace {
148
123
let empty_block_span = open_brace_span. to ( close_brace_span) ;
149
- let sm = self . string_reader . sess . source_map ( ) ;
150
124
if !sm. is_multiline ( empty_block_span) {
151
125
// Only track if the block is in the form of `{}`, otherwise it is
152
126
// likely that it was written on purpose.
153
- self . last_delim_empty_block_spans . insert ( open_delim , empty_block_span) ;
127
+ self . diag_info . empty_block_spans . push ( empty_block_span) ;
154
128
}
155
129
}
156
130
157
- //only add braces
131
+ // only add braces
158
132
if let ( Delimiter :: Brace , Delimiter :: Brace ) = ( open_brace, open_delim) {
159
- self . matching_block_spans . push ( ( open_brace_span, close_brace_span) ) ;
133
+ // Add all the matching spans, we will sort by span later
134
+ self . diag_info . matching_block_spans . push ( ( open_brace_span, close_brace_span) ) ;
160
135
}
161
136
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 {
167
- self . matching_delim_spans . push ( ( open_brace, open_brace_span, close_brace_span) ) ;
168
- }
169
137
// Move past the closing delimiter.
170
138
self . token = self . string_reader . next_token ( ) . 0 ;
171
139
}
@@ -174,36 +142,33 @@ impl<'a> TokenTreesReader<'a> {
174
142
let mut unclosed_delimiter = None ;
175
143
let mut candidate = None ;
176
144
177
- if self . last_unclosed_found_span != Some ( self . token . span ) {
145
+ if self . diag_info . last_unclosed_found_span != Some ( self . token . span ) {
178
146
// do not complain about the same unclosed delimiter multiple times
179
- self . last_unclosed_found_span = Some ( self . token . span ) ;
147
+ self . diag_info . last_unclosed_found_span = Some ( self . token . span ) ;
180
148
// This is a conservative error: only report the last unclosed
181
149
// delimiter. The previous unclosed delimiters could actually be
182
150
// closed! The parser just hasn't gotten to them yet.
183
- if let Some ( & ( _, sp) ) = self . open_braces . last ( ) {
151
+ if let Some ( & ( _, sp) ) = self . diag_info . open_braces . last ( ) {
184
152
unclosed_delimiter = Some ( sp) ;
185
153
} ;
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
- }
154
+ for ( brace, brace_span) in & self . diag_info . open_braces {
155
+ if same_identation_level ( & sm, self . token . span , * brace_span)
156
+ && brace == & close_delim
157
+ {
158
+ // high likelihood of these two corresponding
159
+ candidate = Some ( * brace_span) ;
195
160
}
196
161
}
197
- let ( tok, _) = self . open_braces . pop ( ) . unwrap ( ) ;
198
- self . unmatched_braces . push ( UnmatchedBrace {
162
+ let ( tok, _) = self . diag_info . open_braces . pop ( ) . unwrap ( ) ;
163
+ self . diag_info . unmatched_braces . push ( UnmatchedBrace {
199
164
expected_delim : tok,
200
165
found_delim : Some ( close_delim) ,
201
166
found_span : self . token . span ,
202
167
unclosed_span : unclosed_delimiter,
203
168
candidate_span : candidate,
204
169
} ) ;
205
170
} else {
206
- self . open_braces . pop ( ) ;
171
+ self . diag_info . open_braces . pop ( ) ;
207
172
}
208
173
209
174
// If the incorrect delimiter matches an earlier opening
@@ -213,7 +178,7 @@ impl<'a> TokenTreesReader<'a> {
213
178
// fn foo() {
214
179
// bar(baz(
215
180
// } // Incorrect delimiter but matches the earlier `{`
216
- if !self . open_braces . iter ( ) . any ( |& ( b, _) | b == close_delim) {
181
+ if !self . diag_info . open_braces . iter ( ) . any ( |& ( b, _) | b == close_delim) {
217
182
self . token = self . string_reader . next_token ( ) . 0 ;
218
183
}
219
184
}
@@ -236,22 +201,12 @@ impl<'a> TokenTreesReader<'a> {
236
201
let mut err =
237
202
self . string_reader . sess . span_diagnostic . struct_span_err ( self . token . span , & msg) ;
238
203
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" ) ;
248
- }
249
- } else {
250
- err. span_label ( parent. 0 , "this opening brace..." ) ;
251
- err. span_label ( parent. 1 , "...matches this closing brace" ) ;
252
- }
253
- }
254
-
204
+ report_suspicious_mismatch_block (
205
+ & mut err,
206
+ & self . diag_info ,
207
+ & self . string_reader . sess . source_map ( ) ,
208
+ delim,
209
+ ) ;
255
210
err. span_label ( self . token . span , "unexpected closing delimiter" ) ;
256
211
err
257
212
}
0 commit comments