@@ -130,6 +130,11 @@ type overrideInfo struct {
130
130
// If the method is defined in the overlays and therefore has its
131
131
// own overrides, this will be ignored.
132
132
purgeMethods bool
133
+
134
+ // overrideSignature is the function definition given in the overlays
135
+ // that should be used to replace the signature in the originals.
136
+ // Only receivers, type parameters, parameters, and results will be used.
137
+ overrideSignature * ast.FuncDecl
133
138
}
134
139
135
140
// parseAndAugment parses and returns all .go files of given pkg.
@@ -270,9 +275,14 @@ func augmentOverlayFile(file *ast.File, overrides map[string]overrideInfo) {
270
275
switch d := decl .(type ) {
271
276
case * ast.FuncDecl :
272
277
k := astutil .FuncKey (d )
273
- overrides [ k ] = overrideInfo {
278
+ oi : = overrideInfo {
274
279
keepOriginal : astutil .KeepOriginal (d ),
275
280
}
281
+ if astutil .OverrideSignature (d ) {
282
+ oi .overrideSignature = d
283
+ purgeDecl = true
284
+ }
285
+ overrides [k ] = oi
276
286
case * ast.GenDecl :
277
287
for j , spec := range d .Specs {
278
288
purgeSpec := purgeDecl || astutil .Purge (spec )
@@ -323,11 +333,21 @@ func augmentOriginalFile(file *ast.File, overrides map[string]overrideInfo) {
323
333
switch d := decl .(type ) {
324
334
case * ast.FuncDecl :
325
335
if info , ok := overrides [astutil .FuncKey (d )]; ok {
336
+ removeFunc := true
326
337
if info .keepOriginal {
327
338
// Allow overridden function calls
328
339
// The standard library implementation of foo() becomes _gopherjs_original_foo()
329
340
d .Name .Name = "_gopherjs_original_" + d .Name .Name
330
- } else {
341
+ removeFunc = false
342
+ }
343
+ if overSig := info .overrideSignature ; overSig != nil {
344
+ d .Recv = overSig .Recv
345
+ d .Type .TypeParams = overSig .Type .TypeParams
346
+ d .Type .Params = overSig .Type .Params
347
+ d .Type .Results = overSig .Type .Results
348
+ removeFunc = false
349
+ }
350
+ if removeFunc {
331
351
file .Decls [i ] = nil
332
352
}
333
353
} else if recvKey := astutil .FuncReceiverKey (d ); len (recvKey ) > 0 {
@@ -391,21 +411,40 @@ func augmentOriginalFile(file *ast.File, overrides map[string]overrideInfo) {
391
411
finalizeRemovals (file )
392
412
}
393
413
414
+ // isOnlyImports determines if this file is empty except for imports.
415
+ func isOnlyImports (file * ast.File ) bool {
416
+ for _ , decl := range file .Decls {
417
+ if gen , ok := decl .(* ast.GenDecl ); ! ok || gen .Tok != token .IMPORT {
418
+ return false
419
+ }
420
+ }
421
+ return true
422
+ }
423
+
394
424
// pruneImports will remove any unused imports from the file.
395
425
//
396
- // This will not remove any dot (`.`) or blank (`_`) imports.
426
+ // This will not remove any dot (`.`) or blank (`_`) imports, unless
427
+ // there are no declarations or directives meaning that all the imports
428
+ // should be cleared.
397
429
// If the removal of code causes an import to be removed, the init's from that
398
430
// import may not be run anymore. If we still need to run an init for an import
399
431
// which is no longer used, add it to the overlay as a blank (`_`) import.
400
432
func pruneImports (file * ast.File ) {
433
+ if isOnlyImports (file ) && ! astutil .HasDirectivePrefix (file , `//go:linkname ` ) {
434
+ // The file is empty, remove all imports including any `.` or `_` imports.
435
+ file .Imports = nil
436
+ file .Decls = nil
437
+ return
438
+ }
439
+
401
440
unused := make (map [string ]int , len (file .Imports ))
402
441
for i , in := range file .Imports {
403
442
if name := astutil .ImportName (in ); len (name ) > 0 {
404
443
unused [name ] = i
405
444
}
406
445
}
407
446
408
- // Remove "unused import " for any import which is used.
447
+ // Remove "unused imports " for any import which is used.
409
448
ast .Inspect (file , func (n ast.Node ) bool {
410
449
if sel , ok := n .(* ast.SelectorExpr ); ok {
411
450
if id , ok := sel .X .(* ast.Ident ); ok && id .Obj == nil {
@@ -418,6 +457,24 @@ func pruneImports(file *ast.File) {
418
457
return
419
458
}
420
459
460
+ // Remove "unused imports" for any import used for a directive.
461
+ directiveImports := map [string ]string {
462
+ `unsafe` : `//go:linkname ` ,
463
+ `embed` : `//go:embed ` ,
464
+ }
465
+ for name , index := range unused {
466
+ in := file .Imports [index ]
467
+ path , _ := strconv .Unquote (in .Path .Value )
468
+ directivePrefix , hasPath := directiveImports [path ]
469
+ if hasPath && astutil .HasDirectivePrefix (file , directivePrefix ) {
470
+ delete (unused , name )
471
+ if len (unused ) == 0 {
472
+ return
473
+ }
474
+ break
475
+ }
476
+ }
477
+
421
478
// Remove all unused import specifications
422
479
isUnusedSpec := map [* ast.ImportSpec ]bool {}
423
480
for _ , index := range unused {
@@ -442,9 +499,8 @@ func pruneImports(file *ast.File) {
442
499
}
443
500
444
501
// finalizeRemovals fully removes any declaration, specification, imports
445
- // that have been set to nil. This will also remove the file's top-level
446
- // comment group to remove any unassociated comments, including the comments
447
- // from removed code.
502
+ // that have been set to nil. This will also remove any unassociated comment
503
+ // groups, including the comments from removed code.
448
504
func finalizeRemovals (file * ast.File ) {
449
505
fileChanged := false
450
506
for i , decl := range file .Decls {
@@ -487,8 +543,18 @@ func finalizeRemovals(file *ast.File) {
487
543
if fileChanged {
488
544
file .Decls = astutil .Squeeze (file .Decls )
489
545
}
546
+
490
547
file .Imports = astutil .Squeeze (file .Imports )
491
- file .Comments = nil
548
+
549
+ file .Comments = nil // clear this first so ast.Inspect doesn't walk it.
550
+ remComments := []* ast.CommentGroup {}
551
+ ast .Inspect (file , func (n ast.Node ) bool {
552
+ if cg , ok := n .(* ast.CommentGroup ); ok {
553
+ remComments = append (remComments , cg )
554
+ }
555
+ return true
556
+ })
557
+ file .Comments = remComments
492
558
}
493
559
494
560
// Options controls build process behavior.
0 commit comments