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  }