Repository: goplus/gop
Pull request #1785 (2024-03-04 00:55:38)

cl: types record check selection/index expr is address

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/goplus/gop/pull/1785?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 92.88%. Comparing base [(`ffe6653`)](https://app.codecov.io/gh/goplus/gop/commit/ffe665306b814e8f6a7b7c67d535c929a0660126?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) to head [(`e94f185`)](https://app.codecov.io/gh/goplus/gop/pull/1785?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).
> Report is 23 commits behind head on main.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1785      +/-   ##
==========================================
+ Coverage   92.22%   92.88%   +0.65%     
==========================================
  Files          40       36       -4     
  Lines       11021     9061    -1960     
==========================================
- Hits        10164     8416    -1748     
+ Misses        694      483     -211     
+ Partials      163      162       -1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/goplus/gop/pull/1785?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).


Patch:
diff --git a/cl/recorder.go b/cl/recorder.go
index 3ffb0e02..4b480761 100644
--- a/cl/recorder.go
+++ b/cl/recorder.go
@@ -27,23 +27,29 @@ import (
 )
 
 type goxRecorder struct {
-	rec Recorder
+	rec *typesRecorder
 }
 
 // Member maps identifiers to the objects they denote.
 func (p *goxRecorder) Member(id ast.Node, obj types.Object) {
-	tv := typesutil.NewTypeAndValueForObject(obj)
 	switch v := id.(type) {
 	case *ast.SelectorExpr:
 		sel := v.Sel
 		// TODO: record event for a Go ident
 		if _, ok := fromgo.CheckIdent(sel); !ok {
+			var tv types.TypeAndValue
+			// check v.X call result by value
+			if f, ok := obj.(*types.Var); ok && f.IsField() && p.rec.checkExprByValue(v.X) {
+				tv = typesutil.NewTypeAndValueForValue(obj.Type(), nil, typesutil.Value)
+			} else {
+				tv = typesutil.NewTypeAndValueForObject(obj)
+			}
 			p.rec.Use(sel, obj)
 			p.rec.Type(v, tv)
 		}
 	case *ast.Ident: // it's in a classfile and impossible converted from Go
 		p.rec.Use(v, obj)
-		p.rec.Type(v, tv)
+		p.rec.Type(v, typesutil.NewTypeAndValueForObject(obj))
 	}
 }
 
@@ -71,6 +77,22 @@ type typesRecorder struct {
 	types map[ast.Expr]types.TypeAndValue
 }
 
+func (rec *typesRecorder) checkExprByValue(v ast.Expr) bool {
+	if tv, ok := rec.types[v]; ok {
+		switch v.(type) {
+		case *ast.CallExpr:
+			if _, ok := tv.Type.(*types.Pointer); !ok {
+				return true
+			}
+		default:
+			if tv, ok := rec.types[v]; ok {
+				return !tv.Addressable()
+			}
+		}
+	}
+	return false
+}
+
 func (rec *typesRecorder) Type(expr ast.Expr, tv types.TypeAndValue) {
 	rec.types[expr] = tv
 	rec.Recorder.Type(expr, tv)
@@ -117,12 +139,16 @@ func (rec *typesRecorder) indexExpr(ctx *blockCtx, expr *ast.IndexExpr) {
 			return
 		}
 	}
-	switch expr.X.(type) {
+	op := typesutil.Variable
+	switch e := expr.X.(type) {
 	case *ast.CompositeLit:
-		rec.recordTypeValue(ctx, expr, typesutil.Value)
-	default:
-		rec.recordTypeValue(ctx, expr, typesutil.Variable)
+		op = typesutil.Value
+	case *ast.SelectorExpr:
+		if rec.checkExprByValue(e.X) {
+			op = typesutil.Value
+		}
 	}
+	rec.recordTypeValue(ctx, expr, op)
 }
 
 func (rec *typesRecorder) unaryExpr(ctx *blockCtx, expr *ast.UnaryExpr) {
@@ -168,7 +194,9 @@ func (rec *typesRecorder) recordExpr(ctx *blockCtx, expr ast.Expr, rhs bool) {
 		rec.recordTypeValue(ctx, v, typesutil.Value)
 	case *ast.CallExpr:
 	case *ast.SelectorExpr:
-		rec.recordTypeValue(ctx, v, typesutil.Variable)
+		if _, ok := rec.types[v]; !ok {
+			rec.recordTypeValue(ctx, v, typesutil.Variable)
+		}
 	case *ast.BinaryExpr:
 		rec.recordTypeValue(ctx, v, typesutil.Value)
 	case *ast.UnaryExpr:


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index d4b8a206..5fc9b105 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -4383,3 +4383,57 @@ func main() {
 }
 `)
 }
+
+func TestAddress(t *testing.T) {
+	gopClTest(t, `
+type foo struct{ c int }
+
+func (f foo) ptr() *foo { return &f }
+func (f foo) clone() foo { return f }
+
+type nested struct {
+	f foo
+	a [2]foo
+	s []foo
+}
+
+func _() {
+	getNested := func() nested { return nested{} }
+
+	_ = getNested().f.c
+	_ = getNested().a[0].c
+	_ = getNested().s[0].c
+	_ = getNested().f.ptr().c
+	_ = getNested().f.clone().c
+	_ = getNested().f.clone().ptr().c
+}
+`, `package main
+
+type foo struct {
+	c int
+}
+type nested struct {
+	f foo
+	a [2]foo
+	s []foo
+}
+
+func (f foo) ptr() *foo {
+	return &f
+}
+func (f foo) clone() foo {
+	return f
+}
+func _() {
+	getNested := func() nested {
+		return nested{}
+	}
+	_ = getNested().f.c
+	_ = getNested().a[0].c
+	_ = getNested().s[0].c
+	_ = getNested().f.ptr().c
+	_ = getNested().f.clone().c
+	_ = getNested().f.clone().ptr().c
+}
+`)
+}
diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go
index 8fd27d79..86faca94 100644
--- a/x/typesutil/info_test.go
+++ b/x/typesutil/info_test.go
@@ -1841,3 +1841,41 @@ func TestScopesInfo(t *testing.T) {
 		}
 	}
 }
+
+func TestAddress(t *testing.T) {
+	testInfo(t, `package address
+
+type foo struct{ c int; p *int }
+
+func (f foo) ptr() *foo { return &f }
+func (f foo) clone() foo { return f }
+
+type nested struct {
+	f foo
+	a [2]foo
+	s []foo
+	m map[int]foo
+}
+
+func _() {
+	getNested := func() nested { return nested{} }
+	getNestedPtr := func() *nested { return &nested{} }
+
+	_ = getNested().f.c
+	_ = getNested().a[0].c
+	_ = getNested().s[0].c
+	_ = getNested().m[0].c
+	_ = getNested().f.ptr().c
+	_ = getNested().f.clone().c
+	_ = getNested().f.clone().ptr().c
+
+	_ = getNestedPtr().f.c
+	_ = getNestedPtr().a[0].c
+	_ = getNestedPtr().s[0].c
+	_ = getNestedPtr().m[0].c
+	_ = getNestedPtr().f.ptr().c
+	_ = getNestedPtr().f.clone().c
+	_ = getNestedPtr().f.clone().ptr().c
+}
+`)
+}