nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: goplus/gop
Pull request #2186 (2025-03-23 22:01:20)

tpl.List: converts the matching result of listing operator to a flat list


Patch:
diff --git a/tpl/token/token.go b/tpl/token/token.go
index e03f3112..6dd8e6ac 100644
--- a/tpl/token/token.go
+++ b/tpl/token/token.go
@@ -47,16 +47,21 @@ func (tok Token) Len() int {
 // -----------------------------------------------------------------------------
 
 const (
+	// Special tokens
 	ILLEGAL Token = iota
 	EOF
 	COMMENT
 
+	literal_beg
+	// Identifiers and basic type literals
+	// (these tokens stand for classes of literals)
 	IDENT  // main
 	INT    // 12345
 	FLOAT  // 123.45
 	IMAG   // 123.45i
 	CHAR   // 'a'
 	STRING // "abc"
+	literal_end
 
 	ADD = '+'
 	SUB = '-'
diff --git a/tpl/tpl.go b/tpl/tpl.go
index 35d12000..9d0e80d1 100644
--- a/tpl/tpl.go
+++ b/tpl/tpl.go
@@ -206,3 +206,18 @@ func Fdump(w io.Writer, result any, prefix, indent string, omitSemi bool) {
 }
 
 // -----------------------------------------------------------------------------
+
+// List converts the matching result of (R % ",") to a flat list.
+// R % "," means R *("," R)
+func List(this any) []any {
+	in := this.([]any)
+	next := in[1].([]any)
+	ret := make([]any, len(next)+1)
+	ret[0] = in[0]
+	for i, v := range next {
+		ret[i+1] = v.([]any)[1]
+	}
+	return ret
+}
+
+// -----------------------------------------------------------------------------


Test patch:
diff --git a/tpl/token/token_test.go b/tpl/token/token_test.go
index a275f14f..6478fe0f 100644
--- a/tpl/token/token_test.go
+++ b/tpl/token/token_test.go
@@ -17,10 +17,19 @@
 package token
 
 import (
+	"go/token"
 	"testing"
 )
 
 func TestToken(t *testing.T) {
+	if literal_beg != 3 {
+		t.Fatal("literal_beg")
+	}
+	for i := Token(0); i < literal_end; i++ {
+		if i.String() != token.Token(i).String() {
+			t.Fatal("String:", i)
+		}
+	}
 	if IDENT.String() != "IDENT" {
 		t.Fatal("IDENT")
 	}
Repository: goplus/gop
Pull request #2223 (2025-03-30 11:17:50)

tpl/token: RAT, UNIT


Patch:
diff --git a/tpl/cl/compile.go b/tpl/cl/compile.go
index 70085ea4..2b208469 100644
--- a/tpl/cl/compile.go
+++ b/tpl/cl/compile.go
@@ -168,6 +168,8 @@ var (
 		"IMAG":    token.IMAG,
 		"CHAR":    token.CHAR,
 		"STRING":  token.STRING,
+		"RAT":     token.RAT,
+		"UNIT":    token.UNIT,
 	}
 )
 
diff --git a/tpl/scanner/scanner.go b/tpl/scanner/scanner.go
index 898782f0..ca290c1d 100644
--- a/tpl/scanner/scanner.go
+++ b/tpl/scanner/scanner.go
@@ -51,6 +51,7 @@ type Scanner struct {
 	rdOffset   int  // reading offset (position after current character)
 	lineOffset int  // current line offset
 	insertSemi bool // insert a semicolon before next newline
+	needUnit   bool
 
 	// public state - ok to modify
 	ErrorCount int // number of errors encountered
@@ -374,6 +375,11 @@ exponent:
 	if s.ch == 'i' {
 		tok = token.IMAG
 		s.next()
+	} else if s.ch == 'r' {
+		tok = token.RAT
+		s.next()
+	} else if isLetter(s.ch) {
+		s.needUnit = true
 	}
 
 exit:
@@ -634,6 +640,12 @@ scanAgain:
 
 	// determine token value
 	insertSemi := false
+	if s.needUnit { // number with unit
+		insertSemi = true
+		t.Tok, t.Lit = token.UNIT, s.scanIdentifier()
+		s.needUnit = false
+		goto done
+	}
 	switch ch := s.ch; {
 	case isLetter(ch):
 		t.Lit = s.scanIdentifier()
@@ -808,9 +820,10 @@ scanAgain:
 			t.Lit = string(ch)
 		}
 	}
+
+done:
 	if s.mode&NoInsertSemis == 0 {
 		s.insertSemi = insertSemi
 	}
-
 	return
 }
diff --git a/tpl/token/token.go b/tpl/token/token.go
index 6dd8e6ac..dcb61a8e 100644
--- a/tpl/token/token.go
+++ b/tpl/token/token.go
@@ -61,6 +61,8 @@ const (
 	IMAG   // 123.45i
 	CHAR   // 'a'
 	STRING // "abc"
+	RAT    // 3r, 3.4r
+	UNIT   // 1m, 2.3s, 3ms, 4us, 5ns, 6.5m, 7h, 8d, 9w, 10y
 	literal_end
 
 	ADD = '+'
@@ -149,6 +151,8 @@ var tokens = [...]string{
 	IMAG:   "IMAG",
 	CHAR:   "CHAR",
 	STRING: "STRING",
+	RAT:    "RAT",
+	UNIT:   "UNIT",
 
 	ADD: "+",
 	SUB: "-",


Test patch:
diff --git a/tpl/token/token_test.go b/tpl/token/token_test.go
index 6478fe0f..5ed7a5f9 100644
--- a/tpl/token/token_test.go
+++ b/tpl/token/token_test.go
@@ -26,7 +26,11 @@ func TestToken(t *testing.T) {
 		t.Fatal("literal_beg")
 	}
 	for i := Token(0); i < literal_end; i++ {
-		if i.String() != token.Token(i).String() {
+		s := i.String()
+		if s == "RAT" || s == "UNIT" {
+			continue
+		}
+		if s != token.Token(i).String() {
 			t.Fatal("String:", i)
 		}
 	}
@@ -50,4 +54,11 @@ func TestToken(t *testing.T) {
 	if count != 28 {
 		t.Fatal("ForEach:", count)
 	}
+	NewFileSet()
+	ForEach(0, func(tok Token, name string) int {
+		if tok != SHL {
+			t.Fatal("ForEach:", tok)
+		}
+		return Break
+	})
 }
Repository: goplus/gop
Pull request #2245 (2025-04-01 17:29:34)

minispec: CommandStmt/CommandStmt may end with ...


Patch:
diff --git a/doc/spec/mini/mini.gop b/doc/spec/mini/mini.gop
index 68317f72..bc501a7c 100644
--- a/doc/spec/mini/mini.gop
+++ b/doc/spec/mini/mini.gop
@@ -104,7 +104,7 @@ LabeledStmt = IDENT ":" Statement
 
 SimpleStmt = SendStmt | ShortVarDecl | IncDecStmt | Assignment | ExpressionStmt | EmptyStmt
 
-SendStmt = IDENT ?("." IDENT) "<-" LambdaExprList
+SendStmt = IDENT ?("." IDENT) "<-" LambdaExprList ?"..."
 
 ShortVarDecl = IdentifierList ":=" ExpressionList
 
@@ -116,7 +116,7 @@ Assignment = ExpressionList (
 
 ExpressionStmt = Expression
 
-CommandStmt = IDENT ?("." IDENT) SPACE LambdaExprList
+CommandStmt = IDENT ?("." IDENT) SPACE LambdaExprList ?"..."
 
 EmptyStmt = ""
 
diff --git a/tpl/scanner/scanner.go b/tpl/scanner/scanner.go
index 32adb0e2..c9eb27bc 100644
--- a/tpl/scanner/scanner.go
+++ b/tpl/scanner/scanner.go
@@ -799,6 +799,7 @@ scanAgain:
 			if s.ch == '.' && s.peek() == '.' {
 				s.next()
 				s.next() // consume last '.'
+				insertSemi = true
 				t.Tok = token.ELLIPSIS
 			} else {
 				t.Tok = token.PERIOD


Test patch:
diff --git a/tpl/scanner/_testdata/unit/tpl.expect b/tpl/scanner/_testdata/unit/tpl.expect
index 3688b4d5..b6b4ab2f 100644
--- a/tpl/scanner/_testdata/unit/tpl.expect
+++ b/tpl/scanner/_testdata/unit/tpl.expect
@@ -56,3 +56,5 @@
 152 IDENT echo
 157 STRING "Hello, world"
 171 ... 
+174 ; 
+
Repository: goplus/gop
Pull request #2269 (2025-04-08 18:51:11)

cl: getGameClass fix


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 4654ec66..fa28bab0 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -122,7 +122,7 @@ func (p *gmxProject) getGameClass(ctx *pkgCtx) string {
 	if p.gameIsPtr {
 		tname = tname[1:]
 	}
-	if len(ctx.projs) > 1 && !p.hasMain_ {
+	if ctx.nproj > 1 && !p.hasMain_ {
 		tname = stringutil.Capitalize(path.Base(gt.PkgPaths[0])) + tname
 	}
 	return tname
@@ -248,6 +248,9 @@ func loadClass(ctx *pkgCtx, pkg *gogen.Package, file string, f *ast.File, conf *
 		}
 		p.gameClass_ = tname
 		p.hasMain_ = f.HasShadowEntry()
+		if !p.isTest {
+			ctx.nproj++
+		}
 	} else {
 		p.sptypes = append(p.sptypes, tname)
 	}
diff --git a/cl/compile.go b/cl/compile.go
index da63cb07..9ff2915c 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -332,6 +332,7 @@ func doInitMethods(ld *typeLoader) {
 
 type pkgCtx struct {
 	*nodeInterp
+	nproj    int                    // number of non-test projects
 	projs    map[string]*gmxProject // .gmx => project
 	classes  map[*ast.File]*gmxClass
 	overpos  map[string]token.Pos // overload => pos


Test patch:
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 81d52f90..fb1b5f08 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -58,6 +58,7 @@ func TestGetGameClass(t *testing.T) {
 	}
 	ctx := &pkgCtx{
 		projs: map[string]*gmxProject{".gmx": proj, ".spx": proj},
+		nproj: 2,
 	}
 	if v := proj.getGameClass(ctx); v != "BarApp" {
 		t.Fatal("getGameClass:", v)
Repository: goplus/gop
Pull request #2277 (2025-04-11 03:15:32)

cl: remove outdated stuff


Patch:
diff --git a/cl/internal/spx2/spx2.go b/cl/internal/spx2/spx2.go
index 12172a7a..8b83f551 100644
--- a/cl/internal/spx2/spx2.go
+++ b/cl/internal/spx2/spx2.go
@@ -32,6 +32,3 @@ type Sprite struct {
 
 func Sched() {
 }
-
-type Sprite2 struct {
-}


Test patch:
diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go
index 6bc2704e..e77cf60e 100644
--- a/cl/cltest/cltest.go
+++ b/cl/cltest/cltest.go
@@ -77,19 +77,11 @@ func LookupClass(ext string) (c *modfile.Project, ok bool) {
 			Ext: ".tgmx", Class: "*MyGame",
 			Works:    []*modfile.Class{{Ext: ".tspx", Class: "Sprite"}},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx", "math"}}, true
-	case ".t2gmx", ".t2spx": // , ".t2spx2":
+	case ".t2gmx", ".t2spx":
 		return &modfile.Project{
 			Ext: ".t2gmx", Class: "Game",
 			Works: []*modfile.Class{
 				{Ext: ".t2spx", Class: "Sprite"},
-				// {Ext: ".t2spx2", Class: "Sprite2"},
-			},
-			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
-	case "_t3spx.gox", ".t3spx2":
-		return &modfile.Project{
-			Works: []*modfile.Class{
-				// {Ext: "_t3spx.gox", Class: "Sprite"},
-				{Ext: ".t3spx2", Class: "Sprite2"},
 			},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
 	case "_spx.gox":
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 751e1fd2..97c5cea0 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -499,73 +499,6 @@ func main() {
 `, "Game.t2gmx", "Kai.t2spx")
 }
 
-func _TestSpx3(t *testing.T) {
-	gopSpxTestEx(t, `
-println("Hi, Sprite2")
-`, `
-func onMsg(msg string) {
-}
-`, `package main
-
-import (
-	"fmt"
-	"github.com/goplus/gop/cl/internal/spx2"
-)
-
-type Game struct {
-	spx2.Game
-}
-type Kai struct {
-	spx2.Sprite2
-	*Game
-}
-
-func (this *Game) MainEntry() {
-	fmt.Println("Hi, Sprite2")
-}
-func (this *Game) Main() {
-	(*spx2.Game).Main(&this.Game)
-}
-func (this *Kai) onMsg(msg string) {
-}
-func (this *Kai) Main() {
-}
-func main() {
-	new(Game).Main()
-}
-`, "Game.t2gmx", "Kai.t2spx2")
-}
-
-func _TestSpx4(t *testing.T) {
-	gopSpxTestEx(t, `
-println("Hi, Sprite")
-`, `
-func onMsg(msg string) {
-}
-`, `package main
-
-import (
-	"fmt"
-	"github.com/goplus/gop/cl/internal/spx2"
-)
-
-type Dog struct {
-	spx2.Sprite
-}
-type Kai struct {
-	spx2.Sprite2
-}
-
-func (this *Dog) Main() {
-	fmt.Println("Hi, Sprite")
-}
-func (this *Kai) onMsg(msg string) {
-}
-func (this *Kai) Main() {
-}
-`, "Dog_t3spx.gox", "Kai.t3spx2")
-}
-
 func TestSpxMainEntry(t *testing.T) {
 	conf := *cltest.Conf
 	conf.Importer = nil
Repository: goplus/gop
Pull request #2281 (2025-04-12 00:08:07)

cl gmxProjMain: nil if no work


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 3d8f01df..964b6b00 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -484,11 +484,15 @@ func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
 						tslice := sigParams.At(i).Type()
 						tn := tslice.(*types.Slice).Elem().(*types.Named)
 						sp := spriteByProto(sprites, tn.Obj().Name())
-						for _, spt := range sp.types {
-							spto := pkg.Ref(spt)
-							cb.Val(new).Val(spto).Call(1)
+						if n := len(sp.types); n > 0 {
+							for _, spt := range sp.types {
+								spto := pkg.Ref(spt)
+								cb.Val(new).Val(spto).Call(1)
+							}
+							cb.SliceLitEx(tslice, n, false)
+						} else {
+							cb.Val(nil)
 						}
-						cb.SliceLitEx(tslice, len(sp.types), false)
 					}
 				}
 			}
diff --git a/cl/internal/mcp/classfile.go b/cl/internal/mcp/classfile.go
index b5c0de93..a5eb8571 100644
--- a/cl/internal/mcp/classfile.go
+++ b/cl/internal/mcp/classfile.go
@@ -45,6 +45,12 @@ func (p *Prompt) Main(*Tool) string {
 	return ""
 }
 
+type Resource struct {
+}
+
+func (p *Resource) Main() {
+}
+
 type ToolProto interface {
 	Main(name string) int
 }
@@ -53,5 +59,9 @@ type PromptProto interface {
 	Main(*Tool) string
 }
 
-func Gopt_Game_Main(game interface{ initGame() }, tools []ToolProto, prompts []PromptProto) {
+type ResourceProto interface {
+	Main()
+}
+
+func Gopt_Game_Main(game interface{ initGame() }, resources []ResourceProto, tools []ToolProto, prompts []PromptProto) {
 }


Test patch:
diff --git a/cl/_testspx/multiworks/out.go b/cl/_testspx/multiworks/out.go
index 8645b1a7..33c5326a 100644
--- a/cl/_testspx/multiworks/out.go
+++ b/cl/_testspx/multiworks/out.go
@@ -18,7 +18,7 @@ func (this *Game) MainEntry() {
 	this.Server("protos")
 }
 func (this *Game) Main() {
-	mcp.Gopt_Game_Main(this, []mcp.ToolProto{new(hello)}, []mcp.PromptProto{new(foo)})
+	mcp.Gopt_Game_Main(this, nil, []mcp.ToolProto{new(hello)}, []mcp.PromptProto{new(foo)})
 }
 func (this *foo) Main(_gop_arg0 *mcp.Tool) string {
 	this.Prompt.Main(_gop_arg0)
diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go
index e77cf60e..c03c5634 100644
--- a/cl/cltest/cltest.go
+++ b/cl/cltest/cltest.go
@@ -101,6 +101,7 @@ func LookupClass(ext string) (c *modfile.Project, ok bool) {
 			Works: []*modfile.Class{
 				{Ext: "_tool.gox", Class: "Tool", Proto: "ToolProto"},
 				{Ext: "_prompt.gox", Class: "Prompt", Proto: "PromptProto"},
+				{Ext: "_res.gox", Class: "Resource", Proto: "ResourceProto"},
 			},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/mcp"}}, true
 	}
Repository: goplus/gop
Pull request #2304 (2025-04-25 04:50:21)

class framework: gmxProject.embed fix


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 7f7a8380..0b44709f 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -91,7 +91,7 @@ func (p *gmxProject) embed(flds []*types.Var, pkg *gogen.Package) []*types.Var {
 			for _, spt := range sp.types {
 				spto := pkg.Ref(spt)                // work class
 				pt := types.NewPointer(spto.Type()) // pointer to work class
-				flds = append(flds, types.NewField(token.NoPos, pkg.Types, spto.Name(), pt, true))
+				flds = append(flds, types.NewField(token.NoPos, pkg.Types, spto.Name(), pt, false))
 			}
 		}
 	}


Test patch:
diff --git a/cl/_testspx/multiworks/out.go b/cl/_testspx/multiworks/out.go
index 97546d45..aa9d0972 100644
--- a/cl/_testspx/multiworks/out.go
+++ b/cl/_testspx/multiworks/out.go
@@ -12,7 +12,7 @@ type hello struct {
 }
 type Game struct {
 	mcp.Game
-	*foo
+	foo *foo
 }
 
 func (this *Game) MainEntry() {
Repository: goplus/gop
Pull request #2313 (2025-05-05 23:57:37)

cl: exClassNameAndExt (rename 'init' class)


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 0b44709f..f33a50a8 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -205,17 +205,36 @@ var (
 	repl = strings.NewReplacer(":", "", "#", "", "-", "_", ".", "_")
 )
 
-func ClassNameAndExt(file string) (name, clsfile, ext string) {
+func exClassNameAndExt(file string, keepMain bool) (name, clsfile, ext string) {
 	fname := filepath.Base(file)
 	clsfile, ext = modfile.SplitFname(fname)
 	name = clsfile
 	if strings.ContainsAny(name, ":#-.") {
 		name = repl.Replace(name)
+	} else {
+		switch name {
+		case "main":
+			if !keepMain {
+				name = "_main"
+			}
+		case "init":
+			name = "_init"
+		}
 	}
 	return
 }
 
+// ClassNameAndExt func
+// TODO(xsw): to refactor
+//
+// Deprecated: Don't use it
+func ClassNameAndExt(file string) (name, clsfile, ext string) {
+	return exClassNameAndExt(file, true)
+}
+
 // GetFileClassType get ast.File classType
+//
+// Deprecated: Don't use it (to refactor)
 func GetFileClassType(file *ast.File, filename string, lookupClass func(ext string) (c *Project, ok bool)) (classType string, isTest bool) {
 	if file.IsClass {
 		var ext string
@@ -246,7 +265,7 @@ func isGoxTestFile(ext string) bool {
 }
 
 func loadClass(ctx *pkgCtx, pkg *gogen.Package, file string, f *ast.File, conf *Config) *gmxProject {
-	tname, clsfile, ext := ClassNameAndExt(file)
+	tname, clsfile, ext := exClassNameAndExt(file, true)
 	gt, ok := conf.LookupClass(ext)
 	if !ok {
 		panic("class not found: " + ext)
diff --git a/cl/compile.go b/cl/compile.go
index b3400f06..20b3a69d 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -733,10 +733,7 @@ func preloadGopFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, c
 	var parent = ctx.pkgCtx
 	if f.IsClass {
 		if f.IsNormalGox {
-			classType, _, _ = ClassNameAndExt(file)
-			if classType == "main" {
-				classType = "_main"
-			}
+			classType, _, _ = exClassNameAndExt(file, false)
 			if f.ShadowEntry != nil {
 				parent.goxMainClass = classType
 				parent.goxMain++


Test patch:
diff --git a/cl/_testspx/init/init.tspx b/cl/_testspx/init/init.tspx
new file mode 100644
index 00000000..e69de29b
diff --git a/cl/_testspx/init/out.go b/cl/_testspx/init/out.go
new file mode 100644
index 00000000..56ae55c6
--- /dev/null
+++ b/cl/_testspx/init/out.go
@@ -0,0 +1,20 @@
+package main
+
+import "github.com/goplus/gop/cl/internal/spx"
+
+type _init struct {
+	spx.Sprite
+	*MyGame
+}
+type MyGame struct {
+	*spx.MyGame
+}
+
+func (this *MyGame) Main() {
+	spx.Gopt_MyGame_Main(this)
+}
+func (this *_init) Main() {
+}
+func main() {
+	new(MyGame).Main()
+}
Repository: goplus/gop
Pull request #2318 (2025-05-06 02:35:53)

add test command for xgo


Patch:
diff --git a/cmd/xgo/gop_autogen.go b/cmd/xgo/gop_autogen.go
index ca8ce3aa..d91c975b 100644
--- a/cmd/xgo/gop_autogen.go
+++ b/cmd/xgo/gop_autogen.go
@@ -7,6 +7,7 @@ import (
 	"github.com/goplus/cobra/xcmd"
 	install1 "github.com/goplus/gop/cmd/internal/install"
 	run1 "github.com/goplus/gop/cmd/internal/run"
+	test1 "github.com/goplus/gop/cmd/internal/test"
 	"github.com/goplus/gop/env"
 	"github.com/qiniu/x/stringutil"
 	"runtime"
@@ -22,6 +23,10 @@ type run struct {
 	xcmd.Command
 	*App
 }
+type test struct {
+	xcmd.Command
+	*App
+}
 type version struct {
 	xcmd.Command
 	*App
@@ -33,8 +38,9 @@ type App struct {
 func (this *App) Main() {
 	_gop_obj0 := &install{App: this}
 	_gop_obj1 := &run{App: this}
-	_gop_obj2 := &version{App: this}
-	xcmd.Gopt_App_Main(this, _gop_obj0, _gop_obj1, _gop_obj2)
+	_gop_obj2 := &test{App: this}
+	_gop_obj3 := &version{App: this}
+	xcmd.Gopt_App_Main(this, _gop_obj0, _gop_obj1, _gop_obj2, _gop_obj3)
 }
 //line cmd/xgo/install_cmd.gox:20
 func (this *install) Main(_gop_arg0 string) {
@@ -68,6 +74,22 @@ func (this *run) Main(_gop_arg0 string) {
 func (this *run) Classfname() string {
 	return "run"
 }
+//line cmd/xgo/test_cmd.gox:20
+func (this *test) Main(_gop_arg0 string) {
+	this.Command.Main(_gop_arg0)
+//line cmd/xgo/test_cmd.gox:20:1
+	this.Short("Test Go+ packages")
+//line cmd/xgo/test_cmd.gox:22:1
+	this.FlagOff()
+//line cmd/xgo/test_cmd.gox:24:1
+	this.Run__1(func(args []string) {
+//line cmd/xgo/test_cmd.gox:25:1
+		test1.Cmd.Run(test1.Cmd, args)
+	})
+}
+func (this *test) Classfname() string {
+	return "test"
+}
 //line cmd/xgo/version_cmd.gox:21
 func (this *version) Main(_gop_arg0 string) {
 	this.Command.Main(_gop_arg0)
@@ -83,5 +105,6 @@ func (this *version) Classfname() string {
 	return "version"
 }
 func main() {
+//line cmd/xgo/version_cmd.gox:23:1
 	new(App).Main()
 }


Test patch:
diff --git a/cmd/xgo/test_cmd.gox b/cmd/xgo/test_cmd.gox
new file mode 100644
index 00000000..6e980462
--- /dev/null
+++ b/cmd/xgo/test_cmd.gox
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2025 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.
+ */
+
+import (
+	self "github.com/goplus/gop/cmd/internal/test"
+)
+
+short "Test Go+ packages"
+
+flagOff
+
+run args => {
+	self.Cmd.Run self.Cmd, args
+}
Repository: goplus/gop
Pull request #2349 (2025-05-09 12:01:24)

feat(parser)  enhance parser to determine block statement positions and add tests for shadow entry handling
fix: https://github.com/goplus/builder/issues/1626


Patch:
diff --git a/parser/parser.go b/parser/parser.go
index 918c00f9..4e33604b 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -4062,6 +4062,15 @@ func (p *parser) parseGlobalStmts(sync map[token.Token]bool, pos token.Pos, stmt
 	if p.tok != token.EOF {
 		p.errorExpected(p.pos, "statement", 2)
 	}
+
+	// Determine appropriate positions for the block statement
+	// Use file beginning position as fallback when no statements are available
+	startPos := pos
+	endPos := p.pos
+	if len(list) > 0 {
+		startPos = list[0].Pos()
+		endPos = list[len(list)-1].End()
+	}
 	return &ast.FuncDecl{
 		Name: &ast.Ident{NamePos: pos, Name: "main"},
 		Doc:  doc,
@@ -4069,7 +4078,11 @@ func (p *parser) parseGlobalStmts(sync map[token.Token]bool, pos token.Pos, stmt
 			Func:   pos,
 			Params: &ast.FieldList{},
 		},
-		Body:   &ast.BlockStmt{List: list},
+		Body: &ast.BlockStmt{
+			Lbrace: startPos,
+			List:   list,
+			Rbrace: endPos,
+		},
 		Shadow: true,
 	}
 }


Test patch:
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 46e082b1..0d167e8c 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -90,6 +90,27 @@ func testErrCode(t *testing.T, code string, errExp, panicExp string) {
 	}
 }
 
+func testShadowEntry(t *testing.T, code string, errExp string, decl *ast.FuncDecl) {
+	t.Helper()
+	fset := token.NewFileSet()
+	f, err := ParseEntry(fset, "/foo/bar.gop", code, Config{
+		Mode: ParseGoPlusClass | ParseComments | AllErrors,
+	})
+	if err != nil && err.Error() != errExp {
+		t.Fatal("testShadowEntry error:", err)
+	}
+	if err == nil && errExp != "" {
+		t.Fatal("testShadowEntry: nil")
+	}
+
+	if f.ShadowEntry == nil {
+		t.Fatal("testShadowEntry: nil")
+	}
+	if f.ShadowEntry.Body.Lbrace != decl.Body.Lbrace || f.ShadowEntry.Body.Rbrace != decl.Body.Rbrace {
+		t.Fatal("testShadowEntry: brace mismatch", f.ShadowEntry.Body.Lbrace, decl.Body.Lbrace, f.ShadowEntry.Body.Rbrace, decl.Body.Rbrace)
+	}
+}
+
 func testErrCodeParseExpr(t *testing.T, code string, errExp, panicExp string) {
 	defer func() {
 		if e := recover(); e != nil {
@@ -358,4 +379,46 @@ func TestNumberUnitLit(t *testing.T) {
 	p.toIdent(&ast.NumberUnitLit{})
 }
 
+func TestErrGlobalVarWithSyntaxError(t *testing.T) {
+	// Parse the code
+	testShadowEntry(t, `var (
+	foo int.=2
+	sprites []Sprite
+)
+
+func reset() {
+	foo = 10
+	sprites = make([]Sprite, 0)
+}
+
+onStart => {
+	reset()
+}
+`, `/foo/bar.gop:2:10: expected 'IDENT', found '=' (and 21 more errors)`, &ast.FuncDecl{
+		Body: &ast.BlockStmt{
+			Lbrace: 119,
+			Rbrace: 121,
+		},
+	})
+
+	testShadowEntry(t, `var (
+	foo int
+	sprites []Sprite
+)
+
+func reset() {
+	foo = 10
+	sprites = make([]Sprite, 0)
+}
+
+onStart => {
+	reset()
+}`, "", &ast.FuncDecl{
+		Body: &ast.BlockStmt{
+			Lbrace: 94,
+			Rbrace: 117,
+		},
+	})
+}
+
 // -----------------------------------------------------------------------------