From 82e4fa23307b816eae172bca64f1f97acc7e5161 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 23 Nov 2025 21:52:02 -0500 Subject: [PATCH 1/2] fix anonymous function parsing --- JuliaSyntax/src/julia/parser.jl | 8 ++++---- JuliaSyntax/test/parser.jl | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/JuliaSyntax/src/julia/parser.jl b/JuliaSyntax/src/julia/parser.jl index 4142a010bb6b9..67a1d0cd00884 100644 --- a/JuliaSyntax/src/julia/parser.jl +++ b/JuliaSyntax/src/julia/parser.jl @@ -2200,10 +2200,10 @@ function parse_function_signature(ps::ParseState, is_function::Bool) opts = parse_brackets(ps, K")") do had_commas, had_splat, num_semis, num_subexprs _parsed_call = was_eventually_call(ps) _maybe_grouping_parens = !had_commas && !had_splat && num_semis == 0 && num_subexprs == 1 - # Skip intervening newlines only when the parentheses hold a single - # expression, which is the ambiguous case between a name like (::T) - # and an anonymous function parameter list. - next_kind = peek(ps, 2, skip_newlines=_maybe_grouping_parens) + # Don't skip newlines when checking for following `(` or `.` because + # a newline indicates this is an anonymous function body, not a call. + # E.g., `function (x)\n body\n end` vs `function (::T)(x) end` + next_kind = peek(ps, 2, skip_newlines=false) _needs_parse_call = next_kind ∈ KSet"( ." _is_anon_func = (!_needs_parse_call && !_parsed_call) || had_commas return (needs_parameters = _is_anon_func, diff --git a/JuliaSyntax/test/parser.jl b/JuliaSyntax/test/parser.jl index 6c66ea40123e0..c8b18f5bdd2e1 100644 --- a/JuliaSyntax/test/parser.jl +++ b/JuliaSyntax/test/parser.jl @@ -603,6 +603,7 @@ tests = [ "macro \$f() end" => "(macro (call (\$ f)) (block))" "macro (\$f)() end" => "(macro (call (parens (\$ f))) (block))" "function (x) body end"=> "(function (tuple-p x) (block body))" + "function (x)\n body\nend"=> "(function (tuple-p x) (block body))" "function (x,y) end" => "(function (tuple-p x y) (block))" "function (x,y,) end" => "(function (tuple-p-, x y) (block))" "function (x=1) end" => "(function (tuple-p (= x 1)) (block))" From e5769e57ad01db5174280af676834642efc01de2 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 24 Nov 2025 09:47:55 -0500 Subject: [PATCH 2/2] tryfix --- JuliaSyntax/src/julia/parser.jl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/JuliaSyntax/src/julia/parser.jl b/JuliaSyntax/src/julia/parser.jl index 67a1d0cd00884..c0cb44adfee2e 100644 --- a/JuliaSyntax/src/julia/parser.jl +++ b/JuliaSyntax/src/julia/parser.jl @@ -2200,10 +2200,11 @@ function parse_function_signature(ps::ParseState, is_function::Bool) opts = parse_brackets(ps, K")") do had_commas, had_splat, num_semis, num_subexprs _parsed_call = was_eventually_call(ps) _maybe_grouping_parens = !had_commas && !had_splat && num_semis == 0 && num_subexprs == 1 - # Don't skip newlines when checking for following `(` or `.` because - # a newline indicates this is an anonymous function body, not a call. - # E.g., `function (x)\n body\n end` vs `function (::T)(x) end` - next_kind = peek(ps, 2, skip_newlines=false) + # Check if there's a newline right after the closing paren + has_newline_after = peek(ps) == K"NewlineWs" + # Skip newlines when looking ahead only if there's no newline immediately after ) + # This prevents treating `function (x)\n(body)` as a named function + next_kind = peek(ps, 2, skip_newlines=_maybe_grouping_parens && !has_newline_after) _needs_parse_call = next_kind ∈ KSet"( ." _is_anon_func = (!_needs_parse_call && !_parsed_call) || had_commas return (needs_parameters = _is_anon_func,