9
9
"go/ast"
10
10
"go/token"
11
11
"go/types"
12
+ "slices"
12
13
"strings"
13
14
14
15
_ "embed"
@@ -140,11 +141,6 @@ func (a *analyzer) findAlias(spec *ast.TypeSpec, declInline bool) {
140
141
}
141
142
}
142
143
143
- if spec .TypeParams != nil {
144
- // TODO(jba): handle generic aliases
145
- return
146
- }
147
-
148
144
// Remember that this is an inlinable alias.
149
145
typ := & goFixInlineAliasFact {}
150
146
lhs := a .pass .TypesInfo .Defs [spec .Name ].(* types.TypeName )
@@ -294,7 +290,7 @@ func (a *analyzer) inlineCall(call *ast.CallExpr, cur cursor.Cursor) {
294
290
}
295
291
296
292
// If tn is the TypeName of an inlinable alias, suggest inlining its use at cur.
297
- func (a * analyzer ) inlineAlias (tn * types.TypeName , cur cursor.Cursor ) {
293
+ func (a * analyzer ) inlineAlias (tn * types.TypeName , curId cursor.Cursor ) {
298
294
inalias , ok := a .inlinableAliases [tn ]
299
295
if ! ok {
300
296
var fact goFixInlineAliasFact
@@ -307,12 +303,17 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, cur cursor.Cursor) {
307
303
return // nope
308
304
}
309
305
310
- // Get the alias's RHS. It has everything we need to format the replacement text.
311
- rhs := tn .Type ().(* types.Alias ).Rhs ()
312
-
306
+ alias := tn .Type ().(* types.Alias )
307
+ // Remember the names of the alias's type params. When we check for shadowing
308
+ // later, we'll ignore these because they won't appear in the replacement text.
309
+ typeParamNames := map [* types.TypeName ]bool {}
310
+ for tp := range alias .TypeParams ().TypeParams () {
311
+ typeParamNames [tp .Obj ()] = true
312
+ }
313
+ rhs := alias .Rhs ()
313
314
curPath := a .pass .Pkg .Path ()
314
- curFile := currentFile (cur )
315
- n := cur .Node ().(* ast.Ident )
315
+ curFile := currentFile (curId )
316
+ id := curId .Node ().(* ast.Ident )
316
317
// We have an identifier A here (n), possibly qualified by a package
317
318
// identifier (sel.n), and an inlinable "type A = rhs" elsewhere.
318
319
//
@@ -324,6 +325,10 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, cur cursor.Cursor) {
324
325
edits []analysis.TextEdit
325
326
)
326
327
for _ , tn := range typenames (rhs ) {
328
+ // Ignore the type parameters of the alias: they won't appear in the result.
329
+ if typeParamNames [tn ] {
330
+ continue
331
+ }
327
332
var pkgPath , pkgName string
328
333
if pkg := tn .Pkg (); pkg != nil {
329
334
pkgPath = pkg .Path ()
@@ -333,9 +338,9 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, cur cursor.Cursor) {
333
338
// The name is in the current package or the universe scope, so no import
334
339
// is required. Check that it is not shadowed (that is, that the type
335
340
// it refers to in rhs is the same one it refers to at n).
336
- scope := a .pass .TypesInfo .Scopes [curFile ].Innermost (n .Pos ()) // n's scope
337
- _ , obj := scope .LookupParent (tn .Name (), n .Pos ()) // what qn.name means in n's scope
338
- if obj != tn { // shadowed
341
+ scope := a .pass .TypesInfo .Scopes [curFile ].Innermost (id .Pos ()) // n's scope
342
+ _ , obj := scope .LookupParent (tn .Name (), id .Pos ()) // what qn.name means in n's scope
343
+ if obj != tn {
339
344
return
340
345
}
341
346
} else if ! analysisinternal .CanImport (a .pass .Pkg .Path (), pkgPath ) {
@@ -345,15 +350,40 @@ func (a *analyzer) inlineAlias(tn *types.TypeName, cur cursor.Cursor) {
345
350
// Use AddImport to add pkgPath if it's not there already. Associate the prefix it assigns
346
351
// with the package path for use by the TypeString qualifier below.
347
352
_ , prefix , eds := analysisinternal .AddImport (
348
- a .pass .TypesInfo , curFile , pkgName , pkgPath , tn .Name (), n .Pos ())
353
+ a .pass .TypesInfo , curFile , pkgName , pkgPath , tn .Name (), id .Pos ())
349
354
importPrefixes [pkgPath ] = strings .TrimSuffix (prefix , "." )
350
355
edits = append (edits , eds ... )
351
356
}
352
357
}
353
- // If n is qualified by a package identifier, we'll need the full selector expression.
354
- var expr ast.Expr = n
355
- if e , _ := cur .Edge (); e == edge .SelectorExpr_Sel {
356
- expr = cur .Parent ().Node ().(ast.Expr )
358
+ // Find the complete identifier, which may take any of these forms:
359
+ // Id
360
+ // Id[T]
361
+ // Id[K, V]
362
+ // pkg.Id
363
+ // pkg.Id[T]
364
+ // pkg.Id[K, V]
365
+ var expr ast.Expr = id
366
+ if e , _ := curId .Edge (); e == edge .SelectorExpr_Sel {
367
+ curId = curId .Parent ()
368
+ expr = curId .Node ().(ast.Expr )
369
+ }
370
+ // If expr is part of an IndexExpr or IndexListExpr, we'll need that node.
371
+ // Given C[int], TypeOf(C) is generic but TypeOf(C[int]) is instantiated.
372
+ switch ek , _ := curId .Edge (); ek {
373
+ case edge .IndexExpr_X :
374
+ expr = curId .Parent ().Node ().(* ast.IndexExpr )
375
+ case edge .IndexListExpr_X :
376
+ expr = curId .Parent ().Node ().(* ast.IndexListExpr )
377
+ }
378
+ t := a .pass .TypesInfo .TypeOf (expr ).(* types.Alias ) // type of entire identifier
379
+ if targs := t .TypeArgs (); targs .Len () > 0 {
380
+ // Instantiate the alias with the type args from this use.
381
+ // For example, given type A = M[K, V], compute the type of the use
382
+ // A[int, Foo] as M[int, Foo].
383
+ // Don't validate instantiation: it can't panic unless we have a bug,
384
+ // in which case seeing the stack trace via telemetry would be helpful.
385
+ instAlias , _ := types .Instantiate (nil , alias , slices .Collect (targs .Types ()), false )
386
+ rhs = instAlias .(* types.Alias ).Rhs ()
357
387
}
358
388
// To get the replacement text, render the alias RHS using the package prefixes
359
389
// we assigned above.
0 commit comments