mirror of
https://github.com/roc-lang/roc.git
synced 2024-10-04 14:17:28 +03:00
Merge pull request #6809 from joshuawarner32/blockparse2
Implement block / indent based parsing
This commit is contained in:
commit
2a95f355ad
@ -6,9 +6,9 @@ use bumpalo::Bump;
|
||||
use roc_error_macros::{internal_error, user_error};
|
||||
use roc_fmt::def::fmt_defs;
|
||||
use roc_fmt::module::fmt_module;
|
||||
use roc_fmt::spaces::RemoveSpaces;
|
||||
use roc_fmt::{Ast, Buf};
|
||||
use roc_parse::module::parse_module_defs;
|
||||
use roc_parse::remove_spaces::RemoveSpaces;
|
||||
use roc_parse::{module, parser::SyntaxError, state::State};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -1361,10 +1361,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
||||
And => (ModuleName::BOOL, "and"),
|
||||
Or => (ModuleName::BOOL, "or"),
|
||||
Pizza => unreachable!("Cannot desugar the |> operator"),
|
||||
Assignment => unreachable!("Cannot desugar the = operator"),
|
||||
IsAliasType => unreachable!("Cannot desugar the : operator"),
|
||||
IsOpaqueType => unreachable!("Cannot desugar the := operator"),
|
||||
Backpassing => unreachable!("Cannot desugar the <- operator"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -73,22 +75,22 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @31-42 Identifier {
|
||||
ann_pattern: @31-43 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @31-42 Apply(
|
||||
ann_type: @31-43 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@31-42 Record {
|
||||
@31-43 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@31-42 Inferred,
|
||||
@31-43 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @31-42 Identifier {
|
||||
body_pattern: @31-43 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @31-42 Apply(
|
||||
@ -116,7 +118,7 @@ Defs {
|
||||
),
|
||||
@31-42 Closure(
|
||||
[
|
||||
@31-42 Underscore(
|
||||
@31-43 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -76,7 +78,7 @@ Defs {
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@78-91,
|
||||
@82-91,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
@ -113,8 +115,8 @@ Defs {
|
||||
body_pattern: @78-79 Identifier {
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
body_expr: @78-91 Apply(
|
||||
@78-91 Var {
|
||||
body_expr: @82-91 Apply(
|
||||
@82-91 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
@ -129,7 +131,7 @@ Defs {
|
||||
},
|
||||
],
|
||||
},
|
||||
@78-91 Var {
|
||||
@82-91 Var {
|
||||
module_name: "",
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -38,7 +40,7 @@ Defs {
|
||||
ident: "foo",
|
||||
},
|
||||
],
|
||||
@29-49 LowLevelDbg(
|
||||
@29-36 LowLevelDbg(
|
||||
(
|
||||
"test.roc:3",
|
||||
" ",
|
||||
|
@ -13,17 +13,19 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-24 Apply(
|
||||
@11-24 Var {
|
||||
@11-17 Apply(
|
||||
@11-17 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
@ -32,13 +34,13 @@ Defs {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@11-24 Closure(
|
||||
@11-17 Closure(
|
||||
[
|
||||
@15-17 Identifier {
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@11-24 LowLevelDbg(
|
||||
@11-17 LowLevelDbg(
|
||||
(
|
||||
"test.roc:2",
|
||||
"in",
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -17,11 +17,12 @@ Defs {
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 2, length = 0),
|
||||
Slice(start = 2, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,33 +13,56 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-31 Expect(
|
||||
@18-24 Apply(
|
||||
@20-22 Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
},
|
||||
[
|
||||
@18-19 Num(
|
||||
"1",
|
||||
),
|
||||
@23-24 Num(
|
||||
"2",
|
||||
),
|
||||
@11-31 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
BinOp(
|
||||
Equals,
|
||||
),
|
||||
),
|
||||
regions: [
|
||||
@11-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Expect {
|
||||
condition: @18-24 Apply(
|
||||
@20-22 Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
},
|
||||
[
|
||||
@18-19 Num(
|
||||
"1",
|
||||
),
|
||||
@23-24 Num(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Equals,
|
||||
),
|
||||
),
|
||||
preceding_comment: @11-11,
|
||||
},
|
||||
],
|
||||
},
|
||||
@29-31 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,17 +13,19 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@0-26 Apply(
|
||||
@0-26 Var {
|
||||
@7-26 Apply(
|
||||
@7-26 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -57,8 +59,8 @@ Defs {
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@29-41 Apply(
|
||||
@29-41 Var {
|
||||
@33-41 Apply(
|
||||
@33-41 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,17 +13,19 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-3 Identifier {
|
||||
ident: "run",
|
||||
},
|
||||
@0-22 Apply(
|
||||
@0-22 Var {
|
||||
@6-22 Apply(
|
||||
@6-22 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
@ -32,14 +34,14 @@ Defs {
|
||||
module_name: "",
|
||||
ident: "nextMsg",
|
||||
},
|
||||
@0-22 Closure(
|
||||
@6-22 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@0-22 Apply(
|
||||
@0-22 Var {
|
||||
@6-22 Apply(
|
||||
@6-22 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -28,7 +30,7 @@ Defs {
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@26-56 Defs(
|
||||
@11-56 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
@ -46,22 +48,22 @@ Defs {
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @26-56 Identifier {
|
||||
ann_pattern: @11-57 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
ann_type: @26-56 Apply(
|
||||
ann_type: @11-57 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@26-56 Record {
|
||||
@11-57 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@26-56 Inferred,
|
||||
@11-57 Inferred,
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @26-56 Identifier {
|
||||
body_pattern: @11-57 Identifier {
|
||||
ident: "#!0_stmt",
|
||||
},
|
||||
body_expr: @11-56 Apply(
|
||||
@ -106,7 +108,7 @@ Defs {
|
||||
),
|
||||
@11-56 Closure(
|
||||
[
|
||||
@26-56 Underscore(
|
||||
@11-57 Underscore(
|
||||
"#!stmt",
|
||||
),
|
||||
],
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -28,8 +30,8 @@ Defs {
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
@24-30 Apply(
|
||||
@24-30 Var {
|
||||
@28-30 Apply(
|
||||
@28-30 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
@ -40,7 +42,7 @@ Defs {
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@24-30,
|
||||
@28-30,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
@ -71,19 +73,19 @@ Defs {
|
||||
body_pattern: @24-25 Identifier {
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
body_expr: @24-30 Var {
|
||||
body_expr: @28-30 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@24-30 Var {
|
||||
@28-30 Var {
|
||||
module_name: "",
|
||||
ident: "#!0_expr",
|
||||
},
|
||||
),
|
||||
@24-30 Closure(
|
||||
@28-30 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "r",
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@ -44,27 +46,27 @@ Defs {
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-33 Var {
|
||||
@28-33 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
@15-19 Closure(
|
||||
[
|
||||
@24-33 Identifier {
|
||||
@28-33 Identifier {
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
],
|
||||
@24-33 Apply(
|
||||
@24-33 Var {
|
||||
@28-33 Apply(
|
||||
@28-33 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-33 Var {
|
||||
@28-33 Var {
|
||||
module_name: "",
|
||||
ident: "#!0_arg",
|
||||
},
|
||||
@24-33 Closure(
|
||||
@28-33 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "b",
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -13,9 +13,11 @@ Defs {
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
|
@ -382,7 +382,7 @@ mod test_can {
|
||||
let arena = Bump::new();
|
||||
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||
|
||||
assert_eq!(problems.len(), 0);
|
||||
assert_eq!(problems, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -399,7 +399,7 @@ mod test_can {
|
||||
let arena = Bump::new();
|
||||
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||
|
||||
assert_eq!(problems.len(), 0);
|
||||
assert_eq!(problems, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -543,7 +543,7 @@ impl<'a> Formattable for Expr<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_str_multiline(literal: &StrLiteral) -> bool {
|
||||
pub fn is_str_multiline(literal: &StrLiteral) -> bool {
|
||||
use roc_parse::ast::StrLiteral::*;
|
||||
|
||||
match literal {
|
||||
@ -671,10 +671,6 @@ fn push_op(buf: &mut Buf, op: BinOp) {
|
||||
called_via::BinOp::And => buf.push_str("&&"),
|
||||
called_via::BinOp::Or => buf.push_str("||"),
|
||||
called_via::BinOp::Pizza => buf.push_str("|>"),
|
||||
called_via::BinOp::Assignment => unreachable!(),
|
||||
called_via::BinOp::IsAliasType => unreachable!(),
|
||||
called_via::BinOp::IsOpaqueType => unreachable!(),
|
||||
called_via::BinOp::Backpassing => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1708,10 +1704,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
|
||||
| BinOp::And
|
||||
| BinOp::Or
|
||||
| BinOp::Pizza => true,
|
||||
BinOp::Assignment
|
||||
| BinOp::IsAliasType
|
||||
| BinOp::IsOpaqueType
|
||||
| BinOp::Backpassing => false,
|
||||
})
|
||||
}
|
||||
Expr::If(_, _) => true,
|
||||
|
@ -3,10 +3,8 @@ use std::cmp::max;
|
||||
use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens};
|
||||
use crate::collection::{fmt_collection, Braces};
|
||||
use crate::expr::fmt_str_literal;
|
||||
use crate::spaces::RemoveSpaces;
|
||||
use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT};
|
||||
use crate::Buf;
|
||||
use bumpalo::Bump;
|
||||
use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces};
|
||||
use roc_parse::header::{
|
||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||
@ -58,12 +56,6 @@ macro_rules! keywords {
|
||||
buf.push_str($name::KEYWORD);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for $name {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::annotation::{Formattable, Newlines, Parens};
|
||||
use crate::expr::{fmt_str_literal, format_sq_literal};
|
||||
use crate::expr::{fmt_str_literal, format_sq_literal, is_str_multiline};
|
||||
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
|
||||
use crate::Buf;
|
||||
use roc_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs};
|
||||
@ -48,7 +48,7 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
pattern
|
||||
);
|
||||
|
||||
spaces.iter().any(|s| s.is_comment())
|
||||
spaces.iter().any(|s| s.is_comment()) || pattern.is_multiline()
|
||||
}
|
||||
|
||||
Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()),
|
||||
@ -63,15 +63,17 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
list_rest_spaces.iter().any(|s| s.is_comment()) || pattern_as.is_multiline()
|
||||
}
|
||||
},
|
||||
Pattern::StrLiteral(literal) => is_str_multiline(literal),
|
||||
Pattern::Apply(pat, args) => {
|
||||
pat.is_multiline() || args.iter().any(|a| a.is_multiline())
|
||||
}
|
||||
|
||||
Pattern::Identifier { .. }
|
||||
| Pattern::Tag(_)
|
||||
| Pattern::OpaqueRef(_)
|
||||
| Pattern::Apply(_, _)
|
||||
| Pattern::NumLiteral(..)
|
||||
| Pattern::NonBase10Literal { .. }
|
||||
| Pattern::FloatLiteral(..)
|
||||
| Pattern::StrLiteral(_)
|
||||
| Pattern::SingleQuote(_)
|
||||
| Pattern::Underscore(_)
|
||||
| Pattern::Malformed(_)
|
||||
@ -100,7 +102,13 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
buf.indent(indent);
|
||||
// Sometimes, an Apply pattern needs parens around it.
|
||||
// In particular when an Apply's argument is itself an Apply (> 0) arguments
|
||||
let parens = !loc_arg_patterns.is_empty() && parens == Parens::InApply;
|
||||
let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply);
|
||||
|
||||
let indent_more = if self.is_multiline() {
|
||||
indent + INDENT
|
||||
} else {
|
||||
indent
|
||||
};
|
||||
|
||||
if parens {
|
||||
buf.push('(');
|
||||
@ -110,7 +118,7 @@ impl<'a> Formattable for Pattern<'a> {
|
||||
|
||||
for loc_arg in loc_arg_patterns.iter() {
|
||||
buf.spaces(1);
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent_more);
|
||||
}
|
||||
|
||||
if parens {
|
||||
|
@ -1,23 +1,5 @@
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_parse::{
|
||||
ast::{
|
||||
AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr,
|
||||
Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias,
|
||||
ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation,
|
||||
IngestedFileImport, Module, ModuleImport, ModuleImportParams, OldRecordBuilderField,
|
||||
Pattern, PatternAs, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef, WhenBranch,
|
||||
},
|
||||
header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName,
|
||||
ModuleParams, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
|
||||
ProvidesTo, To, TypedIdent,
|
||||
},
|
||||
ident::{BadIdent, UppercaseIdent},
|
||||
};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
use roc_parse::{ast::CommentOrNewline, remove_spaces::RemoveSpaces};
|
||||
|
||||
use crate::{Ast, Buf};
|
||||
|
||||
@ -211,20 +193,6 @@ fn fmt_docs(buf: &mut Buf, docs: &str) {
|
||||
buf.push_str(docs.trim_end());
|
||||
}
|
||||
|
||||
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
|
||||
///
|
||||
/// Currently this consists of:
|
||||
/// * Removing newlines
|
||||
/// * Removing comments
|
||||
/// * Removing parens in Exprs
|
||||
///
|
||||
/// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting)
|
||||
/// - but there are currently several bugs where they're _not_ preserved.
|
||||
/// TODO: ensure formatting retains comments
|
||||
pub trait RemoveSpaces<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Ast<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
Ast {
|
||||
@ -233,834 +201,3 @@ impl<'a> RemoveSpaces<'a> for Ast<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Defs<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let mut defs = self.clone();
|
||||
|
||||
defs.spaces.clear();
|
||||
defs.space_before.clear();
|
||||
defs.space_after.clear();
|
||||
|
||||
for type_def in defs.type_defs.iter_mut() {
|
||||
*type_def = type_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = value_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
for region_def in defs.regions.iter_mut() {
|
||||
*region_def = region_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
defs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
Spaces {
|
||||
before: &[],
|
||||
item: self.item.remove_spaces(arena),
|
||||
after: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
KeywordItem {
|
||||
keyword: self.keyword.remove_spaces(arena),
|
||||
item: self.item.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ProvidesTo {
|
||||
provides_keyword: self.provides_keyword.remove_spaces(arena),
|
||||
entries: self.entries.remove_spaces(arena),
|
||||
types: self.types.remove_spaces(arena),
|
||||
to_keyword: self.to_keyword.remove_spaces(arena),
|
||||
to: self.to.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Module<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let header = match &self.header {
|
||||
Header::Module(header) => Header::Module(ModuleHeader {
|
||||
after_keyword: &[],
|
||||
params: header.params.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
interface_imports: header.interface_imports.remove_spaces(arena),
|
||||
}),
|
||||
Header::App(header) => Header::App(AppHeader {
|
||||
before_provides: &[],
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
old_imports: header.old_imports.remove_spaces(arena),
|
||||
old_provides_to_new_package: header
|
||||
.old_provides_to_new_package
|
||||
.remove_spaces(arena),
|
||||
}),
|
||||
Header::Package(header) => Header::Package(PackageHeader {
|
||||
before_exposes: &[],
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
}),
|
||||
Header::Platform(header) => Header::Platform(PlatformHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
requires: header.requires.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
}),
|
||||
Header::Hosted(header) => Header::Hosted(HostedHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
generates: header.generates.remove_spaces(arena),
|
||||
generates_with: header.generates_with.remove_spaces(arena),
|
||||
}),
|
||||
};
|
||||
Module {
|
||||
comments: &[],
|
||||
header,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleParams<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleParams {
|
||||
params: self.params.remove_spaces(arena),
|
||||
before_arrow: &[],
|
||||
after_arrow: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Region {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
Region::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for &'a str {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
|
||||
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for To<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
To::ExistingPackage(a) => To::ExistingPackage(a),
|
||||
To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
TypedIdent {
|
||||
ident: self.ident.remove_spaces(arena),
|
||||
spaces_before_colon: &[],
|
||||
ann: self.ann.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
PlatformRequires {
|
||||
rigids: self.rigids.remove_spaces(arena),
|
||||
signature: self.signature.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
PackageEntry {
|
||||
shorthand: self.shorthand,
|
||||
spaces_after_shorthand: &[],
|
||||
platform_marker: match self.platform_marker {
|
||||
Some(_) => Some(&[]),
|
||||
None => None,
|
||||
},
|
||||
package_name: self.package_name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
|
||||
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
|
||||
ImportsEntry::IngestedFile(a, b) => {
|
||||
ImportsEntry::IngestedFile(a, b.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option<T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
self.as_ref().map(|a| a.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc<T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let res = self.value.remove_spaces(arena);
|
||||
Loc::at(Region::zero(), res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
(self.0.remove_spaces(arena), self.1.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let mut items = Vec::with_capacity_in(self.items.len(), arena);
|
||||
for item in self.items {
|
||||
items.push(item.remove_spaces(arena));
|
||||
}
|
||||
Collection::with_items(items.into_bump_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
let mut items = Vec::with_capacity_in(self.len(), arena);
|
||||
for item in *self {
|
||||
let res = item.remove_spaces(arena);
|
||||
items.push(res);
|
||||
}
|
||||
items.into_bump_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for UnaryOp {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for BinOp {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
arena.alloc((*self).remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
use TypeDef::*;
|
||||
|
||||
match *self {
|
||||
Alias {
|
||||
header: TypeHeader { name, vars },
|
||||
ann,
|
||||
} => Alias {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
ann: ann.remove_spaces(arena),
|
||||
},
|
||||
Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
typ,
|
||||
derived,
|
||||
} => Opaque {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
typ: typ.remove_spaces(arena),
|
||||
derived: derived.remove_spaces(arena),
|
||||
},
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_implements: loc_has,
|
||||
members,
|
||||
} => Ability {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
loc_implements: loc_has.remove_spaces(arena),
|
||||
members: members.remove_spaces(arena),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
use ValueDef::*;
|
||||
|
||||
match *self {
|
||||
Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)),
|
||||
Body(a, b) => Body(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
comment: _,
|
||||
body_pattern,
|
||||
body_expr,
|
||||
} => AnnotatedBody {
|
||||
ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)),
|
||||
ann_type: arena.alloc(ann_type.remove_spaces(arena)),
|
||||
comment: None,
|
||||
body_pattern: arena.alloc(body_pattern.remove_spaces(arena)),
|
||||
body_expr: arena.alloc(body_expr.remove_spaces(arena)),
|
||||
},
|
||||
Dbg {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => Dbg {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
Expect {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => Expect {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
ExpectFx {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => ExpectFx {
|
||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||
preceding_comment: Region::zero(),
|
||||
},
|
||||
ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)),
|
||||
IngestedFileImport(ingested_file_import) => {
|
||||
IngestedFileImport(ingested_file_import.remove_spaces(arena))
|
||||
}
|
||||
Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleImport<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleImport {
|
||||
before_name: &[],
|
||||
name: self.name.remove_spaces(arena),
|
||||
params: self.params.remove_spaces(arena),
|
||||
alias: self.alias.remove_spaces(arena),
|
||||
exposed: self.exposed.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ModuleImportParams {
|
||||
before: &[],
|
||||
params: self.params.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
IngestedFileImport {
|
||||
before_path: &[],
|
||||
path: self.path.remove_spaces(arena),
|
||||
name: self.name.remove_spaces(arena),
|
||||
annotation: self.annotation.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ImportedModuleName {
|
||||
package: self.package.remove_spaces(arena),
|
||||
name: self.name.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportAlias<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportAsKeyword {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImportExposingKeyword {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
IngestedFileAnnotation {
|
||||
before_colon: &[],
|
||||
annotation: self.annotation.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Implements<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
Implements::Implements
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for AbilityMember<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
AbilityMember {
|
||||
name: self.name.remove_spaces(arena),
|
||||
typ: self.typ.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for WhenBranch<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
WhenBranch {
|
||||
patterns: self.patterns.remove_spaces(arena),
|
||||
value: self.value.remove_spaces(arena),
|
||||
guard: self.guard.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue(
|
||||
a.remove_spaces(arena),
|
||||
arena.alloc([]),
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue(
|
||||
a.remove_spaces(arena),
|
||||
arena.alloc([]),
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)),
|
||||
AssignedField::Malformed(a) => AssignedField::Malformed(a),
|
||||
AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value(
|
||||
a.remove_spaces(arena),
|
||||
&[],
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue(
|
||||
a.remove_spaces(arena),
|
||||
&[],
|
||||
&[],
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
OldRecordBuilderField::LabelOnly(a) => {
|
||||
OldRecordBuilderField::LabelOnly(a.remove_spaces(arena))
|
||||
}
|
||||
OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a),
|
||||
OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for StrLiteral<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
|
||||
StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)),
|
||||
StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for StrSegment<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
StrSegment::Plaintext(t) => StrSegment::Plaintext(t),
|
||||
StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)),
|
||||
StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c),
|
||||
StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)),
|
||||
StrSegment::DeprecatedInterpolated(t) => {
|
||||
StrSegment::DeprecatedInterpolated(t.remove_spaces(arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Expr::Float(a) => Expr::Float(a),
|
||||
Expr::Num(a) => Expr::Num(a),
|
||||
Expr::NonBase10Int {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
} => Expr::NonBase10Int {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
},
|
||||
Expr::Str(a) => Expr::Str(a.remove_spaces(arena)),
|
||||
Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b),
|
||||
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
|
||||
Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b),
|
||||
Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))),
|
||||
Expr::List(a) => Expr::List(a.remove_spaces(arena)),
|
||||
Expr::RecordUpdate { update, fields } => Expr::RecordUpdate {
|
||||
update: arena.alloc(update.remove_spaces(arena)),
|
||||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
||||
Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)),
|
||||
Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder {
|
||||
mapper: arena.alloc(mapper.remove_spaces(arena)),
|
||||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)),
|
||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||
Expr::Underscore(a) => Expr::Underscore(a),
|
||||
Expr::Tag(a) => Expr::Tag(a),
|
||||
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
||||
Expr::Closure(a, b) => Expr::Closure(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Expr::Crash => Expr::Crash,
|
||||
Expr::Defs(a, b) => {
|
||||
let mut defs = a.clone();
|
||||
defs.space_before = vec![Default::default(); defs.len()];
|
||||
defs.space_after = vec![Default::default(); defs.len()];
|
||||
defs.regions = vec![Region::zero(); defs.len()];
|
||||
defs.spaces.clear();
|
||||
|
||||
for type_def in defs.type_defs.iter_mut() {
|
||||
*type_def = type_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
for value_def in defs.value_defs.iter_mut() {
|
||||
*value_def = value_def.remove_spaces(arena);
|
||||
}
|
||||
|
||||
Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Expr::Backpassing(a, b, c) => Expr::Backpassing(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
Expr::Expect(a, b) => Expr::Expect(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Expr::Dbg(a, b) => Expr::Dbg(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Expr::LowLevelDbg(_, _, _) => unreachable!(
|
||||
"LowLevelDbg should only exist after desugaring, not during formatting"
|
||||
),
|
||||
Expr::Apply(a, b, c) => Expr::Apply(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
b.remove_spaces(arena),
|
||||
c,
|
||||
),
|
||||
Expr::BinOps(a, b) => {
|
||||
Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Expr::UnaryOp(a, b) => {
|
||||
Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
||||
}
|
||||
Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))),
|
||||
Expr::When(a, b) => {
|
||||
Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
||||
}
|
||||
Expr::ParensAround(a) => {
|
||||
// The formatter can remove redundant parentheses, so also remove these when normalizing for comparison.
|
||||
a.remove_spaces(arena)
|
||||
}
|
||||
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Expr::MalformedClosure => Expr::MalformedClosure,
|
||||
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
|
||||
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
||||
Expr::MultipleOldRecordBuilders(a) => Expr::MultipleOldRecordBuilders(a),
|
||||
Expr::UnappliedOldRecordBuilder(a) => Expr::UnappliedOldRecordBuilder(a),
|
||||
Expr::EmptyRecordBuilder(a) => Expr::EmptyRecordBuilder(a),
|
||||
Expr::SingleFieldRecordBuilder(a) => Expr::SingleFieldRecordBuilder(a),
|
||||
Expr::OptionalFieldInRecordBuilder(name, a) => {
|
||||
Expr::OptionalFieldInRecordBuilder(name, a)
|
||||
}
|
||||
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Expr::SingleQuote(a) => Expr::Num(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
|
||||
match ident {
|
||||
BadIdent::Start(_) => BadIdent::Start(Position::zero()),
|
||||
BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()),
|
||||
BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()),
|
||||
BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()),
|
||||
BadIdent::UnderscoreAtStart {
|
||||
position: _,
|
||||
declaration_region,
|
||||
} => BadIdent::UnderscoreAtStart {
|
||||
position: Position::zero(),
|
||||
declaration_region,
|
||||
},
|
||||
BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()),
|
||||
BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()),
|
||||
BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()),
|
||||
BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()),
|
||||
BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()),
|
||||
BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()),
|
||||
BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Pattern::Identifier { ident } => Pattern::Identifier { ident },
|
||||
Pattern::Tag(a) => Pattern::Tag(a),
|
||||
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
||||
Pattern::Apply(a, b) => Pattern::Apply(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)),
|
||||
Pattern::RequiredField(a, b) => {
|
||||
Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Pattern::OptionalField(a, b) => {
|
||||
Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena)))
|
||||
}
|
||||
Pattern::As(pattern, pattern_as) => Pattern::As(
|
||||
arena.alloc(pattern.remove_spaces(arena)),
|
||||
pattern_as.remove_spaces(arena),
|
||||
),
|
||||
Pattern::NumLiteral(a) => Pattern::NumLiteral(a),
|
||||
Pattern::NonBase10Literal {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
} => Pattern::NonBase10Literal {
|
||||
string,
|
||||
base,
|
||||
is_negative,
|
||||
},
|
||||
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
|
||||
Pattern::StrLiteral(a) => Pattern::StrLiteral(a),
|
||||
Pattern::Underscore(a) => Pattern::Underscore(a),
|
||||
Pattern::Malformed(a) => Pattern::Malformed(a),
|
||||
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Pattern::QualifiedIdentifier { module_name, ident } => {
|
||||
Pattern::QualifiedIdentifier { module_name, ident }
|
||||
}
|
||||
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Pattern::SingleQuote(a) => Pattern::SingleQuote(a),
|
||||
Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)),
|
||||
Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)),
|
||||
Pattern::ListRest(opt_pattern_as) => Pattern::ListRest(
|
||||
opt_pattern_as
|
||||
.map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
TypeAnnotation::Function(a, b) => TypeAnnotation::Function(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
arena.alloc(b.remove_spaces(arena)),
|
||||
),
|
||||
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)),
|
||||
TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a),
|
||||
TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As(
|
||||
arena.alloc(a.remove_spaces(arena)),
|
||||
&[],
|
||||
TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
),
|
||||
TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple {
|
||||
elems: fields.remove_spaces(arena),
|
||||
ext: ext.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
|
||||
fields: fields.remove_spaces(arena),
|
||||
ext: ext.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion {
|
||||
ext: ext.remove_spaces(arena),
|
||||
tags: tags.remove_spaces(arena),
|
||||
},
|
||||
TypeAnnotation::Inferred => TypeAnnotation::Inferred,
|
||||
TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
|
||||
TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where(
|
||||
arena.alloc(annot.remove_spaces(arena)),
|
||||
arena.alloc(has_clauses.remove_spaces(arena)),
|
||||
),
|
||||
TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
ImplementsClause {
|
||||
var: self.var.remove_spaces(arena),
|
||||
abilities: self.abilities.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
Tag::Apply { name, args } => Tag::Apply {
|
||||
name: name.remove_spaces(arena),
|
||||
args: args.remove_spaces(arena),
|
||||
},
|
||||
Tag::Malformed(a) => Tag::Malformed(a),
|
||||
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
AbilityImpls::AbilityImpls(impls) => {
|
||||
AbilityImpls::AbilityImpls(impls.remove_spaces(arena))
|
||||
}
|
||||
AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => {
|
||||
has.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
||||
ImplementsAbility::ImplementsAbility {
|
||||
ability: ability.remove_spaces(arena),
|
||||
impls: impls.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => {
|
||||
has.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
ImplementsAbilities::Implements(derived) => {
|
||||
ImplementsAbilities::Implements(derived.remove_spaces(arena))
|
||||
}
|
||||
ImplementsAbilities::SpaceBefore(derived, _)
|
||||
| ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for PatternAs<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
PatternAs {
|
||||
spaces_before: &[],
|
||||
identifier: self.identifier.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3374,7 +3374,7 @@ mod test_reporting {
|
||||
f x y = x
|
||||
"
|
||||
),
|
||||
@r#"
|
||||
@r###"
|
||||
── ARGUMENTS BEFORE EQUALS in tmp/elm_function_syntax/Test.roc ─────────────────
|
||||
|
||||
I am partway through parsing a definition, but I got stuck here:
|
||||
@ -3385,9 +3385,9 @@ mod test_reporting {
|
||||
4│ f x y = x
|
||||
^^^
|
||||
|
||||
Looks like you are trying to define a function. In roc, functions are
|
||||
Looks like you are trying to define a function. In Roc, functions are
|
||||
always written as a lambda, like increment = \n -> n + 1.
|
||||
"#
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
@ -4320,16 +4320,26 @@ mod test_reporting {
|
||||
{ x, y }
|
||||
"
|
||||
),
|
||||
@r"
|
||||
── TOO MANY ARGS in /code/proj/Main.roc ────────────────────────────────────────
|
||||
@r###"
|
||||
── STATEMENT AFTER EXPRESSION in tmp/double_equals_in_def/Test.roc ─────────────
|
||||
|
||||
This value is not a function, but it was given 3 arguments:
|
||||
I just finished parsing an expression with a series of definitions,
|
||||
|
||||
and this line is indented as if it's intended to be part of that
|
||||
expression:
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ x = 3
|
||||
5│ y =
|
||||
6│ x == 5
|
||||
^
|
||||
7│ Num.add 1 2
|
||||
^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
"
|
||||
However, I already saw the final expression in that series of
|
||||
definitions.
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
@ -5018,9 +5028,9 @@ mod test_reporting {
|
||||
I was partway through parsing an `import`, but I got stuck here:
|
||||
|
||||
4│ import svg.Path a
|
||||
^
|
||||
^
|
||||
|
||||
I was expecting to see the `as` keyword, like:
|
||||
I was expecting to see the `as` keyword next, like:
|
||||
|
||||
import svg.Path as SvgPath
|
||||
|
||||
@ -5417,14 +5427,25 @@ mod test_reporting {
|
||||
2 -> 2
|
||||
"
|
||||
),
|
||||
@r"
|
||||
── NOT END OF FILE in tmp/when_outdented_branch/Test.roc ───────────────────────
|
||||
@r###"
|
||||
── UNKNOWN OPERATOR in tmp/when_outdented_branch/Test.roc ──────────────────────
|
||||
|
||||
I expected to reach the end of the file, but got stuck here:
|
||||
This looks like an operator, but it's not one I recognize!
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ when 4 is
|
||||
5│ 5 -> 2
|
||||
6│ 2 -> 2
|
||||
^
|
||||
"
|
||||
^^
|
||||
|
||||
Looks like you are trying to define a function.
|
||||
|
||||
In Roc, functions are always written as a lambda, like
|
||||
|
||||
increment = \n -> n + 1
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
@ -5436,12 +5457,13 @@ mod test_reporting {
|
||||
_ -> 2
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── UNEXPECTED ARROW in tmp/when_over_indented_underscore/Test.roc ──────────────
|
||||
|
||||
I am parsing a `when` expression right now, but this arrow is confusing
|
||||
me:
|
||||
|
||||
4│ when 4 is
|
||||
5│ 5 -> 2
|
||||
6│ _ -> 2
|
||||
^^
|
||||
@ -5461,7 +5483,7 @@ mod test_reporting {
|
||||
|
||||
Notice the indentation. All patterns are aligned, and each branch is
|
||||
indented a bit more than the corresponding pattern. That is important!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
@ -5473,12 +5495,13 @@ mod test_reporting {
|
||||
2 -> 2
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── UNEXPECTED ARROW in tmp/when_over_indented_int/Test.roc ─────────────────────
|
||||
|
||||
I am parsing a `when` expression right now, but this arrow is confusing
|
||||
me:
|
||||
|
||||
4│ when 4 is
|
||||
5│ 5 -> Num.neg
|
||||
6│ 2 -> 2
|
||||
^^
|
||||
@ -5498,7 +5521,7 @@ mod test_reporting {
|
||||
|
||||
Notice the indentation. All patterns are aligned, and each branch is
|
||||
indented a bit more than the corresponding pattern. That is important!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
// TODO I think we can do better here
|
||||
@ -6136,27 +6159,21 @@ All branches in an `if` must have the same type!
|
||||
main = 5 -> 3
|
||||
"
|
||||
),
|
||||
|golden| pretty_assertions::assert_eq!(
|
||||
golden,
|
||||
&format!(
|
||||
r#"── UNKNOWN OPERATOR in tmp/wild_case_arrow/Test.roc ────────────────────────────
|
||||
@r###"
|
||||
── SYNTAX PROBLEM in tmp/wild_case_arrow/Test.roc ──────────────────────────────
|
||||
|
||||
This looks like an operator, but it's not one I recognize!
|
||||
I got stuck here:
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ main = 5 -> 3
|
||||
^^
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
3│ main =
|
||||
4│ main = 5 -> 3
|
||||
^
|
||||
|
||||
Looks like you are trying to define a function.{}
|
||||
|
||||
In roc, functions are always written as a lambda, like{}
|
||||
|
||||
increment = \n -> n + 1"#,
|
||||
' ', ' '
|
||||
)
|
||||
)
|
||||
Whatever I am running into is confusing me a lot! Normally I can give
|
||||
fairly specific hints, but something is really tripping me up this
|
||||
time.
|
||||
"###
|
||||
);
|
||||
|
||||
#[test]
|
||||
@ -10971,13 +10988,13 @@ In roc, functions are always written as a lambda, like{}
|
||||
0
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
||||
|
||||
This destructure assignment doesn't introduce any new variables:
|
||||
|
||||
4│ Pair _ _ = Pair 0 1
|
||||
^^^^
|
||||
^^^^^^^^
|
||||
|
||||
If you don't need to use the value on the right-hand-side of this
|
||||
assignment, consider removing the assignment. Since Roc is purely
|
||||
@ -11019,7 +11036,7 @@ In roc, functions are always written as a lambda, like{}
|
||||
assignment, consider removing the assignment. Since Roc is purely
|
||||
functional, assignments that don't introduce variables cannot affect a
|
||||
program's behavior!
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -833,7 +833,7 @@ fn platform_parse_error() {
|
||||
|
||||
match multiple_modules("platform_parse_error", modules) {
|
||||
Err(report) => {
|
||||
assert!(report.contains("NOT END OF FILE"));
|
||||
assert!(report.contains("STATEMENT AFTER EXPRESSION"));
|
||||
assert!(report.contains("blah 1 2 3 # causing a parse error on purpose"));
|
||||
}
|
||||
Ok(_) => unreachable!("we expect failure here"),
|
||||
|
@ -3,7 +3,7 @@ use self::BinOp::*;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
const PRECEDENCES: [(BinOp, u8); 20] = [
|
||||
const PRECEDENCES: [(BinOp, u8); 16] = [
|
||||
(Caret, 8),
|
||||
(Star, 7),
|
||||
(Slash, 7),
|
||||
@ -20,14 +20,9 @@ const PRECEDENCES: [(BinOp, u8); 20] = [
|
||||
(GreaterThanOrEq, 2),
|
||||
(And, 1),
|
||||
(Or, 0),
|
||||
// These should never come up
|
||||
(Assignment, 255),
|
||||
(IsAliasType, 255),
|
||||
(IsOpaqueType, 255),
|
||||
(Backpassing, 255),
|
||||
];
|
||||
|
||||
const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [
|
||||
const ASSOCIATIVITIES: [(BinOp, Associativity); 16] = [
|
||||
(Caret, RightAssociative),
|
||||
(Star, LeftAssociative),
|
||||
(Slash, LeftAssociative),
|
||||
@ -44,14 +39,9 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [
|
||||
(GreaterThanOrEq, NonAssociative),
|
||||
(And, RightAssociative),
|
||||
(Or, RightAssociative),
|
||||
// These should never come up
|
||||
(Assignment, LeftAssociative),
|
||||
(IsAliasType, LeftAssociative),
|
||||
(IsOpaqueType, LeftAssociative),
|
||||
(Backpassing, LeftAssociative),
|
||||
];
|
||||
|
||||
const DISPLAY_STRINGS: [(BinOp, &str); 20] = [
|
||||
const DISPLAY_STRINGS: [(BinOp, &str); 16] = [
|
||||
(Caret, "^"),
|
||||
(Star, "*"),
|
||||
(Slash, "/"),
|
||||
@ -68,10 +58,6 @@ const DISPLAY_STRINGS: [(BinOp, &str); 20] = [
|
||||
(GreaterThanOrEq, ">="),
|
||||
(And, "&&"),
|
||||
(Or, "||"),
|
||||
(Assignment, "="),
|
||||
(IsAliasType, ":"),
|
||||
(IsOpaqueType, ":="),
|
||||
(Backpassing, "<-"),
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@ -147,10 +133,6 @@ pub enum BinOp {
|
||||
GreaterThanOrEq,
|
||||
And,
|
||||
Or,
|
||||
Assignment,
|
||||
IsAliasType,
|
||||
IsOpaqueType,
|
||||
Backpassing,
|
||||
// lowest precedence
|
||||
}
|
||||
|
||||
@ -161,7 +143,6 @@ impl BinOp {
|
||||
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
|
||||
DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
|
||||
| Pizza => 2,
|
||||
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,25 +176,13 @@ pub enum Associativity {
|
||||
|
||||
impl BinOp {
|
||||
pub fn associativity(self) -> Associativity {
|
||||
// The compiler should never pass any of these to this function!
|
||||
debug_assert_ne!(self, Assignment);
|
||||
debug_assert_ne!(self, IsAliasType);
|
||||
debug_assert_ne!(self, IsOpaqueType);
|
||||
debug_assert_ne!(self, Backpassing);
|
||||
|
||||
const ASSOCIATIVITY_TABLE: [Associativity; 20] = generate_associativity_table();
|
||||
const ASSOCIATIVITY_TABLE: [Associativity; 16] = generate_associativity_table();
|
||||
|
||||
ASSOCIATIVITY_TABLE[self as usize]
|
||||
}
|
||||
|
||||
fn precedence(self) -> u8 {
|
||||
// The compiler should never pass any of these to this function!
|
||||
debug_assert_ne!(self, Assignment);
|
||||
debug_assert_ne!(self, IsAliasType);
|
||||
debug_assert_ne!(self, IsOpaqueType);
|
||||
debug_assert_ne!(self, Backpassing);
|
||||
|
||||
const PRECEDENCE_TABLE: [u8; 20] = generate_precedence_table();
|
||||
const PRECEDENCE_TABLE: [u8; 16] = generate_precedence_table();
|
||||
|
||||
PRECEDENCE_TABLE[self as usize]
|
||||
}
|
||||
@ -233,19 +202,14 @@ impl Ord for BinOp {
|
||||
|
||||
impl std::fmt::Display for BinOp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
debug_assert_ne!(*self, Assignment);
|
||||
debug_assert_ne!(*self, IsAliasType);
|
||||
debug_assert_ne!(*self, IsOpaqueType);
|
||||
debug_assert_ne!(*self, Backpassing);
|
||||
|
||||
const DISPLAY_TABLE: [&str; 20] = generate_display_table();
|
||||
const DISPLAY_TABLE: [&str; 16] = generate_display_table();
|
||||
|
||||
write!(f, "{}", DISPLAY_TABLE[*self as usize])
|
||||
}
|
||||
}
|
||||
|
||||
const fn generate_precedence_table() -> [u8; 20] {
|
||||
let mut table = [0u8; 20];
|
||||
const fn generate_precedence_table() -> [u8; 16] {
|
||||
let mut table = [0u8; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < PRECEDENCES.len() {
|
||||
@ -256,8 +220,8 @@ const fn generate_precedence_table() -> [u8; 20] {
|
||||
table
|
||||
}
|
||||
|
||||
const fn generate_associativity_table() -> [Associativity; 20] {
|
||||
let mut table = [NonAssociative; 20];
|
||||
const fn generate_associativity_table() -> [Associativity; 16] {
|
||||
let mut table = [NonAssociative; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < ASSOCIATIVITIES.len() {
|
||||
@ -268,8 +232,8 @@ const fn generate_associativity_table() -> [Associativity; 20] {
|
||||
table
|
||||
}
|
||||
|
||||
const fn generate_display_table() -> [&'static str; 20] {
|
||||
let mut table = [""; 20];
|
||||
const fn generate_display_table() -> [&'static str; 16] {
|
||||
let mut table = [""; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < DISPLAY_STRINGS.len() {
|
||||
|
@ -21,6 +21,12 @@ pub struct Spaces<'a, T> {
|
||||
pub after: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct SpacesBefore<'a, T> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Spaced<'a, T> {
|
||||
Item(T),
|
||||
@ -1204,6 +1210,21 @@ impl<'a> Defs<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn loc_defs<'b>(
|
||||
&'b self,
|
||||
) -> impl Iterator<Item = Result<Loc<TypeDef<'a>>, Loc<ValueDef<'a>>>> + 'b {
|
||||
self.tags
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, tag)| match tag.split() {
|
||||
Ok(type_index) => Ok(Loc::at(self.regions[i], self.type_defs[type_index.index()])),
|
||||
Err(value_index) => Err(Loc::at(
|
||||
self.regions[i],
|
||||
self.value_defs[value_index.index()],
|
||||
)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_value_defs(&self) -> impl Iterator<Item = (usize, &ValueDef<'a>)> {
|
||||
self.tags
|
||||
.iter()
|
||||
@ -2072,6 +2093,28 @@ pub trait Spaceable<'a> {
|
||||
fn before(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self;
|
||||
fn after(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self;
|
||||
|
||||
fn maybe_before(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self
|
||||
where
|
||||
Self: Sized + 'a,
|
||||
{
|
||||
if spaces.is_empty() {
|
||||
self
|
||||
} else {
|
||||
arena.alloc(self).before(spaces)
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_after(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self
|
||||
where
|
||||
Self: Sized + 'a,
|
||||
{
|
||||
if spaces.is_empty() {
|
||||
self
|
||||
} else {
|
||||
arena.alloc(self).after(spaces)
|
||||
}
|
||||
}
|
||||
|
||||
fn with_spaces_before(&'a self, spaces: &'a [CommentOrNewline<'a>], region: Region) -> Loc<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
|
@ -333,7 +333,7 @@ where
|
||||
let start = state.pos();
|
||||
match spaces().parse(arena, state, min_indent) {
|
||||
Ok((progress, spaces, state)) => {
|
||||
if progress == NoProgress || state.column() >= min_indent {
|
||||
if spaces.is_empty() || state.column() >= min_indent {
|
||||
Ok((progress, spaces, state))
|
||||
} else {
|
||||
Err((progress, indent_problem(start)))
|
||||
@ -344,6 +344,60 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn require_newline_or_eof<'a, E>(newline_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
|
||||
where
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
move |arena: &'a Bump, state: State<'a>, min_indent| {
|
||||
// TODO: we can do this more efficiently by stopping as soon as we see a '#' or a newline
|
||||
let (_, res, _) = space0_e(newline_problem).parse(arena, state.clone(), min_indent)?;
|
||||
|
||||
if !res.is_empty() || state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, newline_problem(state.pos())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loc_space0_e<'a, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<&'a [CommentOrNewline<'a>]>, E>
|
||||
where
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
let mut newlines = Vec::new_in(arena);
|
||||
let start = state.pos();
|
||||
let mut comment_start = None;
|
||||
let mut comment_end = None;
|
||||
|
||||
let res = consume_spaces(state, |start, space, end| {
|
||||
newlines.push(space);
|
||||
if !matches!(space, CommentOrNewline::Newline) {
|
||||
if comment_start.is_none() {
|
||||
comment_start = Some(start);
|
||||
}
|
||||
comment_end = Some(end);
|
||||
}
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok((progress, state)) => {
|
||||
if newlines.is_empty() || state.column() >= min_indent {
|
||||
let start = comment_start.unwrap_or(state.pos());
|
||||
let end = comment_end.unwrap_or(state.pos());
|
||||
let region = Region::new(start, end);
|
||||
Ok((progress, Loc::at(region, newlines.into_bump_slice()), state))
|
||||
} else {
|
||||
Err((progress, indent_problem(start)))
|
||||
}
|
||||
}
|
||||
Err((progress, err)) => Err((progress, err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn begins_with_crlf(bytes: &[u8]) -> bool {
|
||||
bytes.len() >= 2 && bytes[0] == b'\r' && bytes[1] == b'\n'
|
||||
}
|
||||
@ -387,7 +441,7 @@ where
|
||||
F: FnMut(Position, CommentOrNewline<'a>, Position),
|
||||
{
|
||||
let mut progress = NoProgress;
|
||||
let mut found_newline = false;
|
||||
let mut found_newline = state.is_at_start_of_file();
|
||||
loop {
|
||||
let whitespace = fast_eat_whitespace(state.bytes());
|
||||
if whitespace > 0 {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,7 @@ pub mod module;
|
||||
pub mod number_literal;
|
||||
pub mod pattern;
|
||||
pub mod problems;
|
||||
pub mod remove_spaces;
|
||||
pub mod src64;
|
||||
pub mod state;
|
||||
pub mod string_literal;
|
||||
|
@ -330,6 +330,7 @@ pub enum EExpr<'a> {
|
||||
Start(Position),
|
||||
End(Position),
|
||||
BadExprEnd(Position),
|
||||
StmtAfterExpr(Position),
|
||||
Space(BadInputError, Position),
|
||||
|
||||
Dot(Position),
|
||||
@ -355,6 +356,8 @@ pub enum EExpr<'a> {
|
||||
QualifiedTag(Position),
|
||||
BackpassComma(Position),
|
||||
BackpassArrow(Position),
|
||||
BackpassContinue(Position),
|
||||
DbgContinue(Position),
|
||||
|
||||
When(EWhen<'a>, Position),
|
||||
If(EIf<'a>, Position),
|
||||
@ -383,6 +386,7 @@ pub enum EExpr<'a> {
|
||||
IndentEnd(Position),
|
||||
|
||||
UnexpectedComma(Position),
|
||||
UnexpectedTopLevelExpr(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -851,8 +855,9 @@ where
|
||||
let cur_indent = INDENT.with(|i| *i.borrow());
|
||||
|
||||
println!(
|
||||
"{:<5?}: {}{:<50}",
|
||||
"{:<5?}:{:<2} {}{:<50}",
|
||||
state.pos(),
|
||||
min_indent,
|
||||
&indent_text[..cur_indent * 2],
|
||||
self.message
|
||||
);
|
||||
@ -868,8 +873,9 @@ where
|
||||
};
|
||||
|
||||
println!(
|
||||
"{:<5?}: {}{:<50} {:<15} {:?}",
|
||||
"{:<5?}:{:<2} {}{:<50} {:<15} {:?}",
|
||||
state.pos(),
|
||||
min_indent,
|
||||
&indent_text[..cur_indent * 2],
|
||||
self.message,
|
||||
format!("{:?}", progress),
|
||||
|
1749
crates/compiler/parse/src/remove_spaces.rs
Normal file
1749
crates/compiler/parse/src/remove_spaces.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -129,6 +129,10 @@ impl<'a> State<'a> {
|
||||
pub fn len_region(&self, length: u32) -> Region {
|
||||
Region::new(self.pos(), self.pos().bump_column(length))
|
||||
}
|
||||
|
||||
pub fn is_at_start_of_file(&self) -> bool {
|
||||
self.offset == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for State<'a> {
|
||||
|
@ -22,7 +22,7 @@ pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Loc<ast::Expr<'a>>, SourceError<'a, SyntaxError<'a>>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
match crate::expr::test_parse_expr(0, arena, state.clone()) {
|
||||
Ok(loc_expr) => Ok(loc_expr),
|
||||
@ -31,7 +31,7 @@ pub fn parse_loc_with<'a>(
|
||||
}
|
||||
|
||||
pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
parse_module_defs(arena, state, Defs::default())
|
||||
}
|
||||
@ -40,7 +40,7 @@ pub fn parse_header_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<ast::Module<'a>, SyntaxError<'a>> {
|
||||
let state = State::new(input.trim().as_bytes());
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
match crate::module::parse_header(arena, state.clone()) {
|
||||
Ok((header, _)) => Ok(header),
|
||||
|
@ -2176,8 +2176,8 @@ fn refcount_nullable_unwrapped_needing_no_refcount_issue_5027() {
|
||||
await : Effect, (Str -> Effect) -> Effect
|
||||
await = \fx, cont ->
|
||||
after
|
||||
fx
|
||||
cont
|
||||
fx
|
||||
cont
|
||||
|
||||
succeed : {} -> Effect
|
||||
succeed = \{} -> (\{} -> "success")
|
||||
|
@ -11,6 +11,7 @@ cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
test_syntax = { path = "../../test_syntax" }
|
||||
roc_parse = { path = "../../parse" }
|
||||
|
||||
bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||
libfuzzer-sys = "0.4"
|
||||
|
@ -1,14 +1,18 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use bumpalo::Bump;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use roc_parse::ast::Malformed;
|
||||
use test_syntax::test_helpers::Input;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(input) = std::str::from_utf8(data) {
|
||||
let input = Input::Expr(input);
|
||||
let arena = Bump::new();
|
||||
if input.parse_in(&arena).is_ok() {
|
||||
input.check_invariants(|_| (), true);
|
||||
let ast = input.parse_in(&arena);
|
||||
if let Ok(ast) = ast {
|
||||
if !ast.is_malformed() {
|
||||
input.check_invariants(|_| (), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
35
crates/compiler/test_syntax/src/bin/minimize.rs
Normal file
35
crates/compiler/test_syntax/src/bin/minimize.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//! Generate a minimized version of a given input, by removing parts of it.
|
||||
//! This is useful for debugging, when you have a large input that causes a failure,
|
||||
//! and you want to find the smallest input that still causes the failure.
|
||||
//!
|
||||
//! Typical usage:
|
||||
//! `cargo run --release --bin minimize -- full <file_that_triggers_parsing_bug>`
|
||||
//!
|
||||
//! This tool will churn on that for a while, and eventually print out a minimized version
|
||||
//! of the input that still triggers the bug.
|
||||
//!
|
||||
//! Note that `--release` is important, as this tool is very slow in debug mode.
|
||||
|
||||
use test_syntax::{minimize::print_minimizations, test_helpers::InputKind};
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage: {} [expr|full|moduledefs|header] <input>", args[0]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let kind = match args[1].as_str() {
|
||||
"expr" => InputKind::Expr,
|
||||
"full" => InputKind::Full,
|
||||
"moduledefs" => InputKind::ModuleDefs,
|
||||
"header" => InputKind::Header,
|
||||
_ => {
|
||||
eprintln!("Invalid input kind: {}", args[1]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let text = std::fs::read_to_string(&args[2]).unwrap();
|
||||
print_minimizations(&text, kind);
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod minimize;
|
||||
pub mod test_helpers;
|
||||
|
211
crates/compiler/test_syntax/src/minimize.rs
Normal file
211
crates/compiler/test_syntax/src/minimize.rs
Normal file
@ -0,0 +1,211 @@
|
||||
//! Generate a minimized version of a given input, by removing parts of it.
|
||||
//! This is useful for debugging, when you have a large input that causes a failure,
|
||||
//! and you want to find the smallest input that still causes the failure.
|
||||
//!
|
||||
//! Most users will want to use the binary instead of this module directly.
|
||||
//! e.g. `cargo run --release --bin minimize -- full <file_that_triggers_parsing_bug>`
|
||||
|
||||
use crate::test_helpers::{Input, InputKind};
|
||||
use bumpalo::Bump;
|
||||
use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces};
|
||||
|
||||
pub fn print_minimizations(text: &str, kind: InputKind) {
|
||||
let Some(original_error) = round_trip_once_and_extract_error(text, kind) else {
|
||||
eprintln!("No error found");
|
||||
return;
|
||||
};
|
||||
|
||||
eprintln!("Error found: {}", original_error);
|
||||
eprintln!("Proceeding with minimization");
|
||||
|
||||
let mut s = text.to_string();
|
||||
|
||||
loop {
|
||||
let mut found = false;
|
||||
for update in candidate_minimizations(s.clone()) {
|
||||
let mut new_s = String::with_capacity(s.len());
|
||||
let mut offset = 0;
|
||||
for (start, end, replacement) in update.replacements.clone() {
|
||||
new_s.push_str(&s[offset..start]);
|
||||
new_s.push_str(&replacement);
|
||||
offset = end;
|
||||
}
|
||||
new_s.push_str(&s[offset..]);
|
||||
|
||||
assert!(
|
||||
new_s.len() < s.len(),
|
||||
"replacements: {:?}",
|
||||
update.replacements
|
||||
);
|
||||
|
||||
if let Some(result) = round_trip_once_and_extract_error(&new_s, kind) {
|
||||
if result == original_error {
|
||||
eprintln!("Successfully minimized, new length: {}", new_s.len());
|
||||
s = new_s;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
eprintln!("No more minimizations found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("Final result:");
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
fn round_trip_once_and_extract_error(text: &str, kind: InputKind) -> Option<String> {
|
||||
let input = kind.with_text(text);
|
||||
let res = std::panic::catch_unwind(|| round_trip_once(input));
|
||||
|
||||
match res {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
if let Some(s) = e.downcast_ref::<&'static str>() {
|
||||
return Some(s.to_string());
|
||||
}
|
||||
if let Some(s) = e.downcast_ref::<String>() {
|
||||
return Some(s.clone());
|
||||
}
|
||||
Some("Panic during parsing".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn round_trip_once(input: Input<'_>) -> Option<String> {
|
||||
let arena = Bump::new();
|
||||
|
||||
let actual = match input.parse_in(&arena) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
return Some(format!(
|
||||
"Initial parse failed: {:?}",
|
||||
e.remove_spaces(&arena)
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if actual.is_malformed() {
|
||||
return Some("Initial parse is malformed".to_string());
|
||||
}
|
||||
|
||||
let output = actual.format();
|
||||
|
||||
let reparsed_ast = match output.as_ref().parse_in(&arena) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Some(format!("Reparse failed: {:?}", e.remove_spaces(&arena))),
|
||||
};
|
||||
|
||||
let ast_normalized = actual.remove_spaces(&arena);
|
||||
let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena);
|
||||
|
||||
if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") {
|
||||
return Some("Different ast".to_string());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
struct Update {
|
||||
replacements: Vec<(usize, usize, String)>,
|
||||
}
|
||||
|
||||
fn candidate_minimizations(s: String) -> Box<dyn Iterator<Item = Update>> {
|
||||
let mut line_offsets = vec![0];
|
||||
line_offsets.extend(s.match_indices('\n').map(|(i, _)| i + 1));
|
||||
let line_count = line_offsets.len();
|
||||
let s_len = s.len();
|
||||
|
||||
let line_indents = line_offsets
|
||||
.iter()
|
||||
.map(|&offset| s[offset..].chars().take_while(|&c| c == ' ').count())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
|
||||
// first, try to remove every group of 1, 2, 3, ... lines - in reverse order (so, trying removing n lines first, then n-1, etc)
|
||||
let line_removals = (1..=line_count).rev().flat_map(move |n| {
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
(0..line_count - n).map(move |start| {
|
||||
let end = start + n;
|
||||
let start_offset = line_offsets_clone[start];
|
||||
let end_offset = line_offsets_clone[end];
|
||||
let replacement = String::new();
|
||||
let replacements = vec![(start_offset, end_offset, replacement)];
|
||||
Update { replacements }
|
||||
})
|
||||
});
|
||||
|
||||
let line_offsets = line_offsets_clone;
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
|
||||
// then, try to dedent every group of 1, 2, 3, ... lines - in reverse order (so, trying dedenting n lines first, then n-1, etc)
|
||||
// just remove one space at a time, for now
|
||||
let line_dedents = (1..=line_count).rev().flat_map(move |n| {
|
||||
let line_offsets_clone = line_offsets.clone();
|
||||
let line_indents_clone = line_indents.clone();
|
||||
(0..line_count - n).filter_map(move |start| {
|
||||
// first check if all lines are either zero-width or have greater than zero indent
|
||||
let end = start + n;
|
||||
for i in start..end {
|
||||
if line_indents_clone[i] == 0
|
||||
&& line_offsets_clone[i] + 1
|
||||
< line_offsets_clone.get(i + 1).cloned().unwrap_or(s_len)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut replacements = vec![];
|
||||
for i in start..end {
|
||||
let offset = line_offsets_clone[i];
|
||||
let indent = line_indents_clone[i];
|
||||
if indent > 0 {
|
||||
replacements.push((offset, offset + 1, String::new()));
|
||||
}
|
||||
}
|
||||
Some(Update { replacements })
|
||||
})
|
||||
});
|
||||
|
||||
// then, try to select every range of 1, 2, 3, ... lines - in normal order this time!
|
||||
// we remove the lines before and after the range
|
||||
let line_selects = (1..line_count - 1).flat_map(move |n| {
|
||||
assert!(n > 0);
|
||||
let line_offsets_clone = line_offsets_clone.clone();
|
||||
(0..line_count - n).map(move |start| {
|
||||
let end = start + n;
|
||||
let start_offset = line_offsets_clone[start];
|
||||
let end_offset = line_offsets_clone[end];
|
||||
assert!(end_offset > start_offset);
|
||||
assert!(start_offset > 0 || end_offset < s_len);
|
||||
let replacements = vec![
|
||||
(0, start_offset, String::new()),
|
||||
(end_offset, s_len, String::new()),
|
||||
];
|
||||
Update { replacements }
|
||||
})
|
||||
});
|
||||
|
||||
// then, try to remove every range of 1, 2, 3, ... characters - in reverse order (so, trying removing n characters first, then n-1, etc)
|
||||
let charseq_removals = (1..s.len()).rev().flat_map(move |n| {
|
||||
(0..s.len() - n).map(move |start| {
|
||||
let end = start + n;
|
||||
let replacement = String::new();
|
||||
let replacements = vec![(start, end, replacement)];
|
||||
Update { replacements }
|
||||
})
|
||||
});
|
||||
|
||||
Box::new(
|
||||
line_removals
|
||||
.chain(line_dedents)
|
||||
.chain(line_selects)
|
||||
.chain(charseq_removals)
|
||||
.filter(|u| !u.replacements.is_empty()),
|
||||
)
|
||||
}
|
@ -4,12 +4,12 @@ use roc_parse::{
|
||||
ast::{Defs, Expr, Malformed, Module},
|
||||
module::parse_module_defs,
|
||||
parser::{Parser, SyntaxError},
|
||||
remove_spaces::RemoveSpaces,
|
||||
state::State,
|
||||
test_helpers::{parse_defs_with, parse_expr_with, parse_header_with},
|
||||
};
|
||||
use roc_test_utils::assert_multiline_str_eq;
|
||||
|
||||
use roc_fmt::spaces::RemoveSpaces;
|
||||
use roc_fmt::Buf;
|
||||
|
||||
/// Source code to parse. Usually in the form of a test case.
|
||||
@ -28,6 +28,25 @@ pub enum Input<'a> {
|
||||
Full(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum InputKind {
|
||||
Header,
|
||||
ModuleDefs,
|
||||
Expr,
|
||||
Full,
|
||||
}
|
||||
|
||||
impl InputKind {
|
||||
pub fn with_text(self, text: &str) -> Input {
|
||||
match self {
|
||||
InputKind::Header => Input::Header(text),
|
||||
InputKind::ModuleDefs => Input::ModuleDefs(text),
|
||||
InputKind::Expr => Input::Expr(text),
|
||||
InputKind::Full => Input::Full(text),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Owned version of `Input`
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InputOwned {
|
||||
@ -38,7 +57,7 @@ pub enum InputOwned {
|
||||
}
|
||||
|
||||
impl InputOwned {
|
||||
fn as_ref(&self) -> Input {
|
||||
pub fn as_ref(&self) -> Input {
|
||||
match self {
|
||||
InputOwned::Header(s) => Input::Header(s),
|
||||
InputOwned::ModuleDefs(s) => Input::ModuleDefs(s),
|
||||
@ -64,7 +83,7 @@ pub enum Output<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Output<'a> {
|
||||
fn format(&self) -> InputOwned {
|
||||
pub fn format(&self) -> InputOwned {
|
||||
let arena = Bump::new();
|
||||
let mut buf = Buf::new_in(&arena);
|
||||
match self {
|
||||
@ -172,7 +191,7 @@ impl<'a> Input<'a> {
|
||||
|
||||
let (header, defs) = header.upgrade_header_imports(arena);
|
||||
|
||||
let module_defs = parse_module_defs(arena, state, defs).unwrap();
|
||||
let module_defs = parse_module_defs(arena, state, defs)?;
|
||||
|
||||
Ok(Output::Full {
|
||||
header,
|
||||
|
@ -0,0 +1 @@
|
||||
Expr(BackpassContinue(@8), @0)
|
@ -0,0 +1,2 @@
|
||||
u:i
|
||||
e<-x
|
@ -0,0 +1 @@
|
||||
Expr(BadExprEnd(@4), @0)
|
@ -1 +1 @@
|
||||
Expr(DefMissingFinalExpr2(Start(@11), @11), @0)
|
||||
Expr(IndentEnd(@11), @0)
|
@ -0,0 +1 @@
|
||||
Expr(BadExprEnd(@3), @0)
|
@ -1 +1 @@
|
||||
Expr(Start(@0), @0)
|
||||
Expr(IndentEnd(@12), @0)
|
@ -1 +1 @@
|
||||
Expr(If(Else(@16), @0), @0)
|
||||
Expr(If(Else(@17), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(List(End(@6), @0), @0)
|
||||
Expr(List(End(@7), @0), @0)
|
@ -0,0 +1 @@
|
||||
Expr(InParens(Expr(BadExprEnd(@8), @5), @4), @0)
|
@ -0,0 +1,3 @@
|
||||
a : e
|
||||
Na := e
|
||||
e0
|
@ -0,0 +1 @@
|
||||
Expr(BadExprEnd(@11), @0)
|
@ -1 +1 @@
|
||||
Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0)
|
||||
Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0)
|
||||
Expr(Closure(Pattern(PInParens(End(@6), @1), @1), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(Closure(Pattern(PInParens(End(@2), @1), @1), @0), @0)
|
||||
Expr(Closure(Pattern(PInParens(End(@3), @1), @1), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0)
|
||||
Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TRecord(End(@13), @4), @4), @0)
|
||||
Expr(Type(TRecord(End(@14), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TRecord(End(@5), @4), @4), @0)
|
||||
Expr(Type(TRecord(End(@6), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TRecord(End(@16), @4), @4), @0)
|
||||
Expr(Type(TRecord(End(@17), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TTagUnion(End(@9), @4), @4), @0)
|
||||
Expr(Type(TTagUnion(End(@10), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TTagUnion(End(@5), @4), @4), @0)
|
||||
Expr(Type(TTagUnion(End(@6), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TInParens(End(@9), @4), @4), @0)
|
||||
Expr(Type(TInParens(End(@10), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Type(TInParens(End(@5), @4), @4), @0)
|
||||
Expr(Type(TInParens(End(@6), @4), @4), @0)
|
@ -1 +1 @@
|
||||
Expr(Closure(Arrow(@10), @4), @0)
|
||||
Expr(Closure(IndentArrow(@10), @4), @0)
|
@ -0,0 +1 @@
|
||||
Expr(Import(EndNewline(@15), @0), @0)
|
@ -0,0 +1 @@
|
||||
import svg.Path a
|
@ -1 +1 @@
|
||||
Expr(When(Arrow(@26), @0), @0)
|
||||
Expr(When(IndentPattern(@26), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(BadExprEnd(@22), @0)
|
||||
Expr(BadOperator("->", @24), @0)
|
@ -1 +1 @@
|
||||
Expr(When(Branch(BadOperator("->", @34), @19), @0), @0)
|
||||
Expr(When(IndentPattern(@34), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(When(Branch(BadOperator("->", @28), @19), @0), @0)
|
||||
Expr(When(IndentPattern(@28), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(DefMissingFinalExpr2(ElmStyleFunction(@18-22, @23), @11), @0)
|
||||
Expr(BadExprEnd(@11), @0)
|
@ -1 +1 @@
|
||||
Expr(BadOperator("->", @9), @0)
|
||||
Expr(BadExprEnd(@8), @0)
|
@ -0,0 +1,3 @@
|
||||
when x is
|
||||
bar.and -> 1
|
||||
_ -> 4
|
@ -1,40 +1,45 @@
|
||||
When(
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
SpaceAfter(
|
||||
When(
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@14-21 SpaceBefore(
|
||||
Malformed(
|
||||
"bar.and",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @25-26 Num(
|
||||
"1",
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@31-32 SpaceBefore(
|
||||
Underscore(
|
||||
"",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @36-37 Num(
|
||||
"4",
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@14-21 SpaceBefore(
|
||||
Malformed(
|
||||
"bar.and",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @25-26 Num(
|
||||
"1",
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@31-32 SpaceBefore(
|
||||
Underscore(
|
||||
"",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @36-37 Num(
|
||||
"4",
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
Newline,
|
||||
],
|
||||
)
|
||||
|
@ -0,0 +1,3 @@
|
||||
when x is
|
||||
Foo.and -> 1
|
||||
_ -> 4
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user