goscript 0.0.22 → 0.0.23

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.
package/compiler/stmt.go CHANGED
@@ -33,7 +33,6 @@ import (
33
33
  func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
34
34
  switch exp := a.(type) {
35
35
  case *ast.BlockStmt:
36
- // WriteStmtBlock does not currently return an error, assuming it's safe for now.
37
36
  if err := c.WriteStmtBlock(exp, false); err != nil {
38
37
  return fmt.Errorf("failed to write block statement: %w", err)
39
38
  }
@@ -73,7 +72,6 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
73
72
  return fmt.Errorf("failed to write declaration statement: %w", err)
74
73
  }
75
74
  case *ast.ForStmt:
76
- // WriteStmtFor does not currently return an error, assuming it's safe for now.
77
75
  if err := c.WriteStmtFor(exp); err != nil {
78
76
  return fmt.Errorf("failed to write for statement: %w", err)
79
77
  }
@@ -83,7 +81,6 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
83
81
  return fmt.Errorf("failed to write range statement: %w", err)
84
82
  }
85
83
  case *ast.SwitchStmt:
86
- // WriteStmtSwitch does not currently return an error, assuming it's safe for now.
87
84
  if err := c.WriteStmtSwitch(exp); err != nil {
88
85
  return fmt.Errorf("failed to write switch statement: %w", err)
89
86
  }
@@ -108,8 +105,12 @@ func (c *GoToTSCompiler) WriteStmt(a ast.Stmt) error {
108
105
  if err := c.WriteStmtBranch(exp); err != nil {
109
106
  return fmt.Errorf("failed to write branch statement: %w", err)
110
107
  }
108
+ case *ast.TypeSwitchStmt:
109
+ if err := c.WriteStmtTypeSwitch(exp); err != nil {
110
+ return fmt.Errorf("failed to write type switch statement: %w", err)
111
+ }
111
112
  default:
112
- return errors.Errorf("unknown statement: %s\n", a)
113
+ return errors.Errorf("unknown statement: %#v\n", a)
113
114
  }
114
115
  return nil
115
116
  }
@@ -172,17 +173,17 @@ func (c *GoToTSCompiler) WriteStmtBranch(stmt *ast.BranchStmt) error {
172
173
  }
173
174
 
174
175
  // WriteStmtGo translates a Go statement (`ast.GoStmt`) into its TypeScript equivalent.
175
- // It handles `go func(){...}()` and `go namedFunc(args)`.
176
+ // It handles `go func(){...}()`, `go namedFunc(args)`, and `go x.Method(args)`.
176
177
  func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
177
178
  // Handle goroutine statement
178
179
  // Translate 'go func() { ... }()' to 'queueMicrotask(() => { ... compiled body ... })'
179
-
180
- // The call expression's function is the function literal
181
180
  callExpr := exp.Call
182
- if funcLit, ok := callExpr.Fun.(*ast.FuncLit); ok {
181
+
182
+ switch fun := callExpr.Fun.(type) {
183
+ case *ast.FuncLit:
183
184
  // For function literals, we need to check if the function literal itself is async
184
185
  // This happens during analysis in analysisVisitor.Visit for FuncLit nodes
185
- isAsync := c.analysis.IsFuncLitAsync(funcLit)
186
+ isAsync := c.analysis.IsFuncLitAsync(fun)
186
187
  if isAsync {
187
188
  c.tsw.WriteLiterally("queueMicrotask(async () => ")
188
189
  } else {
@@ -190,18 +191,18 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
190
191
  }
191
192
 
192
193
  // Compile the function literal's body directly
193
- if err := c.WriteStmtBlock(funcLit.Body, true); err != nil {
194
+ if err := c.WriteStmtBlock(fun.Body, true); err != nil {
194
195
  return fmt.Errorf("failed to write goroutine function literal body: %w", err)
195
196
  }
196
197
 
197
198
  c.tsw.WriteLine(")") // Close the queueMicrotask statement
198
199
 
199
- } else if ident, ok := callExpr.Fun.(*ast.Ident); ok {
200
+ case *ast.Ident:
200
201
  // Handle named functions: go namedFunc(args)
201
202
  // Get the object for this function
202
- obj := c.pkg.TypesInfo.Uses[ident]
203
+ obj := c.pkg.TypesInfo.Uses[fun]
203
204
  if obj == nil {
204
- return errors.Errorf("could not find object for function: %s", ident.Name)
205
+ return errors.Errorf("could not find object for function: %s", fun.Name)
205
206
  }
206
207
 
207
208
  // Check if the function is async
@@ -221,7 +222,7 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
221
222
  }
222
223
 
223
224
  // Write the function name
224
- c.tsw.WriteLiterally(ident.Name)
225
+ c.tsw.WriteLiterally(fun.Name)
225
226
 
226
227
  // Write the function arguments
227
228
  c.tsw.WriteLiterally("(")
@@ -238,7 +239,62 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
238
239
 
239
240
  c.tsw.Indent(-1)
240
241
  c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
241
- } else {
242
+ case *ast.SelectorExpr:
243
+ // Handle selector expressions: go x.Method(args)
244
+ // Get the object for the selected method
245
+ obj := c.pkg.TypesInfo.Uses[fun.Sel]
246
+ if obj == nil {
247
+ return errors.Errorf("could not find object for selected method: %s", fun.Sel.Name)
248
+ }
249
+
250
+ // Check if the function is async
251
+ isAsync := c.analysis.IsAsyncFunc(obj)
252
+ if isAsync {
253
+ c.tsw.WriteLiterally("queueMicrotask(async () => {")
254
+ } else {
255
+ c.tsw.WriteLiterally("queueMicrotask(() => {")
256
+ }
257
+
258
+ c.tsw.Indent(1)
259
+ c.tsw.WriteLine("")
260
+
261
+ // Write the function call, using await if the function is async
262
+ if isAsync {
263
+ c.tsw.WriteLiterally("await ")
264
+ }
265
+
266
+ // Write the selector expression (e.g., f.Bar)
267
+ // Note: callExpr.Fun is the *ast.SelectorExpr itself
268
+ // For method calls, we need to add null assertion since Go would panic on nil receiver
269
+ if selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
270
+ if err := c.WriteValueExpr(selectorExpr.X); err != nil {
271
+ return fmt.Errorf("failed to write selector base expression in goroutine: %w", err)
272
+ }
273
+ // Add null assertion for method calls - Go would panic if receiver is nil
274
+ c.tsw.WriteLiterally("!.")
275
+ c.WriteIdent(selectorExpr.Sel, true)
276
+ } else {
277
+ if err := c.WriteValueExpr(callExpr.Fun); err != nil {
278
+ return fmt.Errorf("failed to write selector expression in goroutine: %w", err)
279
+ }
280
+ }
281
+
282
+ // Write the function arguments
283
+ c.tsw.WriteLiterally("(")
284
+ for i, arg := range callExpr.Args {
285
+ if i != 0 {
286
+ c.tsw.WriteLiterally(", ")
287
+ }
288
+ if err := c.WriteValueExpr(arg); err != nil {
289
+ return fmt.Errorf("failed to write argument %d in goroutine selector function call: %w", i, err)
290
+ }
291
+ }
292
+ c.tsw.WriteLiterally(")")
293
+ c.tsw.WriteLine("")
294
+
295
+ c.tsw.Indent(-1)
296
+ c.tsw.WriteLine("})") // Close the queueMicrotask callback and the statement
297
+ default:
242
298
  return errors.Errorf("unhandled goroutine function type: %T", callExpr.Fun)
243
299
  }
244
300
  return nil
@@ -258,12 +314,12 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
258
314
  func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
259
315
  // Handle simple channel receive used as a statement (<-ch)
260
316
  if unaryExpr, ok := exp.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
261
- // Translate <-ch to await ch.receive()
262
- c.tsw.WriteLiterally("await ")
317
+ // Translate <-ch to await $.chanRecv(ch)
318
+ c.tsw.WriteLiterally("await $.chanRecv(")
263
319
  if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
264
320
  return fmt.Errorf("failed to write channel expression in receive statement: %w", err)
265
321
  }
266
- c.tsw.WriteLiterally(".receive()") // Use receive() as the value is discarded
322
+ c.tsw.WriteLiterally(")") // Use chanRecv() as the value is discarded
267
323
  c.tsw.WriteLine("")
268
324
  return nil
269
325
  }
@@ -315,12 +371,12 @@ func (c *GoToTSCompiler) WriteStmtExpr(exp *ast.ExprStmt) error {
315
371
  // channel send operations are asynchronous in the TypeScript model.
316
372
  // The statement is terminated with a newline.
317
373
  func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
318
- // Translate ch <- value to await ch.send(value)
319
- c.tsw.WriteLiterally("await ")
374
+ // Translate ch <- value to await $.chanSend(ch, value)
375
+ c.tsw.WriteLiterally("await $.chanSend(")
320
376
  if err := c.WriteValueExpr(exp.Chan); err != nil { // The channel expression
321
377
  return fmt.Errorf("failed to write channel expression in send statement: %w", err)
322
378
  }
323
- c.tsw.WriteLiterally(".send(")
379
+ c.tsw.WriteLiterally(", ")
324
380
  if err := c.WriteValueExpr(exp.Value); err != nil { // The value expression
325
381
  return fmt.Errorf("failed to write value expression in send statement: %w", err)
326
382
  }
@@ -346,16 +402,19 @@ func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
346
402
  // The function aims to produce idiomatic TypeScript `if/else if/else` structures.
347
403
  func (s *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
348
404
  if exp.Init != nil {
349
- s.tsw.WriteLiterally("{")
350
- s.tsw.Indent(1)
405
+ s.tsw.WriteLiterally("{") // Write opening brace
406
+ s.tsw.WriteLine("") // Add newline immediately after opening brace
407
+ s.tsw.Indent(1) // Indent for the initializer
351
408
 
352
- if err := s.WriteStmt(exp.Init); err != nil {
409
+ if err := s.WriteStmt(exp.Init); err != nil { // Write the initializer
353
410
  return err
354
411
  }
355
412
 
413
+ // This defer handles closing the synthetic block for the initializer
356
414
  defer func() {
357
415
  s.tsw.Indent(-1)
358
- s.tsw.WriteLiterally("}")
416
+ s.tsw.WriteLiterally("}") // Write the closing brace at the now-correct indent level
417
+ s.tsw.WriteLine("") // Ensure a newline *after* this '}', critical for preventing '}}'
359
418
  }()
360
419
  }
361
420
 
@@ -406,21 +465,51 @@ func (s *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
406
465
  // The statement is terminated with a newline.
407
466
  func (c *GoToTSCompiler) WriteStmtReturn(exp *ast.ReturnStmt) error {
408
467
  c.tsw.WriteLiterally("return ")
409
- if len(exp.Results) > 1 {
410
- c.tsw.WriteLiterally("[")
411
- }
412
- for i, res := range exp.Results {
413
- if i != 0 {
414
- c.tsw.WriteLiterally(", ")
468
+
469
+ // Check if it's a bare named return
470
+ nodeInfo := c.analysis.NodeData[exp]
471
+ if nodeInfo != nil && nodeInfo.IsBareReturn {
472
+ var namedReturns []string
473
+ if nodeInfo.EnclosingFuncDecl != nil {
474
+ if obj := c.pkg.TypesInfo.ObjectOf(nodeInfo.EnclosingFuncDecl.Name); obj != nil {
475
+ if funcInfo := c.analysis.FunctionData[obj]; funcInfo != nil {
476
+ namedReturns = funcInfo.NamedReturns
477
+ }
478
+ }
479
+ } else if nodeInfo.EnclosingFuncLit != nil {
480
+ if funcInfo := c.analysis.FuncLitData[nodeInfo.EnclosingFuncLit]; funcInfo != nil {
481
+ namedReturns = funcInfo.NamedReturns
482
+ }
415
483
  }
416
- if err := c.WriteValueExpr(res); err != nil { // Return results are values
417
- return err
484
+
485
+ if len(namedReturns) > 0 {
486
+ c.tsw.WriteLiterally("[")
487
+ for i, name := range namedReturns {
488
+ if i != 0 {
489
+ c.tsw.WriteLiterally(", ")
490
+ }
491
+ c.tsw.WriteLiterally(name)
492
+ }
493
+ c.tsw.WriteLiterally("]")
494
+ }
495
+ } else {
496
+ // Handle explicit return values
497
+ if len(exp.Results) > 1 {
498
+ c.tsw.WriteLiterally("[")
499
+ }
500
+ for i, res := range exp.Results {
501
+ if i != 0 {
502
+ c.tsw.WriteLiterally(", ")
503
+ }
504
+ if err := c.WriteValueExpr(res); err != nil { // Return results are values
505
+ return err
506
+ }
507
+ }
508
+ if len(exp.Results) > 1 {
509
+ c.tsw.WriteLiterally("]")
418
510
  }
419
511
  }
420
- if len(exp.Results) > 1 {
421
- c.tsw.WriteLiterally("]")
422
- }
423
- c.tsw.WriteLine("") // Remove semicolon
512
+ c.tsw.WriteLine("")
424
513
  return nil
425
514
  }
426
515
 
@@ -464,7 +553,7 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
464
553
  for _, stmt := range exp.List {
465
554
  if deferStmt, ok := stmt.(*ast.DeferStmt); ok {
466
555
  if funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok {
467
- if c.analysis.IsInAsyncFunctionMap[funcLit] {
556
+ if c.analysis.IsFuncLitAsync(funcLit) {
468
557
  hasAsyncDefer = true
469
558
  break
470
559
  }
@@ -530,7 +619,6 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
530
619
  start = file.Line(cg.Pos())
531
620
  }
532
621
  writeBlank(lastLine, start)
533
- // WriteDoc does not currently return an error, assuming it's safe for now.
534
622
  c.WriteDoc(cg) // WriteDoc will handle the actual comment text
535
623
  if file != nil && cg.End().IsValid() {
536
624
  lastLine = file.Line(cg.End())
@@ -567,7 +655,6 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
567
655
  // only emit if it follows the last content
568
656
  if start > lastLine {
569
657
  writeBlank(lastLine, start)
570
- // WriteDoc does not currently return an error, assuming it's safe for now.
571
658
  c.WriteDoc(cg)
572
659
  if file != nil && cg.End().IsValid() {
573
660
  lastLine = file.Line(cg.End())
@@ -634,7 +721,6 @@ func (c *GoToTSCompiler) WriteStmtSwitch(exp *ast.SwitchStmt) error {
634
721
  // Handle case clauses
635
722
  for _, stmt := range exp.Body.List {
636
723
  if caseClause, ok := stmt.(*ast.CaseClause); ok {
637
- // WriteCaseClause does not currently return an error, assuming it's safe for now.
638
724
  if err := c.WriteCaseClause(caseClause); err != nil {
639
725
  return fmt.Errorf("failed to write case clause in switch statement: %w", err)
640
726
  }
@@ -667,7 +753,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
667
753
  // Determine if the deferred call is to an async function literal using analysis
668
754
  isAsyncDeferred := false
669
755
  if funcLit, ok := exp.Call.Fun.(*ast.FuncLit); ok {
670
- isAsyncDeferred = c.analysis.IsInAsyncFunctionMap[funcLit]
756
+ isAsyncDeferred = c.analysis.IsFuncLitAsync(funcLit)
671
757
  }
672
758
 
673
759
  // Set async prefix based on pre-computed async status
@@ -696,6 +782,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
696
782
  if err := c.WriteValueExpr(exp.Call); err != nil {
697
783
  return fmt.Errorf("failed to write deferred call: %w", err)
698
784
  }
785
+ c.tsw.WriteLine("")
699
786
  }
700
787
 
701
788
  c.tsw.Indent(-1)
@@ -703,205 +790,3 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
703
790
 
704
791
  return nil
705
792
  }
706
-
707
- // WriteStmtSelect translates a Go `select` statement into an asynchronous
708
- // TypeScript operation using the `$.selectStatement` runtime helper.
709
- // Go's `select` provides non-deterministic choice over channel operations.
710
- // This is emulated by constructing an array of `SelectCase` objects, one for
711
- // each `case` in the Go `select`, and passing it to `$.selectStatement`.
712
- //
713
- // Each `SelectCase` object includes:
714
- // - `id`: A unique identifier for the case.
715
- // - `isSend`: `true` for send operations (`case ch <- val:`), `false` for receives.
716
- // - `channel`: The TypeScript channel object.
717
- // - `value` (for sends): The value being sent.
718
- // - `onSelected: async (result) => { ... }`: A callback executed when this case
719
- // is chosen. `result` contains `{ value, ok }` for receives.
720
- // - Inside `onSelected`, assignments for receive operations (e.g., `v := <-ch`,
721
- // `v, ok := <-ch`) are handled by declaring/assigning variables from `result.value`
722
- // and `result.ok`.
723
- // - The original Go case body is then translated within this callback.
724
- //
725
- // A `default` case in Go `select` is translated to a `SelectCase` with `id: -1`
726
- // and its body in the `onSelected` handler. The `$.selectStatement` helper
727
- // is informed if a default case exists.
728
- // The entire `$.selectStatement(...)` call is `await`ed because channel
729
- // operations are asynchronous in the TypeScript model.
730
- func (c *GoToTSCompiler) WriteStmtSelect(exp *ast.SelectStmt) error {
731
- // This is our implementation of the select statement, which will use Promise.race
732
- // to achieve the same semantics as Go's select statement.
733
-
734
- // Variable to track whether we have a default case
735
- hasDefault := false
736
-
737
- // Start the selectStatement call and the array literal
738
- c.tsw.WriteLiterally("await $.selectStatement(")
739
- c.tsw.WriteLine("[") // Put bracket on new line
740
- c.tsw.Indent(1)
741
-
742
- // For each case clause, generate a SelectCase object directly into the array literal
743
- for i, stmt := range exp.Body.List {
744
- if commClause, ok := stmt.(*ast.CommClause); ok {
745
- if commClause.Comm == nil {
746
- // This is a default case
747
- hasDefault = true
748
- // Add a SelectCase object for the default case with a special ID
749
- c.tsw.WriteLiterally("{") // Start object literal
750
- c.tsw.Indent(1)
751
- c.tsw.WriteLine("")
752
- c.tsw.WriteLiterally("id: -1,") // Special ID for default case
753
- c.tsw.WriteLine("")
754
- c.tsw.WriteLiterally("isSend: false,") // Default case is neither send nor receive, but needs a value
755
- c.tsw.WriteLine("")
756
- c.tsw.WriteLiterally("channel: null,") // No channel for default case
757
- c.tsw.WriteLine("")
758
- c.tsw.WriteLiterally("onSelected: async (result) => {") // Mark as async because case body might contain await
759
- c.tsw.Indent(1)
760
- c.tsw.WriteLine("")
761
- // Write the case body
762
- for _, bodyStmt := range commClause.Body {
763
- if err := c.WriteStmt(bodyStmt); err != nil {
764
- return fmt.Errorf("failed to write statement in select default case body (onSelected): %w", err)
765
- }
766
- }
767
- c.tsw.Indent(-1)
768
- c.tsw.WriteLine("}") // Close onSelected handler
769
- c.tsw.Indent(-1)
770
- c.tsw.WriteLiterally("},") // Close SelectCase object and add comma
771
- c.tsw.WriteLine("")
772
-
773
- continue
774
- }
775
-
776
- // Generate a unique ID for this case
777
- caseID := i
778
-
779
- // Start writing the SelectCase object
780
- c.tsw.WriteLiterally("{") // Start object literal
781
- c.tsw.Indent(1)
782
- c.tsw.WriteLine("")
783
- c.tsw.WriteLiterallyf("id: %d,", caseID)
784
- c.tsw.WriteLine("")
785
-
786
- // Handle different types of comm statements
787
- switch comm := commClause.Comm.(type) {
788
- case *ast.AssignStmt:
789
- // This is a receive operation with assignment: case v := <-ch: or case v, ok := <-ch:
790
- if len(comm.Rhs) == 1 {
791
- if unaryExpr, ok := comm.Rhs[0].(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
792
- // It's a receive operation
793
- c.tsw.WriteLiterally("isSend: false,")
794
- c.tsw.WriteLine("")
795
- c.tsw.WriteLiterally("channel: ")
796
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // The channel expression
797
- return fmt.Errorf("failed to write channel expression in select receive case: %w", err)
798
- }
799
- c.tsw.WriteLiterally(",")
800
- c.tsw.WriteLine("")
801
- } else {
802
- c.tsw.WriteCommentLinef("unhandled RHS in select assignment case: %T", comm.Rhs[0])
803
- }
804
- } else {
805
- c.tsw.WriteCommentLinef("unhandled RHS count in select assignment case: %d", len(comm.Rhs))
806
- }
807
- case *ast.ExprStmt:
808
- // This is a simple receive: case <-ch:
809
- if unaryExpr, ok := comm.X.(*ast.UnaryExpr); ok && unaryExpr.Op == token.ARROW {
810
- c.tsw.WriteLiterally("isSend: false,")
811
- c.tsw.WriteLine("")
812
- c.tsw.WriteLiterally("channel: ")
813
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // The channel expression
814
- return fmt.Errorf("failed to write channel expression in select receive case: %w", err)
815
- }
816
- c.tsw.WriteLiterally(",")
817
- c.tsw.WriteLine("")
818
- } else {
819
- c.tsw.WriteCommentLinef("unhandled expression in select case: %T", comm.X)
820
- }
821
- case *ast.SendStmt:
822
- // This is a send operation: case ch <- v:
823
- c.tsw.WriteLiterally("isSend: true,")
824
- c.tsw.WriteLine("")
825
- c.tsw.WriteLiterally("channel: ")
826
- if err := c.WriteValueExpr(comm.Chan); err != nil { // The channel expression
827
- return fmt.Errorf("failed to write channel expression in select send case: %w", err)
828
- }
829
- c.tsw.WriteLiterally(",")
830
- c.tsw.WriteLine("")
831
- c.tsw.WriteLiterally("value: ")
832
- if err := c.WriteValueExpr(comm.Value); err != nil { // The value expression
833
- return fmt.Errorf("failed to write value expression in select send case: %w", err)
834
- }
835
- c.tsw.WriteLiterally(",")
836
- c.tsw.WriteLine("")
837
- default:
838
- c.tsw.WriteCommentLinef("unhandled comm statement in select case: %T", comm)
839
- }
840
-
841
- // Add the onSelected handler to execute the case body after the select resolves
842
- c.tsw.WriteLiterally("onSelected: async (result) => {") // Mark as async because case body might contain await
843
- c.tsw.Indent(1)
844
- c.tsw.WriteLine("")
845
-
846
- // Handle assignment for channel receives if needed (inside the onSelected handler)
847
- if assignStmt, ok := commClause.Comm.(*ast.AssignStmt); ok {
848
- // This is a receive operation with assignment
849
- if len(assignStmt.Lhs) == 1 {
850
- // Simple receive: case v := <-ch:
851
- valIdent, ok := assignStmt.Lhs[0].(*ast.Ident)
852
- if ok && valIdent.Name != "_" { // Check for blank identifier
853
- c.tsw.WriteLiterally("const ")
854
- c.WriteIdent(valIdent, false)
855
- c.tsw.WriteLiterally(" = result.value")
856
- c.tsw.WriteLine("")
857
- }
858
- } else if len(assignStmt.Lhs) == 2 {
859
- // Receive with ok: case v, ok := <-ch:
860
- valIdent, valOk := assignStmt.Lhs[0].(*ast.Ident)
861
- okIdent, okOk := assignStmt.Lhs[1].(*ast.Ident)
862
-
863
- if valOk && valIdent.Name != "_" {
864
- c.tsw.WriteLiterally("const ")
865
- c.WriteIdent(valIdent, false)
866
- c.tsw.WriteLiterally(" = result.value")
867
- c.tsw.WriteLine("")
868
- }
869
-
870
- if okOk && okIdent.Name != "_" {
871
- c.tsw.WriteLiterally("const ")
872
- c.WriteIdent(okIdent, false)
873
- c.tsw.WriteLiterally(" = result.ok")
874
- c.tsw.WriteLine("")
875
- }
876
- }
877
- }
878
- // Note: Simple receive (case <-ch:) and send (case ch <- v:) don't require assignment here,
879
- // as the operation was already performed by selectReceive/selectSend and the result is in 'result'.
880
-
881
- // Write the case body
882
- for _, bodyStmt := range commClause.Body {
883
- if err := c.WriteStmt(bodyStmt); err != nil {
884
- return fmt.Errorf("failed to write statement in select case body (onSelected): %w", err)
885
- }
886
- }
887
-
888
- c.tsw.Indent(-1)
889
- c.tsw.WriteLine("}") // Close onSelected handler
890
- c.tsw.Indent(-1)
891
- c.tsw.WriteLiterally("},") // Close SelectCase object and add comma
892
- c.tsw.WriteLine("")
893
-
894
- } else {
895
- c.tsw.WriteCommentLinef("unknown statement in select body: %T", stmt)
896
- }
897
- }
898
-
899
- // Close the array literal and the selectStatement call
900
- c.tsw.Indent(-1)
901
- c.tsw.WriteLiterally("], ")
902
- c.tsw.WriteLiterallyf("%t", hasDefault)
903
- c.tsw.WriteLiterally(")")
904
- c.tsw.WriteLine("")
905
-
906
- return nil
907
- }