diff --git a/CHANGELOG.md b/CHANGELOG.md index d91f4d80..b7c822fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Luau: Added support for parsing attributes (`@native` / `@deprecated`) on functions - Added support for CfxLua (FiveM) syntax formatting. This is available with `syntax = "cfxlua"` ([#855](https://github.com/JohnnyMorganz/StyLua/issues/855)) - Added a pre-built binary release for `stylua-linux-aarch64-musl.zip` +- Added error hints on parse failurse when a potential Lua syntax conflict is noticed (e.g., Lua 5.2 vs Luau syntax for labels `::` and generics `>>`) ([#960](https://github.com/JohnnyMorganz/StyLua/issues/960) / [#962](https://github.com/JohnnyMorganz/StyLua/issues/962)) ### Changed diff --git a/src/lib.rs b/src/lib.rs index b637757d..9c46e3b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ use context::Context; use full_moon::ast::Ast; +#[cfg(all(feature = "luau", any(feature = "lua52", feature = "lua53")))] +use full_moon::tokenizer::{Symbol, TokenType}; use serde::Deserialize; use thiserror::Error; #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] @@ -299,6 +301,29 @@ pub enum OutputVerification { None, } +#[cfg(all(feature = "luau", feature = "lua53"))] +fn is_luau_and_lua53_conflict(error: &full_moon::Error) -> bool { + match error { + full_moon::Error::AstError(ast_error) => match ast_error.token().token_type() { + TokenType::Symbol { + symbol: Symbol::DoubleGreaterThan, + } => ast_error.error_message() == "expected '>' to close generic type parameter list", + _ => false, + }, + _ => false, + } +} + +#[cfg(all(feature = "luau", feature = "lua52"))] +fn is_luau_and_lua52_conflict(error: &full_moon::Error) -> bool { + match error { + full_moon::Error::AstError(ast_error) => { + ast_error.error_message() == "expected label name after `::`" + } + _ => false, + } +} + fn print_full_moon_error(error: &full_moon::Error) -> String { match error { full_moon::Error::AstError(ast_error) => format!( @@ -315,14 +340,33 @@ fn print_full_moon_error(error: &full_moon::Error) -> String { } fn print_full_moon_errors(errors: &[full_moon::Error]) -> String { - if errors.len() == 1 { + #[allow(unused_mut)] + let mut error_message = if errors.len() == 1 { print_full_moon_error(errors.first().unwrap()) } else { errors .iter() .map(|err| "\n - ".to_string() + &print_full_moon_error(err)) .collect::() + }; + + #[cfg(all(feature = "luau", feature = "lua53"))] + { + let recommend_luau_syntax = errors.iter().any(is_luau_and_lua53_conflict); + if recommend_luau_syntax { + error_message += "\nhint: this looks like a conflict with Lua 5.3 and Luau generics syntax, add `syntax = \"Luau\"` to stylua.toml to resolve"; + } } + + #[cfg(all(feature = "luau", feature = "lua52"))] + { + let recommend_lua52_syntax = errors.iter().any(is_luau_and_lua52_conflict); + if recommend_lua52_syntax { + error_message += "\nhint: this looks like a conflict with Luau and Lua 5.2 label syntax, add `syntax = \"Lua52\"` to stylua.toml to resolve"; + } + } + + error_message } /// A formatting error diff --git a/tests/test_hint_syntax_config.rs b/tests/test_hint_syntax_config.rs new file mode 100644 index 00000000..08defd76 --- /dev/null +++ b/tests/test_hint_syntax_config.rs @@ -0,0 +1,27 @@ +#[cfg(all(feature = "luau", any(feature = "lua52", feature = "lua53")))] +use stylua_lib::{format_code, Config, OutputVerification}; + +#[test] +#[cfg(all(feature = "luau", feature = "lua53"))] +fn test_hint_syntax_luau_for_generic_parameter_list() { + let code = r#" + export type Foo = A> + "#; + let result = format_code(code, Config::default(), None, OutputVerification::None); + let error = result.unwrap_err().to_string(); + assert!(error.ends_with("hint: this looks like a conflict with Lua 5.3 and Luau generics syntax, add `syntax = \"Luau\"` to stylua.toml to resolve")); +} + +#[test] +#[cfg(all(feature = "luau", feature = "lua52"))] +fn test_hint_syntax_lua52_for_labels() { + let code = r#" + do + local x = 1 + ::continue:: + end + "#; + let result = format_code(code, Config::default(), None, OutputVerification::None); + let error = result.unwrap_err().to_string(); + assert!(error.ends_with("hint: this looks like a conflict with Luau and Lua 5.2 label syntax, add `syntax = \"Lua52\"` to stylua.toml to resolve")); +}