rustc_parse/lexer/
tokentrees.rs

1use rustc_ast::token::{self, Delimiter, Token};
2use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
3use rustc_ast_pretty::pprust::token_to_string;
4use rustc_errors::Diag;
5
6use super::diagnostics::{
7    report_missing_open_delim, report_suspicious_mismatch_block, same_indentation_level,
8};
9use super::{Lexer, UnmatchedDelim};
10
11impl<'psess, 'src> Lexer<'psess, 'src> {
12    // Lex into a token stream. The `Spacing` in the result is that of the
13    // opening delimiter.
14    pub(super) fn lex_token_trees(
15        &mut self,
16        is_delimited: bool,
17    ) -> Result<(Spacing, TokenStream), Vec<Diag<'psess>>> {
18        // Move past the opening delimiter.
19        let open_spacing = self.bump_minimal();
20
21        let mut buf = Vec::new();
22        loop {
23            if let Some(delim) = self.token.kind.open_delim() {
24                // Invisible delimiters cannot occur here because `TokenTreesReader` parses
25                // code directly from strings, with no macro expansion involved.
26                debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
27                buf.push(match self.lex_token_tree_open_delim(delim) {
28                    Ok(val) => val,
29                    Err(errs) => return Err(errs),
30                })
31            } else if let Some(delim) = self.token.kind.close_delim() {
32                // Invisible delimiters cannot occur here because `TokenTreesReader` parses
33                // code directly from strings, with no macro expansion involved.
34                debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
35                return if is_delimited {
36                    Ok((open_spacing, TokenStream::new(buf)))
37                } else {
38                    Err(vec![self.close_delim_err(delim)])
39                };
40            } else if self.token.kind == token::Eof {
41                return if is_delimited {
42                    Err(vec![self.eof_err()])
43                } else {
44                    Ok((open_spacing, TokenStream::new(buf)))
45                };
46            } else {
47                // Get the next normal token.
48                let (this_tok, this_spacing) = self.bump();
49                buf.push(TokenTree::Token(this_tok, this_spacing));
50            }
51        }
52    }
53
54    fn eof_err(&mut self) -> Diag<'psess> {
55        let msg = "this file contains an unclosed delimiter";
56        let mut err = self.dcx().struct_span_err(self.token.span, msg);
57
58        let unclosed_delimiter_show_limit = 5;
59        let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len());
60        for &(_, span) in &self.diag_info.open_delimiters[..len] {
61            err.span_label(span, "unclosed delimiter");
62            self.diag_info.unmatched_delims.push(UnmatchedDelim {
63                found_delim: None,
64                found_span: self.token.span,
65                unclosed_span: Some(span),
66                candidate_span: None,
67            });
68        }
69
70        if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit)
71            && self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2
72        {
73            err.span_label(
74                *span,
75                format!(
76                    "another {} unclosed delimiters begin from here",
77                    self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit
78                ),
79            );
80        }
81
82        if let Some((delim, _)) = self.diag_info.open_delimiters.last() {
83            report_suspicious_mismatch_block(
84                &mut err,
85                &self.diag_info,
86                self.psess.source_map(),
87                *delim,
88            )
89        }
90        err
91    }
92
93    fn lex_token_tree_open_delim(
94        &mut self,
95        open_delim: Delimiter,
96    ) -> Result<TokenTree, Vec<Diag<'psess>>> {
97        // The span for beginning of the delimited section.
98        let pre_span = self.token.span;
99
100        self.diag_info.open_delimiters.push((open_delim, self.token.span));
101
102        // Lex the token trees within the delimiters.
103        // We stop at any delimiter so we can try to recover if the user
104        // uses an incorrect delimiter.
105        let (open_spacing, tts) = self.lex_token_trees(/* is_delimited */ true)?;
106
107        // Expand to cover the entire delimited token tree.
108        let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
109        let sm = self.psess.source_map();
110
111        let close_spacing = if let Some(close_delim) = self.token.kind.close_delim() {
112            if close_delim == open_delim {
113                // Correct delimiter.
114                let (open_delimiter, open_delimiter_span) =
115                    self.diag_info.open_delimiters.pop().unwrap();
116                let close_delimiter_span = self.token.span;
117
118                if tts.is_empty() && close_delim == Delimiter::Brace {
119                    let empty_block_span = open_delimiter_span.to(close_delimiter_span);
120                    if !sm.is_multiline(empty_block_span) {
121                        // Only track if the block is in the form of `{}`, otherwise it is
122                        // likely that it was written on purpose.
123                        self.diag_info.empty_block_spans.push(empty_block_span);
124                    }
125                }
126
127                // only add braces
128                if let (Delimiter::Brace, Delimiter::Brace) = (open_delimiter, open_delim) {
129                    // Add all the matching spans, we will sort by span later
130                    self.diag_info
131                        .matching_block_spans
132                        .push((open_delimiter_span, close_delimiter_span));
133                }
134
135                // Move past the closing delimiter.
136                self.bump_minimal()
137            } else {
138                // Incorrect delimiter.
139                let mut unclosed_delimiter = None;
140                let mut candidate = None;
141
142                if self.diag_info.last_unclosed_found_span != Some(self.token.span) {
143                    // do not complain about the same unclosed delimiter multiple times
144                    self.diag_info.last_unclosed_found_span = Some(self.token.span);
145                    // This is a conservative error: only report the last unclosed
146                    // delimiter. The previous unclosed delimiters could actually be
147                    // closed! The lexer just hasn't gotten to them yet.
148                    if let Some(&(_, sp)) = self.diag_info.open_delimiters.last() {
149                        unclosed_delimiter = Some(sp);
150                    };
151                    for (delimiter, delimiter_span) in &self.diag_info.open_delimiters {
152                        if same_indentation_level(sm, self.token.span, *delimiter_span)
153                            && delimiter == &close_delim
154                        {
155                            // high likelihood of these two corresponding
156                            candidate = Some(*delimiter_span);
157                        }
158                    }
159                    let (_, _) = self.diag_info.open_delimiters.pop().unwrap();
160                    self.diag_info.unmatched_delims.push(UnmatchedDelim {
161                        found_delim: Some(close_delim),
162                        found_span: self.token.span,
163                        unclosed_span: unclosed_delimiter,
164                        candidate_span: candidate,
165                    });
166                } else {
167                    self.diag_info.open_delimiters.pop();
168                }
169
170                // If the incorrect delimiter matches an earlier opening
171                // delimiter, then don't consume it (it can be used to
172                // close the earlier one). Otherwise, consume it.
173                // E.g., we try to recover from:
174                // fn foo() {
175                //     bar(baz(
176                // }  // Incorrect delimiter but matches the earlier `{`
177                if !self.diag_info.open_delimiters.iter().any(|&(d, _)| d == close_delim) {
178                    self.bump_minimal()
179                } else {
180                    // The choice of value here doesn't matter.
181                    Spacing::Alone
182                }
183            }
184        } else {
185            assert_eq!(self.token.kind, token::Eof);
186            // Silently recover, the EOF token will be seen again
187            // and an error emitted then. Thus we don't pop from
188            // self.open_delimiters here. The choice of spacing value here
189            // doesn't matter.
190            Spacing::Alone
191        };
192
193        let spacing = DelimSpacing::new(open_spacing, close_spacing);
194
195        Ok(TokenTree::Delimited(delim_span, spacing, open_delim, tts))
196    }
197
198    // Move on to the next token, returning the current token and its spacing.
199    // Will glue adjacent single-char tokens together.
200    fn bump(&mut self) -> (Token, Spacing) {
201        let (this_spacing, next_tok) = loop {
202            let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor();
203
204            if is_next_tok_preceded_by_whitespace {
205                break (Spacing::Alone, next_tok);
206            } else if let Some(glued) = self.token.glue(&next_tok) {
207                self.token = glued;
208            } else {
209                let this_spacing = if next_tok.is_punct() {
210                    Spacing::Joint
211                } else if next_tok == token::Eof {
212                    Spacing::Alone
213                } else {
214                    Spacing::JointHidden
215                };
216                break (this_spacing, next_tok);
217            }
218        };
219        let this_tok = std::mem::replace(&mut self.token, next_tok);
220        (this_tok, this_spacing)
221    }
222
223    // Cut-down version of `bump` used when the token kind is known in advance.
224    fn bump_minimal(&mut self) -> Spacing {
225        let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor();
226
227        let this_spacing = if is_next_tok_preceded_by_whitespace {
228            Spacing::Alone
229        } else {
230            if next_tok.is_punct() {
231                Spacing::Joint
232            } else if next_tok == token::Eof {
233                Spacing::Alone
234            } else {
235                Spacing::JointHidden
236            }
237        };
238
239        self.token = next_tok;
240        this_spacing
241    }
242
243    fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> {
244        // An unexpected closing delimiter (i.e., there is no matching opening delimiter).
245        let token_str = token_to_string(&self.token);
246        let msg = format!("unexpected closing delimiter: `{token_str}`");
247        let mut err = self.dcx().struct_span_err(self.token.span, msg);
248
249        // if there is no missing open delim, report suspicious mismatch block
250        if !report_missing_open_delim(&mut err, &mut self.diag_info.unmatched_delims) {
251            report_suspicious_mismatch_block(
252                &mut err,
253                &self.diag_info,
254                self.psess.source_map(),
255                delim,
256            );
257        }
258
259        err.span_label(self.token.span, "unexpected closing delimiter");
260        err
261    }
262}