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::diagnostics::MismatchedClosingDelimiter;
9use crate::pprust;
10
11#[derive(#[automatically_derived]
impl ::core::default::Default for TokenTreeDiagInfo {
#[inline]
fn default() -> TokenTreeDiagInfo {
TokenTreeDiagInfo {
open_delimiters: ::core::default::Default::default(),
unmatched_delims: ::core::default::Default::default(),
last_unclosed_found_span: ::core::default::Default::default(),
empty_block_spans: ::core::default::Default::default(),
if_let_chain_hint_spans: ::core::default::Default::default(),
matching_block_spans: ::core::default::Default::default(),
}
}
}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 if_let_chain_hint_spans: Vec<Span>,
26
27 pub matching_block_spans: Vec<(Span, Span)>,
30}
31
32pub(super) fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool {
33 match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) {
34 (Some(open_padding), Some(close_padding)) => open_padding == close_padding,
35 _ => false,
36 }
37}
38
39pub(super) fn report_missing_open_delim(
42 err: &mut Diag<'_>,
43 unmatched_delims: &mut Vec<UnmatchedDelim>,
44) -> bool {
45 let mut reported_missing_open = false;
46 unmatched_delims.retain(|unmatch_brace| {
47 if let Some(delim) = unmatch_brace.found_delim
48 && #[allow(non_exhaustive_omitted_patterns)] match delim {
Delimiter::Parenthesis | Delimiter::Bracket => true,
_ => false,
}matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
49 {
50 let missed_open = match delim {
51 Delimiter::Parenthesis => "(",
52 Delimiter::Bracket => "[",
53 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
54 };
55
56 if let Some(unclosed_span) = unmatch_brace.unclosed_span {
57 err.span_label(unclosed_span, "the nearest open delimiter");
58 }
59 err.span_label(
60 unmatch_brace.found_span.shrink_to_lo(),
61 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("missing open `{0}` for this delimiter",
missed_open))
})format!("missing open `{missed_open}` for this delimiter"),
62 );
63 reported_missing_open = true;
64 false
65 } else {
66 true
67 }
68 });
69 reported_missing_open
70}
71
72pub(super) fn report_suspicious_mismatch_block(
73 err: &mut Diag<'_>,
74 diag_info: &TokenTreeDiagInfo,
75 sm: &SourceMap,
76 delim: Delimiter,
77) {
78 let mut matched_spans: Vec<(Span, bool)> = diag_info
79 .matching_block_spans
80 .iter()
81 .map(|&(open, close)| (open.with_hi(close.lo()), same_indentation_level(sm, open, close)))
82 .collect();
83
84 matched_spans.sort_by_key(|(span, _)| span.lo());
86
87 for i in 0..matched_spans.len() {
90 let (block_span, same_ident) = matched_spans[i];
91 if same_ident {
92 for j in i + 1..matched_spans.len() {
93 let (inner_block, inner_same_ident) = matched_spans[j];
94 if block_span.contains(inner_block) && !inner_same_ident {
95 matched_spans[j] = (inner_block, true);
96 }
97 }
98 }
99 }
100
101 let candidate_span =
103 matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span);
104
105 if let Some(block_span) = candidate_span {
106 err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed...");
107 err.span_label(
108 block_span.shrink_to_hi(),
109 "...as it matches this but it has different indentation",
110 );
111
112 if delim == Delimiter::Brace {
114 for span in diag_info.empty_block_spans.iter() {
115 if block_span.contains(*span) {
116 err.span_label(*span, "block is empty, you might have not meant to close it");
117 break;
118 }
119 }
120 }
121 } else {
122 if let Some(parent) = diag_info.matching_block_spans.last()
124 && diag_info.open_delimiters.last().is_none()
125 && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1))
126 {
127 err.span_label(parent.0, "this opening brace...");
128 err.span_label(parent.1, "...matches this closing brace");
129 }
130 }
131
132 for span in diag_info.if_let_chain_hint_spans.iter() {
133 err.span_label(*span, "you might have meant to continue an if-let chain here");
134 }
135}
136
137pub(crate) fn make_errors_for_mismatched_closing_delims<'psess>(
138 unmatcheds: &[UnmatchedDelim],
139 psess: &'psess ParseSess,
140) -> Vec<Diag<'psess>> {
141 unmatcheds
142 .iter()
143 .filter_map(|unmatched| {
144 let found_delim = unmatched.found_delim?;
147 let mut spans = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[unmatched.found_span]))vec![unmatched.found_span];
148 if let Some(sp) = unmatched.unclosed_span {
149 spans.push(sp);
150 };
151 let err = psess.dcx().create_err(MismatchedClosingDelimiter {
152 spans,
153 delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind())
154 .to_string(),
155 unmatched: unmatched.found_span,
156 opening_candidate: unmatched.candidate_span,
157 unclosed: unmatched.unclosed_span,
158 });
159 Some(err)
160 })
161 .collect()
162}