mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge pull request #8502 from roc-lang/fix-qualified-arrow
Fix qualified arrow function calls
This commit is contained in:
commit
2eb0f3e127
7 changed files with 237 additions and 34 deletions
|
|
@ -953,7 +953,19 @@ const Formatter = struct {
|
|||
if (multiline and try fmt.flushCommentsAfter(ld.operator)) {
|
||||
try fmt.pushIndent();
|
||||
}
|
||||
_ = try fmt.formatExprInner(ld.right, .no_indent_on_access);
|
||||
// For arrow syntax, omit empty parens: `foo->bar()` becomes `foo->bar`
|
||||
const right_expr = fmt.ast.store.getExpr(ld.right);
|
||||
if (right_expr == .apply) {
|
||||
const apply = right_expr.apply;
|
||||
if (fmt.ast.store.exprSlice(apply.args).len == 0) {
|
||||
// Zero-arg apply: just format the function, not the empty parens
|
||||
_ = try fmt.formatExprInner(apply.@"fn", .no_indent_on_access);
|
||||
} else {
|
||||
_ = try fmt.formatExprInner(ld.right, .no_indent_on_access);
|
||||
}
|
||||
} else {
|
||||
_ = try fmt.formatExprInner(ld.right, .no_indent_on_access);
|
||||
}
|
||||
},
|
||||
.int => |i| {
|
||||
try fmt.pushTokenText(i.token);
|
||||
|
|
|
|||
|
|
@ -2199,30 +2199,34 @@ pub fn parseExprWithBp(self: *Parser, min_bp: u8) Error!AST.Expr.Idx {
|
|||
} else if (self.peek() == .OpArrow) {
|
||||
const s = self.pos;
|
||||
self.advance();
|
||||
if (self.peek() == .LowerIdent) {
|
||||
const empty_qualifiers = try self.store.tokenSpanFrom(self.store.scratchTokenTop());
|
||||
const ident = try self.store.addExpr(.{ .ident = .{
|
||||
.region = .{ .start = self.pos, .end = self.pos },
|
||||
.token = self.pos,
|
||||
.qualifiers = empty_qualifiers,
|
||||
} });
|
||||
self.advance();
|
||||
const ident_suffixed = try self.parseExprSuffix(s, ident);
|
||||
expression = try self.store.addExpr(.{ .local_dispatch = .{
|
||||
.region = .{ .start = start, .end = self.pos },
|
||||
.operator = s,
|
||||
.left = expression,
|
||||
.right = ident_suffixed,
|
||||
} });
|
||||
} else if (self.peek() == .UpperIdent) { // UpperIdent - should be a tag
|
||||
const empty_qualifiers = try self.store.tokenSpanFrom(self.store.scratchTokenTop());
|
||||
const tag = try self.store.addExpr(.{ .tag = .{
|
||||
.region = .{ .start = self.pos, .end = self.pos },
|
||||
.token = self.pos,
|
||||
.qualifiers = empty_qualifiers,
|
||||
} });
|
||||
self.advance();
|
||||
const ident_suffixed = try self.parseExprSuffix(s, tag);
|
||||
const first_token_tag = self.peek();
|
||||
if (first_token_tag == .LowerIdent or first_token_tag == .UpperIdent) {
|
||||
const ident_start = self.pos;
|
||||
const qual_result = try self.parseQualificationChain();
|
||||
// Use final token as end position to avoid newline tokens
|
||||
self.pos = qual_result.final_token + 1;
|
||||
|
||||
// Determine if final token is a tag (UpperIdent or ends with NoSpaceDotUpperIdent)
|
||||
// For unqualified names, check the original token; for qualified names, use is_upper
|
||||
const is_tag = if (qual_result.qualifiers.span.len == 0)
|
||||
first_token_tag == .UpperIdent
|
||||
else
|
||||
qual_result.is_upper;
|
||||
|
||||
const expr_node = if (is_tag)
|
||||
try self.store.addExpr(.{ .tag = .{
|
||||
.region = .{ .start = ident_start, .end = self.pos },
|
||||
.token = qual_result.final_token,
|
||||
.qualifiers = qual_result.qualifiers,
|
||||
} })
|
||||
else
|
||||
try self.store.addExpr(.{ .ident = .{
|
||||
.region = .{ .start = ident_start, .end = self.pos },
|
||||
.token = qual_result.final_token,
|
||||
.qualifiers = qual_result.qualifiers,
|
||||
} });
|
||||
|
||||
const ident_suffixed = try self.parseExprSuffix(s, expr_node);
|
||||
expression = try self.store.addExpr(.{ .local_dispatch = .{
|
||||
.region = .{ .start = start, .end = self.pos },
|
||||
.operator = s,
|
||||
|
|
|
|||
187
test/snapshots/arrow_qualified_functions.md
Normal file
187
test/snapshots/arrow_qualified_functions.md
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
# META
|
||||
~~~ini
|
||||
description=Arrow syntax with qualified functions and formatter dropping empty parens
|
||||
type=snippet
|
||||
~~~
|
||||
# SOURCE
|
||||
~~~roc
|
||||
# Test qualified function calls with arrow syntax
|
||||
test1 = "hello"->Str.is_empty
|
||||
test2 = "hello"->Str.is_empty()
|
||||
test3 = "hello"->Str.concat("bar")
|
||||
|
||||
# Test unqualified function calls
|
||||
fn0 = |a| a
|
||||
test4 = 10->fn0
|
||||
test5 = 10->fn0()
|
||||
|
||||
# Test tag syntax
|
||||
test6 = 42->Ok
|
||||
test7 = 42->Ok()
|
||||
~~~
|
||||
# EXPECTED
|
||||
NIL
|
||||
# PROBLEMS
|
||||
NIL
|
||||
# TOKENS
|
||||
~~~zig
|
||||
LowerIdent,OpAssign,StringStart,StringPart,StringEnd,OpArrow,UpperIdent,NoSpaceDotLowerIdent,
|
||||
LowerIdent,OpAssign,StringStart,StringPart,StringEnd,OpArrow,UpperIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,CloseRound,
|
||||
LowerIdent,OpAssign,StringStart,StringPart,StringEnd,OpArrow,UpperIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound,
|
||||
LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,LowerIdent,
|
||||
LowerIdent,OpAssign,Int,OpArrow,LowerIdent,
|
||||
LowerIdent,OpAssign,Int,OpArrow,LowerIdent,NoSpaceOpenRound,CloseRound,
|
||||
LowerIdent,OpAssign,Int,OpArrow,UpperIdent,
|
||||
LowerIdent,OpAssign,Int,OpArrow,UpperIdent,NoSpaceOpenRound,CloseRound,
|
||||
EndOfFile,
|
||||
~~~
|
||||
# PARSE
|
||||
~~~clojure
|
||||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-decl
|
||||
(p-ident (raw "test1"))
|
||||
(e-local-dispatch
|
||||
(e-string
|
||||
(e-string-part (raw "hello")))
|
||||
(e-ident (raw "Str.is_empty"))))
|
||||
(s-decl
|
||||
(p-ident (raw "test2"))
|
||||
(e-local-dispatch
|
||||
(e-string
|
||||
(e-string-part (raw "hello")))
|
||||
(e-apply
|
||||
(e-ident (raw "Str.is_empty")))))
|
||||
(s-decl
|
||||
(p-ident (raw "test3"))
|
||||
(e-local-dispatch
|
||||
(e-string
|
||||
(e-string-part (raw "hello")))
|
||||
(e-apply
|
||||
(e-ident (raw "Str.concat"))
|
||||
(e-string
|
||||
(e-string-part (raw "bar"))))))
|
||||
(s-decl
|
||||
(p-ident (raw "fn0"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-ident (raw "a")))
|
||||
(e-ident (raw "a"))))
|
||||
(s-decl
|
||||
(p-ident (raw "test4"))
|
||||
(e-local-dispatch
|
||||
(e-int (raw "10"))
|
||||
(e-ident (raw "fn0"))))
|
||||
(s-decl
|
||||
(p-ident (raw "test5"))
|
||||
(e-local-dispatch
|
||||
(e-int (raw "10"))
|
||||
(e-apply
|
||||
(e-ident (raw "fn0")))))
|
||||
(s-decl
|
||||
(p-ident (raw "test6"))
|
||||
(e-local-dispatch
|
||||
(e-int (raw "42"))
|
||||
(e-tag (raw "Ok"))))
|
||||
(s-decl
|
||||
(p-ident (raw "test7"))
|
||||
(e-local-dispatch
|
||||
(e-int (raw "42"))
|
||||
(e-apply
|
||||
(e-tag (raw "Ok")))))))
|
||||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
# Test qualified function calls with arrow syntax
|
||||
test1 = "hello"->Str.is_empty
|
||||
test2 = "hello"->Str.is_empty
|
||||
test3 = "hello"->Str.concat("bar")
|
||||
|
||||
# Test unqualified function calls
|
||||
fn0 = |a| a
|
||||
test4 = 10->fn0
|
||||
test5 = 10->fn0
|
||||
|
||||
# Test tag syntax
|
||||
test6 = 42->Ok
|
||||
test7 = 42->Ok
|
||||
~~~
|
||||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(d-let
|
||||
(p-assign (ident "test1"))
|
||||
(e-call
|
||||
(e-lookup-external
|
||||
(builtin))
|
||||
(e-string
|
||||
(e-literal (string "hello")))))
|
||||
(d-let
|
||||
(p-assign (ident "test2"))
|
||||
(e-call
|
||||
(e-lookup-external
|
||||
(builtin))
|
||||
(e-string
|
||||
(e-literal (string "hello")))))
|
||||
(d-let
|
||||
(p-assign (ident "test3"))
|
||||
(e-call
|
||||
(e-lookup-external
|
||||
(builtin))
|
||||
(e-string
|
||||
(e-literal (string "hello")))
|
||||
(e-string
|
||||
(e-literal (string "bar")))))
|
||||
(d-let
|
||||
(p-assign (ident "fn0"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-assign (ident "a")))
|
||||
(e-lookup-local
|
||||
(p-assign (ident "a")))))
|
||||
(d-let
|
||||
(p-assign (ident "test4"))
|
||||
(e-call
|
||||
(e-lookup-local
|
||||
(p-assign (ident "fn0")))
|
||||
(e-num (value "10"))))
|
||||
(d-let
|
||||
(p-assign (ident "test5"))
|
||||
(e-call
|
||||
(e-lookup-local
|
||||
(p-assign (ident "fn0")))
|
||||
(e-num (value "10"))))
|
||||
(d-let
|
||||
(p-assign (ident "test6"))
|
||||
(e-tag (name "Ok")
|
||||
(args
|
||||
(e-num (value "42")))))
|
||||
(d-let
|
||||
(p-assign (ident "test7"))
|
||||
(e-tag (name "Ok")
|
||||
(args
|
||||
(e-num (value "42"))))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "Bool"))
|
||||
(patt (type "Bool"))
|
||||
(patt (type "Str"))
|
||||
(patt (type "b -> b"))
|
||||
(patt (type "b where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))
|
||||
(patt (type "b where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))
|
||||
(patt (type "[Ok(b)]_others where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))
|
||||
(patt (type "[Ok(b)]_others where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]")))
|
||||
(expressions
|
||||
(expr (type "Bool"))
|
||||
(expr (type "Bool"))
|
||||
(expr (type "Str"))
|
||||
(expr (type "b -> b"))
|
||||
(expr (type "b where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))
|
||||
(expr (type "b where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))
|
||||
(expr (type "[Ok(b)]_others where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))
|
||||
(expr (type "[Ok(b)]_others where [b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])]"))))
|
||||
~~~
|
||||
|
|
@ -243,7 +243,7 @@ UNUSED VARIABLE - fuzz_crash_023.md:1:1:1:1
|
|||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNUSED VARIABLE - fuzz_crash_023.md:1:1:1:1
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNDEFINED VARIABLE - fuzz_crash_023.md:121:37:121:37
|
||||
UNDEFINED VARIABLE - fuzz_crash_023.md:121:37:121:40
|
||||
UNUSED VARIABLE - fuzz_crash_023.md:121:21:121:27
|
||||
UNUSED VARIABLE - fuzz_crash_023.md:127:4:128:9
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
|
|
@ -594,11 +594,11 @@ This error doesn't have a proper diagnostic report yet. Let us know if you want
|
|||
Nothing is named `add` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**fuzz_crash_023.md:121:37:121:37:**
|
||||
**fuzz_crash_023.md:121:37:121:40:**
|
||||
```roc
|
||||
{ foo: 1, bar: 2, ..rest } => 12->add(34)
|
||||
```
|
||||
^
|
||||
^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ NOT IMPLEMENTED - :0:0:0:0
|
|||
UNUSED VARIABLE - fuzz_crash_027.md:1:1:1:1
|
||||
UNUSED VARIABLE - fuzz_crash_027.md:76:1:76:4
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNDEFINED VARIABLE - fuzz_crash_027.md:82:37:82:37
|
||||
UNDEFINED VARIABLE - fuzz_crash_027.md:82:37:82:40
|
||||
UNUSED VARIABLE - fuzz_crash_027.md:82:21:82:27
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
|
|
@ -603,11 +603,11 @@ This error doesn't have a proper diagnostic report yet. Let us know if you want
|
|||
Nothing is named `add` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**fuzz_crash_027.md:82:37:82:37:**
|
||||
**fuzz_crash_027.md:82:37:82:40:**
|
||||
```roc
|
||||
{ foo: 1, bar: 2, ..rest } => 12->add(34)
|
||||
```
|
||||
^
|
||||
^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -238,7 +238,7 @@ UNUSED VARIABLE - syntax_grab_bag.md:1:1:1:1
|
|||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNUSED VARIABLE - syntax_grab_bag.md:1:1:1:1
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNDEFINED VARIABLE - syntax_grab_bag.md:121:37:121:37
|
||||
UNDEFINED VARIABLE - syntax_grab_bag.md:121:37:121:40
|
||||
UNUSED VARIABLE - syntax_grab_bag.md:121:21:121:27
|
||||
UNUSED VARIABLE - syntax_grab_bag.md:127:4:128:9
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
|
|
@ -529,11 +529,11 @@ This error doesn't have a proper diagnostic report yet. Let us know if you want
|
|||
Nothing is named `add` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**syntax_grab_bag.md:121:37:121:37:**
|
||||
**syntax_grab_bag.md:121:37:121:40:**
|
||||
```roc
|
||||
{ foo: 1, bar: 2, ..rest } => 12->add(34)
|
||||
```
|
||||
^
|
||||
^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue