From cc92cc8f823dd079fa9f8c542039cfa9db1facce Mon Sep 17 00:00:00 2001 From: Charlie Tonneslan Date: Mon, 18 May 2026 13:03:19 -0400 Subject: [PATCH] basicLit: format numbers from the reflect.Value's kind Closes #11. fmt.Sprint(v) on a named numeric type like time.Duration calls the type's Stringer (returns '1h0m0s'), so the produced AST contained an invalid Go literal and gofumpt then errored with 'missing ',' in argument list'. Format INT/FLOAT/UINT from the reflect.Value's kind so the literal is always valid Go syntax. Signed-off-by: Charlie Tonneslan --- valast.go | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/valast.go b/valast.go index b03a7c3..f8f1235 100644 --- a/valast.go +++ b/valast.go @@ -193,8 +193,13 @@ func basicLit(vv reflect.Value, kind token.Token, builtinType string, v interfac if err != nil { return Result{}, err } + // Named numeric types like time.Duration implement Stringer and + // fmt.Sprint hands that back ("1h0m0s") instead of the underlying + // integer literal, which then doesn't parse. Format from the + // reflect.Value's kind so the AST always carries a valid Go literal. + lit := formatBasicLit(vv, kind, v) if opt.Unqualify && vv.Type().Name() == builtinType && vv.Type().PkgPath() == "" { - return Result{AST: ast.NewIdent(fmt.Sprint(v))}, nil + return Result{AST: ast.NewIdent(lit)}, nil } if opt.ExportedOnly && typeExpr.RequiresUnexported { return Result{RequiresUnexported: true}, nil @@ -202,12 +207,34 @@ func basicLit(vv reflect.Value, kind token.Token, builtinType string, v interfac return Result{ AST: &ast.CallExpr{ Fun: typeExpr.AST, - Args: []ast.Expr{ast.NewIdent(fmt.Sprint(v))}, + Args: []ast.Expr{ast.NewIdent(lit)}, }, RequiresUnexported: typeExpr.RequiresUnexported, }, nil } +func formatBasicLit(vv reflect.Value, kind token.Token, v interface{}) string { + switch kind { + case token.INT: + switch vv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(vv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return strconv.FormatUint(vv.Uint(), 10) + } + case token.FLOAT: + switch vv.Kind() { + case reflect.Float32: + return strconv.FormatFloat(vv.Float(), 'g', -1, 32) + case reflect.Float64: + return strconv.FormatFloat(vv.Float(), 'g', -1, 64) + case reflect.Complex64, reflect.Complex128: + return fmt.Sprint(v) + } + } + return fmt.Sprint(v) +} + // ErrInvalidType describes that the value is of a type that cannot be converted to an AST. type ErrInvalidType struct { // Value is the actual value that was being converted.