44with proper keyword escaping and idiomatic Go style.
55"""
66
7- from .codegen_base import CodeGenerator
7+ from .codegen_base import PARSER_CONFIG , CodegenConfig , CodeGenerator
88from .codegen_templates import GO_TEMPLATES
99from .gensym import gensym
1010from .target import (
1111 Call ,
1212 FunDef ,
1313 GetElement ,
14+ Let ,
1415 ListExpr ,
1516 NewMessage ,
1617 OneOf ,
@@ -118,13 +119,14 @@ def _is_nullable_go_type(type_str: str) -> bool:
118119 "[]byte" : "nil" ,
119120 }
120121
121- def __init__ (self , proto_messages = None ):
122- super ().__init__ (proto_messages )
122+ def __init__ (self , proto_messages = None , config : CodegenConfig = PARSER_CONFIG ):
123+ super ().__init__ (proto_messages , config )
123124 self ._oneof_field_to_parent = self ._build_oneof_field_map ()
124125 self ._declared_vars : set [str ] = set ()
125126 self ._current_return_type : str | None = None
126127 self ._current_return_is_option : bool = False
127128 self ._current_return_option_needs_ptr : bool = False
129+ self ._lambda_return_type_stack : list [str | None ] = []
128130 self ._register_builtins ()
129131
130132 def reset_declared_vars (self ) -> None :
@@ -384,12 +386,42 @@ def gen_func_def_header(
384386 params_str = ", " .join (f"{ n } { t } " for n , t in params )
385387 ret = f" { return_type } " if return_type else ""
386388 if is_method :
387- return f"func (p *Parser ) { name } ({ params_str } ){ ret } {{"
389+ return f"func (p *{ self . config . receiver_type } ) { name } ({ params_str } ){ ret } {{"
388390 return f"func { name } ({ params_str } ){ ret } {{"
389391
390392 def gen_func_def_end (self ) -> str :
391393 return "}"
392394
395+ def _generate_Lambda (self , expr , lines : list [str ], indent : str ) -> str :
396+ """Track lambda return type for IfElse type hint inference."""
397+ from .target import Lambda
398+
399+ assert isinstance (expr , Lambda )
400+ ret_type = (
401+ self .gen_type (expr .return_type )
402+ if expr .return_type and not self ._is_void_type (expr .return_type )
403+ else None
404+ )
405+ self ._lambda_return_type_stack .append (ret_type )
406+ try :
407+ result = super ()._generate_Lambda (expr , lines , indent )
408+ finally :
409+ self ._lambda_return_type_stack .pop ()
410+ return result
411+
412+ def _ifelse_type_hint (self , expr ) -> str | None :
413+ """Improve IfElse type hint when the overall type resolves to interface{}.
414+
415+ When the IfElse type has unresolved type variables (yielding interface{}),
416+ use the enclosing lambda's return type as a better hint.
417+ """
418+ hint = super ()._ifelse_type_hint (expr )
419+ if hint == "interface{}" and self ._lambda_return_type_stack :
420+ lambda_ret = self ._lambda_return_type_stack [- 1 ]
421+ if lambda_ret is not None and lambda_ret != "interface{}" :
422+ return lambda_ret
423+ return hint
424+
393425 def _generate_nil_else_branch (
394426 self ,
395427 tmp : str ,
@@ -403,9 +435,23 @@ def _generate_nil_else_branch(
403435 def _generate_GetElement (
404436 self , expr : GetElement , lines : list [str ], indent : str
405437 ) -> str :
406- """Go uses 0-based indexing with type assertion for tuple elements."""
438+ """Go uses 0-based indexing with type assertion for tuple elements.
439+
440+ Type assertions are only needed when the container is []interface{}
441+ (Go tuple). For typed slices (protobuf repeated fields), the element
442+ type is already correct and assertion would fail.
443+ """
444+ from .target import ListType , SequenceType
445+
407446 tuple_code = self .generate_lines (expr .tuple_expr , lines , indent )
408- # Add type assertion since tuple elements are interface{}
447+ # Check if the container is a typed slice (no assertion needed)
448+ try :
449+ container_type = expr .tuple_expr .target_type ()
450+ if isinstance (container_type , (SequenceType , ListType )):
451+ return f"{ tuple_code } [{ expr .index } ]"
452+ except (NotImplementedError , ValueError , TypeError ):
453+ pass
454+ # For tuples ([]interface{}), add type assertion
409455 try :
410456 elem_type = expr .target_type ()
411457 if elem_type is not None :
@@ -444,6 +490,23 @@ def _generate_GetField(self, expr, lines: list[str], indent: str) -> str:
444490 return f"{ obj_code } .{ pascal_field } "
445491 return super ()._generate_GetField (expr , lines , indent )
446492
493+ def _generate_Let (self , expr : Let , lines : list [str ], indent : str ) -> str | None :
494+ """Generate Go let binding, suppressing unused variable errors."""
495+ var_name = self .escape_identifier (expr .var .name )
496+ init_val = self .generate_lines (expr .init , lines , indent )
497+ assert init_val is not None , "Let initializer should not contain a return"
498+ lines .append (
499+ f"{ indent } { self .gen_assignment (var_name , init_val , is_declaration = True )} "
500+ )
501+ body_start = len (lines )
502+ result = self .generate_lines (expr .body , lines , indent )
503+ # Suppress unused variable if the body didn't reference it.
504+ body_lines = lines [body_start :]
505+ var_used = any (var_name in line for line in body_lines ) or result == var_name
506+ if not var_used :
507+ lines .insert (body_start , f"{ indent } _ = { var_name } " )
508+ return result
509+
447510 def _generate_Seq (self , expr : Seq , lines : list [str ], indent : str ) -> str | None :
448511 """Generate Go sequence, suppressing unused variable errors.
449512
@@ -643,6 +706,9 @@ def _generate_Call(self, expr: Call, lines: list[str], indent: str) -> str | Non
643706 )
644707 args .append (arg_code )
645708 args_code = ", " .join (args )
709+ if self ._is_void_expr (expr ):
710+ lines .append (f"{ indent } { f } ({ args_code } )" )
711+ return self .gen_none ()
646712 tmp = gensym ()
647713 lines .append (
648714 f"{ indent } { self .gen_assignment (tmp , f'{ f } ({ args_code } )' , is_declaration = True )} "
@@ -731,11 +797,8 @@ def _generate_option_builtin(
731797 if self ._is_nullable_go_type (inner_go ):
732798 return arg_code
733799 else :
734- # Non-OptionType (e.g., oneof fields): check if the Go
735- # type is already nullable, in which case no deref needed.
736- go_type = self .gen_type (arg_type )
737- if self ._is_nullable_go_type (go_type ):
738- return arg_code
800+ # Non-OptionType: return as-is (no deref needed).
801+ return arg_code
739802 except (NotImplementedError , ValueError , TypeError ):
740803 pass
741804 return f"*{ arg_code } "
0 commit comments