rustc_parse/lexer/
diagnostics.rs1use 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 pub open_delimiters: Vec<(Delimiter, Span)>,
15 pub unmatched_delims: Vec<UnmatchedDelim>,
16
17 pub last_unclosed_found_span: Option<Span>,
19
20 pub empty_block_spans: Vec<Span>,
22
23 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
35pub(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 matched_spans.sort_by_key(|(span, _)| span.lo());
82
83 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 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 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 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 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}