Skip to content

Commit e9c1941

Browse files
committed
Fix multiline function signature parsing
Multiline function signatures with type annotations were incorrectly parsed as tuples instead of calls when newlines appeared between parentheses. For example: ```julia function ( ::A )() end ``` was parsed as `(function (tuple ...) (block))` instead of the correct `(function (call (parens ...)) (block))`, inconsistent with the single-line version `function (::A)() end`. The issue was in parse_function_signature where `peek(ps, 2)` was used to detect if a call pattern follows the closing parenthesis, but this didn't skip newlines. Changed to `peek(ps, 2, skip_newlines=true)` to properly detect the opening parenthesis of the argument list even when separated by whitespace. 🤖 Generated with [Claude Code](https://claude.ai/code) better...
1 parent c5e4c03 commit e9c1941

File tree

2 files changed

+18
-6
lines changed

2 files changed

+18
-6
lines changed

src/julia/parser.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,13 +2199,18 @@ function parse_function_signature(ps::ParseState, is_function::Bool)
21992199
is_empty_tuple = peek(ps, skip_newlines=true) == K")"
22002200
opts = parse_brackets(ps, K")") do had_commas, had_splat, num_semis, num_subexprs
22012201
_parsed_call = was_eventually_call(ps)
2202-
_needs_parse_call = peek(ps, 2) KSet"( ."
2202+
_maybe_grouping_parens = !had_commas && !had_splat && num_semis == 0 && num_subexprs == 1
2203+
# Skip intervening newlines only when the parentheses hold a single
2204+
# expression, which is the ambiguous case between a name like (::T)
2205+
# and an anonymous function parameter list.
2206+
next_kind = peek(ps, 2, skip_newlines=_maybe_grouping_parens)
2207+
_needs_parse_call = next_kind KSet"( ."
22032208
_is_anon_func = (!_needs_parse_call && !_parsed_call) || had_commas
2204-
return (needs_parameters = _is_anon_func,
2205-
is_anon_func = _is_anon_func,
2206-
parsed_call = _parsed_call,
2207-
needs_parse_call = _needs_parse_call,
2208-
maybe_grouping_parens = !had_commas && !had_splat && num_semis == 0 && num_subexprs == 1)
2209+
return (needs_parameters = _is_anon_func,
2210+
is_anon_func = _is_anon_func,
2211+
parsed_call = _parsed_call,
2212+
needs_parse_call = _needs_parse_call,
2213+
maybe_grouping_parens = _maybe_grouping_parens)
22092214
end
22102215
is_anon_func = opts.is_anon_func
22112216
parsed_call = opts.parsed_call

test/parser.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,13 @@ tests = [
619619
"function (::g(x))() end" => "(function (call (parens (::-pre (call g x)))) (block))"
620620
"function (f::T{g(i)})() end" => "(function (call (parens (::-i f (curly T (call g i))))) (block))"
621621
"function (::T)() end" => "(function (call (parens (::-pre T))) (block))"
622+
"function (\n ::T\n )() end" => "(function (call (parens (::-pre T))) (block))"
623+
"function (\n x::T\n )() end" => "(function (call (parens (::-i x T))) (block))"
624+
"function (\n f\n )() end" => "(function (call (parens f)) (block))"
625+
"function (\n A\n ).f() end" => "(function (call (. (parens A) f)) (block))"
626+
"function (\n ::T\n )(x, y) end" => "(function (call (parens (::-pre T)) x y) (block))"
627+
"function (\n f::T{g(i)}\n )() end" => "(function (call (parens (::-i f (curly T (call g i))))) (block))"
628+
"function (\n x, y\n ) x + y end" => "(function (tuple-p x y) (block (call-i x + y)))"
622629
"function (:*=(f))() end" => "(function (call (parens (call (quote-: *=) f))) (block))"
623630
"function begin() end" => "(function (call (error begin)) (block))"
624631
"function f() end" => "(function (call f) (block))"

0 commit comments

Comments
 (0)