Skip to content

Commit c7d2360

Browse files
authored
Add support for generic type aliases (#211)
1 parent 69014e2 commit c7d2360

5 files changed

Lines changed: 210 additions & 17 deletions

File tree

internal/document/document.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -226,28 +226,18 @@ func formatTypeDeclaration(obj *types.TypeName) string {
226226
return fmt.Sprintf("type %s %s", obj.Name(), expandTypeExpr(obj.Pkg(), obj.Type().Underlying()))
227227
}
228228

229-
// qualifiedName returns the name of original, prefixed with its package name
230-
// if it differs from obj's package. Handles nil packages (builtins).
231-
func qualifiedName(obj, original types.Object) string {
232-
objPkg := obj.Pkg()
233-
origPkg := original.Pkg()
234-
if objPkg == nil || origPkg == nil || objPkg.Name() == origPkg.Name() {
235-
return original.Name()
236-
}
237-
return origPkg.Name() + "." + original.Name()
238-
}
239-
240229
// formatAliasDeclaration returns the type declaration for alias types.
241230
func formatAliasDeclaration(obj *types.TypeName) string {
242231
switch ty := obj.Type().(type) {
243232
case *types.Alias:
244-
switch rhs := ty.Rhs().(type) {
245-
case *types.Alias:
246-
return fmt.Sprintf("type %s = %s", obj.Name(), qualifiedName(obj, rhs.Obj()))
247-
case *types.Named:
248-
return fmt.Sprintf("type %s = %s", obj.Name(), qualifiedName(obj, rhs.Obj()))
233+
qual := relativeQualifier(obj.Pkg())
234+
lhs := obj.Name() + formatTypeParamList(ty.TypeParams())
235+
rhs := ty.Rhs()
236+
switch rhs.(type) {
237+
case *types.Alias, *types.Named:
238+
return fmt.Sprintf("type %s = %s", lhs, types.TypeString(rhs, qual))
249239
default:
250-
return fmt.Sprintf("type %s = %s", obj.Name(), expandTypeExpr(obj.Pkg(), rhs))
240+
return fmt.Sprintf("type %s = %s", lhs, expandTypeExpr(obj.Pkg(), rhs))
251241
}
252242
default:
253243
if val := os.Getenv("GODEBUG"); strings.Contains(val, "gotypealias=0") {
@@ -266,6 +256,27 @@ func formatAliasDeclaration(obj *types.TypeName) string {
266256
return fmt.Sprintf("type %s %s", obj.Name(), expandTypeExpr(obj.Pkg(), obj.Type().Underlying()))
267257
}
268258

259+
// formatTypeParamList renders a TypeParamList as "[K C1, V C2]".
260+
// Returns "" when tparams is nil or empty.
261+
func formatTypeParamList(tparams *types.TypeParamList) string {
262+
if tparams == nil || tparams.Len() == 0 {
263+
return ""
264+
}
265+
var buf strings.Builder
266+
buf.WriteByte('[')
267+
for i := range tparams.Len() {
268+
if i > 0 {
269+
buf.WriteString(", ")
270+
}
271+
tp := tparams.At(i)
272+
buf.WriteString(tp.Obj().Name())
273+
buf.WriteByte(' ')
274+
buf.WriteString(tp.Constraint().String())
275+
}
276+
buf.WriteByte(']')
277+
return buf.String()
278+
}
279+
269280
// expandTypeExpr renders a type expression, formatting struct and interface
270281
// types with aligned fields using go/format.
271282
func expandTypeExpr(pkg *types.Package, t types.Type) string {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# pr211 --- Generic type aliases
2+
3+
This test covers generic type alias support, addressing [#112].
4+
5+
Go 1.24 introduced generic type aliases ([go#46477]), allowing type aliases to
6+
carry their own type parameters:
7+
8+
```go
9+
type Set[K comparable] = Map[K, bool]
10+
```
11+
12+
The indexer must correctly:
13+
14+
- Emit definitions for the alias and its type parameters.
15+
- Emit references from the alias's RHS to the aliased type and its type
16+
arguments.
17+
- Render hover documentation that includes the type parameter list.
18+
19+
[#112]: https://github.com/sourcegraph/scip-go/issues/112
20+
[go#46477]: https://github.com/golang/go/issues/46477
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package pr211
2+
3+
// Map is a generic map type.
4+
type Map[K comparable, V any] struct {
5+
entries []entry[K, V]
6+
}
7+
8+
type entry[K comparable, V any] struct {
9+
key K
10+
value V
11+
}
12+
13+
// Set is a generic alias that partially instantiates Map.
14+
type Set[K comparable] = Map[K, bool]
15+
16+
// Alias with a tighter constraint.
17+
type OrderedSet[K ~int | ~string] = Set[K]
18+
19+
// Alias of an alias (chained).
20+
type StringSet = Set[string]
21+
22+
// Alias with all params forwarded.
23+
type PairMap[K comparable, V any] = Map[K, V]
24+
25+
func UseAliases() {
26+
_ = Set[int]{}
27+
_ = OrderedSet[int]{}
28+
_ = StringSet{}
29+
_ = PairMap[string, int]{}
30+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module sg/pr211
2+
3+
go 1.24
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package pr211
2+
// ^^^^^ definition 0.1.test `sg/pr211`/
3+
// display_name pr211
4+
// signature_documentation
5+
// > package pr211
6+
7+
// Map is a generic map type.
8+
type Map[K comparable, V any] struct {
9+
// ^^^ definition 0.1.test `sg/pr211`/Map#
10+
// signature_documentation
11+
// > type Map struct{ entries []entry[K, V] }
12+
// documentation
13+
// > Map is a generic map type.
14+
// ^ definition local 0
15+
// display_name K
16+
// signature_documentation
17+
// > type parameter K comparable
18+
// ^ definition local 1
19+
// display_name V
20+
// signature_documentation
21+
// > type parameter V any
22+
entries []entry[K, V]
23+
// ^^^^^^^ definition 0.1.test `sg/pr211`/Map#entries.
24+
// signature_documentation
25+
// > struct field entries []entry[K, V]
26+
// ^^^^^ reference 0.1.test `sg/pr211`/entry#
27+
// ^ reference local 0
28+
// ^ reference local 1
29+
}
30+
31+
type entry[K comparable, V any] struct {
32+
// ^^^^^ definition 0.1.test `sg/pr211`/entry#
33+
// signature_documentation
34+
// > type entry struct {
35+
// > key K
36+
// > value V
37+
// > }
38+
// ^ definition local 2
39+
// display_name K
40+
// signature_documentation
41+
// > type parameter K comparable
42+
// ^ definition local 3
43+
// display_name V
44+
// signature_documentation
45+
// > type parameter V any
46+
key K
47+
// ^^^ definition 0.1.test `sg/pr211`/entry#key.
48+
// signature_documentation
49+
// > struct field key K
50+
// ^ reference local 2
51+
value V
52+
// ^^^^^ definition 0.1.test `sg/pr211`/entry#value.
53+
// signature_documentation
54+
// > struct field value V
55+
// ^ reference local 3
56+
}
57+
58+
// Set is a generic alias that partially instantiates Map.
59+
type Set[K comparable] = Map[K, bool]
60+
// ^^^ definition 0.1.test `sg/pr211`/Set#
61+
// signature_documentation
62+
// > type Set[K comparable] = Map[K, bool]
63+
// documentation
64+
// > Set is a generic alias that partially instantiates Map.
65+
// ^ definition local 4
66+
// display_name K
67+
// signature_documentation
68+
// > type parameter K comparable
69+
// ^^^ reference 0.1.test `sg/pr211`/Map#
70+
// ^ reference local 4
71+
72+
// Alias with a tighter constraint.
73+
type OrderedSet[K ~int | ~string] = Set[K]
74+
// ^^^^^^^^^^ definition 0.1.test `sg/pr211`/OrderedSet#
75+
// signature_documentation
76+
// > type OrderedSet[K ~int | ~string] = Set[K]
77+
// documentation
78+
// > Alias with a tighter constraint.
79+
// ^ definition local 5
80+
// display_name K
81+
// signature_documentation
82+
// > type parameter K ~int | ~string
83+
// ^^^ reference 0.1.test `sg/pr211`/Set#
84+
// ^ reference local 5
85+
86+
// Alias of an alias (chained).
87+
type StringSet = Set[string]
88+
// ^^^^^^^^^ definition 0.1.test `sg/pr211`/StringSet#
89+
// signature_documentation
90+
// > type StringSet = Set[string]
91+
// documentation
92+
// > Alias of an alias (chained).
93+
// ^^^ reference 0.1.test `sg/pr211`/Set#
94+
95+
// Alias with all params forwarded.
96+
type PairMap[K comparable, V any] = Map[K, V]
97+
// ^^^^^^^ definition 0.1.test `sg/pr211`/PairMap#
98+
// signature_documentation
99+
// > type PairMap[K comparable, V any] = Map[K, V]
100+
// documentation
101+
// > Alias with all params forwarded.
102+
// ^ definition local 6
103+
// display_name K
104+
// signature_documentation
105+
// > type parameter K comparable
106+
// ^ definition local 7
107+
// display_name V
108+
// signature_documentation
109+
// > type parameter V any
110+
// ^^^ reference 0.1.test `sg/pr211`/Map#
111+
// ^ reference local 6
112+
// ^ reference local 7
113+
114+
//⌄ enclosing_range_start 0.1.test `sg/pr211`/UseAliases().
115+
func UseAliases() {
116+
// ^^^^^^^^^^ definition 0.1.test `sg/pr211`/UseAliases().
117+
// signature_documentation
118+
// > func UseAliases()
119+
_ = Set[int]{}
120+
// ^^^ reference 0.1.test `sg/pr211`/Set#
121+
_ = OrderedSet[int]{}
122+
// ^^^^^^^^^^ reference 0.1.test `sg/pr211`/OrderedSet#
123+
_ = StringSet{}
124+
// ^^^^^^^^^ reference 0.1.test `sg/pr211`/StringSet#
125+
_ = PairMap[string, int]{}
126+
// ^^^^^^^ reference 0.1.test `sg/pr211`/PairMap#
127+
}
128+
//⌃ enclosing_range_end 0.1.test `sg/pr211`/UseAliases().
129+

0 commit comments

Comments
 (0)