@@ -235,13 +235,16 @@ where
235235 grammar_path : Option < PathBuf > ,
236236 output_path : Option < PathBuf > ,
237237 mod_name : Option < & ' a str > ,
238- recoverer : RecoveryKind ,
238+ recoverer : Option < RecoveryKind > ,
239239 yacckind : Option < YaccKind > ,
240240 error_on_conflicts : bool ,
241241 warnings_are_errors : bool ,
242242 show_warnings : bool ,
243243 visibility : Visibility ,
244244 rust_edition : RustEdition ,
245+ // test function for inspecting private state
246+ #[ cfg( test) ]
247+ inspect_callback : Option < Box < dyn Fn ( RecoveryKind ) -> Result < ( ) , Box < dyn Error > > > > ,
245248 phantom : PhantomData < LexerTypesT > ,
246249}
247250
@@ -279,13 +282,15 @@ where
279282 grammar_path : None ,
280283 output_path : None ,
281284 mod_name : None ,
282- recoverer : RecoveryKind :: CPCTPlus ,
285+ recoverer : None ,
283286 yacckind : None ,
284287 error_on_conflicts : true ,
285288 warnings_are_errors : true ,
286289 show_warnings : true ,
287290 visibility : Visibility :: Private ,
288291 rust_edition : RustEdition :: Rust2021 ,
292+ #[ cfg( test) ]
293+ inspect_callback : None ,
289294 phantom : PhantomData ,
290295 }
291296 }
@@ -378,7 +383,7 @@ where
378383
379384 /// Set the recoverer for this parser to `rk`. Defaults to `RecoveryKind::CPCTPlus`.
380385 pub fn recoverer ( mut self , rk : RecoveryKind ) -> Self {
381- self . recoverer = rk ;
386+ self . recoverer = Some ( rk ) ;
382387 self
383388 }
384389
@@ -416,6 +421,15 @@ where
416421 self
417422 }
418423
424+ #[ cfg( test) ]
425+ pub fn inspect_recoverer (
426+ mut self ,
427+ cb : Box < dyn for <' h , ' y > Fn ( RecoveryKind ) -> Result < ( ) , Box < dyn Error > > > ,
428+ ) -> Self {
429+ self . inspect_callback = Some ( cb) ;
430+ self
431+ }
432+
419433 /// Statically compile the Yacc file specified by [CTParserBuilder::grammar_path()] into Rust,
420434 /// placing the output into the file spec [CTParserBuilder::output_path()]. Note that three
421435 /// additional files will be created with the same name as specified in [self.output_path] but
@@ -494,6 +508,18 @@ where
494508 }
495509 } ,
496510 }
511+ if let Some ( recoverer) = self . recoverer {
512+ match header. entry ( "recoverer" . to_string ( ) ) {
513+ Entry :: Occupied ( _) => unreachable ! ( ) ,
514+ Entry :: Vacant ( v) => {
515+ let rk_value: Value = Value :: try_from ( recoverer) ?;
516+ let mut o =
517+ v. insert_entry ( ( Location :: Other ( "CTParserBuilder" . to_string ( ) ) , rk_value) ) ;
518+ o. set_merge_behavior ( MergeBehavior :: Ours ) ;
519+ }
520+ }
521+ }
522+
497523 {
498524 let mut lk = GENERATED_PATHS . lock ( ) . unwrap ( ) ;
499525 if lk. contains ( outp. as_path ( ) ) {
@@ -505,18 +531,16 @@ where
505531 let inc =
506532 read_to_string ( grmp) . map_err ( |e| format ! ( "When reading '{}': {e}" , grmp. display( ) ) ) ?;
507533 let ast_validation = ASTWithValidityInfo :: new ( & mut header, & inc) ;
508- let unused_keys = header. unused ( ) ;
509- if !unused_keys. is_empty ( ) {
510- return Err ( format ! ( "Unused keys in header: {}" , unused_keys. join( ", " ) ) . into ( ) ) ;
511- }
512- let missing_keys = header. missing ( ) ;
513- if !missing_keys. is_empty ( ) {
514- return Err ( format ! (
515- "Required values were missing from the header: {}" ,
516- unused_keys. join( ", " )
517- )
518- . into ( ) ) ;
534+ header. mark_used ( & "recoverer" . to_string ( ) ) ;
535+ let rk_val = header. get ( "recoverer" ) . map ( |( _, rk_val) | rk_val) ;
536+
537+ if let Some ( rk_val) = rk_val {
538+ self . recoverer = Some ( RecoveryKind :: try_from ( rk_val) ?) ;
539+ } else {
540+ // Fallback to the default recoverykind.
541+ self . recoverer = Some ( RecoveryKind :: CPCTPlus ) ;
519542 }
543+
520544 self . yacckind = ast_validation. yacc_kind ( ) ;
521545 let warnings = ast_validation. ast ( ) . warnings ( ) ;
522546 let loc_fmt = |err_str, loc, inc : & str , line_cache : & NewlineCache | match loc {
@@ -602,6 +626,24 @@ where
602626 }
603627 } ;
604628
629+ #[ cfg( test) ]
630+ if let Some ( cb) = & self . inspect_callback {
631+ cb ( self . recoverer . expect ( "has a default value" ) ) ?;
632+ }
633+
634+ let unused_keys = header. unused ( ) ;
635+ if !unused_keys. is_empty ( ) {
636+ return Err ( format ! ( "Unused keys in header: {}" , unused_keys. join( ", " ) ) . into ( ) ) ;
637+ }
638+ let missing_keys = header. missing ( ) ;
639+ if !missing_keys. is_empty ( ) {
640+ return Err ( format ! (
641+ "Required values were missing from the header: {}" ,
642+ unused_keys. join( ", " )
643+ )
644+ . into ( ) ) ;
645+ }
646+
605647 let rule_ids = grm
606648 . tokens_map ( )
607649 . iter ( )
@@ -797,6 +839,8 @@ where
797839 show_warnings : self . show_warnings ,
798840 visibility : self . visibility . clone ( ) ,
799841 rust_edition : self . rust_edition ,
842+ #[ cfg( test) ]
843+ inspect_callback : None ,
800844 phantom : PhantomData ,
801845 } ;
802846 Ok ( cl. build ( ) ?. rule_ids )
@@ -873,7 +917,7 @@ where
873917 // rustc forces a recompile, this will change this value, causing anything which depends on
874918 // this build of lrpar to be recompiled too.
875919 let Self {
876- // All variables except for `output_path` and `phantom` should
920+ // All variables except for `output_path`, `inspect_callback` and `phantom` should
877921 // be written into the cache.
878922 grammar_path,
879923 mod_name,
@@ -885,6 +929,8 @@ where
885929 show_warnings,
886930 visibility,
887931 rust_edition,
932+ #[ cfg( test) ]
933+ inspect_callback : _,
888934 phantom : _,
889935 } = self ;
890936 let build_time = env ! ( "VERGEN_BUILD_TIMESTAMP" ) ;
@@ -1573,4 +1619,75 @@ C : 'a';"
15731619 }
15741620 }
15751621 }
1622+
1623+ #[ cfg( test) ]
1624+ #[ test]
1625+ fn test_recoverer_header ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
1626+ use crate :: RecoveryKind as RK ;
1627+ #[ rustfmt:: skip]
1628+ let recovery_kinds = [
1629+ // Builder, Header setting, Expected result.
1630+ // ----------- ------------------ -------------------
1631+ ( Some ( RK :: None ) , Some ( RK :: None ) , Some ( RK :: None ) ) ,
1632+ ( Some ( RK :: None ) , Some ( RK :: CPCTPlus ) , Some ( RK :: None ) ) ,
1633+ ( Some ( RK :: CPCTPlus ) , Some ( RK :: CPCTPlus ) , Some ( RK :: CPCTPlus ) ) ,
1634+ ( Some ( RK :: CPCTPlus ) , Some ( RK :: None ) , Some ( RK :: CPCTPlus ) ) ,
1635+ ( None , Some ( RK :: CPCTPlus ) , Some ( RK :: CPCTPlus ) ) ,
1636+ ( None , Some ( RK :: None ) , Some ( RK :: None ) ) ,
1637+ ( None , None , Some ( RK :: CPCTPlus ) ) ,
1638+ ( Some ( RK :: None ) , None , Some ( RK :: None ) ) ,
1639+ ( Some ( RK :: CPCTPlus ) , None , Some ( RK :: CPCTPlus ) ) ,
1640+ ] ;
1641+
1642+ for ( i, ( builder_arg, header_arg, expected_rk) ) in
1643+ recovery_kinds. iter ( ) . cloned ( ) . enumerate ( )
1644+ {
1645+ let y_src = if let Some ( header_arg) = header_arg {
1646+ format ! (
1647+ "\
1648+ %grmtools{{yacckind: Original(NoAction), recoverer: {}}} \
1649+ %% \
1650+ start: ; \
1651+ ",
1652+ match header_arg {
1653+ RK :: None => "RecoveryKind::None" ,
1654+ RK :: CPCTPlus => "RecoveryKind::CPCTPlus" ,
1655+ }
1656+ )
1657+ } else {
1658+ r#"
1659+ %grmtools{yacckind: Original(NoAction)}
1660+ %%
1661+ Start: ;
1662+ "#
1663+ . to_string ( )
1664+ } ;
1665+ let out_dir = std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ;
1666+ let y_path = format ! ( "{out_dir}/recoverykind_test_{i}.y" ) ;
1667+ let y_out_path = format ! ( "{y_path}.rs" ) ;
1668+ std:: fs:: File :: create ( y_path. clone ( ) ) . unwrap ( ) ;
1669+ std:: fs:: write ( y_path. clone ( ) , y_src) . unwrap ( ) ;
1670+ let mut cp_builder = CTParserBuilder :: < TestLexerTypes > :: new ( ) ;
1671+ cp_builder = cp_builder
1672+ . output_path ( y_out_path. clone ( ) )
1673+ . grammar_path ( y_path. clone ( ) ) ;
1674+ cp_builder = if let Some ( builder_arg) = builder_arg {
1675+ cp_builder. recoverer ( builder_arg)
1676+ } else {
1677+ cp_builder
1678+ }
1679+ . inspect_recoverer ( Box :: new ( move |rk| {
1680+ if matches ! (
1681+ ( rk, expected_rk) ,
1682+ ( RK :: None , Some ( RK :: None ) ) | ( RK :: CPCTPlus , Some ( RK :: CPCTPlus ) )
1683+ ) {
1684+ Ok ( ( ) )
1685+ } else {
1686+ panic ! ( "Unexpected recovery kind" )
1687+ }
1688+ } ) ) ;
1689+ cp_builder. build ( ) ?;
1690+ }
1691+ Ok ( ( ) )
1692+ }
15761693}
0 commit comments