Skip to content

Commit

Permalink
Merge pull request #1759 from visualfc/types_scope
Browse files Browse the repository at this point in the history
record types scope
  • Loading branch information
xushiwei committed Feb 21, 2024
2 parents 62d7ee7 + e8e9c3e commit 2abed6f
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 15 deletions.
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)
}
}
}
}

0 comments on commit 2abed6f

Please sign in to comment.