@@ -237,9 +237,23 @@ func (fc *funcContext) newConst(t types.Type, value constant.Value) ast.Expr {
237
237
// local variable name. In this context "local" means "in scope of the current"
238
238
// functionContext.
239
239
func (fc * funcContext ) newLocalVariable (name string ) string {
240
- return fc .newVariable (name , false )
240
+ return fc .newVariable (name , varFuncLocal )
241
241
}
242
242
243
+ // varLevel specifies at which level a JavaScript variable should be declared.
244
+ type varLevel int
245
+
246
+ const (
247
+ // A variable defined at a function level (e.g. local variables).
248
+ varFuncLocal = iota
249
+ // A variable that should be declared in a generic type or function factory.
250
+ // This is mainly for type parameters and generic-dependent types.
251
+ varGenericFactory
252
+ // A variable that should be declared in a package factory. This user is for
253
+ // top-level functions, types, etc.
254
+ varPackage
255
+ )
256
+
243
257
// newVariable assigns a new JavaScript variable name for the given Go variable
244
258
// or type.
245
259
//
@@ -252,7 +266,7 @@ func (fc *funcContext) newLocalVariable(name string) string {
252
266
// to this functionContext, as well as all parents, but not to the list of local
253
267
// variables. If false, it is added to this context only, as well as the list of
254
268
// local vars.
255
- func (fc * funcContext ) newVariable (name string , pkgLevel bool ) string {
269
+ func (fc * funcContext ) newVariable (name string , level varLevel ) string {
256
270
if name == "" {
257
271
panic ("newVariable: empty name" )
258
272
}
@@ -261,7 +275,7 @@ func (fc *funcContext) newVariable(name string, pkgLevel bool) string {
261
275
i := 0
262
276
for {
263
277
offset := int ('a' )
264
- if pkgLevel {
278
+ if level == varPackage {
265
279
offset = int ('A' )
266
280
}
267
281
j := i
@@ -286,9 +300,22 @@ func (fc *funcContext) newVariable(name string, pkgLevel bool) string {
286
300
varName = fmt .Sprintf ("%s$%d" , name , n )
287
301
}
288
302
289
- if pkgLevel {
290
- for c2 := fc .parent ; c2 != nil ; c2 = c2 .parent {
291
- c2 .allVars [name ] = n + 1
303
+ // Package-level variables are registered in all outer scopes.
304
+ if level == varPackage {
305
+ for c := fc .parent ; c != nil ; c = c .parent {
306
+ c .allVars [name ] = n + 1
307
+ }
308
+ return varName
309
+ }
310
+
311
+ // Generic-factory level variables are registered in outer scopes up to the
312
+ // level of the generic function or method.
313
+ if level == varGenericFactory {
314
+ for c := fc ; c != nil ; c = c .parent {
315
+ c .allVars [name ] = n + 1
316
+ if c .sigTypes .IsGeneric () {
317
+ break
318
+ }
292
319
}
293
320
return varName
294
321
}
@@ -331,14 +358,20 @@ func isVarOrConst(o types.Object) bool {
331
358
return false
332
359
}
333
360
334
- func isPkgLevel (o types.Object ) bool {
335
- return o .Parent () != nil && o .Parent ().Parent () == types .Universe
361
+ func typeVarLevel (o types.Object ) varLevel {
362
+ if _ , ok := o .Type ().(* types.TypeParam ); ok {
363
+ return varGenericFactory
364
+ }
365
+ if o .Parent () != nil && o .Parent ().Parent () == types .Universe {
366
+ return varPackage
367
+ }
368
+ return varFuncLocal
336
369
}
337
370
338
371
// objectName returns a JS identifier corresponding to the given types.Object.
339
372
// Repeated calls for the same object will return the same name.
340
373
func (fc * funcContext ) objectName (o types.Object ) string {
341
- if isPkgLevel (o ) {
374
+ if typeVarLevel (o ) == varPackage {
342
375
fc .pkgCtx .dependencies [o ] = true
343
376
344
377
if o .Pkg () != fc .pkgCtx .Pkg || (isVarOrConst (o ) && o .Exported ()) {
@@ -348,7 +381,7 @@ func (fc *funcContext) objectName(o types.Object) string {
348
381
349
382
name , ok := fc .pkgCtx .objectNames [o ]
350
383
if ! ok {
351
- name = fc .newVariable (o .Name (), isPkgLevel (o ))
384
+ name = fc .newVariable (o .Name (), typeVarLevel (o ))
352
385
fc .pkgCtx .objectNames [o ] = name
353
386
}
354
387
@@ -359,13 +392,13 @@ func (fc *funcContext) objectName(o types.Object) string {
359
392
}
360
393
361
394
func (fc * funcContext ) varPtrName (o * types.Var ) string {
362
- if isPkgLevel (o ) && o .Exported () {
395
+ if typeVarLevel (o ) == varPackage && o .Exported () {
363
396
return fc .pkgVar (o .Pkg ()) + "." + o .Name () + "$ptr"
364
397
}
365
398
366
399
name , ok := fc .pkgCtx .varPtrNames [o ]
367
400
if ! ok {
368
- name = fc .newVariable (o .Name ()+ "$ptr" , isPkgLevel (o ))
401
+ name = fc .newVariable (o .Name ()+ "$ptr" , typeVarLevel (o ))
369
402
fc .pkgCtx .varPtrNames [o ] = name
370
403
}
371
404
return name
@@ -385,6 +418,8 @@ func (fc *funcContext) typeName(ty types.Type) string {
385
418
return "$error"
386
419
}
387
420
return fc .objectName (t .Obj ())
421
+ case * types.TypeParam :
422
+ return fc .objectName (t .Obj ())
388
423
case * types.Interface :
389
424
if t .Empty () {
390
425
return "$emptyInterface"
@@ -397,8 +432,8 @@ func (fc *funcContext) typeName(ty types.Type) string {
397
432
// repeatedly.
398
433
anonType , ok := fc .pkgCtx .anonTypeMap .At (ty ).(* types.TypeName )
399
434
if ! ok {
400
- fc .initArgs (ty ) // cause all embedded types to be registered
401
- varName := fc .newVariable (strings .ToLower (typeKind (ty )[5 :])+ "Type" , true )
435
+ fc .initArgs (ty ) // cause all dependency types to be registered
436
+ varName := fc .newVariable (strings .ToLower (typeKind (ty )[5 :])+ "Type" , varPackage )
402
437
anonType = types .NewTypeName (token .NoPos , fc .pkgCtx .Pkg , varName , ty ) // fake types.TypeName
403
438
fc .pkgCtx .anonTypes = append (fc .pkgCtx .anonTypes , anonType )
404
439
fc .pkgCtx .anonTypeMap .Set (ty , anonType )
@@ -815,6 +850,12 @@ func (st signatureTypes) HasNamedResults() bool {
815
850
return st .HasResults () && st .Sig .Results ().At (0 ).Name () != ""
816
851
}
817
852
853
+ // IsGeneric returns true if the signature represents a generic function or a
854
+ // method of a generic type.
855
+ func (st signatureTypes ) IsGeneric () bool {
856
+ return st .Sig .TypeParams ().Len () > 0 || st .Sig .RecvTypeParams ().Len () > 0
857
+ }
858
+
818
859
// ErrorAt annotates an error with a position in the source code.
819
860
func ErrorAt (err error , fset * token.FileSet , pos token.Pos ) error {
820
861
return fmt .Errorf ("%s: %w" , fset .Position (pos ), err )
0 commit comments