Repository: goplus/gop
Pull request #1564 (2023-11-29 12:37:00)

cl: add typesRecorder for types check


Patch:
diff --git a/cl/compile.go b/cl/compile.go
index 65558388..1499ff0d 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -31,7 +31,6 @@ import (
 
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/ast/fromgo"
-	"github.com/goplus/gop/cl/internal/typesutil"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 	"github.com/goplus/gox/cpackages"
@@ -221,27 +220,6 @@ type Config struct {
 	Outline bool
 }
 
-type goxRecorder struct {
-	rec Recorder
-}
-
-// Member maps identifiers to the objects they denote.
-func (p *goxRecorder) Member(id ast.Node, obj types.Object) {
-	tv := typesutil.NewTypeAndValueForObject(obj)
-	switch v := id.(type) {
-	case *ast.SelectorExpr:
-		sel := v.Sel
-		// TODO: record event for a Go ident
-		if _, ok := fromgo.CheckIdent(sel); !ok {
-			p.rec.Use(sel, obj)
-			p.rec.Type(v, tv)
-		}
-	case *ast.Ident: // it's in a classfile and impossible converted from Go
-		p.rec.Use(v, obj)
-		p.rec.Type(v, tv)
-	}
-}
-
 type nodeInterp struct {
 	fset       *token.FileSet
 	files      map[string]*ast.File
@@ -398,14 +376,14 @@ type blockCtx struct {
 	targetDir    string
 	classRecv    *ast.FieldList // available when gmxSettings != nil
 	fileScope    *types.Scope   // only valid when isGopFile
-	rec          Recorder
+	rec          *typesRecorder
 	fileLine     bool
 	relativePath bool
 	isClass      bool
 	isGopFile    bool // is Go+ file or not
 }
 
-func (bc *blockCtx) recorder() Recorder {
+func (bc *blockCtx) recorder() *typesRecorder {
 	if bc.isGopFile {
 		return bc.rec
 	}
@@ -536,8 +514,10 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		PkgPathIox:      ioxPkgPath,
 		DbgPositioner:   interp,
 	}
+	var rec *typesRecorder
 	if conf.Recorder != nil {
-		confGox.Recorder = &goxRecorder{rec: conf.Recorder}
+		rec = newTypeRecord(conf.Recorder)
+		confGox.Recorder = &goxRecorder{rec: rec}
 	}
 	if enableRecover {
 		defer func() {
@@ -584,7 +564,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		fileScope := types.NewScope(p.Types.Scope(), f.Pos(), f.End(), fpath)
 		ctx := &blockCtx{
 			pkg: p, pkgCtx: ctx, cb: p.CB(), targetDir: targetDir, fileScope: fileScope,
-			fileLine: fileLine, relativePath: conf.RelativePath, isClass: f.IsClass, rec: conf.Recorder,
+			fileLine: fileLine, relativePath: conf.RelativePath, isClass: f.IsClass, rec: rec,
 			c2goBase: c2goBase(conf.C2goBase), imports: make(map[string]pkgImp), isGopFile: true,
 		}
 		if rec := ctx.rec; rec != nil {
diff --git a/cl/expr.go b/cl/expr.go
index b8dc8c84..6f7c9b46 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -28,7 +28,6 @@ import (
 	"strings"
 
 	"github.com/goplus/gop/ast"
-	"github.com/goplus/gop/cl/internal/typesutil"
 	"github.com/goplus/gop/printer"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
@@ -161,12 +160,10 @@ find:
 	if rec := ctx.recorder(); rec != nil {
 		e := ctx.cb.Get(-1)
 		if oldo != nil && gox.IsTypeEx(e.Type) {
-			rec.Use(ident, oldo)
-			rec.Type(ident, typesutil.NewTypeAndValueForObject(oldo))
+			rec.recordIdent(ctx, ident, oldo)
 			return
 		}
-		rec.Use(ident, o)
-		rec.Type(ident, typesutil.NewTypeAndValueForObject(o))
+		rec.recordIdent(ctx, ident, o)
 	}
 	return
 }
@@ -202,31 +199,18 @@ func compileExprLHS(ctx *blockCtx, expr ast.Expr) {
 		compileIndexExprLHS(ctx, v)
 	case *ast.SelectorExpr:
 		compileSelectorExprLHS(ctx, v)
-		recordTypesVariable(ctx, v, -1)
 	case *ast.StarExpr:
 		compileStarExprLHS(ctx, v)
 	default:
 		log.Panicln("compileExpr failed: unknown -", reflect.TypeOf(v))
 	}
-}
-
-func twoValue(inFlags []int) bool {
-	return inFlags != nil && (inFlags[0]&clCallWithTwoValue) != 0
-}
-
-func recordTypesValue(ctx *blockCtx, expr ast.Expr, n int) {
 	if rec := ctx.recorder(); rec != nil {
-		e := ctx.cb.Get(n)
-		rec.Type(expr, typesutil.NewTypeAndValueForValue(e.Type, e.CVal))
+		rec.recordExpr(ctx, expr, true)
 	}
 }
 
-func recordTypesVariable(ctx *blockCtx, expr ast.Expr, n int) {
-	if rec := ctx.recorder(); rec != nil {
-		e := ctx.cb.Get(n)
-		t, _ := gox.DerefType(e.Type)
-		rec.Type(expr, typesutil.NewTypeAndValueForVariable(t))
-	}
+func twoValue(inFlags []int) bool {
+	return inFlags != nil && (inFlags[0]&clCallWithTwoValue) != 0
 }
 
 func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
@@ -239,7 +223,6 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 		compileIdent(ctx, v, flags)
 	case *ast.BasicLit:
 		compileBasicLit(ctx, v)
-		recordTypesValue(ctx, v, -1)
 	case *ast.CallExpr:
 		flags := 0
 		if inFlags != nil {
@@ -254,10 +237,8 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 		compileSelectorExpr(ctx, v, flags)
 	case *ast.BinaryExpr:
 		compileBinaryExpr(ctx, v)
-		recordTypesValue(ctx, v, -1)
 	case *ast.UnaryExpr:
 		compileUnaryExpr(ctx, v, twoValue(inFlags))
-		recordTypesValue(ctx, v, -1)
 	case *ast.FuncLit:
 		compileFuncLit(ctx, v)
 	case *ast.CompositeLit:
@@ -301,6 +282,9 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 	default:
 		log.Panicln("compileExpr failed: unknown -", reflect.TypeOf(v))
 	}
+	if rec := ctx.recorder(); rec != nil {
+		rec.recordExpr(ctx, expr, false)
+	}
 }
 
 func compileExprOrNone(ctx *blockCtx, expr ast.Expr) {
@@ -399,10 +383,6 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, flags int) {
 		}
 	default:
 		compileExpr(ctx, v.X)
-		if rec := ctx.recorder(); rec != nil {
-			e := ctx.cb.Get(-1)
-			rec.Type(v.X, typesutil.NewTypeAndValueForType(e.Type))
-		}
 	}
 	if err := compileMember(ctx, v, v.Sel.Name, flags); err != nil {
 		panic(err)
@@ -563,7 +543,7 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 		err := compileCallArgs(fn, fnt, ctx, v, ellipsis, flags)
 		if err == nil {
 			if rec := ctx.recorder(); rec != nil {
-				rec.Type(v, typesutil.NewTypeAndValueForCallResult(ctx.cb.Get(-1).Type))
+				rec.recordCallExpr(ctx, v, fnt)
 			}
 			break
 		}
@@ -903,7 +883,7 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type
 	if t, ok := underlying.(*types.Struct); ok && kind == compositeLitKeyVal {
 		compileStructLitInKeyVal(ctx, v.Elts, t, typ, v)
 		if rec := ctx.recorder(); rec != nil {
-			rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil))
+			rec.recordCompositeLit(ctx, v, typ)
 		}
 		if hasPtr {
 			ctx.cb.UnaryOp(gotoken.AND)
@@ -920,8 +900,7 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type
 		return
 	}
 	if rec := ctx.recorder(); rec != nil {
-		rec.Type(v.Type, typesutil.NewTypeAndValueForType(typ))
-		rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil))
+		rec.recordCompositeLit(ctx, v, typ)
 	}
 	switch underlying.(type) {
 	case *types.Slice:
diff --git a/cl/func_type_and_var.go b/cl/func_type_and_var.go
index 4622a029..ca8a37f1 100644
--- a/cl/func_type_and_var.go
+++ b/cl/func_type_and_var.go
@@ -25,7 +25,6 @@ import (
 	"strconv"
 
 	"github.com/goplus/gop/ast"
-	"github.com/goplus/gop/cl/internal/typesutil"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 )
@@ -116,7 +115,7 @@ func toParam(ctx *blockCtx, fld *ast.Field, args []*gox.Param) []*gox.Param {
 func toType(ctx *blockCtx, typ ast.Expr) (t types.Type) {
 	if rec := ctx.recorder(); rec != nil {
 		defer func() {
-			rec.Type(typ, typesutil.NewTypeAndValueForType(t))
+			rec.recordType(ctx, typ, t)
 		}()
 	}
 	switch v := typ.(type) {
@@ -233,8 +232,7 @@ func toIdentType(ctx *blockCtx, ident *ast.Ident) (ret types.Type) {
 	if rec := ctx.recorder(); rec != nil {
 		defer func() {
 			if obj != nil {
-				rec.Use(ident, obj)
-				rec.Type(ident, typesutil.NewTypeAndValueForObject(obj))
+				rec.recordIdent(ctx, ident, obj)
 			}
 		}()
 	}
@@ -348,11 +346,7 @@ func toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {
 			}
 		}
 	}
-	st := types.NewStruct(fields, tags)
-	if rec != nil {
-		rec.Type(v, typesutil.NewTypeAndValueForType(st))
-	}
-	return st
+	return types.NewStruct(fields, tags)
 }
 
 func toFieldTag(v *ast.BasicLit) string {
diff --git a/cl/internal/typesutil/api.go b/cl/internal/typesutil/api.go
index 000ce81f..00183b27 100644
--- a/cl/internal/typesutil/api.go
+++ b/cl/internal/typesutil/api.go
@@ -54,7 +54,7 @@ func NewTypeAndValueForType(typ types.Type) (ret types.TypeAndValue) {
 	case *gox.TypeType:
 		typ = t.Type()
 	}
-	ret.Type = types.Default(typ)
+	ret.Type = typ
 	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = typexpr
 	return
 }
@@ -66,7 +66,7 @@ func NewTypeAndValueForValue(typ types.Type, val constant.Value) (ret types.Type
 	} else {
 		mode = value
 	}
-	ret.Type = types.Default(typ)
+	ret.Type = typ
 	ret.Value = val
 	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode
 	return
@@ -78,14 +78,27 @@ func NewTypeAndValueForVariable(typ types.Type) (ret types.TypeAndValue) {
 	return
 }
 
-func NewTypeAndValueForCallResult(typ types.Type) (ret types.TypeAndValue) {
+func NewTypeAndValueForMapIndex(typ types.Type) (ret types.TypeAndValue) {
+	ret.Type = typ
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mapindex
+	return
+}
+
+func NewTypeAndValueForCallResult(typ types.Type, val constant.Value) (ret types.TypeAndValue) {
+	var mode operandMode
 	if typ == nil {
 		ret.Type = &types.Tuple{}
-		(*TypeAndValue)(unsafe.Pointer(&ret)).mode = novalue
-		return
+		mode = novalue
+	} else {
+		ret.Type = typ
+		if val != nil {
+			ret.Value = val
+			mode = constant_
+		} else {
+			mode = value
+		}
 	}
-	ret.Type = typ
-	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = value
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode
 	return
 }
 
diff --git a/cl/recorder.go b/cl/recorder.go
new file mode 100644
index 00000000..a8ef3fa7
--- /dev/null
+++ b/cl/recorder.go
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cl
+
+import (
+	"go/types"
+
+	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/ast/fromgo"
+	"github.com/goplus/gop/cl/internal/typesutil"
+	"github.com/goplus/gox"
+)
+
+type goxRecorder struct {
+	rec Recorder
+}
+
+// Member maps identifiers to the objects they denote.
+func (p *goxRecorder) Member(id ast.Node, obj types.Object) {
+	tv := typesutil.NewTypeAndValueForObject(obj)
+	switch v := id.(type) {
+	case *ast.SelectorExpr:
+		sel := v.Sel
+		// TODO: record event for a Go ident
+		if _, ok := fromgo.CheckIdent(sel); !ok {
+			p.rec.Use(sel, obj)
+			p.rec.Type(v, tv)
+		}
+	case *ast.Ident: // it's in a classfile and impossible converted from Go
+		p.rec.Use(v, obj)
+		p.rec.Type(v, tv)
+	}
+}
+
+type typesRecorder struct {
+	Recorder
+	types map[ast.Expr]types.TypeAndValue
+}
+
+func (rec *typesRecorder) Type(expr ast.Expr, tv types.TypeAndValue) {
+	rec.types[expr] = tv
+	rec.Recorder.Type(expr, tv)
+}
+
+func newTypeRecord(rec Recorder) *typesRecorder {
+	return &typesRecorder{rec, make(map[ast.Expr]types.TypeAndValue)}
+}
+
+func (rec *typesRecorder) recordTypeValue(ctx *blockCtx, expr ast.Expr) {
+	e := ctx.cb.Get(-1)
+	rec.Type(expr, typesutil.NewTypeAndValueForValue(e.Type, e.CVal))
+}
+
+func (rec *typesRecorder) recordTypeVariable(ctx *blockCtx, expr ast.Expr) {
+	e := ctx.cb.Get(-1)
+	t, _ := gox.DerefType(e.Type)
+	rec.Type(expr, typesutil.NewTypeAndValueForVariable(t))
+}
+
+func (rec *typesRecorder) recordTypeMapIndex(ctx *blockCtx, expr ast.Expr) {
+	e := ctx.cb.Get(-1)
+	t, _ := gox.DerefType(e.Type)
+	rec.Type(expr, typesutil.NewTypeAndValueForMapIndex(t))
+}
+
+func (rec *typesRecorder) indexExpr(ctx *blockCtx, expr *ast.IndexExpr) {
+	if tv, ok := rec.types[expr.X]; ok {
+		switch tv.Type.(type) {
+		case *types.Map:
+			rec.recordTypeMapIndex(ctx, expr)
+			return
+		case *types.Slice:
+			rec.recordTypeVariable(ctx, expr)
+			return
+		}
+	}
+	switch expr.X.(type) {
+	case *ast.CompositeLit:
+		rec.recordTypeValue(ctx, expr)
+	default:
+		rec.recordTypeVariable(ctx, expr)
+	}
+}
+
+func (rec *typesRecorder) recordCallExpr(ctx *blockCtx, v *ast.CallExpr, fnt types.Type) {
+	e := ctx.cb.Get(-1)
+	rec.Type(v.Fun, typesutil.NewTypeAndValueForValue(fnt, nil))
+	rec.Type(v, typesutil.NewTypeAndValueForCallResult(e.Type, e.CVal))
+}
+
+func (rec *typesRecorder) recordCompositeLit(ctx *blockCtx, v *ast.CompositeLit, typ types.Type) {
+	rec.Type(v.Type, typesutil.NewTypeAndValueForType(typ))
+	rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil))
+}
+
+func (rec *typesRecorder) recordType(ctx *blockCtx, typ ast.Expr, t types.Type) {
+	rec.Type(typ, typesutil.NewTypeAndValueForType(t))
+}
+
+func (rec *typesRecorder) recordIdent(ctx *blockCtx, ident *ast.Ident, obj types.Object) {
+	rec.Use(ident, obj)
+	rec.Type(ident, typesutil.NewTypeAndValueForObject(obj))
+}
+
+func (rec *typesRecorder) recordExpr(ctx *blockCtx, expr ast.Expr, rhs bool) {
+	switch v := expr.(type) {
+	case *ast.Ident:
+	case *ast.BasicLit:
+		rec.recordTypeValue(ctx, v)
+	case *ast.CallExpr:
+	case *ast.SelectorExpr:
+		rec.recordTypeVariable(ctx, v)
+	case *ast.BinaryExpr:
+		rec.recordTypeValue(ctx, v)
+	case *ast.UnaryExpr:
+		rec.recordTypeValue(ctx, v)
+	case *ast.FuncLit:
+	case *ast.CompositeLit:
+	case *ast.SliceLit:
+	case *ast.RangeExpr:
+	case *ast.IndexExpr:
+		rec.indexExpr(ctx, v)
+	case *ast.IndexListExpr:
+	case *ast.SliceExpr:
+		rec.recordTypeValue(ctx, v)
+	case *ast.StarExpr:
+		rec.recordTypeVariable(ctx, v)
+	case *ast.ArrayType:
+	case *ast.MapType:
+	case *ast.StructType:
+	case *ast.ChanType:
+	case *ast.InterfaceType:
+	case *ast.ComprehensionExpr:
+	case *ast.TypeAssertExpr:
+	case *ast.ParenExpr:
+		rec.recordTypeValue(ctx, v)
+	case *ast.ErrWrapExpr:
+	case *ast.FuncType:
+	case *ast.Ellipsis:
+	case *ast.KeyValueExpr:
+	default:
+	}
+}


Test patch:
diff --git a/cl/internal/typesutil/api_test.go b/cl/internal/typesutil/api_test.go
index ec3a9f76..1a4af278 100644
--- a/cl/internal/typesutil/api_test.go
+++ b/cl/internal/typesutil/api_test.go
@@ -39,11 +39,19 @@ func TestTypeAndValue(t *testing.T) {
 	if !ret.Addressable() {
 		t.Fatal("NewTypeAndValueForVariable: not variable?")
 	}
-	ret = NewTypeAndValueForCallResult(tyInt)
+	ret = NewTypeAndValueForMapIndex(tyInt)
+	if !(!ret.Addressable() && ret.Assignable()) {
+		t.Fatal("NewTypeAndValueForVariable: not map?")
+	}
+	ret = NewTypeAndValueForCallResult(tyInt, nil)
 	if !ret.IsValue() {
 		t.Fatal("NewTypeAndValueForCall: not value?")
 	}
-	ret = NewTypeAndValueForCallResult(nil)
+	ret = NewTypeAndValueForCallResult(tyInt, constant.MakeInt64(1))
+	if !ret.IsValue() || ret.Value == nil {
+		t.Fatal("NewTypeAndValueForCall: not const?")
+	}
+	ret = NewTypeAndValueForCallResult(nil, nil)
 	if !ret.IsVoid() {
 		t.Fatal("NewTypeAndValueForCall: not void?")
 	}
diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go
index 840ab8d2..2380c723 100644
--- a/x/typesutil/info_test.go
+++ b/x/typesutil/info_test.go
@@ -83,7 +83,7 @@ func testInfo(t *testing.T, src interface{}) {
 	if err != nil {
 		t.Fatal("parserGoSource error", err)
 	}
-	testItems(t, "types", typesList(fset, info.Types), goTypesList(fset, goinfo.Types))
+	testItems(t, "types", typesList(fset, info.Types, true), goTypesList(fset, goinfo.Types, true))
 	testItems(t, "defs", defsList(fset, info.Defs, true), goDefsList(fset, goinfo.Defs, true))
 	testItems(t, "uses", usesList(fset, info.Uses), goUsesList(fset, goinfo.Uses))
 	// TODO check selections
@@ -117,12 +117,17 @@ func sortItems(items []string) []string {
 	return items
 }
 
-func typesList(fset *token.FileSet, types map[ast.Expr]types.TypeAndValue) []string {
+func typesList(fset *token.FileSet, types map[ast.Expr]types.TypeAndValue, skipBasicLit bool) []string {
 	var items []string
 	for expr, tv := range types {
 		var buf strings.Builder
 		posn := fset.Position(expr.Pos())
 		tvstr := tv.Type.String()
+		if skipBasicLit {
+			if t, ok := expr.(*ast.BasicLit); ok {
+				tvstr = t.Kind.String()
+			}
+		}
 		if tv.Value != nil {
 			tvstr += " = " + tv.Value.String()
 		}
@@ -135,12 +140,17 @@ func typesList(fset *token.FileSet, types map[ast.Expr]types.TypeAndValue) []str
 	return sortItems(items)
 }
 
-func goTypesList(fset *token.FileSet, types map[goast.Expr]types.TypeAndValue) []string {
+func goTypesList(fset *token.FileSet, types map[goast.Expr]types.TypeAndValue, skipBasicLit bool) []string {
 	var items []string
 	for expr, tv := range types {
 		var buf strings.Builder
 		posn := fset.Position(expr.Pos())
 		tvstr := tv.Type.String()
+		if skipBasicLit {
+			if t, ok := expr.(*goast.BasicLit); ok {
+				tvstr = t.Kind.String()
+			}
+		}
 		if tv.Value != nil {
 			tvstr += " = " + tv.Value.String()
 		}
@@ -327,15 +337,31 @@ type T struct {
 	y int
 }
 var v *int = nil
-var v1 []int;
-var v2 map[int8]string;
-var v3 struct{};
-var v4 *T = &T{100,200};
+var v1 []int
+var v2 map[int8]string
+var v3 struct{}
+var v4 *T = &T{100,200}
+var v5 = [6]int{}
+var v6 = v5[0]
+var v7 = [6]int{}[0]
+var m map[int]string
+func init() {
+	v5[0] = 100
+	_ = v5[:][0]
+	m[0] = "hello"
+	_ = m[0]
+	_ = map[int]string{}[0]
+	_ = &v3
+	_ = *(&v3)
+	a := []int{1,2,3,4,5}[0]
+	_ = a
+}
 `)
 }
 
 func TestStruct(t *testing.T) {
 	testInfo(t, `package main
+import "fmt"
 
 type Person struct {
 	name string
@@ -347,41 +373,7 @@ func test() {
 		name: "jack",
 	}
 	_ = p.name
-}
-`)
-}
-
-func _TestStruct2(t *testing.T) {
-	testInfo(t, `package main
-
-type Person struct {
-	name string
-	age  int8
-}
-
-func test() {
-	p := Person{
-		name: "jack",
-	}
 	p.name = "name"
-}
-`)
-}
-
-func _TestStruct3(t *testing.T) {
-	testInfo(t, `package main
-
-import "fmt"
-
-type Person struct {
-	name string
-	age  int8
-}
-
-func test() {
-	p := Person{
-		name: "jack",
-	}
 	fmt.Println(p)
 }
 `)