Skip to content
Draft
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
20 changes: 18 additions & 2 deletions cl/func_type_and_var.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
if star {
t = types.NewPointer(t)
}
ret := ctx.pkg.NewParam(v.Pos(), name, t)
ret := ctx.pkg.NewRecv(v.Pos(), name, t)

Check failure on line 49 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (macos-latest, 1.24.x)

ctx.pkg.NewRecv undefined (type *gogen.Package has no field or method NewRecv)

Check failure on line 49 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (macos-latest, 1.25.x)

ctx.pkg.NewRecv undefined (type *gogen.Package has no field or method NewRecv)

Check failure on line 49 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.25.x)

ctx.pkg.NewRecv undefined (type *gogen.Package has no field or method NewRecv)

Check failure on line 49 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.24.x)

ctx.pkg.NewRecv undefined (type *gogen.Package has no field or method NewRecv)
if rec := ctx.recorder(); rec != nil {
dRecv := recv.List[0]
if names := dRecv.Names; len(names) == 1 {
Expand Down Expand Up @@ -76,7 +76,7 @@
n := len(flds)
args := make([]*types.Var, 0, n)
for _, fld := range flds {
args = toParam(ctx, fld, args)
args = toResult(ctx, fld, args)
}
return types.NewTuple(args...)
}
Expand Down Expand Up @@ -111,6 +111,22 @@
return args
}

func toResult(ctx *blockCtx, fld *ast.Field, args []*gogen.Param) []*gogen.Param {
typ := toType(ctx, fld.Type)
pkg := ctx.pkg
if len(fld.Names) == 0 {
return append(args, pkg.NewResult(fld.Pos(), "", typ))

Check failure on line 118 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (macos-latest, 1.24.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)

Check failure on line 118 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (macos-latest, 1.25.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)

Check failure on line 118 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.25.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)

Check failure on line 118 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.24.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)
}
for _, name := range fld.Names {
param := pkg.NewResult(name.Pos(), name.Name, typ)

Check failure on line 121 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (macos-latest, 1.24.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)

Check failure on line 121 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (macos-latest, 1.25.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)

Check failure on line 121 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.25.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)

Check failure on line 121 in cl/func_type_and_var.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.24.x)

pkg.NewResult undefined (type *gogen.Package has no field or method NewResult)
args = append(args, param)
if rec := ctx.recorder(); rec != nil {
rec.Def(name, param)
}
Comment on lines +121 to +125

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The variable param is used for a result variable within toResult, which is confusing. Renaming it to result would improve clarity and maintainability.

Suggested change
param := pkg.NewResult(name.Pos(), name.Name, typ)
args = append(args, param)
if rec := ctx.recorder(); rec != nil {
rec.Def(name, param)
}
result := pkg.NewResult(name.Pos(), name.Name, typ)
args = append(args, result)
if rec := ctx.recorder(); rec != nil {
rec.Def(name, result)
}

}
return args
}

// -----------------------------------------------------------------------------

func toType(ctx *blockCtx, typ ast.Expr) (t types.Type) {
Expand Down
309 changes: 309 additions & 0 deletions x/typesutil/varkind_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
//go:build go1.25

package typesutil_test

import (
"fmt"
"go/token"
"go/types"
"slices"
"sort"
"testing"

"github.com/goplus/xgo/x/typesutil"
)

func testXGoVarKind(t *testing.T, name string, src any, want []string) {
fset := token.NewFileSet()
_, info, err := parseSource(fset, name, src, 0)
if err != nil {
t.Fatal("parserSource error", err)
}
testVarKind(t, info, want)
}

func testVarKind(t *testing.T, info *typesutil.Info, want []string) {
var got []string
for _, obj := range info.Defs {
if v, ok := obj.(*types.Var); ok {
got = append(got, fmt.Sprintf("%s: %v", v.Name(), v.Kind()))
}
}
sort.Strings(got)
if !slices.Equal(got, want) {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
}
}

func TestVarKind(t *testing.T) {
src := `package p

var global1 = 100
var global2 int

type T struct { field int }

func (recv T) f(param int) (result int) {
var local int
var local1 = 100
local2 := 0
switch local3 := any(local).(type) {
default:
_ = local3
}
return local2
}
`
want := []string{
"field: FieldVar",
"global1: PackageVar",
"global2: PackageVar",
"local1: LocalVar",
"local2: LocalVar",
"local: LocalVar",
"param: ParamVar",
"recv: RecvVar",
"result: ResultVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestGoxVarKind(t *testing.T) {
src := `
var (
info int
title string
)

var global int

func addInt(a, b int) (c int) {
return a + b
}
`
want := []string{
"a: ParamVar",
"b: ParamVar",
"c: ResultVar",
"global: PackageVar",
"info: FieldVar",
"this: RecvVar",
"title: FieldVar",
}
testXGoVarKind(t, "Rect.gox", src, want)
}

func TestSpxVarKind(t *testing.T) {
src := `
var (
a int
)

type info struct {
x int
y int
}

func onInit() {
a = 1
clone
clone info{1,2}
clone &info{1,2}
}

func onCloned() {
say("Hi")
}

func demo(p1 int, p2 int) (r int) {
return a + p1 + p2
}

`
fset := token.NewFileSet()
_, info, _, err := parseMixedSource(spxMod, fset, "Kai.tspx", src, "", "", spxParserConf(), false)
if err != nil {
t.Fatal("parserSource error", err)
}
want := []string{
"a: FieldVar",
"p1: ParamVar",
"p2: ParamVar",
"r: ResultVar",
"this: RecvVar",
"x: FieldVar",
"y: FieldVar",
}
testVarKind(t, info, want)
}

func TestRangeVarKind(t *testing.T) {
src := `
a := []int{100,200}
for k, v := range a {
_ = k
_ = v
}
var m map[int]string
for k, v := range m {
_ = k
_ = v
}
for v := range m {
_ = v
}
`
want := []string{
"a: LocalVar",
"k: LocalVar",
"k: LocalVar",
"m: LocalVar",
"v: LocalVar",
"v: LocalVar",
"v: LocalVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestForPhraseVarKind(t *testing.T) {
src := `
sum := 0
for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {
sum = sum + x
}
println sum
`
want := []string{
"sum: LocalVar",
"x: LocalVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestMapComprehensionVarKind(t *testing.T) {
src := `
y := {x: i for i, x <- ["1", "3", "5", "7", "11"]}
println y
`
want := []string{
"i: LocalVar",
"x: LocalVar",
"y: LocalVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestErrorWrapVarKind(t *testing.T) {
src := `
import (
"strconv"
)

func add(x, y string) (r int, e error) {
return strconv.atoi(x)? + strconv.atoi(y)?, nil
}

func addSafe(x, y string) int {
return strconv.atoi(x)?:0 + strconv.atoi(y)?:0
}

// Error handling
// We reinvent the error handling specification in XGo. We call them ErrWrap expressions:

// expr! // panic if err
// expr? // return if err
// expr?:defval // use defval if err

n := add("100", "23")!
sum, err := add("10", "abc")
n2 := addSafe("10", "abc")
`
want := []string{
"e: ResultVar",
"err: LocalVar",
"n2: LocalVar",
"n: LocalVar",
"r: ResultVar",
"sum: LocalVar",
"x: ParamVar",
"x: ParamVar",
"y: ParamVar",
"y: ParamVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestTupleVarKind(t *testing.T) {
src := `
type Point (x, y int)

pt := Point(2, 3)
echo pt.x, pt.y

pt = (100, 200)
echo pt

pt2 := Point(pt)
echo pt2

pt3 := Point(y = 5, x = 3)
echo pt3.x, pt3.y
`
want := []string{
"pt2: LocalVar",
"pt3: LocalVar",
"pt: LocalVar",
"x: FieldVar",
"y: FieldVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestDQLXGoVarKind(t *testing.T) {
src := `
doc := xgo` + "`" + `
x, y := "Hi", 123
echo x
print y
` + "`" + `!

stmts := doc.shadowEntry.body.list.*@(self.class == "ExprStmt")
for fn in stmts.x@(self.class == "CallExpr").fun@(self.class == "Ident") {
echo fn.$name
}
`
want := []string{
"doc: LocalVar",
"fn: LocalVar",
"stmts: LocalVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}

func TestKwArgsVarKind(t *testing.T) {
src := `
type Config (timeout, maxRetries int, debug bool)

func run(task int, cfg Config?) {
if cfg.timeout == 0 {
cfg.timeout = 30
}
if cfg.maxRetries == 0 {
cfg.maxRetries = 3
}
echo "timeout:", cfg.timeout, "maxRetries:", cfg.maxRetries, "debug:", cfg.debug
echo "task:", task
}

run 100, timeout = 60, maxRetries = 5
run 200
`
want := []string{
"cfg: VarKind(255)",
"debug: FieldVar",
"maxRetries: FieldVar",
"task: ParamVar",
"timeout: FieldVar",
}
testXGoVarKind(t, "main.xgo", src, want)
}
Loading