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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy