gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/tsenart/deadcode/deadcode.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "go/ast" 7 "go/parser" 8 "go/token" 9 "os" 10 "sort" 11 "strings" 12 ) 13 14 var exitCode int 15 16 func main() { 17 flag.Parse() 18 if flag.NArg() == 0 { 19 doDir(".") 20 } else { 21 for _, name := range flag.Args() { 22 // Is it a directory? 23 if fi, err := os.Stat(name); err == nil && fi.IsDir() { 24 doDir(name) 25 } else { 26 errorf("not a directory: %s", name) 27 } 28 } 29 } 30 os.Exit(exitCode) 31 } 32 33 // error formats the error to standard error, adding program 34 // identification and a newline 35 func errorf(format string, args ...interface{}) { 36 fmt.Fprintf(os.Stderr, "deadcode: "+format+"\n", args...) 37 exitCode = 2 38 } 39 40 func doDir(name string) { 41 notests := func(info os.FileInfo) bool { 42 if !info.IsDir() && strings.HasSuffix(info.Name(), ".go") && 43 !strings.HasSuffix(info.Name(), "_test.go") { 44 return true 45 } 46 return false 47 } 48 fs := token.NewFileSet() 49 pkgs, err := parser.ParseDir(fs, name, notests, parser.Mode(0)) 50 if err != nil { 51 errorf("%s", err) 52 return 53 } 54 for _, pkg := range pkgs { 55 doPackage(fs, pkg) 56 } 57 } 58 59 type Package struct { 60 p *ast.Package 61 fs *token.FileSet 62 decl map[string]ast.Node 63 used map[string]bool 64 } 65 66 func doPackage(fs *token.FileSet, pkg *ast.Package) { 67 p := &Package{ 68 p: pkg, 69 fs: fs, 70 decl: make(map[string]ast.Node), 71 used: make(map[string]bool), 72 } 73 for _, file := range pkg.Files { 74 for _, decl := range file.Decls { 75 switch n := decl.(type) { 76 case *ast.GenDecl: 77 // var, const, types 78 for _, spec := range n.Specs { 79 switch s := spec.(type) { 80 case *ast.ValueSpec: 81 // constants and variables. 82 for _, name := range s.Names { 83 p.decl[name.Name] = n 84 } 85 case *ast.TypeSpec: 86 // type definitions. 87 p.decl[s.Name.Name] = n 88 } 89 } 90 case *ast.FuncDecl: 91 // function declarations 92 // TODO(remy): do methods 93 if n.Recv == nil { 94 p.decl[n.Name.Name] = n 95 } 96 } 97 } 98 } 99 // init() and _ are always used 100 p.used["init"] = true 101 p.used["_"] = true 102 if pkg.Name != "main" { 103 // exported names are marked used for non-main packages. 104 for name := range p.decl { 105 if ast.IsExported(name) { 106 p.used[name] = true 107 } 108 } 109 } else { 110 // in main programs, main() is called. 111 p.used["main"] = true 112 } 113 for _, file := range pkg.Files { 114 // walk file looking for used nodes. 115 ast.Walk(p, file) 116 } 117 // reports. 118 reports := Reports(nil) 119 for name, node := range p.decl { 120 if !p.used[name] { 121 reports = append(reports, Report{node.Pos(), name}) 122 } 123 } 124 sort.Sort(reports) 125 for _, report := range reports { 126 errorf("%s: %s is unused", fs.Position(report.pos), report.name) 127 } 128 } 129 130 type Report struct { 131 pos token.Pos 132 name string 133 } 134 type Reports []Report 135 136 func (l Reports) Len() int { return len(l) } 137 func (l Reports) Less(i, j int) bool { return l[i].pos < l[j].pos } 138 func (l Reports) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 139 140 // Visits files for used nodes. 141 func (p *Package) Visit(node ast.Node) ast.Visitor { 142 u := usedWalker(*p) // hopefully p fields are references. 143 switch n := node.(type) { 144 // don't walk whole file, but only: 145 case *ast.ValueSpec: 146 // - variable initializers 147 for _, value := range n.Values { 148 ast.Walk(&u, value) 149 } 150 // variable types. 151 if n.Type != nil { 152 ast.Walk(&u, n.Type) 153 } 154 case *ast.BlockStmt: 155 // - function bodies 156 for _, stmt := range n.List { 157 ast.Walk(&u, stmt) 158 } 159 case *ast.FuncDecl: 160 // - function signatures 161 ast.Walk(&u, n.Type) 162 case *ast.TypeSpec: 163 // - type declarations 164 ast.Walk(&u, n.Type) 165 } 166 return p 167 } 168 169 type usedWalker Package 170 171 // Walks through the AST marking used identifiers. 172 func (p *usedWalker) Visit(node ast.Node) ast.Visitor { 173 // just be stupid and mark all *ast.Ident 174 switch n := node.(type) { 175 case *ast.Ident: 176 p.used[n.Name] = true 177 } 178 return p 179 }