Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions testdata/src/g/g.go
Original file line number Diff line number Diff line change
@@ -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>"
Value T // required
Label string
}

// Generic struct with multiple required fields.
type Pair[K comparable, V any] struct { // want Pair:"required<Key, Value>"
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>"
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>"
ID int // required
Children []ConcreteNode
}

func concreteNode() {
fmt.Println(ConcreteNode{}) // want "missing required fields: ID"
fmt.Println(ConcreteNode{ID: 1})
}
16 changes: 16 additions & 0 deletions testdata/src/g/g_go1.26.go
Original file line number Diff line number Diff line change
@@ -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>"
ID int // required
Children []N
}

func selfReferencing() {
fmt.Println(ConcreteNode{}) // want "missing required fields: ID"
fmt.Println(ConcreteNode{ID: 1})
}
126 changes: 126 additions & 0 deletions testdata/src/h/h.go
Original file line number Diff line number Diff line change
@@ -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<Container>"
g.Container[int] // required
}

// Struct that embeds a generic alias (marked required).
type EmbedAlias struct { // want EmbedAlias:"required<IntContainer>"
IntContainer // required
}

// Struct that embeds a pointer to a generic type.
type EmbedGenericPtr struct { // want EmbedGenericPtr:"required<Container>"
*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})
}