github.com/taylorchu/generic@v0.0.0-20171113184323-cd81575befa2/rewrite/prefix_top_level_decl.go (about)

     1  package rewrite
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  )
     7  
     8  // prefixTopLevelDecl adds a prefix to top-level identifiers and their uses.
     9  //
    10  // This prevents name conflicts when a package is rewritten to $PWD.
    11  func (s *Spec) prefixTopLevelDecl(pkg *Package) error {
    12  	if !s.Local {
    13  		return nil
    14  	}
    15  
    16  	prefixIdent := func(name string) string {
    17  		if name == "_" {
    18  			// skip unnamed
    19  			return "_"
    20  		}
    21  		return lintName(fmt.Sprintf("%s_%s", s.Name, name))
    22  	}
    23  
    24  	declMap := make(map[interface{}]string)
    25  
    26  	for _, node := range pkg.Files {
    27  		for _, decl := range node.Decls {
    28  			switch decl := decl.(type) {
    29  			case *ast.FuncDecl:
    30  				if decl.Recv != nil {
    31  					continue
    32  				}
    33  				decl.Name.Name = prefixIdent(decl.Name.Name)
    34  				declMap[decl] = decl.Name.Name
    35  			case *ast.GenDecl:
    36  				for _, spec := range decl.Specs {
    37  					switch spec := spec.(type) {
    38  					case *ast.TypeSpec:
    39  						obj := spec.Name.Obj
    40  						if obj != nil && obj.Kind == ast.Typ {
    41  							if to, ok := s.TypeMap[obj.Name]; ok && spec.Name.Name == to.Expr {
    42  								// If this identifier is already rewritten before, we don't need to prefix it.
    43  								continue
    44  							}
    45  						}
    46  						spec.Name.Name = prefixIdent(spec.Name.Name)
    47  						declMap[spec] = spec.Name.Name
    48  					case *ast.ValueSpec:
    49  						for _, ident := range spec.Names {
    50  							ident.Name = prefixIdent(ident.Name)
    51  							declMap[spec] = ident.Name
    52  						}
    53  					}
    54  				}
    55  			}
    56  		}
    57  	}
    58  
    59  	// After top-level identifiers are renamed, find where they are used, and rewrite those.
    60  	for _, node := range pkg.Files {
    61  		ast.Inspect(node, func(n ast.Node) bool {
    62  			switch x := n.(type) {
    63  			case *ast.Ident:
    64  				if x.Obj == nil || x.Obj.Decl == nil {
    65  					return false
    66  				}
    67  				name, ok := declMap[x.Obj.Decl]
    68  				if !ok {
    69  					return false
    70  				}
    71  				x.Name = name
    72  				return false
    73  			}
    74  			return true
    75  		})
    76  	}
    77  	return nil
    78  }