Skip to main content

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