gotools/internal/apidiff/testdata/tests.go

// This file is split into two packages, old and new.
// It is syntactically valid Go so that gofmt can process it.
//
// If a comment begins with:   Then:
//  old                        write subsequent lines to the "old" package
//  new                        write subsequent lines to the "new" package
//  both                       write subsequent lines to both packages
//  c                          expect a compatible error with the following text
//  i                          expect an incompatible error with the following text
package ignore

// both
import "io"

//////////////// Basics

//// Same type in both: OK.
// both
type A int

//// Changing the type is an incompatible change.
// old
type B int

// new
// i B: changed from int to string
type B string

//// Adding a new type, whether alias or not, is a compatible change.
// new
// c AA: added
type AA = A

// c B1: added
type B1 bool

//// Change of type for an unexported name doesn't matter...
// old
type t int

// new
type t string // OK: t isn't part of the API

//// ...unless it is exposed.
// both
var V2 u

// old
type u string

// new
// i u: changed from string to int
type u int

//// An exposed, unexported type can be renamed.
// both
type u2 int

// old
type u1 int

var V5 u1

// new
var V5 u2 // OK: V5 has changed type, but old u1 corresponds to new u2

//// Splitting a single type into two is an incompatible change.
// both
type u3 int

// old
type (
	Split1 = u1
	Split2 = u1
)

// new
type (
	Split1 = u2 // OK, since old u1 corresponds to new u2

	// This tries to make u1 correspond to u3
	// i Split2: changed from u1 to u3
	Split2 = u3
)

//// Merging two types into one is OK.
// old
type (
	GoodMerge1 = u2
	GoodMerge2 = u3
)

// new
type (
	GoodMerge1 = u3
	GoodMerge2 = u3
)

//// Merging isn't OK here because a method is lost.
// both
type u4 int

func (u4) M() {}

// old
type (
	BadMerge1 = u3
	BadMerge2 = u4
)

// new
type (
	BadMerge1 = u3
	// i u4.M: removed
	// What's really happening here is that old u4 corresponds to new u3,
	// and new u3's method set is not a superset of old u4's.
	BadMerge2 = u3
)

// old
type Rem int

// new
// i Rem: removed

//////////////// Constants

//// type changes
// old
const (
	C1     = 1
	C2 int = 2
	C3     = 3
	C4 u1  = 4
)

var V8 int

// new
const (
	// i C1: changed from untyped int to untyped string
	C1 = "1"
	// i C2: changed from int to untyped int
	C2 = -1
	// i C3: changed from untyped int to int
	C3 int = 3
	// i V8: changed from var to const
	V8 int = 1
	C4 u2  = 4 // OK: u1 corresponds to u2
)

// value change
// old
const (
	Cr1 = 1
	Cr2 = "2"
	Cr3 = 3.5
	Cr4 = complex(0, 4.1)
)

// new
const (
	// i Cr1: value changed from 1 to -1
	Cr1 = -1
	// i Cr2: value changed from "2" to "3"
	Cr2 = "3"
	// i Cr3: value changed from 3.5 to 3.8
	Cr3 = 3.8
	// i Cr4: value changed from (0 + 4.1i) to (4.1 + 0i)
	Cr4 = complex(4.1, 0)
)

//////////////// Variables

//// simple type changes
// old
var (
	V1 string
	V3 A
	V7 <-chan int
)

// new
var (
	// i V1: changed from string to []string
	V1 []string
	V3 A // OK: same
	// i V7: changed from <-chan int to chan int
	V7 chan int
)

//// interface type  changes
// old
var (
	V9  interface{ M() }
	V10 interface{ M() }
	V11 interface{ M() }
)

// new
var (
	// i V9: changed from interface{M()} to interface{}
	V9 interface{}
	// i V10: changed from interface{M()} to interface{M(); M2()}
	V10 interface {
		M2()
		M()
	}
	// i V11: changed from interface{M()} to interface{M(int)}
	V11 interface{ M(int) }
)

//// struct type changes
// old
var (
	VS1 struct{ A, B int }
	VS2 struct{ A, B int }
	VS3 struct{ A, B int }
	VS4 struct {
		A int
		u1
	}
)

// new
var (
	// i VS1: changed from struct{A int; B int} to struct{B int; A int}
	VS1 struct{ B, A int }
	// i VS2: changed from struct{A int; B int} to struct{A int}
	VS2 struct{ A int }
	// i VS3: changed from struct{A int; B int} to struct{A int; B int; C int}
	VS3 struct{ A, B, C int }
	VS4 struct {
		A int
		u2
	}
)

//////////////// Types

// old
const C5 = 3

type (
	A1 [1]int
	A2 [2]int
	A3 [C5]int
)

// new
// i C5: value changed from 3 to 4
const C5 = 4

type (
	A1 [1]int
	// i A2: changed from [2]int to [2]bool
	A2 [2]bool
	// i A3: changed from [3]int to [4]int
	A3 [C5]int
)

// old
type (
	Sl []int
	P1 *int
	P2 *u1
)

// new
type (
	// i Sl: changed from []int to []string
	Sl []string
	// i P1: changed from *int to **bool
	P1 **bool
	P2 *u2 // OK: u1 corresponds to u2
)

// old
type Bc1 int32
type Bc2 uint
type Bc3 float32
type Bc4 complex64

// new
// c Bc1: changed from int32 to int
type Bc1 int

// c Bc2: changed from uint to uint64
type Bc2 uint64

// c Bc3: changed from float32 to float64
type Bc3 float64

// c Bc4: changed from complex64 to complex128
type Bc4 complex128

// old
type Bi1 int32
type Bi2 uint
type Bi3 float64
type Bi4 complex128

// new
// i Bi1: changed from int32 to int16
type Bi1 int16

// i Bi2: changed from uint to uint32
type Bi2 uint32

// i Bi3: changed from float64 to float32
type Bi3 float32

// i Bi4: changed from complex128 to complex64
type Bi4 complex64

// old
type (
	M1 map[string]int
	M2 map[string]int
	M3 map[string]int
)

// new
type (
	M1 map[string]int
	// i M2: changed from map[string]int to map[int]int
	M2 map[int]int
	// i M3: changed from map[string]int to map[string]string
	M3 map[string]string
)

// old
type (
	Ch1 chan int
	Ch2 <-chan int
	Ch3 chan int
	Ch4 <-chan int
)

// new
type (
	// i Ch1, element type: changed from int to bool
	Ch1 chan bool
	// i Ch2: changed direction
	Ch2 chan<- int
	// i Ch3: changed direction
	Ch3 <-chan int
	// c Ch4: removed direction
	Ch4 chan int
)

// old
type I1 interface {
	M1()
	M2()
}

// new
type I1 interface {
	// M1()
	// i I1.M1: removed
	M2(int)
	// i I1.M2: changed from func() to func(int)
	M3()
	// i I1.M3: added
	m()
	// i I1.m: added unexported method
}

// old
type I2 interface {
	M1()
	m()
}

// new
type I2 interface {
	M1()
	// m() Removing an unexported method is OK.
	m2() // OK, because old already had an unexported method
	// c I2.M2: added
	M2()
}

// old
type I3 interface {
	io.Reader
	M()
}

// new
// OK: what matters is the method set; the name of the embedded
// interface isn't important.
type I3 interface {
	M()
	Read([]byte) (int, error)
}

// old
type I4 io.Writer

// new
// OK: in both, I4 is a distinct type from io.Writer, and
// the old and new I4s have the same method set.
type I4 interface {
	Write([]byte) (int, error)
}

// old
type I5 = io.Writer

// new
// i I5: changed from io.Writer to I5
// In old, I5 and io.Writer are the same type; in new,
// they are different. That can break something like:
//   var _ func(io.Writer) = func(pkg.I6) {}
type I5 io.Writer

// old
type I6 interface{ Write([]byte) (int, error) }

// new
// i I6: changed from I6 to io.Writer
// Similar to the above.
type I6 = io.Writer

//// correspondence with a basic type
// Basic types are technically defined types, but they aren't
// represented that way in go/types, so the cases below are special.

// both
type T1 int

// old
var VT1 T1

// new
// i VT1: changed from T1 to int
// This fails because old T1 corresponds to both int and new T1.
var VT1 int

// old
type t2 int

var VT2 t2

// new
// OK: t2 corresponds to int. It's fine that old t2
// doesn't exist in new.
var VT2 int

// both
type t3 int

func (t3) M() {}

// old
var VT3 t3

// new
// i t3.M: removed
// Here the change from t3 to int is incompatible
// because old t3 has an exported method.
var VT3 int

// old
var VT4 int

// new
type t4 int

// i VT4: changed from int to t4
// This is incompatible because of code like
//    VT4 + int(1)
// which works in old but fails in new.
// The difference from the above cases is that
// in those, we were merging two types into one;
// here, we are splitting int into t4 and int.
var VT4 t4

//////////////// Functions

// old
func F1(a int, b string) map[u1]A { return nil }
func F2(int)                      {}
func F3(int)                      {}
func F4(int) int                  { return 0 }
func F5(int) int                  { return 0 }
func F6(int)                      {}
func F7(interface{})              {}

// new
func F1(c int, d string) map[u2]AA { return nil } //OK: same (since u1 corresponds to u2)

// i F2: changed from func(int) to func(int) bool
func F2(int) bool { return true }

// i F3: changed from func(int) to func(int, int)
func F3(int, int) {}

// i F4: changed from func(int) int to func(bool) int
func F4(bool) int { return 0 }

// i F5: changed from func(int) int to func(int) string
func F5(int) string { return "" }

// i F6: changed from func(int) to func(...int)
func F6(...int) {}

// i F7: changed from func(interface{}) to func(interface{x()})
func F7(a interface{ x() }) {}

// old
func F8(bool) {}

// new
// c F8: changed from func to var
var F8 func(bool)

// old
var F9 func(int)

// new
// i F9: changed from var to func
func F9(int) {}

// both
// OK, even though new S1 is incompatible with old S1 (see below)
func F10(S1) {}

//////////////// Structs

// old
type S1 struct {
	A int
	B string
	C bool
	d float32
}

// new
type S1 = s1

type s1 struct {
	C chan int
	// i S1.C: changed from bool to chan int
	A int
	// i S1.B: removed
	// i S1: old is comparable, new is not
	x []int
	d float32
	E bool
	// c S1.E: added
}

// old
type embed struct {
	E string
}

type S2 struct {
	A int
	embed
}

// new
type embedx struct {
	E string
}

type S2 struct {
	embedx // OK: the unexported embedded field changed names, but the exported field didn't
	A      int
}

// both
type F int

// old
type S3 struct {
	A int
	embed
}

// new
type embed struct{ F int }

type S3 struct {
	// i S3.E: removed
	embed
	// c S3.F: added
	A int
}

// old
type embed2 struct {
	embed3
	F // shadows embed3.F
}

type embed3 struct {
	F bool
}

type alias = struct{ D bool }

type S4 struct {
	int
	*embed2
	embed
	E int // shadows embed.E
	alias
	A1
	*S4
}

// new
type S4 struct {
	// OK: removed unexported fields
	// D and F marked as added because they are now part of the immediate fields
	D bool
	// c S4.D: added
	E int // OK: same as in old
	F F
	// c S4.F: added
	A1  // OK: same
	*S4 // OK: same (recursive embedding)
}

//// Difference between exported selectable fields and exported immediate fields.
// both
type S5 struct{ A int }

// old
// Exported immediate fields: A, S5
// Exported selectable fields: A int, S5 S5
type S6 struct {
	S5 S5
	A  int
}

// new
// Exported immediate fields: S5
// Exported selectable fields: A int, S5 S5.

// i S6.A: removed
type S6 struct {
	S5
}

//// Ambiguous fields can exist; they just can't be selected.
// both
type (
	embed7a struct{ E int }
	embed7b struct{ E bool }
)

// old
type S7 struct { // legal, but no selectable fields
	embed7a
	embed7b
}

// new
type S7 struct {
	embed7a
	embed7b
	// c S7.E: added
	E string
}

//////////////// Method sets

// old
type SM struct {
	embedm
	Embedm
}

func (SM) V1() {}
func (SM) V2() {}
func (SM) V3() {}
func (SM) V4() {}
func (SM) v()  {}

func (*SM) P1() {}
func (*SM) P2() {}
func (*SM) P3() {}
func (*SM) P4() {}
func (*SM) p()  {}

type embedm int

func (embedm) EV1()  {}
func (embedm) EV2()  {}
func (embedm) EV3()  {}
func (*embedm) EP1() {}
func (*embedm) EP2() {}
func (*embedm) EP3() {}

type Embedm struct {
	A int
}

func (Embedm) FV()  {}
func (*Embedm) FP() {}

type RepeatEmbedm struct {
	Embedm
}

// new
type SM struct {
	embedm2
	embedm3
	Embedm
	// i SM.A: changed from int to bool
}

// c SMa: added
type SMa = SM

func (SM) V1() {} // OK: same

// func (SM) V2() {}
// i SM.V2: removed

// i SM.V3: changed from func() to func(int)
func (SM) V3(int) {}

// c SM.V5: added
func (SM) V5() {}

func (SM) v(int) {} // OK: unexported method change
func (SM) v2()   {} // OK: unexported method added

func (*SM) P1() {} // OK: same
//func (*SM) P2() {}
// i (*SM).P2: removed

// i (*SM).P3: changed from func() to func(int)
func (*SMa) P3(int) {}

// c (*SM).P5: added
func (*SM) P5() {}

// func (*SM) p() {}  // OK: unexported method removed

// Changing from a value to a pointer receiver or vice versa
// just looks like adding and removing a method.

// i SM.V4: removed
// i (*SM).V4: changed from func() to func(int)
func (*SM) V4(int) {}

// c SM.P4: added
// P4 is not removed from (*SM) because value methods
// are in the pointer method set.
func (SM) P4() {}

type embedm2 int

// i embedm.EV1: changed from func() to func(int)
func (embedm2) EV1(int) {}

// i embedm.EV2, method set of SM: removed
// i embedm.EV2, method set of *SM: removed

// i (*embedm).EP2, method set of *SM: removed
func (*embedm2) EP1() {}

type embedm3 int

func (embedm3) EV3()  {} // OK: compatible with old embedm.EV3
func (*embedm3) EP3() {} // OK: compatible with old (*embedm).EP3

type Embedm struct {
	// i Embedm.A: changed from int to bool
	A bool
}

// i Embedm.FV: changed from func() to func(int)
func (Embedm) FV(int) {}
func (*Embedm) FP()   {}

type RepeatEmbedm struct {
	// i RepeatEmbedm.A: changed from int to bool
	Embedm
}

//////////////// Whole-package interface satisfaction

// old
type WI1 interface {
	M1()
	m1()
}

type WI2 interface {
	M2()
	m2()
}

type WS1 int

func (WS1) M1() {}
func (WS1) m1() {}

type WS2 int

func (WS2) M2() {}
func (WS2) m2() {}

// new
type WI1 interface {
	M1()
	m()
}

type WS1 int

func (WS1) M1() {}

// i WS1: no longer implements WI1
//func (WS1) m1() {}

type WI2 interface {
	M2()
	m2()
	// i WS2: no longer implements WI2
	m3()
}

type WS2 int

func (WS2) M2() {}
func (WS2) m2() {}

//////////////// Miscellany

// This verifies that the code works even through
// multiple levels of unexported typed.

// old
var Z w

type w []x
type x []z
type z int

// new
var Z w

type w []x
type x []z

// i z: changed from int to bool
type z bool

// old
type H struct{}

func (H) M() {}

// new
// i H: changed from struct{} to interface{M()}
type H interface {
	M()
}

//// Splitting types

//// OK: in both old and new, {J1, K1, L1} name the same type.
// old
type (
	J1 = K1
	K1 = L1
	L1 int
)

// new
type (
	J1 = K1
	K1 int
	L1 = J1
)

//// Old has one type, K2; new has J2 and K2.
// both
type K2 int

// old
type J2 = K2

// new
// i K2: changed from K2 to K2
type J2 K2 // old K2 corresponds with new J2
// old K2 also corresponds with new K2: problem

// both
type k3 int

var Vj3 j3 // expose j3

// old
type j3 = k3

// new
// OK: k3 isn't exposed
type j3 k3

// both
type k4 int

var Vj4 j4 // expose j4
var VK4 k4 // expose k4

// old
type j4 = k4

// new
// i Vj4: changed from k4 to j4
// e.g. p.Vj4 = p.Vk4
type j4 k4