Skip to content

Commit

Permalink
Merge branch 'master' into bobg/identical-is-commutative
Browse files Browse the repository at this point in the history
  • Loading branch information
bobg committed Nov 14, 2024
2 parents cbbedc2 + ca281bc commit fa2d7b1
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 112 deletions.
4 changes: 2 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Modver

[![Go Reference](https://pkg.go.dev/badge/github.com/bobg/modver.svg)](https://pkg.go.dev/github.com/bobg/modver/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/bobg/modver)](https://goreportcard.com/report/github.com/bobg/modver)
[![Go Reference](https://pkg.go.dev/badge/github.com/bobg/modver/v2.svg)](https://pkg.go.dev/github.com/bobg/modver/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/bobg/modver/v2)](https://goreportcard.com/report/github.com/bobg/modver/v2)
[![Tests](https://github.com/bobg/modver/actions/workflows/go.yml/badge.svg)](https://github.com/bobg/modver/actions/workflows/go.yml)
[![Coverage Status](https://coveralls.io/repos/github/bobg/modver/badge.svg?branch=master)](https://coveralls.io/github/bobg/modver?branch=master)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
Expand Down
37 changes: 18 additions & 19 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,33 @@ go 1.22

require (
github.com/bobg/errors v1.1.0
github.com/go-git/go-git/v5 v5.6.1
github.com/go-git/go-git/v5 v5.12.0
github.com/google/go-github/v50 v50.2.0
golang.org/x/mod v0.15.0
golang.org/x/oauth2 v0.16.0
golang.org/x/tools v0.17.0
golang.org/x/mod v0.20.0
golang.org/x/oauth2 v0.22.0
golang.org/x/tools v0.24.0
)

require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/cloudflare/circl v1.3.2 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/cloudflare/circl v1.3.9 // indirect
github.com/cyphar/filepath-securejoin v0.3.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.32.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
146 changes: 56 additions & 90 deletions go.sum

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions result.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ func (r ResultCode) String() string {
}
}

func (r ResultCode) MarshalText() ([]byte, error) {
switch r {
case None, Patchlevel, Minor, Major:
return []byte(r.String()), nil
}
return nil, fmt.Errorf("unknown ResultCode value %d", r)
}

func (r *ResultCode) UnmarshalText(text []byte) error {
switch string(text) {
case "None":
*r = None
case "Patchlevel":
*r = Patchlevel
case "Minor":
*r = Minor
case "Major":
*r = Major
default:
return fmt.Errorf("unknown ResultCode value %q", text)
}
return nil
}

type wrapped struct {
r Result
whyfmt string
Expand Down
51 changes: 51 additions & 0 deletions result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package modver

import (
"bytes"
"encoding/json"
"fmt"
"testing"
)

Expand All @@ -21,3 +23,52 @@ func TestPretty(t *testing.T) {
t.Errorf("got %s, want %s", buf, want)
}
}

func TestMarshalResultCode(t *testing.T) {
cases := []struct {
rc ResultCode
want string
wantErr bool
}{{
rc: None,
want: `"None"`,
}, {
rc: Patchlevel,
want: `"Patchlevel"`,
}, {
rc: Minor,
want: `"Minor"`,
}, {
rc: Major,
want: `"Major"`,
}, {
rc: ResultCode(42),
wantErr: true,
}}

for i, tc := range cases {
t.Run(fmt.Sprintf("case_%02d", i+1), func(t *testing.T) {
got, err := json.Marshal(tc.rc)
if err != nil && tc.wantErr {
return
}
if err != nil {
t.Fatal(err)
}
if tc.wantErr {
t.Fatal("got no error but want one")
}
if string(got) != tc.want {
t.Errorf("marshaling: got %s, want %s", string(got), tc.want)
}

var rc ResultCode
if err := json.Unmarshal(got, &rc); err != nil {
t.Fatal(err)
}
if rc != tc.rc {
t.Errorf("unmarshaling: got %v, want %v", rc, tc.rc)
}
})
}
}
20 changes: 20 additions & 0 deletions testdata/minor/addmethod1.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// -*- mode: go -*-

// {{ define "older" }}
package addmethod1

type X interface {
A() int
unexported()
}
// {{ end }}

// {{ define "newer" }}
package addmethod1

type X interface {
A() int
B() string
unexported()
}
// {{ end }}
34 changes: 34 additions & 0 deletions testdata/minor/addmethod2.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// -*- mode: go -*-

// {{ define "older/internal/q.go" }}
package internal

type Q int
// {{ end }}

// {{ define "newer/internal/q.go" }}
package internal

type Q int
// {{ end }}

// {{ define "older" }}
package addmethod2

import "addmethod2/internal"

type X interface {
A() internal.Q
}
// {{ end }}

// {{ define "newer" }}
package addmethod2

import "addmethod2/internal"

type X interface {
A() internal.Q
B() string
}
// {{ end }}
63 changes: 62 additions & 1 deletion types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"go/types"
"reflect"
"regexp"
"strings"

"golang.org/x/tools/go/packages"
)
Expand Down Expand Up @@ -237,7 +238,14 @@ func (c *comparer) compareInterfaces(older, newer *types.Interface) Result {

if c.implements(newer, older) {
if !c.implements(older, newer) {
res = rwrapf(Major, "new interface %s is a superset of older", newer)
switch {
case anyUnexportedMethods(older):
res = rwrapf(Minor, "new interface %s is a superset of older, with unexported methods", newer)
case anyInternalTypes(older):
res = rwrapf(Minor, "new interface %s is a superset of older, using internal types", newer)
default:
res = rwrapf(Major, "new interface %s is a superset of older", newer)
}
}
} else {
return rwrapf(Major, "new interface %s does not implement old", newer)
Expand Down Expand Up @@ -302,6 +310,59 @@ func (c *comparer) compareInterfaces(older, newer *types.Interface) Result {
return rwrapf(Major, "constraint type unions differ")
}

func anyUnexportedMethods(intf *types.Interface) bool {
for i := 0; i < intf.NumMethods(); i++ {
if !ast.IsExported(intf.Method(i).Name()) {
return true
}
}
return false
}

// Do any of the types in the method args or results have "internal" in their pkgpaths?
func anyInternalTypes(intf *types.Interface) bool {
for i := 0; i < intf.NumMethods(); i++ {
sig, ok := intf.Method(i).Type().(*types.Signature)
if !ok {
// Should be impossible.
continue
}
if anyInternalTypesInTuple(sig.Params()) || anyInternalTypesInTuple(sig.Results()) {
return true
}
if recv := sig.Recv(); recv != nil && isInternalType(recv.Type()) {
return true
}
}
return false
}

func anyInternalTypesInTuple(tup *types.Tuple) bool {
for i := 0; i < tup.Len(); i++ {
if isInternalType(tup.At(i).Type()) {
return true
}
}
return false
}

func isInternalType(typ types.Type) bool {
s := types.TypeString(typ, nil)
if strings.HasPrefix(s, "internal.") {
return true
}
if strings.Contains(s, "/internal.") {
return true
}
if strings.Contains(s, "/internal/") {
return true
}
if strings.HasPrefix(s, "main.") {
return true
}
return strings.Contains(s, "/main.")
}

// This takes an interface and flattens its typelists by traversing embeds.
func termsOf(typ types.Type) []*types.Term {
var res []*types.Term
Expand Down

0 comments on commit fa2d7b1

Please sign in to comment.