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'" + ); + } + } + } } 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> {