Repository: goplus/gop
Pull request #1577 (2023-12-14 02:01:53)

cl: compileComprehensionExpr types record define kv
depends by https://github.com/goplus/gop/pull/1575


Patch:
diff --git a/cl/expr.go b/cl/expr.go
index a6191d3e..3b17097f 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -999,16 +999,20 @@ func compileComprehensionExpr(ctx *blockCtx, v *ast.ComprehensionExpr, twoValue
 	end := 0
 	for i := len(v.Fors) - 1; i >= 0; i-- {
 		names := make([]string, 0, 2)
+		defineNames := make([]*ast.Ident, 0, 2)
 		forStmt := v.Fors[i]
 		if forStmt.Key != nil {
 			names = append(names, forStmt.Key.Name)
+			defineNames = append(defineNames, forStmt.Key)
 		} else {
 			names = append(names, "_")
 		}
 		names = append(names, forStmt.Value.Name)
+		defineNames = append(defineNames, forStmt.Value)
 		cb.ForRange(names...)
 		compileExpr(ctx, forStmt.X)
 		cb.RangeAssignThen(forStmt.TokPos)
+		defNames(ctx, defineNames, cb.Scope())
 		if forStmt.Cond != nil {
 			cb.If()
 			if forStmt.Init != nil {
diff --git a/cl/stmt.go b/cl/stmt.go
index 149365ab..8bb4a30c 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -384,17 +384,23 @@ func compileForPhraseStmt(ctx *blockCtx, v *ast.ForPhraseStmt) {
 	cb := ctx.cb
 	comments, once := cb.BackupComments()
 	names := make([]string, 1, 2)
+	defineNames := make([]*ast.Ident, 0, 2)
 	if v.Key == nil {
 		names[0] = "_"
 	} else {
 		names[0] = v.Key.Name
+		defineNames = append(defineNames, v.Key)
 	}
 	if v.Value != nil {
 		names = append(names, v.Value.Name)
+		defineNames = append(defineNames, v.Value)
 	}
 	cb.ForRange(names...)
 	compileExpr(ctx, v.X)
 	cb.RangeAssignThen(v.TokPos)
+	if len(defineNames) > 0 {
+		defNames(ctx, defineNames, cb.Scope())
+	}
 	if v.Cond != nil {
 		cb.If()
 		compileExpr(ctx, v.Cond)


Test patch:
diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go
index f69f6ddf..2fd0f69b 100644
--- a/x/typesutil/info_test.go
+++ b/x/typesutil/info_test.go
@@ -15,14 +15,60 @@ import (
 	"testing"
 	"unsafe"
 
+	"github.com/goplus/gop"
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/format"
 	"github.com/goplus/gop/parser"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gop/x/typesutil"
+	"github.com/goplus/mod/env"
 	"github.com/goplus/mod/gopmod"
 )
 
+func parserMixedSource(fset *token.FileSet, src string, gosrc string) (*typesutil.Info, *types.Info, error) {
+	f, err := parser.ParseEntry(fset, "main.gop", src, parser.Config{
+		Mode: parser.ParseComments,
+	})
+	if err != nil {
+		return nil, nil, err
+	}
+	var gofiles []*goast.File
+	if len(gosrc) > 0 {
+		f, err := goparser.ParseFile(fset, "main.go", gosrc, goparser.ParseComments)
+		if err != nil {
+			return nil, nil, err
+		}
+		gofiles = append(gofiles, f)
+	}
+
+	conf := &types.Config{}
+	conf.Importer = gop.NewImporter(nil, &env.Gop{Root: "../..", Version: "1.0"}, fset)
+	chkOpts := &typesutil.Config{
+		Types: types.NewPackage("main", "main"),
+		Fset:  fset,
+		Mod:   gopmod.Default,
+	}
+	info := &typesutil.Info{
+		Types:      make(map[ast.Expr]types.TypeAndValue),
+		Defs:       make(map[*ast.Ident]types.Object),
+		Uses:       make(map[*ast.Ident]types.Object),
+		Implicits:  make(map[ast.Node]types.Object),
+		Selections: make(map[*ast.SelectorExpr]*types.Selection),
+		Scopes:     make(map[ast.Node]*types.Scope),
+	}
+	ginfo := &types.Info{
+		Types:      make(map[goast.Expr]types.TypeAndValue),
+		Defs:       make(map[*goast.Ident]types.Object),
+		Uses:       make(map[*goast.Ident]types.Object),
+		Implicits:  make(map[goast.Node]types.Object),
+		Selections: make(map[*goast.SelectorExpr]*types.Selection),
+		Scopes:     make(map[goast.Node]*types.Scope),
+	}
+	check := typesutil.NewChecker(conf, chkOpts, ginfo, info)
+	err = check.Files(gofiles, []*ast.File{f})
+	return info, ginfo, err
+}
+
 func parserSource(fset *token.FileSet, filename string, src interface{}, mode parser.Mode) (*typesutil.Info, error) {
 	f, err := parser.ParseEntry(fset, filename, src, parser.Config{
 		Mode: mode,
@@ -73,6 +119,26 @@ func parserGoSource(fset *token.FileSet, filename string, src interface{}, mode
 	return info, err
 }
 
+func testGopInfo(t *testing.T, src string, gosrc string, expect string) {
+	fset := token.NewFileSet()
+	info, _, err := parserMixedSource(fset, src, gosrc)
+	if err != nil {
+		t.Fatal("parserMixedSource error", err)
+	}
+	var list []string
+	list = append(list, "== types ==")
+	list = append(list, typesList(fset, info.Types, false)...)
+	list = append(list, "== defs ==")
+	list = append(list, defsList(fset, info.Defs, true)...)
+	list = append(list, "== uses ==")
+	list = append(list, usesList(fset, info.Uses)...)
+	result := strings.Join(list, "\n")
+	t.Log(result)
+	if result != expect {
+		t.Fatal("bad expect\n", expect)
+	}
+}
+
 func testInfo(t *testing.T, src interface{}) {
 	fset := token.NewFileSet()
 	info, err := parserSource(fset, "main.gop", src, parser.ParseComments)
@@ -440,3 +506,226 @@ func test() {
 }
 `)
 }
+
+func TestSliceLit(t *testing.T) {
+	testGopInfo(t, `
+a := [100,200]
+println a
+`, ``, `== types ==
+000:  2: 7 | 100                 *ast.BasicLit                  | value   : untyped int = 100 | constant
+001:  2:11 | 200                 *ast.BasicLit                  | value   : untyped int = 200 | constant
+002:  3: 1 | println             *ast.Ident                     | builtin : invalid type | built-in
+003:  3: 1 | println a           *ast.CallExpr                  | value   : (n int, err error) | value
+004:  3: 9 | a                   *ast.Ident                     | var     : []int | variable
+== defs ==
+000:  2: 1 | a                   | var a []int
+001:  2: 1 | main                | func main.main()
+== uses ==
+000:  3: 1 | println             | builtin println
+001:  3: 9 | a                   | var a []int`)
+}
+
+func TestForPhrase1(t *testing.T) {
+	testGopInfo(t, `
+sum := 0
+for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {
+	sum = sum + x
+}
+println sum
+`, ``, `== types ==
+000:  2: 8 | 0                   *ast.BasicLit                  | value   : untyped int = 0 | constant
+001:  3:11 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+002:  3:14 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant
+003:  3:17 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant
+004:  3:20 | 7                   *ast.BasicLit                  | value   : untyped int = 7 | constant
+005:  3:23 | 11                  *ast.BasicLit                  | value   : untyped int = 11 | constant
+006:  3:27 | 13                  *ast.BasicLit                  | value   : untyped int = 13 | constant
+007:  3:31 | 17                  *ast.BasicLit                  | value   : untyped int = 17 | constant
+008:  3:36 | x                   *ast.Ident                     | var     : int | variable
+009:  3:36 | x > 3               *ast.BinaryExpr                | value   : untyped bool | value
+010:  3:40 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant
+011:  4: 2 | sum                 *ast.Ident                     | var     : int | variable
+012:  4: 8 | sum                 *ast.Ident                     | var     : int | variable
+013:  4: 8 | sum + x             *ast.BinaryExpr                | value   : int | value
+014:  4:14 | x                   *ast.Ident                     | var     : int | variable
+015:  6: 1 | println             *ast.Ident                     | builtin : invalid type | built-in
+016:  6: 1 | println sum         *ast.CallExpr                  | value   : (n int, err error) | value
+017:  6: 9 | sum                 *ast.Ident                     | var     : int | variable
+== defs ==
+000:  2: 1 | main                | func main.main()
+001:  2: 1 | sum                 | var sum int
+002:  3: 5 | x                   | var x int
+== uses ==
+000:  3:36 | x                   | var x int
+001:  4: 2 | sum                 | var sum int
+002:  4: 8 | sum                 | var sum int
+003:  4:14 | x                   | var x int
+004:  6: 1 | println             | builtin println
+005:  6: 9 | sum                 | var sum int`)
+}
+
+func TestForPhrase2(t *testing.T) {
+	testGopInfo(t, `
+sum := 0
+for i, x <- [1, 3, 5, 7, 11, 13, 17], i%2 == 1 && x > 3 {
+	sum = sum + x
+}
+println sum
+`, ``, `== types ==
+000:  2: 8 | 0                   *ast.BasicLit                  | value   : untyped int = 0 | constant
+001:  3:14 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+002:  3:17 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant
+003:  3:20 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant
+004:  3:23 | 7                   *ast.BasicLit                  | value   : untyped int = 7 | constant
+005:  3:26 | 11                  *ast.BasicLit                  | value   : untyped int = 11 | constant
+006:  3:30 | 13                  *ast.BasicLit                  | value   : untyped int = 13 | constant
+007:  3:34 | 17                  *ast.BasicLit                  | value   : untyped int = 17 | constant
+008:  3:39 | i                   *ast.Ident                     | var     : int | variable
+009:  3:39 | i % 2               *ast.BinaryExpr                | value   : int | value
+010:  3:39 | i%2 == 1            *ast.BinaryExpr                | value   : untyped bool | value
+011:  3:39 | i%2 == 1 && x > 3   *ast.BinaryExpr                | value   : untyped bool | value
+012:  3:41 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant
+013:  3:46 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+014:  3:51 | x                   *ast.Ident                     | var     : int | variable
+015:  3:51 | x > 3               *ast.BinaryExpr                | value   : untyped bool | value
+016:  3:55 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant
+017:  4: 2 | sum                 *ast.Ident                     | var     : int | variable
+018:  4: 8 | sum                 *ast.Ident                     | var     : int | variable
+019:  4: 8 | sum + x             *ast.BinaryExpr                | value   : int | value
+020:  4:14 | x                   *ast.Ident                     | var     : int | variable
+021:  6: 1 | println             *ast.Ident                     | builtin : invalid type | built-in
+022:  6: 1 | println sum         *ast.CallExpr                  | value   : (n int, err error) | value
+023:  6: 9 | sum                 *ast.Ident                     | var     : int | variable
+== defs ==
+000:  2: 1 | main                | func main.main()
+001:  2: 1 | sum                 | var sum int
+002:  3: 5 | i                   | var i int
+003:  3: 8 | x                   | var x int
+== uses ==
+000:  3:39 | i                   | var i int
+001:  3:51 | x                   | var x int
+002:  4: 2 | sum                 | var sum int
+003:  4: 8 | sum                 | var sum int
+004:  4:14 | x                   | var x int
+005:  6: 1 | println             | builtin println
+006:  6: 9 | sum                 | var sum int`)
+}
+
+func TestMapComprehension(t *testing.T) {
+	testGopInfo(t, `
+y := {x: i for i, x <- ["1", "3", "5", "7", "11"]}
+println y
+`, ``, `== types ==
+000:  2: 7 | x                   *ast.Ident                     | var     : string | variable
+001:  2:10 | i                   *ast.Ident                     | var     : int | variable
+002:  2:25 | "1"                 *ast.BasicLit                  | value   : untyped string = "1" | constant
+003:  2:30 | "3"                 *ast.BasicLit                  | value   : untyped string = "3" | constant
+004:  2:35 | "5"                 *ast.BasicLit                  | value   : untyped string = "5" | constant
+005:  2:40 | "7"                 *ast.BasicLit                  | value   : untyped string = "7" | constant
+006:  2:45 | "11"                *ast.BasicLit                  | value   : untyped string = "11" | constant
+007:  3: 1 | println             *ast.Ident                     | builtin : invalid type | built-in
+008:  3: 1 | println y           *ast.CallExpr                  | value   : (n int, err error) | value
+009:  3: 9 | y                   *ast.Ident                     | var     : map[string]int | variable
+== defs ==
+000:  2: 1 | main                | func main.main()
+001:  2: 1 | y                   | var y map[string]int
+002:  2:16 | i                   | var i int
+003:  2:19 | x                   | var x string
+== uses ==
+000:  2: 7 | x                   | var x string
+001:  2:10 | i                   | var i int
+002:  3: 1 | println             | builtin println
+003:  3: 9 | y                   | var y map[string]int`)
+}
+
+func TestListComprehension(t *testing.T) {
+	testGopInfo(t, `
+a := [1, 3.4, 5]
+b := [x*x for x <- a]
+_ = b
+`, ``, `== types ==
+000:  2: 7 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+001:  2:10 | 3.4                 *ast.BasicLit                  | value   : untyped float = 3.4 | constant
+002:  2:15 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant
+003:  3: 7 | x                   *ast.Ident                     | var     : float64 | variable
+004:  3: 7 | x * x               *ast.BinaryExpr                | value   : float64 | value
+005:  3: 9 | x                   *ast.Ident                     | var     : float64 | variable
+006:  3:20 | a                   *ast.Ident                     | var     : []float64 | variable
+007:  4: 5 | b                   *ast.Ident                     | var     : []float64 | variable
+== defs ==
+000:  2: 1 | a                   | var a []float64
+001:  2: 1 | main                | func main.main()
+002:  3: 1 | b                   | var b []float64
+003:  3:15 | x                   | var x float64
+== uses ==
+000:  3: 7 | x                   | var x float64
+001:  3: 9 | x                   | var x float64
+002:  3:20 | a                   | var a []float64
+003:  4: 5 | b                   | var b []float64`)
+}
+
+func TestListComprehensionMultiLevel(t *testing.T) {
+	testGopInfo(t, `
+arr := [1, 2, 3, 4.1, 5, 6]
+x := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+println("x:", x)
+`, ``, `== types ==
+000:  2: 9 | 1                   *ast.BasicLit                  | value   : untyped int = 1 | constant
+001:  2:12 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant
+002:  2:15 | 3                   *ast.BasicLit                  | value   : untyped int = 3 | constant
+003:  2:18 | 4.1                 *ast.BasicLit                  | value   : untyped float = 4.1 | constant
+004:  2:23 | 5                   *ast.BasicLit                  | value   : untyped int = 5 | constant
+005:  2:26 | 6                   *ast.BasicLit                  | value   : untyped int = 6 | constant
+006:  3: 8 | a                   *ast.Ident                     | var     : float64 | variable
+007:  3:11 | b                   *ast.Ident                     | var     : float64 | variable
+008:  3:23 | arr                 *ast.Ident                     | var     : []float64 | variable
+009:  3:28 | a                   *ast.Ident                     | var     : float64 | variable
+010:  3:28 | a < b               *ast.BinaryExpr                | value   : untyped bool | value
+011:  3:32 | b                   *ast.Ident                     | var     : float64 | variable
+012:  3:43 | arr                 *ast.Ident                     | var     : []float64 | variable
+013:  3:48 | b                   *ast.Ident                     | var     : float64 | variable
+014:  3:48 | b > 2               *ast.BinaryExpr                | value   : untyped bool | value
+015:  3:52 | 2                   *ast.BasicLit                  | value   : untyped int = 2 | constant
+016:  4: 1 | println             *ast.Ident                     | builtin : invalid type | built-in
+017:  4: 1 | println("x:", x)    *ast.CallExpr                  | value   : (n int, err error) | value
+018:  4: 9 | "x:"                *ast.BasicLit                  | value   : untyped string = "x:" | constant
+019:  4:15 | x                   *ast.Ident                     | var     : [][]float64 | variable
+== defs ==
+000:  2: 1 | arr                 | var arr []float64
+001:  2: 1 | main                | func main.main()
+002:  3: 1 | x                   | var x [][]float64
+003:  3:18 | a                   | var a float64
+004:  3:38 | b                   | var b float64
+== uses ==
+000:  3: 8 | a                   | var a float64
+001:  3:11 | b                   | var b float64
+002:  3:23 | arr                 | var arr []float64
+003:  3:28 | a                   | var a float64
+004:  3:32 | b                   | var b float64
+005:  3:43 | arr                 | var arr []float64
+006:  3:48 | b                   | var b float64
+007:  4: 1 | println             | builtin println
+008:  4:15 | x                   | var x [][]float64`)
+}
+
+func TestFileEnumLines(t *testing.T) {
+	testGopInfo(t, `
+import "os"
+
+for line <- os.Stdin {
+	println line
+}
+`, ``, `== types ==
+000:  4:13 | os.Stdin            *ast.SelectorExpr              | var     : *os.File | variable
+001:  5: 2 | println             *ast.Ident                     | builtin : invalid type | built-in
+002:  5: 2 | println line        *ast.CallExpr                  | value   : (n int, err error) | value
+003:  5:10 | line                *ast.Ident                     | var     : string | variable
+== defs ==
+000:  4: 1 | main                | func main.main()
+001:  4: 5 | line                | var line string
+== uses ==
+000:  4:13 | os                  | package os
+001:  4:16 | Stdin               | var os.Stdin *os.File
+002:  5: 2 | println             | builtin println
+003:  5:10 | line                | var line string`)
+}