rustc_parse/lexer/
diagnostics.rs

1use rustc_ast::token::Delimiter;
2use rustc_errors::Diag;
3use rustc_session::parse::ParseSess;
4use rustc_span::Span;
5use rustc_span::source_map::SourceMap;
6
7use super::UnmatchedDelim;
8use crate::errors::MismatchedClosingDelimiter;
9use crate::pprust;
10
11#[derive(Default)]
12pub(super) struct TokenTreeDiagInfo {
13    /// Stack of open delimiters and their spans. Used for error message.
14    pub open_delimiters: Vec<(Delimiter, Span)>,
15    pub unmatched_delims: Vec<UnmatchedDelim>,
16
17    /// Used only for error recovery when arriving to EOF with mismatched braces.
18    pub last_unclosed_found_span: Option<Span>,
19
20    /// Collect empty block spans that might have been auto-inserted by editors.
21    pub empty_block_spans: Vec<Span>,
22
23    /// Collect the spans of braces (Open, Close). Used only
24    /// for detecting if blocks are empty and only braces.
25    pub matching_block_spans: Vec<(Span, Span)>,
26}
27
28pub(super) fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool {
29    match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) {
30        (Some(open_padding), Some(close_padding)) => open_padding == close_padding,
31        _ => false,
32    }
33}
34
35// When we get a `)` or `]` for `{`, we should emit help message here
36// it's more friendly compared to report `unmatched error` in later phase
37pub(super) fn report_missing_open_delim(
38    err: &mut Diag<'_>,
39    unmatched_delims: &mut Vec<UnmatchedDelim>,
40) -> bool {
41    let mut reported_missing_open = false;
42    unmatched_delims.retain(|unmatch_brace| {
43        if let Some(delim) = unmatch_brace.found_delim
44            && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
45        {
46            let missed_open = match delim {
47                Delimiter::Parenthesis => "(",
48                Delimiter::Bracket => "[",
49                _ => unreachable!(),
50            };
51
52            if let Some(unclosed_span) = unmatch_brace.unclosed_span {
53                err.span_label(unclosed_span, "the nearest open delimiter");
54            }
55            err.span_label(
56                unmatch_brace.found_span.shrink_to_lo(),
57                format!("missing open `{missed_open}` for this delimiter"),
58            );
59            reported_missing_open = true;
60            false
61        } else {
62            true
63        }
64    });
65    reported_missing_open
66}
67
68pub(super) fn report_suspicious_mismatch_block(
69    err: &mut Diag<'_>,
70    diag_info: &TokenTreeDiagInfo,
71    sm: &SourceMap,
72    delim: Delimiter,
73) {
74    let mut matched_spans: Vec<(Span, bool)> = diag_info
75        .matching_block_spans
76        .iter()
77        .map(|&(open, close)| (open.with_hi(close.lo()), same_indentation_level(sm, open, close)))
78        .collect();
79
80    // sort by `lo`, so the large block spans in the front
81    matched_spans.sort_by_key(|(span, _)| span.lo());
82
83    // We use larger block whose indentation is well to cover those inner mismatched blocks
84    // O(N^2) here, but we are on error reporting path, so it is fine
85    for i in 0..matched_spans.len() {
86        let (block_span, same_ident) = matched_spans[i];
87        if same_ident {
88            for j in i + 1..matched_spans.len() {
89                let (inner_block, inner_same_ident) = matched_spans[j];
90                if block_span.contains(inner_block) && !inner_same_ident {
91                    matched_spans[j] = (inner_block, true);
92                }
93            }
94        }
95    }
96
97    // Find the innermost span candidate for final report
98    let candidate_span =
99        matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span);
100
101    if let Some(block_span) = candidate_span {
102        err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed...");
103        err.span_label(
104            block_span.shrink_to_hi(),
105            "...as it matches this but it has different indentation",
106        );
107
108        // If there is a empty block in the mismatched span, note it
109        if delim == Delimiter::Brace {
110            for span in diag_info.empty_block_spans.iter() {
111                if block_span.contains(*span) {
112                    err.span_label(*span, "block is empty, you might have not meant to close it");
113                    break;
114                }
115            }
116        }
117    } else {
118        // If there is no suspicious span, give the last properly closed block may help
119        if let Some(parent) = diag_info.matching_block_spans.last()
120            && diag_info.open_delimiters.last().is_none()
121            && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1))
122        {
123            err.span_label(parent.0, "this opening brace...");
124            err.span_label(parent.1, "...matches this closing brace");
125        }
126    }
127}
128
129pub(crate) fn make_unclosed_delims_error(
130    unmatched: UnmatchedDelim,
131    psess: &ParseSess,
132) -> Option<Diag<'_>> {
133    // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
134    // `unmatched_delims` only for error recovery in the `Parser`.
135    let found_delim = unmatched.found_delim?;
136    let mut spans = vec![unmatched.found_span];
137    if let Some(sp) = unmatched.unclosed_span {
138        spans.push(sp);
139    };
140    let err = psess.dcx().create_err(MismatchedClosingDelimiter {
141        spans,
142        delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(),
143        unmatched: unmatched.found_span,
144        opening_candidate: unmatched.candidate_span,
145        unclosed: unmatched.unclosed_span,
146    });
147    Some(err)
148}
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