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 }