| // Copyright 2022 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package satisfy_test | |
| import ( | |
| "fmt" | |
| "go/ast" | |
| "go/importer" | |
| "go/parser" | |
| "go/token" | |
| "go/types" | |
| "reflect" | |
| "sort" | |
| "testing" | |
| "golang.org/x/tools/internal/testenv" | |
| "golang.org/x/tools/refactor/satisfy" | |
| ) | |
| // This test exercises various operations on core types of type parameters. | |
| // (It also provides pretty decent coverage of the non-generic operations.) | |
| func TestGenericCoreOperations(t *testing.T) { | |
| const src = `package foo | |
| import "unsafe" | |
| type I interface { f() } | |
| type impl struct{} | |
| func (impl) f() {} | |
| // A big pile of single-serving types that implement I. | |
| type A struct{impl} | |
| type B struct{impl} | |
| type C struct{impl} | |
| type D struct{impl} | |
| type E struct{impl} | |
| type F struct{impl} | |
| type G struct{impl} | |
| type H struct{impl} | |
| type J struct{impl} | |
| type K struct{impl} | |
| type L struct{impl} | |
| type M struct{impl} | |
| type N struct{impl} | |
| type O struct{impl} | |
| type P struct{impl} | |
| type Q struct{impl} | |
| type R struct{impl} | |
| type S struct{impl} | |
| type T struct{impl} | |
| type U struct{impl} | |
| type V struct{impl} | |
| type W struct{impl} | |
| type X struct{impl} | |
| type Generic[T any] struct{impl} | |
| func (Generic[T]) g(T) {} | |
| type GI[T any] interface{ | |
| g(T) | |
| } | |
| func _[Slice interface{ []I }](s Slice) Slice { | |
| s[0] = L{} // I <- L | |
| return append(s, A{}) // I <- A | |
| } | |
| func _[Func interface{ func(I) B }](fn Func) { | |
| b := fn(C{}) // I <- C | |
| var _ I = b // I <- B | |
| } | |
| func _[Chan interface{ chan D }](ch Chan) { | |
| var i I | |
| for i = range ch {} // I <- D | |
| _ = i | |
| } | |
| func _[Chan interface{ chan E }](ch Chan) { | |
| var _ I = <-ch // I <- E | |
| } | |
| func _[Chan interface{ chan I }](ch Chan) { | |
| ch <- F{} // I <- F | |
| } | |
| func _[Map interface{ map[G]H }](m Map) { | |
| var k, v I | |
| for k, v = range m {} // I <- G, I <- H | |
| _, _ = k, v | |
| } | |
| func _[Map interface{ map[I]K }](m Map) { | |
| var _ I = m[J{}] // I <- J, I <- K | |
| delete(m, R{}) // I <- R | |
| _, _ = m[J{}] | |
| } | |
| func _[Array interface{ [1]I }](a Array) { | |
| a[0] = M{} // I <- M | |
| } | |
| func _[Array interface{ [1]N }](a Array) { | |
| var _ I = a[0] // I <- N | |
| } | |
| func _[Array interface{ [1]O }](a Array) { | |
| var v I | |
| for _, v = range a {} // I <- O | |
| _ = v | |
| } | |
| func _[ArrayPtr interface{ *[1]P }](a ArrayPtr) { | |
| var v I | |
| for _, v = range a {} // I <- P | |
| _ = v | |
| } | |
| func _[Slice interface{ []Q }](s Slice) { | |
| var v I | |
| for _, v = range s {} // I <- Q | |
| _ = v | |
| } | |
| func _[Func interface{ func() (S, bool) }](fn Func) { | |
| var i I | |
| i, _ = fn() // I <- S | |
| _ = i | |
| } | |
| func _() I { | |
| var _ I = T{} // I <- T | |
| var _ I = Generic[T]{} // I <- Generic[T] | |
| var _ I = Generic[string]{} // I <- Generic[string] | |
| return U{} // I <- U | |
| } | |
| var _ GI[string] = Generic[string]{} // GI[string] <- Generic[string] | |
| // universally quantified constraints: | |
| // the type parameter may appear on the left, the right, or both sides. | |
| func _[T any](g Generic[T]) GI[T] { | |
| return g // GI[T] <- Generic[T] | |
| } | |
| func _[T any]() { | |
| type GI2[T any] interface{ g(string) } | |
| var _ GI2[T] = Generic[string]{} // GI2[T] <- Generic[string] | |
| } | |
| type Gen2[T any] struct{} | |
| func (f Gen2[T]) g(string) { global = f } // GI[string] <- Gen2[T] | |
| var global GI[string] | |
| func _() { | |
| var x [3]V | |
| // golang/go#56227: the finder should visit calls in the unsafe package. | |
| _ = unsafe.Slice(&x[0], func() int { var _ I = x[0]; return 3 }()) // I <- V | |
| } | |
| func _[P ~struct{F I}]() { | |
| _ = P{W{}} | |
| _ = P{F: X{}} | |
| } | |
| // golang/go#77625: don't panic on a method expression of an anonymous interface. | |
| var _ = interface{ m() }.m | |
| ` | |
| got := constraints(t, src) | |
| want := []string{ | |
| "p.GI2[T] <- p.Generic[string]", // implicitly "forall T" quantified | |
| "p.GI[T] <- p.Generic[T]", // implicitly "forall T" quantified | |
| "p.GI[string] <- p.Gen2[T]", // implicitly "forall T" quantified | |
| "p.GI[string] <- p.Generic[string]", | |
| "p.I <- p.A", | |
| "p.I <- p.B", | |
| "p.I <- p.C", | |
| "p.I <- p.D", | |
| "p.I <- p.E", | |
| "p.I <- p.F", | |
| "p.I <- p.G", | |
| "p.I <- p.Generic[p.T]", | |
| "p.I <- p.Generic[string]", | |
| "p.I <- p.H", | |
| "p.I <- p.J", | |
| "p.I <- p.K", | |
| "p.I <- p.L", | |
| "p.I <- p.M", | |
| "p.I <- p.N", | |
| "p.I <- p.O", | |
| "p.I <- p.P", | |
| "p.I <- p.Q", | |
| "p.I <- p.R", | |
| "p.I <- p.S", | |
| "p.I <- p.T", | |
| "p.I <- p.U", | |
| "p.I <- p.V", | |
| "p.I <- p.W", | |
| "p.I <- p.X", | |
| } | |
| if !reflect.DeepEqual(got, want) { | |
| t.Fatalf("found unexpected constraints: got %s, want %s", got, want) | |
| } | |
| } | |
| func TestNewExpr(t *testing.T) { | |
| testenv.NeedsGo1Point(t, 26) | |
| const src = `package p | |
| type I interface{ f() } | |
| type C int | |
| func (C) f() {} | |
| var _ I = new(C(123)) | |
| ` | |
| got := constraints(t, src) | |
| want := []string{ | |
| "p.I <- *p.C", | |
| } | |
| if !reflect.DeepEqual(got, want) { | |
| t.Fatalf("found unexpected constraints: got %s, want %s", got, want) | |
| } | |
| } | |
| // TestIssue79734 is a regression test for a crash caused by | |
| // inappropriate use of the obsolete CoreType operator on operands | |
| // that (legally) have no core type; see go.dev/issue/79734. | |
| func TestIssue79734(t *testing.T) { | |
| const src = `package p | |
| type I interface { f() } | |
| type Key struct{} | |
| func (Key) f() {} | |
| type Val1 struct{} | |
| type Val2 struct{} | |
| func _[Map interface{ map[I]Val1 | map[I]Val2 }](m Map) { | |
| delete(m, Key{}) | |
| } | |
| ` | |
| got := constraints(t, src) | |
| want := []string{ | |
| "p.I <- p.Key", | |
| } | |
| if !reflect.DeepEqual(got, want) { | |
| t.Fatalf("found unexpected constraints: got %s, want %s", got, want) | |
| } | |
| } | |
| func constraints(t *testing.T, src string) []string { | |
| // parse | |
| fset := token.NewFileSet() | |
| f, err := parser.ParseFile(fset, "p.go", src, 0) | |
| if err != nil { | |
| t.Fatal(err) // parse error | |
| } | |
| files := []*ast.File{f} | |
| // type-check | |
| info := &types.Info{ | |
| Types: make(map[ast.Expr]types.TypeAndValue), | |
| Defs: make(map[*ast.Ident]types.Object), | |
| Uses: make(map[*ast.Ident]types.Object), | |
| Implicits: make(map[ast.Node]types.Object), | |
| Instances: make(map[*ast.Ident]types.Instance), | |
| Scopes: make(map[ast.Node]*types.Scope), | |
| Selections: make(map[*ast.SelectorExpr]*types.Selection), | |
| FileVersions: make(map[*ast.File]string), | |
| } | |
| conf := types.Config{ | |
| Importer: importer.Default(), | |
| } | |
| if _, err := conf.Check("p", fset, files, info); err != nil { | |
| t.Fatal(err) // type error | |
| } | |
| // gather constraints | |
| var finder satisfy.Finder | |
| finder.Find(info, files) | |
| var constraints []string | |
| for c := range finder.Result { | |
| constraints = append(constraints, fmt.Sprintf("%v <- %v", c.LHS, c.RHS)) | |
| } | |
| sort.Strings(constraints) | |
| return constraints | |
| } | |
| // avoid panic on ill-typed input | |
| func TestIllTyped73109(t *testing.T) { | |
| const src = `package p | |
| var x, y = f() | |
| var _ = func(a undefined) {} | |
| var g int | |
| func g() {} | |
| ` | |
| // parse | |
| fset := token.NewFileSet() | |
| f, err := parser.ParseFile(fset, "p.go", src, 0) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| files := []*ast.File{f} | |
| // type-check (expecting errors) | |
| info := &types.Info{ | |
| Types: make(map[ast.Expr]types.TypeAndValue), | |
| Defs: make(map[*ast.Ident]types.Object), | |
| Uses: make(map[*ast.Ident]types.Object), | |
| Implicits: make(map[ast.Node]types.Object), | |
| Instances: make(map[*ast.Ident]types.Instance), | |
| Scopes: make(map[ast.Node]*types.Scope), | |
| Selections: make(map[*ast.SelectorExpr]*types.Selection), | |
| FileVersions: make(map[*ast.File]string), | |
| } | |
| conf := types.Config{ | |
| Importer: importer.Default(), | |
| Error: func(err error) {}, // ignore errors | |
| } | |
| conf.Check("p", fset, files, info) | |
| // Convert panic into failure | |
| defer func() { | |
| if r := recover(); r != nil { | |
| t.Fatalf("Finder.Find panicked on ill-typed input: %v", r) | |
| } | |
| }() | |
| // used to panic | |
| var finder satisfy.Finder | |
| finder.Find(info, files) | |
| } |