Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

record types scope #1759

Merged
merged 3 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
c2goBase: c2goBase(conf.C2goBase), imports: make(map[string]pkgImp), isGopFile: true,
}
if rec := ctx.rec; rec != nil {
rec.Scope(f, fileScope)
rec.Scope(f.File, fileScope)
}
preloadGopFile(p, ctx, f.path, f.File, conf)
}
Expand Down Expand Up @@ -1235,7 +1235,7 @@ func loadFunc(ctx *blockCtx, recv *types.Var, d *ast.FuncDecl, genBody bool) {
commentFunc(ctx, fn, d)
if rec := ctx.recorder(); rec != nil {
rec.Def(d.Name, fn.Func)
if recv == nil {
if recv == nil && d.Name.Name != "_" {
ctx.fileScope.Insert(fn.Func)
}
}
Expand Down Expand Up @@ -1308,6 +1308,14 @@ var unaryGopNames = map[string]string{
func loadFuncBody(ctx *blockCtx, fn *gox.Func, body *ast.BlockStmt, src ast.Node) {
cb := fn.BodyStart(ctx.pkg, body)
compileStmts(ctx, body.List)
if rec := ctx.recorder(); rec != nil {
switch fn := src.(type) {
case *ast.FuncDecl:
rec.Scope(fn.Type, cb.Scope())
case *ast.FuncLit:
rec.Scope(fn.Type, cb.Scope())
}
}
cb.End(src)
}

Expand Down Expand Up @@ -1354,6 +1362,9 @@ func loadImport(ctx *blockCtx, spec *ast.ImportSpec) {
} else {
rec.Implicit(spec, pkgName)
}
if name != "_" {
ctx.fileScope.Insert(pkgName)
}
}

if specName != nil {
Expand Down
36 changes: 36 additions & 0 deletions cl/stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
case *ast.BlockStmt:
ctx.cb.Block()
compileStmts(ctx, v.List)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v, ctx.cb.Scope())
}
ctx.cb.End()
return
case *ast.SelectStmt:
Expand Down Expand Up @@ -363,8 +366,14 @@ func compileRangeStmt(ctx *blockCtx, v *ast.RangeStmt) {
if len(defineNames) > 0 {
defNames(ctx, defineNames, cb.Scope())
}
if rec := ctx.recorder(); rec != nil {
rec.Scope(v, cb.Scope())
}
cb.VBlock()
compileStmts(ctx, v.Body.List)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v.Body, cb.Scope())
}
cb.End(v.Body)
cb.SetComments(comments, once)
setBodyHandler(ctx)
Expand Down Expand Up @@ -509,6 +518,9 @@ func compileForStmt(ctx *blockCtx, v *ast.ForStmt) {
cb := ctx.cb
comments, once := cb.BackupComments()
cb.For(v)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v, cb.Scope())
}
if v.Init != nil {
compileStmt(ctx, v.Init)
}
Expand All @@ -518,6 +530,9 @@ func compileForStmt(ctx *blockCtx, v *ast.ForStmt) {
cb.None()
}
cb.Then(v.Body)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v.Body, cb.Scope())
}
compileStmts(ctx, v.Body.List)
if v.Post != nil {
cb.Post()
Expand All @@ -541,6 +556,9 @@ func compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {
compileStmt(ctx, v.Init)
}
compileExpr(ctx, v.Cond)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v, cb.Scope())
}
cb.Then(v.Body)
compileStmts(ctx, v.Body.List)
if e := v.Else; e != nil {
Expand All @@ -552,6 +570,9 @@ func compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {
}
}
cb.SetComments(comments, once)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v.Body, cb.Scope())
}
cb.End(v)
}

Expand Down Expand Up @@ -586,6 +607,9 @@ func compileTypeSwitchStmt(ctx *blockCtx, v *ast.TypeSwitchStmt) {
panic("TODO: type switch syntax error, please use x.(type)")
}
cb.TypeSwitch(name, v)
if rec := ctx.recorder(); rec != nil {
rec.Scope(v, cb.Scope())
}
if v.Init != nil {
compileStmt(ctx, v.Init)
}
Expand Down Expand Up @@ -636,6 +660,9 @@ func compileTypeSwitchStmt(ctx *blockCtx, v *ast.TypeSwitchStmt) {
cb.Then()
compileStmts(ctx, c.Body)
commentStmt(ctx, stmt)
if rec := ctx.recorder(); rec != nil {
rec.Scope(c, cb.Scope())
}
cb.End(c)
}
cb.SetComments(comments, once)
Expand Down Expand Up @@ -666,6 +693,9 @@ func compileSwitchStmt(ctx *blockCtx, v *ast.SwitchStmt) {
} else {
cb.None() // switch {...}
}
if rec := ctx.recorder(); rec != nil {
rec.Scope(v, cb.Scope())
}
cb.Then(v.Body)
seen := make(valueMap)
var firstDefault ast.Stmt
Expand Down Expand Up @@ -715,6 +745,9 @@ func compileSwitchStmt(ctx *blockCtx, v *ast.SwitchStmt) {
cb.Fallthrough()
}
commentStmt(ctx, stmt)
if rec := ctx.recorder(); rec != nil {
rec.Scope(c, cb.Scope())
}
cb.End(c)
}
cb.SetComments(comments, once)
Expand Down Expand Up @@ -764,6 +797,9 @@ func compileSelectStmt(ctx *blockCtx, v *ast.SelectStmt) {
cb.Then()
compileStmts(ctx, c.Body)
commentStmt(ctx, stmt)
if rec := ctx.recorder(); rec != nil {
rec.Scope(c, cb.Scope())
}
cb.End(c)
}
cb.SetComments(comments, once)
Expand Down
156 changes: 143 additions & 13 deletions x/typesutil/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func spxParserConf() parser.Config {
}
}

func parserMixedSource(mod *gopmod.Module, fset *token.FileSet, name, src string, goname string, gosrc string, parserConf parser.Config) (*typesutil.Info, *types.Info, error) {
func parseMixedSource(mod *gopmod.Module, fset *token.FileSet, name, src string, goname string, gosrc string, parserConf parser.Config) (*typesutil.Info, *types.Info, error) {
f, err := parser.ParseEntry(fset, name, src, parserConf)
if err != nil {
return nil, nil, err
Expand All @@ -78,7 +78,7 @@ func parserMixedSource(mod *gopmod.Module, fset *token.FileSet, name, src string
conf := &types.Config{}
conf.Importer = gop.NewImporter(nil, &env.Gop{Root: "../..", Version: "1.0"}, fset)
chkOpts := &typesutil.Config{
Types: types.NewPackage("main", "main"),
Types: types.NewPackage("main", f.Name.Name),
Fset: fset,
Mod: mod,
}
Expand All @@ -104,18 +104,19 @@ func parserMixedSource(mod *gopmod.Module, fset *token.FileSet, name, src string
return info, ginfo, err
}

func parserSource(fset *token.FileSet, filename string, src interface{}, mode parser.Mode) (*typesutil.Info, error) {
func parseSource(fset *token.FileSet, filename string, src interface{}, mode parser.Mode) (*types.Package, *typesutil.Info, error) {
f, err := parser.ParseEntry(fset, filename, src, parser.Config{
Mode: mode,
})
if err != nil {
return nil, err
return nil, nil, err
}

pkg := types.NewPackage("", f.Name.Name)
conf := &types.Config{}
conf.Importer = importer.Default()
chkOpts := &typesutil.Config{
Types: types.NewPackage("main", "main"),
Types: pkg,
Fset: fset,
Mod: gopmod.Default,
}
Expand All @@ -130,13 +131,13 @@ func parserSource(fset *token.FileSet, filename string, src interface{}, mode pa
}
check := typesutil.NewChecker(conf, chkOpts, nil, info)
err = check.Files(nil, []*ast.File{f})
return info, err
return pkg, info, err
}

func parserGoSource(fset *token.FileSet, filename string, src interface{}, mode goparser.Mode) (*types.Info, error) {
func parseGoSource(fset *token.FileSet, filename string, src interface{}, mode goparser.Mode) (*types.Package, *types.Info, error) {
f, err := goparser.ParseFile(fset, filename, src, mode)
if err != nil {
return nil, err
return nil, nil, err
}

conf := &types.Config{}
Expand All @@ -149,10 +150,10 @@ func parserGoSource(fset *token.FileSet, filename string, src interface{}, mode
Selections: make(map[*goast.SelectorExpr]*types.Selection),
Scopes: make(map[goast.Node]*types.Scope),
}
pkg := types.NewPackage("main", "main")
pkg := types.NewPackage("", f.Name.Name)
check := types.NewChecker(conf, fset, pkg, info)
err = check.Files([]*goast.File{f})
return info, err
return pkg, info, err
}

func testGopInfo(t *testing.T, src string, gosrc string, expect string) {
Expand All @@ -165,7 +166,7 @@ func testSpxInfo(t *testing.T, name string, src string, expect string) {

func testGopInfoEx(t *testing.T, mod *gopmod.Module, name string, src string, goname string, gosrc string, expect string, parseConf parser.Config) {
fset := token.NewFileSet()
info, _, err := parserMixedSource(mod, fset, name, src, goname, gosrc, parseConf)
info, _, err := parseMixedSource(mod, fset, name, src, goname, gosrc, parseConf)
if err != nil {
t.Fatal("parserMixedSource error", err)
}
Expand All @@ -185,11 +186,11 @@ func testGopInfoEx(t *testing.T, mod *gopmod.Module, name string, src string, go

func testInfo(t *testing.T, src interface{}) {
fset := token.NewFileSet()
info, err := parserSource(fset, "main.gop", src, parser.ParseComments)
_, info, err := parseSource(fset, "main.gop", src, parser.ParseComments)
if err != nil {
t.Fatal("parserSource error", err)
}
goinfo, err := parserGoSource(fset, "main.go", src, goparser.ParseComments)
_, goinfo, err := parseGoSource(fset, "main.go", src, goparser.ParseComments)
if err != nil {
t.Fatal("parserGoSource error", err)
}
Expand Down Expand Up @@ -1670,3 +1671,132 @@ func onCloned() {
009: 15: 9 | info | type main.info struct{x int; y int}
010: 19: 2 | say | func (*github.com/goplus/gop/cl/internal/spx.Sprite).Say(msg string, secs ...float64)`)
}

func TestScopesInfo(t *testing.T) {
var tests = []struct {
src string
scopes []string // list of scope descriptors of the form kind:varlist
}{
{`package p0`, []string{
"file:",
}},
{`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{
"file:fmt m",
}},
{`package p2; func _() {}`, []string{
"file:", "func:",
}},
{`package p3; func _(x, y int) {}`, []string{
"file:", "func:x y",
}},
{`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{
"file:", "func:x y z", // redeclaration of x
}},
{`package p5; func _(x, y int) (u, _ int) { return }`, []string{
"file:", "func:u x y",
}},
{`package p6; func _() { { var x int; _ = x } }`, []string{
"file:", "func:", "block:x",
}},
{`package p7; func _() { if true {} }`, []string{
"file:", "func:", "if:", "block:",
}},
{`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{
"file:", "func:", "if:x", "block:y",
}},
{`package p9; func _() { switch x := 0; x {} }`, []string{
"file:", "func:", "switch:x",
}},
{`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{
"file:", "func:", "switch:x", "case:y", "case:",
}},
{`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{
"file:", "func:t", "type switch:",
}},
{`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{
"file:", "func:t", "type switch:t",
}},
{`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{
"file:", "func:t", "type switch:", "case:x", // x implicitly declared
}},
{`package p14; func _() { select{} }`, []string{
"file:", "func:",
}},
{`package p15; func _(c chan int) { select{ case <-c: } }`, []string{
"file:", "func:c", "comm:",
}},
{`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{
"file:", "func:c", "comm:i x",
}},
{`package p17; func _() { for{} }`, []string{
"file:", "func:", "for:", "block:",
}},
{`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{
"file:", "func:n", "for:i", "block:",
}},
{`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{
"file:", "func:a", "range:i", "block:",
}},
{`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{
"file:", "func:a", "range:i x", "block:",
}},
{`package p21; var s int; func _(a []int) { for i, x := range a { c := i; println(c) } }`, []string{
"file:", "func:a", "range:i x", "block:c",
}},
}

for _, test := range tests {
pkg, info, err := parseSource(token.NewFileSet(), "src.gop", test.src, 0)
if err != nil {
t.Fatalf("parse source failed: %v", test.src)
}
name := pkg.Name()
// number of scopes must match
if len(info.Scopes) != len(test.scopes) {
t.Errorf("package %s: got %d scopes; want %d\n%v\n%v", name, len(info.Scopes), len(test.scopes),
test.scopes, info.Scopes)
}

// scope descriptions must match
for node, scope := range info.Scopes {
kind := "<unknown node kind>"
switch node.(type) {
case *ast.File:
kind = "file"
case *ast.FuncType:
kind = "func"
case *ast.BlockStmt:
kind = "block"
case *ast.IfStmt:
kind = "if"
case *ast.SwitchStmt:
kind = "switch"
case *ast.TypeSwitchStmt:
kind = "type switch"
case *ast.CaseClause:
kind = "case"
case *ast.CommClause:
kind = "comm"
case *ast.ForStmt:
kind = "for"
case *ast.RangeStmt:
kind = "range"
default:
kind = fmt.Sprintf("<unknown node kind> %T", node)
}

// look for matching scope description
desc := kind + ":" + strings.Join(scope.Names(), " ")
found := false
for _, d := range test.scopes {
if desc == d {
found = true
break
}
}
if !found {
t.Errorf("package %s: no matching scope found for %s", name, desc)
}
}
}
}