diff --git a/testdata/src/g/g.go b/testdata/src/g/g.go new file mode 100644 index 0000000..7637cae --- /dev/null +++ b/testdata/src/g/g.go @@ -0,0 +1,127 @@ +package g + +import ( + "errors" + "fmt" + "math/rand" +) + +// Generic struct with a required field. +type Container[T any] struct { // want Container:"required" + Value T // required + Label string +} + +// Generic struct with multiple required fields. +type Pair[K comparable, V any] struct { // want Pair:"required" + Key K // required + Value V // required +} + +// Generic struct with all optional fields (no diagnostics expected). +type Box[T any] struct { + Contents T + Size int +} + +func basicGeneric() { + // Missing required field. + fmt.Println(Container[int]{}) // want "missing required fields: Value" + fmt.Println(Container[string]{}) // want "missing required fields: Value" + fmt.Println(Container[[]byte]{}) // want "missing required fields: Value" + fmt.Println(Container[int]{Label: "x"}) // want "missing required fields: Value" + + // All required fields set. + fmt.Println(Container[int]{Value: 42}) + fmt.Println(Container[int]{Value: 0, Label: "zero"}) + fmt.Println(Container[string]{Value: ""}) + + // All optional struct is fine empty. + fmt.Println(Box[int]{}) +} + +func multipleRequiredFields() { + fmt.Println(Pair[string, int]{}) // want "missing required fields: Key, Value" + fmt.Println(Pair[string, int]{Key: "x"}) // want "missing required fields: Value" + fmt.Println(Pair[string, int]{Value: 42}) // want "missing required fields: Key" + fmt.Println(Pair[string, int]{Key: "x", Value: 42}) +} + +func pointerToGenericStruct() { + fmt.Println(&Container[int]{}) // want "missing required fields: Value" + fmt.Println(&Container[int]{Value: 1}) + fmt.Println(&Pair[int, int]{}) // want "missing required fields: Key, Value" + fmt.Println(&Pair[int, int]{Key: 1, Value: 2}) +} + +func genericStructInMap() { + // As map value. + fmt.Println(map[string]Container[int]{ + "a": {}, // want "missing required fields: Value" + "b": {Value: 42}, + }) + + // As map key. + fmt.Println(map[Container[int]]string{ + {Value: 1}: "one", + {Label: ""}: "bad", // want "missing required fields: Value" + }) +} + +func genericStructInSlice() { + fmt.Println([]Container[int]{ + {}, // want "missing required fields: Value" + {Value: 42}, + }) +} + +func unkeyedGenericLiteral() { + // Unkeyed literals set all fields, so no diagnostic. + fmt.Println(Container[int]{42, "label"}) + fmt.Println(Pair[string, int]{"key", 42}) +} + +func returnGenericWithError() (Container[int], error) { + switch rand.Int() % 3 { + case 0: + return Container[int]{}, errors.New("fail") // ok: non-nil error + case 1: + return Container[int]{}, nil // want "missing required fields: Value" + default: + return Container[int]{Value: 1}, nil + } +} + +func returnGenericPairWithError() (Pair[string, int], error) { + if rand.Int()%2 == 0 { + return Pair[string, int]{}, errors.New("fail") // ok: non-nil error + } + return Pair[string, int]{}, nil // want "missing required fields: Key, Value" +} + +// Nested generic: a generic struct whose type parameter +// is itself a struct with required fields. +type Wrapper[T any] struct { // want Wrapper:"required" + Inner T // required + Tag string +} + +func nestedGeneric() { + fmt.Println(Wrapper[Container[int]]{}) // want "missing required fields: Inner" + fmt.Println(Wrapper[Container[int]]{ + Inner: Container[int]{}, // want "missing required fields: Value" + }) + fmt.Println(Wrapper[Container[int]]{ + Inner: Container[int]{Value: 1}, + }) +} + +type ConcreteNode struct { // want ConcreteNode:"required" + ID int // required + Children []ConcreteNode +} + +func concreteNode() { + fmt.Println(ConcreteNode{}) // want "missing required fields: ID" + fmt.Println(ConcreteNode{ID: 1}) +} diff --git a/testdata/src/g/g_go1.26.go b/testdata/src/g/g_go1.26.go new file mode 100644 index 0000000..29f1c16 --- /dev/null +++ b/testdata/src/g/g_go1.26.go @@ -0,0 +1,16 @@ +//go:build go1.26 + +package g + +import "fmt" + +// Self-referencing generic type (valid since Go 1.26). +type Node[N Node[N]] struct { // want Node:"required" + ID int // required + Children []N +} + +func selfReferencing() { + fmt.Println(ConcreteNode{}) // want "missing required fields: ID" + fmt.Println(ConcreteNode{ID: 1}) +} diff --git a/testdata/src/h/h.go b/testdata/src/h/h.go new file mode 100644 index 0000000..b9b7c19 --- /dev/null +++ b/testdata/src/h/h.go @@ -0,0 +1,126 @@ +package h + +import ( + "fmt" + "g" +) + +// Non-generic alias to an instantiated generic struct. +type IntContainer = g.Container[int] + +// Parameterized alias forwarding type params. +type Alias[T any] = g.Container[T] + +// Parameterized alias with narrower constraints. +type NumContainer[T interface{ ~int | ~float64 }] = g.Container[T] + +// Chained aliases: parameterized alias to another alias. +type AliasOfAlias[T any] = Alias[T] + +// Chained alias: non-generic alias to a parameterized alias. +type StringAlias = Alias[string] + +// Double chain: non-generic alias to chained alias. +type DoubleChain = AliasOfAlias[bool] + +func nonGenericAliasToGeneric() { + fmt.Println(IntContainer{}) // want "missing required fields: Value" + fmt.Println(IntContainer{Label: "x"}) // want "missing required fields: Value" + fmt.Println(IntContainer{Value: 42}) + fmt.Println(IntContainer{Value: 0, Label: "zero"}) +} + +func parameterizedAlias() { + fmt.Println(Alias[int]{}) // want "missing required fields: Value" + fmt.Println(Alias[string]{}) // want "missing required fields: Value" + fmt.Println(Alias[int]{Value: 1}) + fmt.Println(Alias[string]{Value: "x", Label: "ok"}) +} + +func narrowerConstraintAlias() { + fmt.Println(NumContainer[int]{}) // want "missing required fields: Value" + fmt.Println(NumContainer[float64]{}) // want "missing required fields: Value" + fmt.Println(NumContainer[int]{Value: 42}) +} + +func chainedAliases() { + fmt.Println(AliasOfAlias[int]{}) // want "missing required fields: Value" + fmt.Println(AliasOfAlias[int]{Value: 1}) + + fmt.Println(StringAlias{}) // want "missing required fields: Value" + fmt.Println(StringAlias{Value: "x"}) + + fmt.Println(DoubleChain{}) // want "missing required fields: Value" + fmt.Println(DoubleChain{Value: true}) +} + +func pointerToAliasedGeneric() { + fmt.Println(&IntContainer{}) // want "missing required fields: Value" + fmt.Println(&Alias[int]{}) // want "missing required fields: Value" + fmt.Println(&IntContainer{Value: 1}) + fmt.Println(&Alias[int]{Value: 1}) +} + +// Struct that embeds a generic type (marked required). +type EmbedGeneric struct { // want EmbedGeneric:"required" + g.Container[int] // required +} + +// Struct that embeds a generic alias (marked required). +type EmbedAlias struct { // want EmbedAlias:"required" + IntContainer // required +} + +// Struct that embeds a pointer to a generic type. +type EmbedGenericPtr struct { // want EmbedGenericPtr:"required" + *g.Container[int] // required +} + +func embeddingGenericTypes() { + fmt.Println(EmbedGeneric{}) // want "missing required fields: Container" + fmt.Println(EmbedGeneric{ + Container: g.Container[int]{}, // want "missing required fields: Value" + }) + fmt.Println(EmbedGeneric{ + Container: g.Container[int]{Value: 1}, + }) + + fmt.Println(EmbedAlias{}) // want "missing required fields: IntContainer" + fmt.Println(EmbedAlias{ + IntContainer: IntContainer{}, // want "missing required fields: Value" + }) + fmt.Println(EmbedAlias{ + IntContainer: IntContainer{Value: 1}, + }) + + fmt.Println(EmbedGenericPtr{}) // want "missing required fields: Container" +} + +func genericStructInMapViaAlias() { + fmt.Println(map[string]IntContainer{ + "a": {}, // want "missing required fields: Value" + "b": {Value: 42}, + }) + + fmt.Println(map[string]Alias[int]{ + "x": {}, // want "missing required fields: Value" + "y": {Value: 1}, + }) +} + +// Pair alias to verify multi-field generics through aliases. +type IntPair = g.Pair[string, int] + +func pairAlias() { + fmt.Println(IntPair{}) // want "missing required fields: Key, Value" + fmt.Println(IntPair{Key: "x"}) // want "missing required fields: Value" + fmt.Println(IntPair{Key: "x", Value: 42}) +} + +// Cross-package generic usage without aliases. +func crossPackageDirect() { + fmt.Println(g.Container[int]{}) // want "missing required fields: Value" + fmt.Println(g.Container[int]{Value: 1}) + fmt.Println(g.Pair[string, int]{}) // want "missing required fields: Key, Value" + fmt.Println(g.Pair[string, int]{Key: "a", Value: 1}) +}