]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_parse/src/lexer/diagnostics.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_parse / src / lexer / diagnostics.rs
CommitLineData
9ffffee4
FG
1use super::UnmatchedDelim;
2use rustc_ast::token::Delimiter;
3use rustc_errors::Diagnostic;
4use rustc_span::source_map::SourceMap;
5use rustc_span::Span;
6
7#[derive(Default)]
8pub struct TokenTreeDiagInfo {
9 /// Stack of open delimiters and their spans. Used for error message.
10 pub open_braces: Vec<(Delimiter, Span)>,
11 pub unmatched_delims: Vec<UnmatchedDelim>,
12
13 /// Used only for error recovery when arriving to EOF with mismatched braces.
14 pub last_unclosed_found_span: Option<Span>,
15
16 /// Collect empty block spans that might have been auto-inserted by editors.
17 pub empty_block_spans: Vec<Span>,
18
19 /// Collect the spans of braces (Open, Close). Used only
20 /// for detecting if blocks are empty and only braces.
21 pub matching_block_spans: Vec<(Span, Span)>,
22}
23
353b0b11 24pub fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool {
9ffffee4
FG
25 match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) {
26 (Some(open_padding), Some(close_padding)) => open_padding == close_padding,
27 _ => false,
28 }
29}
30
31// When we get a `)` or `]` for `{`, we should emit help message here
32// it's more friendly compared to report `unmatched error` in later phase
33pub fn report_missing_open_delim(
34 err: &mut Diagnostic,
35 unmatched_delims: &[UnmatchedDelim],
36) -> bool {
37 let mut reported_missing_open = false;
38 for unmatch_brace in unmatched_delims.iter() {
39 if let Some(delim) = unmatch_brace.found_delim
40 && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
41 {
42 let missed_open = match delim {
43 Delimiter::Parenthesis => "(",
44 Delimiter::Bracket => "[",
45 _ => unreachable!(),
46 };
47 err.span_label(
48 unmatch_brace.found_span.shrink_to_lo(),
49 format!("missing open `{}` for this delimiter", missed_open),
50 );
51 reported_missing_open = true;
52 }
53 }
54 reported_missing_open
55}
56
57pub fn report_suspicious_mismatch_block(
58 err: &mut Diagnostic,
59 diag_info: &TokenTreeDiagInfo,
60 sm: &SourceMap,
61 delim: Delimiter,
62) {
63 if report_missing_open_delim(err, &diag_info.unmatched_delims) {
64 return;
65 }
66
67 let mut matched_spans: Vec<(Span, bool)> = diag_info
68 .matching_block_spans
69 .iter()
353b0b11 70 .map(|&(open, close)| (open.with_hi(close.lo()), same_indentation_level(sm, open, close)))
9ffffee4
FG
71 .collect();
72
73 // sort by `lo`, so the large block spans in the front
353b0b11 74 matched_spans.sort_by_key(|(span, _)| span.lo());
9ffffee4 75
353b0b11 76 // We use larger block whose indentation is well to cover those inner mismatched blocks
9ffffee4
FG
77 // O(N^2) here, but we are on error reporting path, so it is fine
78 for i in 0..matched_spans.len() {
79 let (block_span, same_ident) = matched_spans[i];
80 if same_ident {
81 for j in i + 1..matched_spans.len() {
82 let (inner_block, inner_same_ident) = matched_spans[j];
83 if block_span.contains(inner_block) && !inner_same_ident {
84 matched_spans[j] = (inner_block, true);
85 }
86 }
87 }
88 }
89
90 // Find the inner-most span candidate for final report
91 let candidate_span =
92 matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span);
93
94 if let Some(block_span) = candidate_span {
95 err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed...");
96 err.span_label(
97 block_span.shrink_to_hi(),
98 "...as it matches this but it has different indentation",
99 );
100
101 // If there is a empty block in the mismatched span, note it
102 if delim == Delimiter::Brace {
103 for span in diag_info.empty_block_spans.iter() {
104 if block_span.contains(*span) {
105 err.span_label(*span, "block is empty, you might have not meant to close it");
106 break;
107 }
108 }
109 }
110 } else {
111 // If there is no suspicious span, give the last properly closed block may help
112 if let Some(parent) = diag_info.matching_block_spans.last()
113 && diag_info.open_braces.last().is_none()
114 && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) {
115 err.span_label(parent.0, "this opening brace...");
116 err.span_label(parent.1, "...matches this closing brace");
117 }
118 }
119}