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
45 changes: 28 additions & 17 deletions internal/document/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,28 +226,18 @@ func formatTypeDeclaration(obj *types.TypeName) string {
return fmt.Sprintf("type %s %s", obj.Name(), expandTypeExpr(obj.Pkg(), obj.Type().Underlying()))
}

// qualifiedName returns the name of original, prefixed with its package name
// if it differs from obj's package. Handles nil packages (builtins).
func qualifiedName(obj, original types.Object) string {
objPkg := obj.Pkg()
origPkg := original.Pkg()
if objPkg == nil || origPkg == nil || objPkg.Name() == origPkg.Name() {
return original.Name()
}
return origPkg.Name() + "." + original.Name()
}

// formatAliasDeclaration returns the type declaration for alias types.
func formatAliasDeclaration(obj *types.TypeName) string {
switch ty := obj.Type().(type) {
case *types.Alias:
switch rhs := ty.Rhs().(type) {
case *types.Alias:
return fmt.Sprintf("type %s = %s", obj.Name(), qualifiedName(obj, rhs.Obj()))
case *types.Named:
return fmt.Sprintf("type %s = %s", obj.Name(), qualifiedName(obj, rhs.Obj()))
qual := relativeQualifier(obj.Pkg())
lhs := obj.Name() + formatTypeParamList(ty.TypeParams())
rhs := ty.Rhs()
switch rhs.(type) {
case *types.Alias, *types.Named:
return fmt.Sprintf("type %s = %s", lhs, types.TypeString(rhs, qual))
default:
return fmt.Sprintf("type %s = %s", obj.Name(), expandTypeExpr(obj.Pkg(), rhs))
return fmt.Sprintf("type %s = %s", lhs, expandTypeExpr(obj.Pkg(), rhs))
}
default:
if val := os.Getenv("GODEBUG"); strings.Contains(val, "gotypealias=0") {
Expand All @@ -266,6 +256,27 @@ func formatAliasDeclaration(obj *types.TypeName) string {
return fmt.Sprintf("type %s %s", obj.Name(), expandTypeExpr(obj.Pkg(), obj.Type().Underlying()))
}

// formatTypeParamList renders a TypeParamList as "[K C1, V C2]".
// Returns "" when tparams is nil or empty.
func formatTypeParamList(tparams *types.TypeParamList) string {
if tparams == nil || tparams.Len() == 0 {
return ""
}
var buf strings.Builder
buf.WriteByte('[')
for i := range tparams.Len() {
if i > 0 {
buf.WriteString(", ")
}
tp := tparams.At(i)
buf.WriteString(tp.Obj().Name())
buf.WriteByte(' ')
buf.WriteString(tp.Constraint().String())
}
buf.WriteByte(']')
return buf.String()
}

// expandTypeExpr renders a type expression, formatting struct and interface
// types with aligned fields using go/format.
func expandTypeExpr(pkg *types.Package, t types.Type) string {
Expand Down
20 changes: 20 additions & 0 deletions internal/testdata/snapshots/input/pr211/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# pr211 --- Generic type aliases

This test covers generic type alias support, addressing [#112].

Go 1.24 introduced generic type aliases ([go#46477]), allowing type aliases to
carry their own type parameters:

```go
type Set[K comparable] = Map[K, bool]
```

The indexer must correctly:

- Emit definitions for the alias and its type parameters.
- Emit references from the alias's RHS to the aliased type and its type
arguments.
- Render hover documentation that includes the type parameter list.

[#112]: https://github.com/sourcegraph/scip-go/issues/112
[go#46477]: https://github.com/golang/go/issues/46477
30 changes: 30 additions & 0 deletions internal/testdata/snapshots/input/pr211/generic_alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package pr211

// Map is a generic map type.
type Map[K comparable, V any] struct {
entries []entry[K, V]
}

type entry[K comparable, V any] struct {
key K
value V
}

// Set is a generic alias that partially instantiates Map.
type Set[K comparable] = Map[K, bool]

// Alias with a tighter constraint.
type OrderedSet[K ~int | ~string] = Set[K]

// Alias of an alias (chained).
type StringSet = Set[string]

// Alias with all params forwarded.
type PairMap[K comparable, V any] = Map[K, V]

func UseAliases() {
_ = Set[int]{}
_ = OrderedSet[int]{}
_ = StringSet{}
_ = PairMap[string, int]{}
}
3 changes: 3 additions & 0 deletions internal/testdata/snapshots/input/pr211/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module sg/pr211

go 1.24
129 changes: 129 additions & 0 deletions internal/testdata/snapshots/output/pr211/generic_alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package pr211
// ^^^^^ definition 0.1.test `sg/pr211`/
// display_name pr211
// signature_documentation
// > package pr211

// Map is a generic map type.
type Map[K comparable, V any] struct {
// ^^^ definition 0.1.test `sg/pr211`/Map#
// signature_documentation
// > type Map struct{ entries []entry[K, V] }
// documentation
// > Map is a generic map type.
// ^ definition local 0
// display_name K
// signature_documentation
// > type parameter K comparable
// ^ definition local 1
// display_name V
// signature_documentation
// > type parameter V any
entries []entry[K, V]
// ^^^^^^^ definition 0.1.test `sg/pr211`/Map#entries.
// signature_documentation
// > struct field entries []entry[K, V]
// ^^^^^ reference 0.1.test `sg/pr211`/entry#
// ^ reference local 0
// ^ reference local 1
}

type entry[K comparable, V any] struct {
// ^^^^^ definition 0.1.test `sg/pr211`/entry#
// signature_documentation
// > type entry struct {
// > key K
// > value V
// > }
// ^ definition local 2
// display_name K
// signature_documentation
// > type parameter K comparable
// ^ definition local 3
// display_name V
// signature_documentation
// > type parameter V any
key K
// ^^^ definition 0.1.test `sg/pr211`/entry#key.
// signature_documentation
// > struct field key K
// ^ reference local 2
value V
// ^^^^^ definition 0.1.test `sg/pr211`/entry#value.
// signature_documentation
// > struct field value V
// ^ reference local 3
}

// Set is a generic alias that partially instantiates Map.
type Set[K comparable] = Map[K, bool]
// ^^^ definition 0.1.test `sg/pr211`/Set#
// signature_documentation
// > type Set[K comparable] = Map[K, bool]
// documentation
// > Set is a generic alias that partially instantiates Map.
// ^ definition local 4
// display_name K
// signature_documentation
// > type parameter K comparable
// ^^^ reference 0.1.test `sg/pr211`/Map#
// ^ reference local 4

// Alias with a tighter constraint.
type OrderedSet[K ~int | ~string] = Set[K]
// ^^^^^^^^^^ definition 0.1.test `sg/pr211`/OrderedSet#
// signature_documentation
// > type OrderedSet[K ~int | ~string] = Set[K]
// documentation
// > Alias with a tighter constraint.
// ^ definition local 5
// display_name K
// signature_documentation
// > type parameter K ~int | ~string
// ^^^ reference 0.1.test `sg/pr211`/Set#
// ^ reference local 5

// Alias of an alias (chained).
type StringSet = Set[string]
// ^^^^^^^^^ definition 0.1.test `sg/pr211`/StringSet#
// signature_documentation
// > type StringSet = Set[string]
// documentation
// > Alias of an alias (chained).
// ^^^ reference 0.1.test `sg/pr211`/Set#

// Alias with all params forwarded.
type PairMap[K comparable, V any] = Map[K, V]
// ^^^^^^^ definition 0.1.test `sg/pr211`/PairMap#
// signature_documentation
// > type PairMap[K comparable, V any] = Map[K, V]
// documentation
// > Alias with all params forwarded.
// ^ definition local 6
// display_name K
// signature_documentation
// > type parameter K comparable
// ^ definition local 7
// display_name V
// signature_documentation
// > type parameter V any
// ^^^ reference 0.1.test `sg/pr211`/Map#
// ^ reference local 6
// ^ reference local 7

//⌄ enclosing_range_start 0.1.test `sg/pr211`/UseAliases().
func UseAliases() {
// ^^^^^^^^^^ definition 0.1.test `sg/pr211`/UseAliases().
// signature_documentation
// > func UseAliases()
_ = Set[int]{}
// ^^^ reference 0.1.test `sg/pr211`/Set#
_ = OrderedSet[int]{}
// ^^^^^^^^^^ reference 0.1.test `sg/pr211`/OrderedSet#
_ = StringSet{}
// ^^^^^^^^^ reference 0.1.test `sg/pr211`/StringSet#
_ = PairMap[string, int]{}
// ^^^^^^^ reference 0.1.test `sg/pr211`/PairMap#
}
//⌃ enclosing_range_end 0.1.test `sg/pr211`/UseAliases().

Loading