From 6a015f6b5f42394d066506784e75a87fd21c01ee Mon Sep 17 00:00:00 2001 From: matt rice Date: Sun, 1 Mar 2026 05:13:48 -0800 Subject: [PATCH 1/2] Improve error when `CTParserBuilder::mod_name` is not a valid rust ident. This uses the suggested method in https://github.com/dtolnay/quote/issues/172 to check the ident for validity, and produce a helpful error. --- lrpar/src/lib/ctbuilder.rs | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lrpar/src/lib/ctbuilder.rs b/lrpar/src/lib/ctbuilder.rs index be141fec9..726908d1c 100644 --- a/lrpar/src/lib/ctbuilder.rs +++ b/lrpar/src/lib/ctbuilder.rs @@ -973,7 +973,15 @@ where None }; - let mod_name = format_ident!("{}", mod_name); + let mod_name = + match syn::parse_str::(mod_name) { + Ok(s) => s, + Err(e) => return Err(format!( + "CTParserBuilder::mod_name(\"{}\") is not a valid rust identifier due to '{}'", + mod_name, e + ) + .into()), + }; let out_tokens = quote! { #visibility mod #mod_name { // At the top so that `user_actions` may contain #![inner_attribute] @@ -1801,6 +1809,37 @@ C : 'a';" } } + #[test] + /// Tests a yacc .y filename containing a dash character leading to an invalid rust identifier + /// when that dash is subsequently used as the default `CTParserBuilder::mod_name`. + fn test_invalid_identifier_in_derived_mod_name() { + let temp = TempDir::new().unwrap(); + let mut file_path = PathBuf::from(temp.as_ref()); + file_path.push("contains-a-dash.y"); + let mut f = File::create(&file_path).unwrap(); + let _ = f.write_all( + "%start A +%% +A : 'a';" + .as_bytes(), + ); + match CTParserBuilder::::new() + .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree)) + .grammar_path(file_path.to_str().unwrap()) + .output_path(file_path.with_extension("ignored")) + .build() + { + Ok(_) => panic!("Expected error"), + Err(e) => { + let err_string = e.to_string(); + assert_eq!( + err_string, + "CTParserBuilder::mod_name(\"contains-a-dash_y\") is not a valid rust identifier due to 'unexpected token'" + ); + } + } + } + #[cfg(test)] #[test] fn test_recoverer_header() -> Result<(), Box> { From bab8c60ff74385e7a07ac3319cd3d28b495320f4 Mon Sep 17 00:00:00 2001 From: matt rice Date: Sun, 1 Mar 2026 07:05:11 -0800 Subject: [PATCH 2/2] Improve error when `CTLexerBuilder::mod_name` is not a valid rust ident. --- lrlex/src/lib/ctbuilder.rs | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/lrlex/src/lib/ctbuilder.rs b/lrlex/src/lib/ctbuilder.rs index 788ca782d..8550df4ea 100644 --- a/lrlex/src/lib/ctbuilder.rs +++ b/lrlex/src/lib/ctbuilder.rs @@ -753,7 +753,15 @@ where format!("{}_l", stem) } }; - let mod_name = format_ident!("{}", mod_name); + let mod_name = + match syn::parse_str::(&mod_name) { + Ok(s) => s, + Err(e) => return Err(format!( + "CTLexerBuilder::mod_name(\"{}\") is not a valid rust identifier due to '{}'", + mod_name, e + ) + .into()), + }; let mut lexerdef_func_impl = { let LexFlags { allow_wholeline_comments, @@ -1488,4 +1496,34 @@ mod test { .unwrap(); } } + + #[test] + /// Tests a yacc .y filename containing a dash character leading to an invalid rust identifier + /// when that dash is subsequently used as the default `CTParserBuilder::mod_name`. + fn test_invalid_identifier_in_derived_mod_name() { + let mut lex_path = std::path::PathBuf::from(env!("OUT_DIR")); + lex_path.push("contains-a-dash.l"); + let mut f = File::create(&lex_path).unwrap(); + let _ = f.write_all( + r#" +%% +A "A" +"# + .as_bytes(), + ); + match CTLexerBuilder::new() + .output_path(format!("{}.rs", lex_path.display())) + .lexer_path(lex_path.clone()) + .build() + { + Ok(_) => panic!("Expected error"), + Err(e) => { + let err_string = e.to_string(); + assert_eq!( + err_string, + "CTLexerBuilder::mod_name(\"contains-a-dash_l\") is not a valid rust identifier due to 'unexpected token'" + ); + } + } + } }