goscript 0.0.45 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,568 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/types"
7
+
8
+ "github.com/pkg/errors"
9
+ )
10
+
11
+ // hasSliceConstraint checks if an interface constraint includes slice types
12
+ // For constraints like ~[]E, this returns true
13
+ func hasSliceConstraint(iface *types.Interface) bool {
14
+ // Check if the interface has type terms that include slice types
15
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
16
+ embedded := iface.EmbeddedType(i)
17
+ if union, ok := embedded.(*types.Union); ok {
18
+ for j := 0; j < union.Len(); j++ {
19
+ term := union.Term(j)
20
+ if _, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
21
+ return true
22
+ }
23
+ }
24
+ } else if _, isSlice := embedded.Underlying().(*types.Slice); isSlice {
25
+ return true
26
+ }
27
+ }
28
+ return false
29
+ }
30
+
31
+ // getSliceElementTypeFromConstraint extracts the element type from a slice constraint
32
+ // For constraints like ~[]E, this returns E
33
+ func getSliceElementTypeFromConstraint(iface *types.Interface) types.Type {
34
+ // Check if the interface has type terms that include slice types
35
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
36
+ embedded := iface.EmbeddedType(i)
37
+ if union, ok := embedded.(*types.Union); ok {
38
+ for j := 0; j < union.Len(); j++ {
39
+ term := union.Term(j)
40
+ if sliceType, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
41
+ return sliceType.Elem()
42
+ }
43
+ }
44
+ } else if sliceType, isSlice := embedded.Underlying().(*types.Slice); isSlice {
45
+ return sliceType.Elem()
46
+ }
47
+ }
48
+ return nil
49
+ }
50
+
51
+ // hasMixedStringByteConstraint checks if an interface constraint includes both string and []byte types
52
+ // For constraints like string | []byte, this returns true
53
+ // For pure slice constraints like ~[]E, this returns false
54
+ func hasMixedStringByteConstraint(iface *types.Interface) bool {
55
+ hasString := false
56
+ hasByteSlice := false
57
+
58
+ // Check if the interface has type terms that include both string and []byte
59
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
60
+ embedded := iface.EmbeddedType(i)
61
+ if union, ok := embedded.(*types.Union); ok {
62
+ for j := 0; j < union.Len(); j++ {
63
+ term := union.Term(j)
64
+ termType := term.Type().Underlying()
65
+
66
+ // Check for string type
67
+ if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
68
+ hasString = true
69
+ }
70
+
71
+ // Check for []byte type
72
+ if sliceType, isSlice := termType.(*types.Slice); isSlice {
73
+ if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
74
+ hasByteSlice = true
75
+ }
76
+ }
77
+ }
78
+ } else {
79
+ // Handle non-union embedded types
80
+ termType := embedded.Underlying()
81
+
82
+ // Check for string type
83
+ if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
84
+ hasString = true
85
+ }
86
+
87
+ // Check for []byte type
88
+ if sliceType, isSlice := termType.(*types.Slice); isSlice {
89
+ if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
90
+ hasByteSlice = true
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ // Return true only if we have both string and []byte in the constraint
97
+ return hasString && hasByteSlice
98
+ }
99
+
100
+ // getTypeHintForSliceElement returns the appropriate type hint for makeSlice based on the Go element type
101
+ func (c *GoToTSCompiler) getTypeHintForSliceElement(elemType types.Type) string {
102
+ if basicType, isBasic := elemType.(*types.Basic); isBasic {
103
+ switch basicType.Kind() {
104
+ case types.Int, types.Int8, types.Int16, types.Int32, types.Int64,
105
+ types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64,
106
+ types.Float32, types.Float64, types.Complex64, types.Complex128:
107
+ return "number"
108
+ case types.Bool:
109
+ return "boolean"
110
+ case types.String:
111
+ return "string"
112
+ }
113
+ }
114
+ // For other types (structs, interfaces, pointers, etc.), don't provide a hint
115
+ // This will use the default null initialization which is appropriate for object types
116
+ return ""
117
+ }
118
+
119
+ // WriteCallExprMake handles make() function calls and translates them to TypeScript.
120
+ // It handles channel, map, and slice creation with different type patterns including:
121
+ // - Channel creation with different directions
122
+ // - Map creation for various type patterns
123
+ // - Slice creation with special handling for []byte, generic types, named types, instantiated generics, and selector expressions
124
+ func (c *GoToTSCompiler) WriteCallExprMake(exp *ast.CallExpr) error {
125
+ // First check if we have a channel type
126
+ if typ := c.pkg.TypesInfo.TypeOf(exp.Args[0]); typ != nil {
127
+ if chanType, ok := typ.Underlying().(*types.Chan); ok {
128
+ // Handle channel creation: make(chan T, bufferSize) or make(chan T)
129
+ c.tsw.WriteLiterally("$.makeChannel<")
130
+ c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
131
+ c.tsw.WriteLiterally(">(")
132
+
133
+ // If buffer size is provided, add it
134
+ if len(exp.Args) >= 2 {
135
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil {
136
+ return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
137
+ }
138
+ } else {
139
+ // Default to 0 (unbuffered channel)
140
+ c.tsw.WriteLiterally("0")
141
+ }
142
+
143
+ c.tsw.WriteLiterally(", ") // Add comma for zero value argument
144
+
145
+ // Write the zero value for the channel's element type
146
+ if chanType.Elem().String() == "struct{}" {
147
+ c.tsw.WriteLiterally("{}")
148
+ } else {
149
+ c.WriteZeroValueForType(chanType.Elem())
150
+ }
151
+
152
+ // Add direction parameter
153
+ c.tsw.WriteLiterally(", ")
154
+
155
+ // Determine channel direction
156
+ switch chanType.Dir() {
157
+ case types.SendRecv:
158
+ c.tsw.WriteLiterally("'both'")
159
+ case types.SendOnly:
160
+ c.tsw.WriteLiterally("'send'")
161
+ case types.RecvOnly:
162
+ c.tsw.WriteLiterally("'receive'")
163
+ default:
164
+ c.tsw.WriteLiterally("'both'") // Default to bidirectional
165
+ }
166
+
167
+ c.tsw.WriteLiterally(")")
168
+ return nil // Handled make for channel
169
+ }
170
+ }
171
+ // Handle make for slices: make([]T, len, cap) or make([]T, len)
172
+ if len(exp.Args) >= 1 {
173
+ // Handle map creation: make(map[K]V)
174
+ if mapType, ok := exp.Args[0].(*ast.MapType); ok {
175
+ c.tsw.WriteLiterally("$.makeMap<")
176
+ c.WriteTypeExpr(mapType.Key) // Write the key type
177
+ c.tsw.WriteLiterally(", ")
178
+ c.WriteTypeExpr(mapType.Value) // Write the value type
179
+ c.tsw.WriteLiterally(">()")
180
+ return nil // Handled make for map
181
+ }
182
+
183
+ // Handle slice creation
184
+ if _, ok := exp.Args[0].(*ast.ArrayType); ok {
185
+ // Get the slice type information
186
+ sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
187
+ if sliceType == nil {
188
+ return errors.New("could not get type information for slice in make call")
189
+ }
190
+ goUnderlyingType, ok := sliceType.Underlying().(*types.Slice)
191
+ if !ok {
192
+ return errors.New("expected slice type for make call")
193
+ }
194
+ goElemType := goUnderlyingType.Elem()
195
+
196
+ // Check if it's make([]byte, ...)
197
+ if c.isByteSliceType(sliceType) {
198
+ var lengthArg, capacityArg interface{}
199
+ if len(exp.Args) >= 2 {
200
+ lengthArg = exp.Args[1]
201
+ }
202
+ if len(exp.Args) == 3 {
203
+ capacityArg = exp.Args[2]
204
+ }
205
+ return c.writeByteSliceCreation(lengthArg, capacityArg)
206
+ }
207
+
208
+ // Check if the element type is a generic type parameter
209
+ if _, isTypeParam := goElemType.(*types.TypeParam); isTypeParam {
210
+ // This is make([]E, n) where E is a type parameter
211
+ c.tsw.WriteLiterally("$.makeSlice<")
212
+ c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type parameter
213
+ c.tsw.WriteLiterally(">(")
214
+
215
+ if len(exp.Args) >= 2 {
216
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
217
+ return err
218
+ }
219
+ if len(exp.Args) == 3 {
220
+ c.tsw.WriteLiterally(", ")
221
+ if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
222
+ return err
223
+ }
224
+ } else if len(exp.Args) > 3 {
225
+ return errors.New("makeSlice expects 2 or 3 arguments")
226
+ }
227
+ } else {
228
+ // If no length is provided, default to 0
229
+ c.tsw.WriteLiterally("0")
230
+ }
231
+ c.tsw.WriteLiterally(")")
232
+ return nil // Handled make for []E where E is type parameter
233
+ }
234
+
235
+ var lengthArg, capacityArg interface{}
236
+ if len(exp.Args) >= 2 {
237
+ lengthArg = exp.Args[1]
238
+ }
239
+ if len(exp.Args) == 3 {
240
+ capacityArg = exp.Args[2]
241
+ } else if len(exp.Args) > 3 {
242
+ return errors.New("makeSlice expects 2 or 3 arguments")
243
+ }
244
+
245
+ return c.writeGenericSliceCreation(goElemType, lengthArg, capacityArg)
246
+ }
247
+
248
+ // Handle generic type parameter make calls: make(S, len, cap) where S ~[]E
249
+ if ident, ok := exp.Args[0].(*ast.Ident); ok {
250
+ // Check if this identifier refers to a type parameter
251
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
252
+ if typeName, isTypeName := obj.(*types.TypeName); isTypeName {
253
+ if typeParam, isTypeParam := typeName.Type().(*types.TypeParam); isTypeParam {
254
+ // Check if the type parameter is constrained to slice types
255
+ constraint := typeParam.Constraint()
256
+ if constraint != nil {
257
+ underlying := constraint.Underlying()
258
+ if iface, isInterface := underlying.(*types.Interface); isInterface {
259
+ // Check if the constraint includes slice types
260
+ // For constraints like ~[]E, we need to look at the type terms
261
+ if hasSliceConstraint(iface) {
262
+ // This is a generic slice type parameter
263
+ // We need to determine the element type from the constraint
264
+ elemType := getSliceElementTypeFromConstraint(iface)
265
+ if elemType != nil {
266
+ // Check if it's make(S, ...) where S constrains to []byte
267
+ if c.isByteSliceType(types.NewSlice(elemType)) {
268
+ var lengthArg, capacityArg interface{}
269
+ if len(exp.Args) >= 2 {
270
+ lengthArg = exp.Args[1]
271
+ }
272
+ if len(exp.Args) == 3 {
273
+ capacityArg = exp.Args[2]
274
+ }
275
+ return c.writeByteSliceCreation(lengthArg, capacityArg)
276
+ }
277
+
278
+ var lengthArg, capacityArg interface{}
279
+ if len(exp.Args) >= 2 {
280
+ lengthArg = exp.Args[1]
281
+ }
282
+ if len(exp.Args) == 3 {
283
+ capacityArg = exp.Args[2]
284
+ } else if len(exp.Args) > 3 {
285
+ return errors.New("makeSlice expects 2 or 3 arguments")
286
+ }
287
+ return c.writeGenericSliceCreation(elemType, lengthArg, capacityArg)
288
+ }
289
+ }
290
+ }
291
+ }
292
+ } else {
293
+ // Handle named types with slice underlying types: make(NamedSliceType, len, cap)
294
+ // This handles cases like: type appendSliceWriter []byte; make(appendSliceWriter, 0, len(s))
295
+ namedType := typeName.Type()
296
+ if sliceType, isSlice := namedType.Underlying().(*types.Slice); isSlice {
297
+ goElemType := sliceType.Elem()
298
+
299
+ // Check if it's a named type with []byte underlying type
300
+ if c.isByteSliceType(sliceType) {
301
+ var lengthArg, capacityArg interface{}
302
+ if len(exp.Args) >= 2 {
303
+ lengthArg = exp.Args[1]
304
+ }
305
+ if len(exp.Args) == 3 {
306
+ capacityArg = exp.Args[2]
307
+ }
308
+ return c.writeByteSliceCreation(lengthArg, capacityArg)
309
+ }
310
+
311
+ // Handle other named slice types
312
+ var lengthArg, capacityArg interface{}
313
+ if len(exp.Args) >= 2 {
314
+ lengthArg = exp.Args[1]
315
+ }
316
+ if len(exp.Args) == 3 {
317
+ capacityArg = exp.Args[2]
318
+ } else if len(exp.Args) > 3 {
319
+ return errors.New("makeSlice expects 2 or 3 arguments")
320
+ }
321
+ return c.writeGenericSliceCreation(goElemType, lengthArg, capacityArg)
322
+ }
323
+
324
+ // Handle named types with map underlying types: make(NamedMapType)
325
+ if mapType, isMap := namedType.Underlying().(*types.Map); isMap {
326
+ c.tsw.WriteLiterally("$.makeMap<")
327
+ c.WriteGoType(mapType.Key(), GoTypeContextGeneral) // Write the key type
328
+ c.tsw.WriteLiterally(", ")
329
+ c.WriteGoType(mapType.Elem(), GoTypeContextGeneral) // Write the value type
330
+ c.tsw.WriteLiterally(">()")
331
+ return nil // Handled make for named map type
332
+ }
333
+
334
+ // Handle named types with channel underlying types: make(NamedChannelType, bufferSize)
335
+ if chanType, isChan := namedType.Underlying().(*types.Chan); isChan {
336
+ c.tsw.WriteLiterally("$.makeChannel<")
337
+ c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
338
+ c.tsw.WriteLiterally(">(")
339
+
340
+ // If buffer size is provided, add it
341
+ if len(exp.Args) >= 2 {
342
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil {
343
+ return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
344
+ }
345
+ } else {
346
+ // Default to 0 (unbuffered channel)
347
+ c.tsw.WriteLiterally("0")
348
+ }
349
+
350
+ c.tsw.WriteLiterally(", ") // Add comma for zero value argument
351
+
352
+ // Write the zero value for the channel's element type
353
+ if chanType.Elem().String() == "struct{}" {
354
+ c.tsw.WriteLiterally("{}")
355
+ } else {
356
+ c.WriteZeroValueForType(chanType.Elem())
357
+ }
358
+
359
+ // Add direction parameter
360
+ c.tsw.WriteLiterally(", ")
361
+
362
+ // Determine channel direction
363
+ switch chanType.Dir() {
364
+ case types.SendRecv:
365
+ c.tsw.WriteLiterally("'both'")
366
+ case types.SendOnly:
367
+ c.tsw.WriteLiterally("'send'")
368
+ case types.RecvOnly:
369
+ c.tsw.WriteLiterally("'receive'")
370
+ default:
371
+ c.tsw.WriteLiterally("'both'") // Default to bidirectional
372
+ }
373
+
374
+ c.tsw.WriteLiterally(")")
375
+ return nil // Handled make for named channel type
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ // Handle instantiated generic types: make(GenericType[TypeArg], ...)
383
+ // This handles cases like: make(Ints[int64]) where Ints[T] is a generic type
384
+ if indexExpr, ok := exp.Args[0].(*ast.IndexExpr); ok {
385
+ // Get the type information for the instantiated generic type
386
+ if typ := c.pkg.TypesInfo.TypeOf(indexExpr); typ != nil {
387
+ // Check the underlying type of the instantiated generic type
388
+ underlying := typ.Underlying()
389
+
390
+ // Handle instantiated generic map types: make(GenericMap[K, V])
391
+ if mapType, isMap := underlying.(*types.Map); isMap {
392
+ c.tsw.WriteLiterally("$.makeMap<")
393
+ c.WriteGoType(mapType.Key(), GoTypeContextGeneral) // Write the key type
394
+ c.tsw.WriteLiterally(", ")
395
+ c.WriteGoType(mapType.Elem(), GoTypeContextGeneral) // Write the value type
396
+ c.tsw.WriteLiterally(">()")
397
+ return nil // Handled make for instantiated generic map type
398
+ }
399
+
400
+ // Handle instantiated generic slice types: make(GenericSlice[T], len, cap)
401
+ if sliceType, isSlice := underlying.(*types.Slice); isSlice {
402
+ goElemType := sliceType.Elem()
403
+
404
+ // Check if it's an instantiated generic type with []byte underlying type
405
+ if c.isByteSliceType(types.NewSlice(goElemType)) {
406
+ var lengthArg, capacityArg interface{}
407
+ if len(exp.Args) >= 2 {
408
+ lengthArg = exp.Args[1]
409
+ }
410
+ if len(exp.Args) == 3 {
411
+ capacityArg = exp.Args[2]
412
+ }
413
+ return c.writeByteSliceCreation(lengthArg, capacityArg)
414
+ }
415
+
416
+ // Handle other instantiated generic slice types
417
+ var lengthArg, capacityArg interface{}
418
+ if len(exp.Args) >= 2 {
419
+ lengthArg = exp.Args[1]
420
+ }
421
+ if len(exp.Args) == 3 {
422
+ capacityArg = exp.Args[2]
423
+ } else if len(exp.Args) > 3 {
424
+ return errors.New("makeSlice expects 2 or 3 arguments")
425
+ }
426
+ return c.writeGenericSliceCreation(goElemType, lengthArg, capacityArg)
427
+ }
428
+
429
+ // Handle instantiated generic channel types: make(GenericChannel[T], bufferSize)
430
+ if chanType, isChan := underlying.(*types.Chan); isChan {
431
+ c.tsw.WriteLiterally("$.makeChannel<")
432
+ c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
433
+ c.tsw.WriteLiterally(">(")
434
+
435
+ // If buffer size is provided, add it
436
+ if len(exp.Args) >= 2 {
437
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil {
438
+ return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
439
+ }
440
+ } else {
441
+ // Default to 0 (unbuffered channel)
442
+ c.tsw.WriteLiterally("0")
443
+ }
444
+
445
+ c.tsw.WriteLiterally(", ") // Add comma for zero value argument
446
+
447
+ // Write the zero value for the channel's element type
448
+ if chanType.Elem().String() == "struct{}" {
449
+ c.tsw.WriteLiterally("{}")
450
+ } else {
451
+ c.WriteZeroValueForType(chanType.Elem())
452
+ }
453
+
454
+ // Add direction parameter
455
+ c.tsw.WriteLiterally(", ")
456
+
457
+ // Determine channel direction
458
+ switch chanType.Dir() {
459
+ case types.SendRecv:
460
+ c.tsw.WriteLiterally("'both'")
461
+ case types.SendOnly:
462
+ c.tsw.WriteLiterally("'send'")
463
+ case types.RecvOnly:
464
+ c.tsw.WriteLiterally("'receive'")
465
+ default:
466
+ c.tsw.WriteLiterally("'both'") // Default to bidirectional
467
+ }
468
+
469
+ c.tsw.WriteLiterally(")")
470
+ return nil // Handled make for instantiated generic channel type
471
+ }
472
+ }
473
+ }
474
+ // Handle selector expressions: make(pkg.TypeName, ...)
475
+ // This handles cases like: make(fstest.MapFS) where fstest.MapFS is map[string]*MapFile
476
+ if selectorExpr, ok := exp.Args[0].(*ast.SelectorExpr); ok {
477
+ // Get the type information for the selector expression
478
+ if typ := c.pkg.TypesInfo.TypeOf(selectorExpr); typ != nil {
479
+ // Check the underlying type of the selector expression
480
+ underlying := typ.Underlying()
481
+
482
+ // Handle selector expression map types: make(pkg.MapType)
483
+ if mapType, isMap := underlying.(*types.Map); isMap {
484
+ c.tsw.WriteLiterally("$.makeMap<")
485
+ c.WriteGoType(mapType.Key(), GoTypeContextGeneral) // Write the key type
486
+ c.tsw.WriteLiterally(", ")
487
+ c.WriteGoType(mapType.Elem(), GoTypeContextGeneral) // Write the value type
488
+ c.tsw.WriteLiterally(">()")
489
+ return nil // Handled make for selector expression map type
490
+ }
491
+
492
+ // Handle selector expression slice types: make(pkg.SliceType, len, cap)
493
+ if sliceType, isSlice := underlying.(*types.Slice); isSlice {
494
+ goElemType := sliceType.Elem()
495
+
496
+ // Check if it's a selector expression with []byte underlying type
497
+ if c.isByteSliceType(sliceType) {
498
+ var lengthArg, capacityArg interface{}
499
+ if len(exp.Args) >= 2 {
500
+ lengthArg = exp.Args[1]
501
+ }
502
+ if len(exp.Args) == 3 {
503
+ capacityArg = exp.Args[2]
504
+ }
505
+ return c.writeByteSliceCreation(lengthArg, capacityArg)
506
+ }
507
+
508
+ // Handle other selector expression slice types
509
+ var lengthArg, capacityArg interface{}
510
+ if len(exp.Args) >= 2 {
511
+ lengthArg = exp.Args[1]
512
+ }
513
+ if len(exp.Args) == 3 {
514
+ capacityArg = exp.Args[2]
515
+ } else if len(exp.Args) > 3 {
516
+ return errors.New("makeSlice expects 2 or 3 arguments")
517
+ }
518
+ return c.writeGenericSliceCreation(goElemType, lengthArg, capacityArg)
519
+ }
520
+
521
+ // Handle selector expression channel types: make(pkg.ChannelType, bufferSize)
522
+ if chanType, isChan := underlying.(*types.Chan); isChan {
523
+ c.tsw.WriteLiterally("$.makeChannel<")
524
+ c.WriteGoType(chanType.Elem(), GoTypeContextGeneral)
525
+ c.tsw.WriteLiterally(">(")
526
+
527
+ // If buffer size is provided, add it
528
+ if len(exp.Args) >= 2 {
529
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil {
530
+ return fmt.Errorf("failed to write buffer size in makeChannel: %w", err)
531
+ }
532
+ } else {
533
+ // Default to 0 (unbuffered channel)
534
+ c.tsw.WriteLiterally("0")
535
+ }
536
+
537
+ c.tsw.WriteLiterally(", ") // Add comma for zero value argument
538
+
539
+ // Write the zero value for the channel's element type
540
+ if chanType.Elem().String() == "struct{}" {
541
+ c.tsw.WriteLiterally("{}")
542
+ } else {
543
+ c.WriteZeroValueForType(chanType.Elem())
544
+ }
545
+
546
+ // Add direction parameter
547
+ c.tsw.WriteLiterally(", ")
548
+
549
+ // Determine channel direction
550
+ switch chanType.Dir() {
551
+ case types.SendRecv:
552
+ c.tsw.WriteLiterally("'both'")
553
+ case types.SendOnly:
554
+ c.tsw.WriteLiterally("'send'")
555
+ case types.RecvOnly:
556
+ c.tsw.WriteLiterally("'receive'")
557
+ default:
558
+ c.tsw.WriteLiterally("'both'") // Default to bidirectional
559
+ }
560
+
561
+ c.tsw.WriteLiterally(")")
562
+ return nil // Handled make for selector expression channel type
563
+ }
564
+ }
565
+ }
566
+ // Fallthrough for unhandled make calls (e.g., channels)
567
+ return errors.New("unhandled make call")
568
+ }