this repo has no description
1use crate::assert_js;
2
3const BOOL_MODULE: &str = "
4pub fn guard(
5 when condition: Bool,
6 return value: a,
7 otherwise callback: fn() -> a,
8) -> a {
9 case condition {
10 True -> value
11 False -> callback()
12 }
13}
14
15pub fn lazy_guard(
16 when condition: Bool,
17 return consequence: fn() -> a,
18 otherwise alternative: fn() -> a,
19) -> a {
20 case condition {
21 True -> consequence()
22 False -> alternative()
23 }
24}
25";
26
27const RESULT_MODULE: &str = "
28pub fn try(result: Result(a, e), apply f: fn(a) -> Result(b, e)) -> Result(b, e) {
29 case result {
30 Ok(value) -> f(value)
31 Error(error) -> Error(error)
32 }
33}
34
35pub fn map(over result: Result(a, e), with f: fn(a) -> b) -> Result(b, e) {
36 case result {
37 Ok(value) -> Ok(f(value))
38 Error(error) -> Error(error)
39 }
40}
41";
42
43#[test]
44fn inline_higher_order_function() {
45 assert_js!(
46 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
47 "
48import gleam/result
49
50pub fn main() {
51 result.map(over: Ok(10), with: double)
52}
53
54fn double(x) { x + x }
55"
56 );
57}
58
59#[test]
60fn inline_higher_order_function_with_capture() {
61 assert_js!(
62 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
63 "
64import gleam/result
65
66pub fn main() {
67 result.try(Ok(10), divide(_, 2))
68}
69
70fn divide(a: Int, b: Int) -> Result(Int, Nil) {
71 case a % b {
72 0 -> Ok(a / b)
73 _ -> Error(Nil)
74 }
75}
76"
77 );
78}
79
80#[test]
81fn inline_higher_order_function_anonymous() {
82 assert_js!(
83 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
84 "
85import gleam/result
86
87pub fn main() {
88 result.try(Ok(10), fn(value) {
89 Ok({ value + 2 } * 4)
90 })
91}
92"
93 );
94}
95
96#[test]
97fn inline_function_which_calls_other_function() {
98 // This function calls `result.try`, meaning this must be inlined twice to
99 // achieve the desired result.
100 assert_js!(
101 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
102 (
103 "gleam_stdlib",
104 "testing",
105 "
106import gleam/result.{try}
107
108pub fn always_inline(result, f) -> Result(b, e) {
109 try(result, f)
110}
111"
112 ),
113 "
114import testing
115
116pub fn main() {
117 testing.always_inline(Ok(10), Error)
118}
119"
120 );
121}
122
123#[test]
124fn inline_function_with_use() {
125 assert_js!(
126 ("gleam_stdlib", "gleam/bool", BOOL_MODULE),
127 "
128import gleam/bool
129
130pub fn divide(a, b) {
131 use <- bool.guard(when: b == 0, return: 0)
132 a / b
133}
134"
135 );
136}
137
138#[test]
139fn inline_function_with_use_and_anonymous() {
140 assert_js!(
141 ("gleam_stdlib", "gleam/bool", BOOL_MODULE),
142 r#"
143import gleam/bool
144
145pub fn divide(a, b) {
146 use <- bool.lazy_guard(b == 0, fn() { panic as "Cannot divide by 0" })
147 a / b
148}
149"#
150 );
151}
152
153#[test]
154fn inline_function_with_use_becomes_tail_recursive() {
155 assert_js!(
156 ("gleam_stdlib", "gleam/bool", BOOL_MODULE),
157 "
158import gleam/bool
159
160pub fn count(from: Int, to: Int) -> Int {
161 use <- bool.guard(when: from >= to, return: from)
162 echo from
163 count(from + 1, to)
164}
165"
166 );
167}
168
169#[test]
170fn do_not_inline_parameters_used_more_than_once() {
171 // Since the `something` parameter is used more than once in the body of the
172 // function, it should not be inlined, and should be assigned once at the
173 // beginning of the function.
174 assert_js!(
175 (
176 "gleam_stdlib",
177 "testing",
178 "
179pub fn always_inline(something) {
180 case something {
181 True -> something
182 False -> False
183 }
184}
185"
186 ),
187 "
188import testing
189
190pub fn main() {
191 testing.always_inline(True)
192}
193"
194 );
195}
196
197#[test]
198fn do_not_inline_parameters_that_have_side_effects() {
199 assert_js!(
200 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
201 r#"
202import gleam/result
203
204pub fn main() {
205 result.map(Ok(10), do_side_effects())
206}
207
208fn do_side_effects() {
209 let function = fn(x) { x + 1 }
210 panic as "Side effects"
211 function
212}
213"#
214 );
215}
216
217#[test]
218fn inline_anonymous_function_call() {
219 assert_js!(
220 "
221pub fn main() {
222 fn(a, b) { #(a, b) }(42, False)
223}
224"
225 );
226}
227
228#[test]
229fn inline_anonymous_function_in_pipe() {
230 assert_js!(
231 "
232pub fn main() {
233 1 |> fn(x) { x + 1 } |> fn(y) { y * y }
234}
235"
236 );
237}
238
239#[test]
240fn inline_function_capture_in_pipe() {
241 // The function capture is desugared to an anonymous function, so it should
242 // be turned into a direct call to `add`
243 assert_js!(
244 "
245pub fn main() {
246 1 |> add(4, _)
247}
248
249fn add(a, b) { a + b }
250"
251 );
252}
253
254#[test]
255fn inlining_works_through_blocks() {
256 assert_js!(
257 "
258pub fn main() {
259 { fn(x) { Ok(x + 1) } }(41)
260}
261"
262 );
263}
264
265#[test]
266fn blocks_get_preserved_when_needed() {
267 assert_js!(
268 "
269pub fn main() {
270 { 4 |> make_adder }(6)
271}
272
273fn make_adder(a) {
274 fn(b) { a + b }
275}
276"
277 );
278}
279
280#[test]
281fn blocks_get_preserved_when_needed2() {
282 assert_js!(
283 "
284pub fn main() {
285 fn(x) { 1 + x }(2) * 3
286}
287"
288 );
289}
290
291#[test]
292fn parameters_from_nested_functions_are_correctly_inlined() {
293 assert_js!(
294 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
295 "
296import gleam/result
297
298pub fn halve_all(a, b, c) {
299 use x <- result.try(divide(a, 2))
300 use y <- result.try(divide(b, 2))
301 use z <- result.map(divide(c, 2))
302
303 #(x, y, z)
304}
305
306fn divide(a, b) {
307 case a % b {
308 0 -> Ok(a / b)
309 _ -> Error(Nil)
310 }
311}
312"
313 );
314}
315
316// https://github.com/gleam-lang/gleam/issues/4852
317#[test]
318fn inlining_works_properly_with_record_updates() {
319 assert_js!(
320 ("gleam_stdlib", "gleam/result", RESULT_MODULE),
321 "
322import gleam/result
323
324pub type Wibble {
325 Wibble(a: Int, b: Int)
326}
327
328pub fn main() {
329 let w = Wibble(1, 2)
330 use b <- result.map(Ok(3))
331 Wibble(..w, b:)
332}
333"
334 );
335}
336
337// https://github.com/gleam-lang/gleam/issues/4877
338#[test]
339fn inline_shadowed_variable() {
340 assert_js!(
341 "
342pub fn main() {
343 let a = 10
344 let b = 20
345
346 fn(x) {
347 let a = 7
348 x + a
349 }(a + b)
350
351 a
352}
353"
354 );
355}
356
357#[test]
358fn inline_variable_shadowing_parameter() {
359 assert_js!(
360 "
361pub fn sum(a, b) {
362 fn(x) {
363 let a = 7
364 x + a
365 }(a + b)
366
367 a
368}
369"
370 );
371}
372
373#[test]
374fn inline_shadowed_variable_nested() {
375 assert_js!(
376 "
377pub fn sum(a, b) {
378 fn(x) {
379 let a = 7
380 fn(y) {
381 let a = 10
382 y - a
383 }(x + a)
384
385 a
386 }(a + b)
387
388 a
389}
390"
391 );
392}
393
394#[test]
395fn inline_variable_shadowed_in_case_pattern() {
396 assert_js!(
397 "
398pub fn sum() {
399 let a = 10
400 let b = 20
401
402 fn(x) {
403 case 7, 8 {
404 a, b -> a + b + x
405 }
406 }(a + b)
407
408 a + b
409}
410"
411 );
412}
413
414#[test]
415fn inline_variable_shadowing_case_pattern() {
416 assert_js!(
417 "
418pub fn sum() {
419 case 1, 2 {
420 a, b -> fn(x) {
421 let a = 7
422 x + a
423 }(a + b)
424 }
425}
426"
427 );
428}