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