@@ -2,9 +2,10 @@ use std::borrow::Cow;
22
33use rustc_ast:: token:: { self , Token } ;
44use rustc_ast:: tokenstream:: TokenStream ;
5- use rustc_errors:: { Applicability , Diag , DiagCtxtHandle , DiagMessage } ;
5+ use rustc_errors:: { Applicability , Diag , DiagCtxtHandle , DiagMessage , pluralize } ;
66use rustc_hir:: attrs:: diagnostic:: { CustomDiagnostic , Directive , FormatArgs } ;
77use rustc_macros:: Subdiagnostic ;
8+ use rustc_middle:: bug;
89use rustc_parse:: parser:: { Parser , Recovery , token_descr} ;
910use rustc_session:: parse:: ParseSess ;
1011use rustc_span:: source_map:: SourceMap ;
@@ -16,7 +17,7 @@ use crate::expand::{AstFragmentKind, parse_ast_fragment};
1617use crate :: mbe:: macro_parser:: ParseResult :: * ;
1718use crate :: mbe:: macro_parser:: { MatcherLoc , NamedParseResult , TtParser } ;
1819use 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
2223pub ( 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.
147148struct 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
156158struct 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
163173impl 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
232279impl < ' 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