@@ -817,11 +817,15 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
817817 continue
818818 }
819819 if arg.expr in [ast.Ident, ast.StringLiteral, ast.SelectorExpr, ast.ComptimeSelector]
820- || (arg.expr is ast.CallExpr && arg.expr.or_block.kind != .absent ) {
820+ || autofree_expr_has_or_block_in_chain (arg.expr) {
821821 // Simple expressions like variables, string literals, selector expressions
822822 // (`x.field`) can't result in allocations and don't need to be assigned to
823823 // temporary vars.
824824 // Only expressions like `str + 'b'` need to be freed.
825+ // Expressions with result/option propagation in the call chain (e.g.
826+ // `get_str()!.to_upper()`) cannot be pre-generated safely by
827+ // autofree_call_pregen, because the or_block unwrapping internally calls
828+ // go_before_last_stmt() which garbles the output buffer.
825829 continue
826830 }
827831 if arg.expr is ast.CallExpr && arg.expr.name in ['json.encode' , 'json.encode_pretty' ] {
@@ -4161,3 +4165,19 @@ fn (mut c Checker) check_variadic_arg(arg_expr ast.Expr, typ ast.Type, expected_
41614165 arg_pos)
41624166 }
41634167}
4168+
4169+ // autofree_expr_has_or_block_in_chain returns true if expr is a CallExpr whose call chain
4170+ // contains an or_block (i.e. result/option propagation via `!` or `?`). Used by autofree to
4171+ // skip tmp-var pre-generation for arguments like `get_str()!.to_upper()`, where the inner
4172+ // call's or_block would interfere with output-buffer positioning in autofree_call_pregen.
4173+ fn autofree_expr_has_or_block_in_chain (expr ast.Expr) bool {
4174+ if expr is ast.CallExpr {
4175+ if expr.or_block.kind != .absent {
4176+ return true
4177+ }
4178+ if expr.is_method {
4179+ return autofree_expr_has_or_block_in_chain (expr.left)
4180+ }
4181+ }
4182+ return false
4183+ }
0 commit comments