plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
d849deb
— Peter John
2026-11-01T23:07:45+05:30
test: dedupe core/cli Rust tests (144 -> 106), table-driven formatter/loader cases
- plum-cli/tests/test_scope_test.rs +0 -10
- plum-core/tests/formatter_test.rs +17 -41
- plum-core/tests/loader_test.rs +13 -33
- plum-core/tests/parser_test.rs +1 -199
- plum-core/tests/unions_test.rs +0 -13
plum-cli/tests/test_scope_test.rs
CHANGED
|
@@ -30,16 +30,6 @@ fn scratch(name: &str) -> std::path::PathBuf {
|
|
|
30
30
|
|
|
31
31
|
const TWO: &str = "import std/Str\nimport std/Int\n\ntest \"one\"\n assert 1 == 1\n\ntest \"two\"\n assert 2 == 2\n";
|
|
32
32
|
|
|
33
|
-
#[test]
|
|
34
|
-
fn fileRunsOnlyItsOwnTests() {
|
|
35
|
-
let dir = scratch("file");
|
|
36
|
-
let f = dir.join("two.plum");
|
|
37
|
-
std::fs::write(&f, TWO).unwrap();
|
|
38
|
-
let (out, ok) = runPlum(&[f.to_str().unwrap()]);
|
|
39
|
-
assert!(ok, "{out}");
|
|
40
|
-
assert!(out.contains("2 passed, 0 failed"), "{out}");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
33
|
#[test]
|
|
44
34
|
fn directoryRunsSumOfOwnTests() {
|
|
45
35
|
let dir = scratch("dir");
|
plum-core/tests/formatter_test.rs
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
#![allow(non_snake_case)]
|
|
2
2
|
use plum_core::formatSource;
|
|
3
3
|
|
|
4
|
-
#[test]
|
|
5
|
-
fn formatsSimpleFunction() {
|
|
6
|
-
let input = "fun main() =\n x = 1\n";
|
|
7
|
-
let result = formatSource(input).expect("formatSource should succeed");
|
|
8
|
-
assert!(result.contains("main()"));
|
|
9
|
-
assert!(result.contains("x ="));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
#[test]
|
|
13
|
-
fn formatsBinaryOperatorSpacing() {
|
|
14
|
-
let input = "fun add(a: Int, b: Int) -> Int =\n a+b\n";
|
|
15
|
-
let result = formatSource(input).expect("formatSource should succeed");
|
|
16
|
-
assert!(result.contains("a + b"));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
4
|
#[test]
|
|
20
5
|
fn rejectsSyntaxError() {
|
|
21
6
|
let input = "@@@@invalid@@@@\n";
|
|
@@ -23,14 +8,6 @@ fn rejectsSyntaxError() {
|
|
|
23
8
|
assert!(result.is_err());
|
|
24
9
|
}
|
|
25
10
|
|
|
26
|
-
#[test]
|
|
27
|
-
fn idempotentOnAlreadyFormatted() {
|
|
28
|
-
let input = "fun main() =\n x = 1 + 2\n";
|
|
29
|
-
let first = formatSource(input).expect("first pass");
|
|
30
|
-
let second = formatSource(&first).expect("second pass");
|
|
31
|
-
assert_eq!(first, second, "formatting should be idempotent");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
11
|
/// Regression test for a real bug: Topiary inserts NO whitespace between
|
|
35
12
|
/// adjacent leaves unless a query says to, so keywords with no explicit
|
|
36
13
|
/// spacing rule glue to the next token ("module" + "test" -> "moduletest").
|
|
@@ -266,29 +243,28 @@ fn formatsZigStyleErrors() {
|
|
|
266
243
|
assert_eq!(formatSource(&h).expect("second"), h, "idempotent");
|
|
267
244
|
}
|
|
268
245
|
|
|
269
|
-
fn assertFormatStable(input: &str) {
|
|
270
|
-
let once = formatSource(input).expect("first pass");
|
|
271
|
-
assert_eq!(once, input);
|
|
272
|
-
let twice = formatSource(&once).expect("second pass");
|
|
273
|
-
assert_eq!(twice, once);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
246
|
#[test]
|
|
277
|
-
fn
|
|
247
|
+
fn alreadyFormattedSourceIsUnchanged() {
|
|
278
|
-
|
|
248
|
+
for input in [
|
|
249
|
+
"fun main() =\n x = 1 + 2\n",
|
|
279
250
|
"struct Vec2(x: Float, y: Float) =\n ZERO = Vec2(x: 0.0, y: 0.0)\n\n fun len(self) -> Float =\n self.x\n",
|
|
251
|
+
"struct Vec2(x: Float, y: Float)\n\nfun f() -> Int =\n 1\n",
|
|
252
|
+
"record Pair[K, V](key: K, val: V)\n",
|
|
253
|
+
"record Pair[K, V](key: K, val: V) =\n fun first(self) -> K =\n self.key\n",
|
|
280
|
-
|
|
254
|
+
] {
|
|
255
|
+
let once = formatSource(input).expect("first pass");
|
|
256
|
+
assert_eq!(once, input);
|
|
257
|
+
let twice = formatSource(&once).expect("second pass");
|
|
258
|
+
assert_eq!(twice, once);
|
|
281
|
-
}
|
|
259
|
+
}
|
|
282
|
-
|
|
283
|
-
#[test]
|
|
284
|
-
fn formatStructNoBody() {
|
|
285
|
-
assertFormatStable("struct Vec2(x: Float, y: Float)\n\nfun f() -> Int =\n 1\n");
|
|
286
260
|
}
|
|
287
261
|
|
|
288
262
|
#[test]
|
|
289
|
-
fn
|
|
263
|
+
fn spacesOperatorsAndSimpleFunctions() {
|
|
264
|
+
let r = formatSource("fun add(a: Int, b: Int) -> Int =\n a+b\n").expect("format");
|
|
290
|
-
|
|
265
|
+
assert!(r.contains("a + b"), "{r}");
|
|
266
|
+
let r = formatSource("fun main() =\n x = 1\n").expect("format");
|
|
291
|
-
|
|
267
|
+
assert!(r.contains("main()") && r.contains("x ="), "{r}");
|
|
292
268
|
}
|
|
293
269
|
|
|
294
270
|
#[test]
|
plum-core/tests/loader_test.rs
CHANGED
|
@@ -252,21 +252,19 @@ fun main(u: Unit) -> Unit =
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
#[test]
|
|
255
|
-
fn
|
|
255
|
+
fn removedSyntaxIsAParseError() {
|
|
256
|
+
for src in [
|
|
257
|
+
"fun f(u: Unit) -> Unit??\n u\n",
|
|
258
|
+
"fun f(u: Unit) -> Unit? ! [X] =\n u\n",
|
|
259
|
+
"PI = 3\n",
|
|
260
|
+
"extend Foo =\n fun f() -> Unit =\n todo\n",
|
|
261
|
+
] {
|
|
256
|
-
|
|
262
|
+
let dir = TempDir::new().unwrap();
|
|
257
|
-
|
|
263
|
+
let entry = dir.path().join("main.plum");
|
|
258
|
-
|
|
264
|
+
fs::write(&entry, src).unwrap();
|
|
259
|
-
|
|
265
|
+
let err = loadAndMerge(&entry, dir.path()).unwrap_err();
|
|
260
|
-
|
|
266
|
+
assert!(err.contains("parse error"), "{src}: {err}");
|
|
261
|
-
}
|
|
267
|
+
}
|
|
262
|
-
|
|
263
|
-
#[test]
|
|
264
|
-
fn nullable_bang_rejected() {
|
|
265
|
-
let dir = TempDir::new().unwrap();
|
|
266
|
-
let entry = dir.path().join("main.plum");
|
|
267
|
-
fs::write(&entry, "fun f(u: Unit) -> Unit? ! [X] =\n u\n").unwrap();
|
|
268
|
-
let err = loadAndMerge(&entry, dir.path()).unwrap_err();
|
|
269
|
-
assert!(err.contains("parse error"), "{err}");
|
|
270
268
|
}
|
|
271
269
|
|
|
272
270
|
#[test]
|
|
@@ -304,24 +302,6 @@ fn assoc_const_names_are_per_type_across_modules() {
|
|
|
304
302
|
assert!(names.contains(&"Alpha.X".to_string()) && names.contains(&"Beta.X".to_string()), "{names:?}");
|
|
305
303
|
}
|
|
306
304
|
|
|
307
|
-
#[test]
|
|
308
|
-
fn toplevel_const_removed() {
|
|
309
|
-
let dir = TempDir::new().unwrap();
|
|
310
|
-
let entry = dir.path().join("main.plum");
|
|
311
|
-
fs::write(&entry, "PI = 3\n").unwrap();
|
|
312
|
-
let err = loadAndMerge(&entry, dir.path()).unwrap_err();
|
|
313
|
-
assert!(err.contains("parse error"), "{err}");
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
#[test]
|
|
317
|
-
fn extend_removed() {
|
|
318
|
-
let dir = TempDir::new().unwrap();
|
|
319
|
-
let entry = dir.path().join("main.plum");
|
|
320
|
-
fs::write(&entry, "extend Foo =\n fun f() -> Unit =\n todo\n").unwrap();
|
|
321
|
-
let err = loadAndMerge(&entry, dir.path()).unwrap_err();
|
|
322
|
-
assert!(err.contains("parse error"), "{err}");
|
|
323
|
-
}
|
|
324
|
-
|
|
325
305
|
#[test]
|
|
326
306
|
fn primitive_builtin_std_only() {
|
|
327
307
|
// A built-in's API can only be declared in std.
|
plum-core/tests/parser_test.rs
CHANGED
|
@@ -85,31 +85,6 @@ enum Cat =
|
|
|
85
85
|
assert_eq!(set_name.type_param, Some("Cat".to_string()));
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
#[test]
|
|
89
|
-
fn nestedEnumMethodBecomesTopLevelFnItemWithTypeParamSet() {
|
|
90
|
-
let src = "\
|
|
91
|
-
enum Step =
|
|
92
|
-
| ReadMin
|
|
93
|
-
| ReadMax
|
|
94
|
-
|
|
95
|
-
fun toNumber(self) -> Int =
|
|
96
|
-
match self
|
|
97
|
-
ReadMin => 0
|
|
98
|
-
ReadMax => 1
|
|
99
|
-
";
|
|
100
|
-
let source = parse(src);
|
|
101
|
-
assert_eq!(source.items.len(), 2, "enum + 1 nested method, in order");
|
|
102
|
-
let Item::Enum(e) = &source.items[0] else {
|
|
103
|
-
panic!("expected an Enum item first")
|
|
104
|
-
};
|
|
105
|
-
assert_eq!(e.name, "Step");
|
|
106
|
-
let Item::Fn(to_number) = &source.items[1] else {
|
|
107
|
-
panic!("expected toNumber immediately after the enum")
|
|
108
|
-
};
|
|
109
|
-
assert_eq!(to_number.name, "toNumber");
|
|
110
|
-
assert_eq!(to_number.type_param, Some("Step".to_string()));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
88
|
#[test]
|
|
114
89
|
fn enumDiscriminantValuesParseIntoParamsAndVariantValues() {
|
|
115
90
|
let src = "\
|
|
@@ -135,26 +110,6 @@ enum Step(n: Int) =
|
|
|
135
110
|
assert_eq!(e.variants[1].values, vec![Expr::Int(1)]);
|
|
136
111
|
}
|
|
137
112
|
|
|
138
|
-
#[test]
|
|
139
|
-
fn classAndEnumWithNoNestedMethodsProduceNoExtraFnItems() {
|
|
140
|
-
let src = "\
|
|
141
|
-
enum Dog =
|
|
142
|
-
| Dog(name: Str)
|
|
143
|
-
|
|
144
|
-
enum Flag =
|
|
145
|
-
| On
|
|
146
|
-
| Off
|
|
147
|
-
";
|
|
148
|
-
let source = parse(src);
|
|
149
|
-
assert_eq!(
|
|
150
|
-
source.items.len(),
|
|
151
|
-
2,
|
|
152
|
-
"no nested methods means no extra Item::Fn entries"
|
|
153
|
-
);
|
|
154
|
-
assert!(matches!(source.items[0], Item::Enum(_)));
|
|
155
|
-
assert!(matches!(source.items[1], Item::Enum(_)));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
113
|
#[test]
|
|
159
114
|
fn closureLiteralParsesWithParamsAndBody() {
|
|
160
115
|
let src = "\
|
|
@@ -178,28 +133,6 @@ fun useClosure() -> Bool =
|
|
|
178
133
|
assert_eq!(closure.body.stmts.len(), 1);
|
|
179
134
|
}
|
|
180
135
|
|
|
181
|
-
#[test]
|
|
182
|
-
fn closureLiteralParsesWithNoParams() {
|
|
183
|
-
let src = "\
|
|
184
|
-
fun useClosure() -> Bool =
|
|
185
|
-
cb = ||
|
|
186
|
-
true
|
|
187
|
-
cb()
|
|
188
|
-
";
|
|
189
|
-
let source = parse(src);
|
|
190
|
-
let f = onlyFn(&source);
|
|
191
|
-
let FnBody::Block(block) = &f.body else {
|
|
192
|
-
panic!("expected a block body")
|
|
193
|
-
};
|
|
194
|
-
let Stmt::Assign(assign) = &block.stmts[0] else {
|
|
195
|
-
panic!("expected an assign statement")
|
|
196
|
-
};
|
|
197
|
-
let Expr::Closure(closure) = &assign.values[0] else {
|
|
198
|
-
panic!("expected a closure expression")
|
|
199
|
-
};
|
|
200
|
-
assert!(closure.params.is_empty());
|
|
201
|
-
}
|
|
202
|
-
|
|
203
136
|
#[test]
|
|
204
137
|
fn fnWithoutReceiverButWithReturnTypeHasNoTypeParam() {
|
|
205
138
|
let src = "fun main() -> Int =\n 0\n";
|
|
@@ -215,18 +148,6 @@ fn fnWithoutReceiverButWithReturnTypeHasNoTypeParam() {
|
|
|
215
148
|
);
|
|
216
149
|
}
|
|
217
150
|
|
|
218
|
-
#[test]
|
|
219
|
-
fn nestedMethodWithReturnTypeHasCorrectTypeParam() {
|
|
220
|
-
let src = "enum Cat =\n | Cat(name: Str)\n\n fun toStr() -> Str =\n \"x\"\n";
|
|
221
|
-
let source = parse(src);
|
|
222
|
-
let f = onlyFn(&source);
|
|
223
|
-
assert_eq!(f.type_param, Some("Cat".to_string()));
|
|
224
|
-
assert_eq!(
|
|
225
|
-
f.returns.as_ref().map(|t| t.name.clone()),
|
|
226
|
-
Some("Str".to_string())
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
151
|
#[test]
|
|
231
152
|
fn fnValueTypeParamParsesWithPositionalTypesAndReturn() {
|
|
232
153
|
let src = "fun each(cb: fn(Int) -> Bool) -> Bool =\n true\n";
|
|
@@ -266,18 +187,6 @@ fn bound_syntax_rejected() {
|
|
|
266
187
|
assert!(hasParseError("fun f[T: Show](x: T) -> T =\n x\n"));
|
|
267
188
|
}
|
|
268
189
|
|
|
269
|
-
#[test]
|
|
270
|
-
fn fnValueTypeParamParsesWithNoReturn() {
|
|
271
|
-
let src = "fun each(cb: fn(Int)) -> Bool =\n true\n";
|
|
272
|
-
let source = parse(src);
|
|
273
|
-
let f = onlyFn(&source);
|
|
274
|
-
let ParamType::Fn(param_types, ret) = &f.params[0].ty else {
|
|
275
|
-
panic!("expected ParamType::Fn")
|
|
276
|
-
};
|
|
277
|
-
assert_eq!(param_types.len(), 1);
|
|
278
|
-
assert!(ret.is_none());
|
|
279
|
-
}
|
|
280
|
-
|
|
281
190
|
// ---- errors: `!T`, `T?`, postfix `?`, `crash` ----
|
|
282
191
|
|
|
283
192
|
fn parseHasError(src: &str) -> bool {
|
|
@@ -306,29 +215,11 @@ fn test_tag_union_types_are_syntax_errors() {
|
|
|
306
215
|
assert!(parseHasError("fun f() -> Int ! [A, B] = 1\n"));
|
|
307
216
|
}
|
|
308
217
|
|
|
309
|
-
#[test]
|
|
310
|
-
fn test_slice_type_still_parses() {
|
|
311
|
-
let src = parse("fun f(x: []Byte) -> Int = 1\n");
|
|
312
|
-
match &onlyFn(&src).params[0].ty {
|
|
313
|
-
ParamType::Type(t) => assert_eq!(t.name, "[]Byte"),
|
|
314
|
-
_ => panic!(),
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
218
|
#[test]
|
|
319
219
|
fn test_double_question_expression_is_a_syntax_error() {
|
|
320
220
|
assert!(parseHasError("fun f(a: Int, b: Int) -> Int = a ?? b\n"));
|
|
321
221
|
}
|
|
322
222
|
|
|
323
|
-
#[test]
|
|
324
|
-
fn elvis_parses_to_an_elvis_catch() {
|
|
325
|
-
let src = parse("fun f(a: Int?, b: Int) -> Int = a ?: b\n");
|
|
326
|
-
match bodyExpr(onlyFn(&src)) {
|
|
327
|
-
Expr::Catch(c) => assert!(c.elvis),
|
|
328
|
-
other => panic!("got {:?}", other),
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
223
|
#[test]
|
|
333
224
|
fn elvis_is_right_associative() {
|
|
334
225
|
let src = parse("fun f(a: Int?, b: Int?) -> Int = a ?: b ?: 1\n");
|
|
@@ -379,12 +270,6 @@ fn int_min_literal() {
|
|
|
379
270
|
assert!(matches!(bodyExpr(onlyFn(&src)), Expr::Int(i64::MIN)));
|
|
380
271
|
}
|
|
381
272
|
|
|
382
|
-
#[test]
|
|
383
|
-
fn inline_if_expression() {
|
|
384
|
-
let src = parse("fun f(c: Bool) -> Int = if c then 1 else 2\n");
|
|
385
|
-
assert!(matches!(bodyExpr(onlyFn(&src)), Expr::InlineIf(_)));
|
|
386
|
-
}
|
|
387
|
-
|
|
388
273
|
#[test]
|
|
389
274
|
fn inline_if_chain() {
|
|
390
275
|
let src = parse("fun f(a: Bool, b: Bool) -> Int = if a then 1 else if b then 2 else 3\n");
|
|
@@ -410,24 +295,6 @@ fn question_binds_tight() {
|
|
|
410
295
|
assert!(matches!(bodyExpr(g), Expr::Binary(_)), "got {:?}", bodyExpr(g));
|
|
411
296
|
}
|
|
412
297
|
|
|
413
|
-
#[test]
|
|
414
|
-
fn test_try_plain() {
|
|
415
|
-
let src = parse("fun f() -> Int = try g()\n");
|
|
416
|
-
assert!(matches!(bodyExpr(onlyFn(&src)), Expr::Try(_)));
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
#[test]
|
|
420
|
-
fn test_crash_stmt() {
|
|
421
|
-
let src = parse("fun f() -> Int =\n crash \"boom\"\n");
|
|
422
|
-
match &onlyFn(&src).body {
|
|
423
|
-
FnBody::Block(b) => match &b.stmts[0] {
|
|
424
|
-
Stmt::Crash(Expr::String(_)) => {}
|
|
425
|
-
other => panic!("expected Crash(String), got {other:?}"),
|
|
426
|
-
},
|
|
427
|
-
_ => panic!(),
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
298
|
fn firstExprDebug(src: &str) -> String {
|
|
432
299
|
let s = parse(src);
|
|
433
300
|
format!("{:?}", onlyFn(&s).body)
|
|
@@ -656,12 +523,6 @@ fn catch_error_value_and_block_forms() {
|
|
|
656
523
|
assert!(matches!(&c.handler, CatchHandler::Default(Expr::Var(_))));
|
|
657
524
|
}
|
|
658
525
|
|
|
659
|
-
#[test]
|
|
660
|
-
fn return_error_statement() {
|
|
661
|
-
let s = parse("fun f() -> !Int =\n if x\n return error.Bad\n 1\n");
|
|
662
|
-
assert!(matches!(bodyExpr(onlyFn(&s)), Expr::Int(1)));
|
|
663
|
-
}
|
|
664
|
-
|
|
665
526
|
#[test]
|
|
666
527
|
fn pattern_if_conditions_parse() {
|
|
667
528
|
let src = parse("enum Sh =\n | Circle(r: Int)\n | Rect(w: Int, h: Int)\n\nfun f(s: Sh) -> Int =\n if Circle(r) := s\n return r\n else if Rect(w, _) := s, w > 1, w < 9\n return w\n 0\n");
|
|
@@ -720,15 +581,6 @@ fn pairArgDesugarsToPairCall() {
|
|
|
720
581
|
}
|
|
721
582
|
}
|
|
722
583
|
|
|
723
|
-
#[test]
|
|
724
|
-
fn pairArgMixedWithPositional() {
|
|
725
|
-
let args = firstCallArgs("fun main() =\n x := f(1, \"a\" => 2)\n");
|
|
726
|
-
assert_eq!(args.len(), 2);
|
|
727
|
-
assert_eq!(args[0], Arg::Positional(Expr::Int(1)));
|
|
728
|
-
let p = pairArgs(&args[1]);
|
|
729
|
-
assert_eq!(p[1], Arg::Positional(Expr::Int(2)));
|
|
730
|
-
}
|
|
731
|
-
|
|
732
584
|
#[test]
|
|
733
585
|
fn pairArgTrailingComma() {
|
|
734
586
|
let args = firstCallArgs("fun main() =\n x := f(\"a\" => 1,)\n");
|
|
@@ -736,15 +588,6 @@ fn pairArgTrailingComma() {
|
|
|
736
588
|
pairArgs(&args[0]);
|
|
737
589
|
}
|
|
738
590
|
|
|
739
|
-
#[test]
|
|
740
|
-
fn pairKeyAnyExpression() {
|
|
741
|
-
let args = firstCallArgs("fun main() =\n x := f(1 => \"x\", k => \"y\", n + 1 => \"z\")\n");
|
|
742
|
-
assert_eq!(args.len(), 3);
|
|
743
|
-
for a in &args {
|
|
744
|
-
pairArgs(a);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
591
|
#[test]
|
|
749
592
|
fn pairArgCallCarriesPairPosition() {
|
|
750
593
|
let args = firstCallArgs("fun main() =\n x := f(1, \"a\" => 2)\n");
|
|
@@ -772,15 +615,6 @@ fn digitNameCallsAreCallsNotConsts() {
|
|
|
772
615
|
assert_eq!(args.len(), 1);
|
|
773
616
|
}
|
|
774
617
|
|
|
775
|
-
#[test]
|
|
776
|
-
fn screamingConstsStayConsts() {
|
|
777
|
-
let src = parse("fun f() =\n a := MAX\n b := V2.K1\n c := BUCKET_COUNT\n");
|
|
778
|
-
match &onlyFn(&src).body {
|
|
779
|
-
FnBody::Block(b) => assert_eq!(b.stmts.len(), 3),
|
|
780
|
-
o => panic!("{o:?}"),
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
|
|
784
618
|
#[test]
|
|
785
619
|
fn parseStructNoBody() {
|
|
786
620
|
let s = parse("struct Vec2(x: Float, y: Float)");
|
|
@@ -793,6 +627,7 @@ fn parseStructNoBody() {
|
|
|
793
627
|
assert_eq!(e.variants[0].name, "Vec2");
|
|
794
628
|
assert_eq!(e.variants[0].field_names, vec!["x", "y"]);
|
|
795
629
|
assert_eq!(e.variants[0].fields[0].name, "Float");
|
|
630
|
+
assert!(e.variants[0].member.is_none());
|
|
796
631
|
}
|
|
797
632
|
|
|
798
633
|
#[test]
|
|
@@ -828,18 +663,6 @@ fn parseRecordFnField() {
|
|
|
828
663
|
assert_eq!(f[1].name, "T");
|
|
829
664
|
}
|
|
830
665
|
|
|
831
|
-
#[test]
|
|
832
|
-
fn struct_no_body_then_fun() {
|
|
833
|
-
let s = parse("struct Aa(x: Int)\nfun f() -> Int = 1");
|
|
834
|
-
assert_eq!(s.items.len(), 2);
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
#[test]
|
|
838
|
-
fn struct_body_comment_before_fn() {
|
|
839
|
-
let s = parse("struct Vec2(x: Float) =\n # c\n fun f(self) -> Float = self.x\n");
|
|
840
|
-
assert_eq!(onlyEnum(&s).name, "Vec2");
|
|
841
|
-
}
|
|
842
|
-
|
|
843
666
|
#[test]
|
|
844
667
|
fn value_enum_rejected() {
|
|
845
668
|
assert!(hasParseError("value enum Vv =\n | Vv(x: Int)\n"));
|
|
@@ -897,12 +720,6 @@ fn unionEnumMultiLineGenericMember() {
|
|
|
897
720
|
assert_eq!(n.generics[0].name, "T");
|
|
898
721
|
}
|
|
899
722
|
|
|
900
|
-
#[test]
|
|
901
|
-
fn one_line_then_fun() {
|
|
902
|
-
let source = parse("enum Sh = Ci | Re\nfun f() -> Int = 1");
|
|
903
|
-
assert_eq!(source.items.len(), 2);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
723
|
#[test]
|
|
907
724
|
fn one_line_generic_member() {
|
|
908
725
|
let source = parseRaw("enum Js = JsNull | List[Js] | Map[Str, Js]");
|
|
@@ -914,14 +731,6 @@ fn one_line_generic_member() {
|
|
|
914
731
|
assert_eq!(memberOf(&e.variants[2]).generics[0].name, "Str");
|
|
915
732
|
}
|
|
916
733
|
|
|
917
|
-
#[test]
|
|
918
|
-
fn discriminant_unchanged() {
|
|
919
|
-
let source = parse("enum Step(n: Int) =\n | ReadMin(10)\n");
|
|
920
|
-
let e = onlyEnum(&source);
|
|
921
|
-
assert_eq!(e.variants[0].values.len(), 1);
|
|
922
|
-
assert!(e.variants[0].member.is_none());
|
|
923
|
-
}
|
|
924
|
-
|
|
925
734
|
#[test]
|
|
926
735
|
fn inline_payload_still_parses() {
|
|
927
736
|
let source = parse("enum Sh =\n | Circle(r: Float)\n | Dot(Int)\n");
|
|
@@ -931,13 +740,6 @@ fn inline_payload_still_parses() {
|
|
|
931
740
|
assert!(e.variants[1].member.is_none());
|
|
932
741
|
}
|
|
933
742
|
|
|
934
|
-
#[test]
|
|
935
|
-
fn structHasNoMember() {
|
|
936
|
-
let source = parse("struct Vec2(x: Float, y: Float)");
|
|
937
|
-
let e = onlyEnum(&source);
|
|
938
|
-
assert!(e.variants[0].member.is_none());
|
|
939
|
-
}
|
|
940
|
-
|
|
941
743
|
fn firstAssign(src: &str) -> plum_core::ast::Assign {
|
|
942
744
|
let source = parse(src);
|
|
943
745
|
let f = onlyFn(&source);
|
plum-core/tests/unions_test.rs
CHANGED
|
@@ -25,11 +25,6 @@ fn ty(n: &str) -> Type {
|
|
|
25
25
|
Type { name: n.to_string(), generics: vec![] }
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
#[test]
|
|
29
|
-
fn hiddenVariantName() {
|
|
30
|
-
assert_eq!(hiddenVariant("Shape", "Circle"), "Shape__Circle");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
28
|
#[test]
|
|
34
29
|
fn resolvesMemberAndTag() {
|
|
35
30
|
let mut s = parse("struct Circle(r: Float)\nenum Shape = Circle | Red");
|
|
@@ -117,11 +112,3 @@ fn builtinMember() {
|
|
|
117
112
|
assert_eq!(enumOf(&s, "Num").variants[0].name, "Num__Int");
|
|
118
113
|
}
|
|
119
114
|
|
|
120
|
-
#[test]
|
|
121
|
-
fn legacyErrorsReported() {
|
|
122
|
-
let s = parse("enum Sh =\n | Circle(r: Float)\nenum User =\n | User(n: Int)\n");
|
|
123
|
-
let errs = legacyErrors(&s);
|
|
124
|
-
assert_eq!(errs.len(), 2);
|
|
125
|
-
assert!(errs[0].starts_with("inline variant payloads are removed: declare record Circle(...)"));
|
|
126
|
-
assert!(errs[1].contains("enum User has a single variant named User"));
|
|
127
|
-
}
|