From fc4a0607a2154899c6db81dd0177a266e4b9f551 Mon Sep 17 00:00:00 2001 From: Bob Glickstein Date: Thu, 11 Dec 2025 19:46:34 -0800 Subject: [PATCH 1/2] Bug 27 regression test. --- _testdata/bug27/go.mod | 3 +++ _testdata/bug27/main.go | 8 ++++++++ hist.go | 3 +++ regression_test.go | 12 ++++++++++++ 4 files changed, 26 insertions(+) create mode 100644 _testdata/bug27/go.mod create mode 100644 _testdata/bug27/main.go diff --git a/_testdata/bug27/go.mod b/_testdata/bug27/go.mod new file mode 100644 index 0000000..d2fac6f --- /dev/null +++ b/_testdata/bug27/go.mod @@ -0,0 +1,3 @@ +module bug27 + +go 1.23 diff --git a/_testdata/bug27/main.go b/_testdata/bug27/main.go new file mode 100644 index 0000000..c95a0b8 --- /dev/null +++ b/_testdata/bug27/main.go @@ -0,0 +1,8 @@ +package main + +import "crypto/tls" + +func main() { + var foo tls.QUICSessionTicketOptions + print(foo.Extra) +} diff --git a/hist.go b/hist.go index 5c4e0f9..2eb1fca 100644 --- a/hist.go +++ b/hist.go @@ -48,6 +48,9 @@ func (p pkgHistory) lookup(id, typ string) int { if typ == "" { return p.ids[id] } + if idx := strings.LastIndex(typ, "."); idx >= 0 { // xxx can we make this unnecessary? + typ = typ[idx+1:] + } if t, ok := p.types[typ]; ok { return t[id] } diff --git a/regression_test.go b/regression_test.go index 76e9a77..af59b66 100644 --- a/regression_test.go +++ b/regression_test.go @@ -13,3 +13,15 @@ func TestBug14(t *testing.T) { t.Errorf("got %d, want 16", v) } } + +// https://github.com/bobg/mingo/issues/27 +func TestBug27(t *testing.T) { + var s Scanner + res, err := s.ScanDir("_testdata/bug27") + if err != nil { + t.Fatal(err) + } + if v := res.Version(); v != 23 { + t.Errorf("got %d, want 23", v) + } +} From a784552aeb9018478f068be7f53a7b37593a5455 Mon Sep 17 00:00:00 2001 From: Bob Glickstein Date: Thu, 11 Dec 2025 21:14:18 -0800 Subject: [PATCH 2/2] Rewrite selectorExpr, preferring p.info.Selections and switching on SelectionKind. --- .github/workflows/go.yml | 2 +- _testdata/1/bar.go | 6 ++- expr.go | 92 +++++++++++++++++++++++----------------- hist.go | 3 -- 4 files changed, 57 insertions(+), 46 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 5dafb1d..d69641c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -36,7 +36,7 @@ jobs: - name: Modver if: ${{ github.event_name == 'pull_request' }} - uses: bobg/modver@v2.12.1 + uses: bobg/modver@v2.13.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} pull_request_url: https://github.com/${{ github.repository }}/pull/${{ github.event.number }} diff --git a/_testdata/1/bar.go b/_testdata/1/bar.go index e079f8b..a548194 100644 --- a/_testdata/1/bar.go +++ b/_testdata/1/bar.go @@ -1,8 +1,10 @@ type one2 struct{} -func (one2) x() {} +func (one2) x() int { + return 42 +} -func one2func() func() { +func one2func() func() int { var y one2 return y.x } diff --git a/expr.go b/expr.go index f8ae495..0158658 100644 --- a/expr.go +++ b/expr.go @@ -208,9 +208,42 @@ func (p *pkgScanner) selectorExpr(expr *ast.SelectorExpr, isCallFun bool) (bool, return isMax, err } - if obj, ok := p.info.Uses[expr.Sel]; ok && obj != nil { - var typ string - if sig, ok := obj.Type().(*types.Signature); ok { + if sel, ok := p.info.Selections[expr]; ok { + obj := sel.Obj() + if obj == nil { + return false, nil + } + + typ := sel.Recv() + if ptr, ok := typ.(*types.Pointer); ok { + typ = ptr.Elem() + } + typestr := typ.String() + if dot := strings.LastIndex(typestr, "."); dot >= 0 { + typestr = typestr[dot+1:] + } + + pkg := obj.Pkg() + if pkg == nil { + return false, nil + } + pkgpath := pkg.Path() + + switch sel.Kind() { + case types.FieldVal: + v := p.s.lookup(pkgpath, expr.Sel.Name, typestr) + if v == 0 { + return false, nil + } + + selResult := posResult{ + version: v, + pos: p.fset.Position(expr.Pos()), + desc: fmt.Sprintf(`"%s".%s.%s`, pkgpath, typestr, expr.Sel.Name), + } + return p.result(selResult), nil + + case types.MethodVal: if !isCallFun { p.result(posResult{ version: 1, @@ -218,21 +251,31 @@ func (p *pkgScanner) selectorExpr(expr *ast.SelectorExpr, isCallFun bool) (bool, desc: "method used as value", }) } - if recv := sig.Recv(); recv != nil { - recvtype := recv.Type().String() - if lastDot := strings.LastIndex(recvtype, "."); lastDot >= 0 { - typ = recvtype[lastDot+1:] + fallthrough + + case types.MethodExpr: + if v := p.s.lookup(pkgpath, expr.Sel.Name, typestr); v > 0 { + selResult := posResult{ + version: v, + pos: p.fset.Position(expr.Pos()), + desc: fmt.Sprintf(`"%s".%s`, pkgpath, expr.Sel.Name), + } + if p.result(selResult) { + return true, nil } } } + return false, nil + } + if obj, ok := p.info.Uses[expr.Sel]; ok && obj != nil { pkg := obj.Pkg() if pkg == nil { return false, nil } pkgpath := pkg.Path() - if v := p.s.lookup(pkgpath, expr.Sel.Name, typ); v > 0 { + if v := p.s.lookup(pkgpath, expr.Sel.Name, ""); v > 0 { selResult := posResult{ version: v, pos: p.fset.Position(expr.Pos()), @@ -242,40 +285,9 @@ func (p *pkgScanner) selectorExpr(expr *ast.SelectorExpr, isCallFun bool) (bool, return true, nil } } - return false, nil - } - - sel, ok := p.info.Selections[expr] - if !ok { - return false, nil - } - obj := sel.Obj() - if obj == nil { - return false, nil } - pkg := obj.Pkg() - if pkg == nil { - return false, nil - } - pkgpath := pkg.Path() - - typ := sel.Recv() - if ptr, ok := typ.(*types.Pointer); ok { - typ = ptr.Elem() - } - typestr := typ.String() - v := p.s.lookup(pkgpath, expr.Sel.Name, typestr) - if v == 0 { - return false, nil - } - - selResult := posResult{ - version: v, - pos: p.fset.Position(expr.Pos()), - desc: fmt.Sprintf(`"%s".%s.%s`, pkgpath, typestr, expr.Sel.Name), - } - return p.result(selResult), nil + return false, nil } func (p *pkgScanner) indexExpr(expr *ast.IndexExpr) (bool, error) { diff --git a/hist.go b/hist.go index 2eb1fca..5c4e0f9 100644 --- a/hist.go +++ b/hist.go @@ -48,9 +48,6 @@ func (p pkgHistory) lookup(id, typ string) int { if typ == "" { return p.ids[id] } - if idx := strings.LastIndex(typ, "."); idx >= 0 { // xxx can we make this unnecessary? - typ = typ[idx+1:] - } if t, ok := p.types[typ]; ok { return t[id] }