Skip to content

Commit 8321aba

Browse files
committed
Auto merge of #158577 - bal-e:macro-parsing-polish, r=<try>
Polish some macro parsing code try-job: test-various
2 parents 3659db0 + c1aa8d7 commit 8321aba

3 files changed

Lines changed: 212 additions & 140 deletions

File tree

compiler/rustc_expand/src/mbe/diagnostics.rs

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use std::borrow::Cow;
22

33
use rustc_ast::token::{self, Token};
44
use rustc_ast::tokenstream::TokenStream;
5-
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage};
5+
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, pluralize};
66
use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
77
use rustc_macros::Subdiagnostic;
8+
use rustc_middle::bug;
89
use rustc_parse::parser::{Parser, Recovery, token_descr};
910
use rustc_session::parse::ParseSess;
1011
use rustc_span::source_map::SourceMap;
@@ -16,7 +17,7 @@ use crate::expand::{AstFragmentKind, parse_ast_fragment};
1617
use crate::mbe::macro_parser::ParseResult::*;
1718
use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
1819
use crate::mbe::macro_rules::{
19-
Tracker, try_match_macro, try_match_macro_attr, try_match_macro_derive,
20+
Tracker, WhichMatcher, try_match_macro, try_match_macro_attr, try_match_macro_derive,
2021
};
2122

2223
pub(super) enum FailedMacro<'a> {
@@ -44,7 +45,7 @@ pub(super) fn failed_to_match_macro(
4445

4546
// An error occurred, try the expansion again, tracking the expansion closely for better
4647
// diagnostics.
47-
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
48+
let mut tracker = CollectTrackerAndEmitter::new(name, psess.dcx(), sp);
4849

4950
let try_success_result = match args {
5051
FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker),
@@ -121,7 +122,7 @@ pub(super) fn failed_to_match_macro(
121122
for rule in rules {
122123
let MacroRule::Func { lhs, .. } = rule else { continue };
123124
let parser = parser_from_cx(psess, body.clone(), Recovery::Allowed);
124-
let mut tt_parser = TtParser::new(name);
125+
let mut tt_parser = TtParser::new();
125126

126127
if let Success(_) =
127128
tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
@@ -145,6 +146,7 @@ pub(super) fn failed_to_match_macro(
145146

146147
/// The tracker used for the slow error path that collects useful info for diagnostics.
147148
struct CollectTrackerAndEmitter<'dcx, 'matcher> {
149+
macro_name: Ident,
148150
dcx: DiagCtxtHandle<'dcx>,
149151
remaining_matcher: Option<&'matcher MatcherLoc>,
150152
/// Which arm's failure should we report? (the one furthest along)
@@ -155,14 +157,22 @@ struct CollectTrackerAndEmitter<'dcx, 'matcher> {
155157

156158
struct BestFailure {
157159
token: Token,
158-
position_in_tokenstream: (bool, u32),
160+
161+
/// The matcher in which the failure occurred.
162+
matcher: WhichMatcher,
163+
164+
/// The approximate (parser) position of the failure.
165+
///
166+
/// This is relative to [`Self::matcher`].
167+
position: u32,
168+
159169
msg: &'static str,
160170
remaining_matcher: MatcherLoc,
161171
}
162172

163173
impl BestFailure {
164-
fn is_better_position(&self, position: (bool, u32)) -> bool {
165-
position > self.position_in_tokenstream
174+
fn is_better_position(&self, matcher: WhichMatcher, position: u32) -> bool {
175+
(matcher, position) > (self.matcher, self.position)
166176
}
167177
}
168178

@@ -181,8 +191,8 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
181191
}
182192
}
183193

184-
fn after_arm(&mut self, in_body: bool, result: &NamedParseResult<Self::Failure>) {
185-
match result {
194+
fn after_arm(&mut self, which_matcher: WhichMatcher, result: &NamedParseResult<Self::Failure>) {
195+
match *result {
186196
Success(_) => {
187197
// Nonterminal parser recovery might turn failed matches into successful ones,
188198
// but for that it must have emitted an error already
@@ -194,15 +204,13 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
194204
Failure((token, approx_position, msg)) => {
195205
debug!(?token, ?msg, "a new failure of an arm");
196206

197-
let position_in_tokenstream = (in_body, *approx_position);
198-
if self
199-
.best_failure
200-
.as_ref()
201-
.is_none_or(|failure| failure.is_better_position(position_in_tokenstream))
202-
{
207+
if self.best_failure.as_ref().is_none_or(|failure| {
208+
failure.is_better_position(which_matcher, approx_position)
209+
}) {
203210
self.best_failure = Some(BestFailure {
204-
token: *token,
205-
position_in_tokenstream,
211+
token,
212+
matcher: which_matcher,
213+
position: approx_position,
206214
msg,
207215
remaining_matcher: self
208216
.remaining_matcher
@@ -211,15 +219,54 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
211219
})
212220
}
213221
}
214-
Error(err_sp, msg) => {
215-
let span = err_sp.substitute_dummy(self.root_span);
216-
let guar = self.dcx.span_err(span, msg.clone());
217-
self.result = Some((span, guar));
222+
Ambiguity => {
223+
if self.result.is_none() {
224+
bug!("`Error(..)` is only constructed through `Self::ambiguity()`");
225+
}
218226
}
219-
ErrorReported(guar) => self.result = Some((self.root_span, *guar)),
227+
ErrorReported(guar) => self.result = Some((self.root_span, guar)),
220228
}
221229
}
222230

231+
fn ambiguity(
232+
&mut self,
233+
parser: &Parser<'_>,
234+
bb_locs: impl IntoIterator<Item = &'matcher MatcherLoc>,
235+
next_locs: impl IntoIterator<Item = &'matcher MatcherLoc>,
236+
) {
237+
let span = parser.token.span.substitute_dummy(self.root_span);
238+
239+
if parser.token == token::Eof {
240+
let msg = "ambiguity: multiple successful parses".to_string();
241+
let guar = self.dcx.span_err(span, msg);
242+
self.result = Some((span, guar));
243+
return;
244+
}
245+
246+
let nts = bb_locs
247+
.into_iter()
248+
.map(|loc| match loc {
249+
MatcherLoc::MetaVarDecl { bind, kind, .. } => {
250+
format!("{kind} ('{bind}')")
251+
}
252+
_ => unreachable!(),
253+
})
254+
.collect::<Vec<String>>()
255+
.join(" or ");
256+
257+
let msg = format!(
258+
"local ambiguity when calling macro `{}`: multiple parsing options: {}",
259+
self.macro_name,
260+
match next_locs.into_iter().count() {
261+
0 => format!("built-in NTs {nts}."),
262+
n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)),
263+
}
264+
);
265+
266+
let guar = self.dcx.span_err(span, msg);
267+
self.result = Some((span, guar));
268+
}
269+
223270
fn description() -> &'static str {
224271
"detailed"
225272
}
@@ -230,8 +277,15 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
230277
}
231278

232279
impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> {
233-
fn new(dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self {
234-
Self { dcx, remaining_matcher: None, best_failure: None, root_span, result: None }
280+
fn new(macro_name: Ident, dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self {
281+
Self {
282+
macro_name,
283+
dcx,
284+
remaining_matcher: None,
285+
best_failure: None,
286+
root_span,
287+
result: None,
288+
}
235289
}
236290
}
237291

0 commit comments

Comments
 (0)