1use crate::{assert_js, assert_ts_def};
2
3#[test]
4fn exported_functions() {
5 assert_js!(
6 r#"
7pub fn add(x, y) {
8 x + y
9}"#,
10 );
11}
12
13#[test]
14fn calling_functions() {
15 assert_js!(
16 r#"
17pub fn twice(f: fn(t) -> t, x: t) -> t {
18 f(f(x))
19}
20pub fn add_one(x: Int) -> Int {
21 x + 1
22}
23pub fn add_two(x: Int) -> Int {
24 twice(add_one, x)
25}
26
27pub fn take_two(x: Int) -> Int {
28 twice(fn(y) {y - 1}, x)
29}
30"#,
31 );
32}
33
34#[test]
35fn function_formatting() {
36 assert_js!(
37 r#"
38pub fn add(the_first_variable_that_should_be_added, the_second_variable_that_should_be_added) {
39 the_first_variable_that_should_be_added + the_second_variable_that_should_be_added
40}"#,
41 );
42}
43
44#[test]
45fn function_formatting1() {
46 assert_js!(
47 r#"
48pub fn this_function_really_does_have_a_ludicrously_unfeasibly_long_name_for_a_function(x, y) {
49x + y
50}"#,
51 );
52}
53
54#[test]
55fn function_formatting2() {
56 assert_js!(
57 r#"
58pub fn add(x, y) {
59x + y
60}
61
62pub fn long() {
63 add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, add(1, 1)))))))))))))))
64}"#,
65 );
66}
67
68#[test]
69fn function_formatting3() {
70 assert_js!(
71 r#"
72pub fn math(x, y) {
73 fn() {
74 x + y
75 x - y
76 2 * x
77 }
78}"#,
79 );
80}
81
82#[test]
83fn function_formatting_typescript() {
84 assert_ts_def!(
85 r#"
86pub fn add(the_first_variable_that_should_be_added, the_second_variable_that_should_be_added) {
87 the_first_variable_that_should_be_added + the_second_variable_that_should_be_added
88}"#,
89 );
90}
91
92#[test]
93fn function_formatting_typescript1() {
94 assert_ts_def!(
95 r#"
96pub fn this_function_really_does_have_a_ludicrously_unfeasibly_long_name_for_a_function(x, y) {
97x + y
98}"#,
99 );
100}
101
102#[test]
103fn tail_call() {
104 assert_js!(
105 r#"
106pub fn count(xs, n) {
107 case xs {
108 [] -> n
109 [_, ..xs] -> count(xs, n + 1)
110 }
111}
112"#,
113 );
114}
115
116#[test]
117fn tail_call_doesnt_clobber_tail_position_tracking() {
118 assert_js!(
119 r#"
120pub fn loop(indentation) {
121 case indentation > 0 {
122 True -> loop(indentation - 1)
123 False -> Nil
124 }
125}
126"#,
127 );
128}
129
130#[test]
131fn pipe_last() {
132 assert_js!(
133 r#"fn id(x) { x }
134pub fn main() {
135 1
136 |> id
137}
138"#,
139 );
140}
141
142#[test]
143fn calling_fn_literal() {
144 assert_js!(
145 r#"pub fn main() {
146 fn(x) { x }(1)
147}
148"#,
149 );
150}
151
152// Don't mistake calling a function with the same name as the current function
153// as tail recursion
154#[test]
155fn shadowing_current() {
156 assert_js!(
157 r#"pub fn main() {
158 let main = fn() { 0 }
159 main()
160}
161"#,
162 );
163}
164
165#[test]
166fn recursion_with_discards() {
167 assert_js!(
168 r#"pub fn main(f, _) {
169 f()
170 main(f, 1)
171}
172"#,
173 );
174}
175
176#[test]
177fn no_recur_in_anon_fn() {
178 assert_js!(
179 r#"pub fn main() {
180 fn() { main() }
181 1
182}
183"#,
184 );
185}
186
187#[test]
188fn case_in_call() {
189 assert_js!(
190 r#"pub fn main(f, x) {
191 f(case x {
192 1 -> 2
193 _ -> 0
194 })
195}
196"#,
197 );
198}
199
200#[test]
201fn reserved_word_fn() {
202 assert_js!(
203 r#"pub fn class() {
204 Nil
205}
206"#,
207 );
208}
209
210#[test]
211fn reserved_word_imported() {
212 assert_js!(
213 ("for", "pub fn class() { 1 }"),
214 r#"import for.{class}
215
216pub fn export() {
217 class()
218}
219"#,
220 );
221}
222
223#[test]
224fn reserved_word_imported_alias() {
225 assert_js!(
226 ("for", "pub fn class() { 1 }"),
227 r#"import for.{class as while} as function
228
229pub fn export() {
230 let delete = function.class
231 while()
232}
233"#,
234 );
235}
236
237#[test]
238fn reserved_word_const() {
239 assert_js!(
240 r#"const in = 1
241
242pub fn export() {
243 in
244}
245"#,
246 );
247}
248
249// https://github.com/gleam-lang/gleam/issues/1208
250#[test]
251fn reserved_word_argument() {
252 assert_js!(
253 r#"pub fn main(with) {
254 with
255}
256"#,
257 );
258}
259
260// https://github.com/gleam-lang/gleam/issues/1186
261#[test]
262fn multiple_discard() {
263 assert_js!(
264 r#"pub fn main(_, _, _) {
265 1
266}
267"#,
268 );
269}
270
271#[test]
272fn keyword_in_recursive_function() {
273 assert_js!(
274 r#"pub fn main(with: Int) -> Nil {
275 main(with - 1)
276}
277"#,
278 );
279}
280
281#[test]
282fn reserved_word_in_function_arguments() {
283 assert_js!(
284 r#"pub fn main(arguments, eval) {
285 #(arguments, eval)
286}
287"#,
288 );
289}
290
291#[test]
292fn let_last() {
293 assert_js!(
294 r#"pub fn main() {
295 let x = 1
296}
297"#,
298 );
299}
300
301#[test]
302fn assert_last() {
303 assert_js!(
304 r#"pub fn main() {
305 let assert x = 1
306}
307"#,
308 );
309}
310
311#[test]
312fn fn_return_fn_typescript() {
313 assert_ts_def!(
314 r#"pub fn main(f: fn(Int) -> Int) {
315 let func = fn(x, y) { f(x) + f(y) }
316 func
317}
318"#,
319 );
320}
321
322// https://github.com/gleam-lang/gleam/issues/1637
323#[test]
324fn variable_rewriting_in_anon_fn_with_matching_parameter() {
325 assert_js!(
326 r#"pub fn bad() {
327 fn(state) {
328 let state = state
329 state
330 }
331}
332"#,
333 );
334}
335
336// https://github.com/gleam-lang/gleam/issues/1637
337#[test]
338fn variable_rewriting_in_anon_fn_with_matching_parameter_in_case() {
339 assert_js!(
340 r#"pub fn bad() {
341 fn(state) {
342 let state = case Nil {
343 _ -> state
344 }
345 state
346 }
347}
348"#,
349 );
350}
351
352// https://github.com/gleam-lang/gleam/issues/1508
353#[test]
354fn pipe_variable_rebinding() {
355 assert_js!(
356 "
357pub fn main() {
358 let version = 1 |> version()
359 version
360}
361
362pub fn version(n) {
363 Ok(1)
364}"
365 )
366}
367
368#[test]
369fn pipe_shadow_import() {
370 assert_js!(
371 ("wibble", "pub fn println(x: String) { }"),
372 r#"
373 import wibble.{println}
374 pub fn main() {
375 let println =
376 "oh dear"
377 |> println
378 println
379 }"#
380 );
381}
382
383#[test]
384fn module_const_fn() {
385 assert_js!(
386 r#"
387pub fn int_identity(i: Int) -> Int { i }
388pub const int_identity_alias: fn(Int) -> Int = int_identity
389pub fn use_int_identity_alias() { int_identity_alias(42) }
390
391pub const compound: #(fn(Int) -> Int, fn(Int) -> Int) = #(int_identity, int_identity_alias)
392pub fn use_compound() { compound.0(compound.1(42)) }"#
393 );
394}
395
396#[test]
397fn module_const_fn1() {
398 assert_ts_def!(
399 r#"
400pub fn int_identity(i: Int) -> Int { i }
401pub const int_identity_alias: fn(Int) -> Int = int_identity
402pub const compound: #(fn(Int) -> Int, fn(Int) -> Int) =
403 #(int_identity, int_identity_alias)"#
404 )
405}
406
407// https://github.com/gleam-lang/gleam/issues/2399
408#[test]
409fn bad_comma() {
410 assert_js!(
411 r#"
412fn function_with_a_long_name_that_is_intended_to_sit_right_on_the_limit() {
413 Nil
414}
415
416fn identity(x) {
417 x
418}
419
420pub fn main() {
421 function_with_a_long_name_that_is_intended_to_sit_right_on_the_limit()
422 |> identity
423}
424"#
425 )
426}
427
428// https://github.com/gleam-lang/gleam/issues/2518
429#[test]
430fn function_literals_get_properly_wrapped_1() {
431 assert_js!(
432 r#"pub fn main() {
433 fn(n) { n + 1 }(10)
434}
435"#
436 );
437}
438
439// https://github.com/gleam-lang/gleam/issues/2518
440#[test]
441fn function_literals_get_properly_wrapped_2() {
442 assert_js!(
443 r#"pub fn main() {
444 { fn(n) { n + 1 } }(10)
445}
446"#
447 );
448}
449
450// https://github.com/gleam-lang/gleam/issues/2518
451#[test]
452fn function_literals_get_properly_wrapped_3() {
453 assert_js!(
454 r#"pub fn main() {
455 { let a = fn(n) { n + 1 } }(10)
456}
457"#
458 );
459}
460
461#[test]
462fn labelled_argument_ordering() {
463 // https://github.com/gleam-lang/gleam/issues/3671
464 assert_js!(
465 "
466type A { A }
467type B { B }
468type C { C }
469type D { D }
470
471fn wibble(a a: A, b b: B, c c: C, d d: D) {
472 Nil
473}
474
475pub fn main() {
476 wibble(A, C, D, b: B)
477 wibble(A, C, D, b: B)
478 wibble(B, C, D, a: A)
479 wibble(B, C, a: A, d: D)
480 wibble(B, C, d: D, a: A)
481 wibble(B, D, a: A, c: C)
482 wibble(B, D, c: C, a: A)
483 wibble(C, D, b: B, a: A)
484}
485"
486 );
487}
488
489// During the implementation of https://github.com/gleam-lang/gleam/pull/4337,
490// a bug was found where this code would compile incorrectly.
491#[test]
492fn two_pipes_in_a_row() {
493 assert_js!(
494 "
495pub type Function(a) {
496 Function(fn() -> a)
497}
498
499pub fn main() {
500 [fn() { 1 } |> Function, fn() { 2 } |> Function]
501}
502"
503 );
504}
505
506// https://github.com/gleam-lang/gleam/issues/4472
507#[test]
508fn pipe_into_block() {
509 assert_js!(
510 "
511fn side_effects(x) { x }
512
513pub fn main() {
514 1
515 |> side_effects
516 |> {
517 side_effects(2)
518 side_effects
519 }
520}
521"
522 );
523}
524
525// https://github.com/gleam-lang/gleam/issues/4472
526#[test]
527fn pipe_with_block_in_the_middle() {
528 assert_js!(
529 "
530fn side_effects(x) { x }
531
532pub fn main() {
533 1
534 |> side_effects
535 |> {
536 side_effects(2)
537 side_effects
538 }
539 |> side_effects
540}
541"
542 );
543}
544
545// https://github.com/gleam-lang/gleam/issues/4533
546#[test]
547fn immediately_invoked_function_expressions_include_statement_level() {
548 assert_js!(
549 "
550fn identity(x) { x }
551
552pub type Wibble {
553 Wibble(a: Int, b: Int)
554}
555
556pub fn main() {
557 let w = Wibble(1, 2)
558 identity(Wibble(..w |> identity, b: 4)) |> identity
559}
560"
561 );
562}
563
564#[test]
565fn public_function_gets_jsdoc() {
566 assert_js!(
567 "
568/// Hello! This is the documentation of the `main`
569/// function.
570///
571pub fn main() { 1 }
572"
573 );
574}
575
576#[test]
577fn internal_function_gets_ignored_jsdoc() {
578 assert_js!(
579 "
580/// Hello! This is the documentation of the `main`
581/// function, which is internal!
582///
583@internal
584pub fn main() { 1 }
585"
586 );
587}