gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/opennota/check/cmd/varcheck/varcheck.go (about)

     1  // This program is free software: you can redistribute it and/or modify
     2  // it under the terms of the GNU General Public License as published by
     3  // the Free Software Foundation, either version 3 of the License, or
     4  // (at your option) any later version.
     5  //
     6  // This program is distributed in the hope that it will be useful,
     7  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     8  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     9  // GNU General Public License for more details.
    10  //
    11  // You should have received a copy of the GNU General Public License
    12  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    13  
    14  package main
    15  
    16  import (
    17  	"flag"
    18  	"fmt"
    19  	"go/ast"
    20  	"go/build"
    21  	"go/token"
    22  	"log"
    23  	"os"
    24  	"sort"
    25  	"strings"
    26  
    27  	"github.com/kisielk/gotool"
    28  	"golang.org/x/tools/go/loader"
    29  	"go/types"
    30  )
    31  
    32  var (
    33  	reportExported = flag.Bool("e", false, "Report exported variables and constants")
    34  )
    35  
    36  type object struct {
    37  	pkgPath string
    38  	name    string
    39  }
    40  
    41  type visitor struct {
    42  	prog       *loader.Program
    43  	pkg        *loader.PackageInfo
    44  	uses       map[object]int
    45  	positions  map[object]token.Position
    46  	insideFunc bool
    47  }
    48  
    49  func getKey(obj types.Object) object {
    50  	if obj == nil {
    51  		return object{}
    52  	}
    53  
    54  	pkg := obj.Pkg()
    55  	pkgPath := ""
    56  	if pkg != nil {
    57  		pkgPath = pkg.Path()
    58  	}
    59  
    60  	return object{
    61  		pkgPath: pkgPath,
    62  		name:    obj.Name(),
    63  	}
    64  }
    65  
    66  func (v *visitor) decl(obj types.Object) {
    67  	key := getKey(obj)
    68  	if _, ok := v.uses[key]; !ok {
    69  		v.uses[key] = 0
    70  	}
    71  	if _, ok := v.positions[key]; !ok {
    72  		v.positions[key] = v.prog.Fset.Position(obj.Pos())
    73  	}
    74  }
    75  
    76  func (v *visitor) use(obj types.Object) {
    77  	key := getKey(obj)
    78  	if _, ok := v.uses[key]; ok {
    79  		v.uses[key]++
    80  	} else {
    81  		v.uses[key] = 1
    82  	}
    83  }
    84  
    85  func isReserved(name string) bool {
    86  	return name == "_" || strings.HasPrefix(strings.ToLower(name), "_cgo_")
    87  }
    88  
    89  func (v *visitor) Visit(node ast.Node) ast.Visitor {
    90  	switch node := node.(type) {
    91  	case *ast.Ident:
    92  		v.use(v.pkg.Info.Uses[node])
    93  
    94  	case *ast.ValueSpec:
    95  		if !v.insideFunc {
    96  			for _, ident := range node.Names {
    97  				if !isReserved(ident.Name) {
    98  					v.decl(v.pkg.Info.Defs[ident])
    99  				}
   100  			}
   101  		}
   102  		for _, val := range node.Values {
   103  			ast.Walk(v, val)
   104  		}
   105  		if node.Type != nil {
   106  			ast.Walk(v, node.Type)
   107  		}
   108  		return nil
   109  
   110  	case *ast.FuncDecl:
   111  		if node.Body != nil {
   112  			v.insideFunc = true
   113  			ast.Walk(v, node.Body)
   114  			v.insideFunc = false
   115  		}
   116  
   117  		if node.Recv != nil {
   118  			ast.Walk(v, node.Recv)
   119  		}
   120  		if node.Type != nil {
   121  			ast.Walk(v, node.Type)
   122  		}
   123  
   124  		return nil
   125  	}
   126  
   127  	return v
   128  }
   129  
   130  func main() {
   131  	flag.Parse()
   132  	exitStatus := 0
   133  	importPaths := gotool.ImportPaths(flag.Args())
   134  	if len(importPaths) == 0 {
   135  		importPaths = []string{"."}
   136  	}
   137  
   138  	ctx := build.Default
   139  	loadcfg := loader.Config{
   140  		Build: &ctx,
   141  	}
   142  	rest, err := loadcfg.FromArgs(importPaths, true)
   143  	if err != nil {
   144  		log.Fatalf("could not parse arguments: %s", err)
   145  	}
   146  	if len(rest) > 0 {
   147  		log.Fatalf("unhandled extra arguments: %v", rest)
   148  	}
   149  
   150  	program, err := loadcfg.Load()
   151  	if err != nil {
   152  		log.Fatalf("could not type check: %s", err)
   153  	}
   154  
   155  	uses := make(map[object]int)
   156  	positions := make(map[object]token.Position)
   157  
   158  	for _, pkgInfo := range program.InitialPackages() {
   159  		if pkgInfo.Pkg.Path() == "unsafe" {
   160  			continue
   161  		}
   162  
   163  		v := &visitor{
   164  			prog:      program,
   165  			pkg:       pkgInfo,
   166  			uses:      uses,
   167  			positions: positions,
   168  		}
   169  
   170  		for _, f := range v.pkg.Files {
   171  			ast.Walk(v, f)
   172  		}
   173  	}
   174  
   175  	var lines []string
   176  
   177  	for obj, useCount := range uses {
   178  		if useCount == 0 && (*reportExported || !ast.IsExported(obj.name)) {
   179  			pos := positions[obj]
   180  			lines = append(lines, fmt.Sprintf("%s: %s:%d:%d: %s", obj.pkgPath, pos.Filename, pos.Line, pos.Column, obj.name))
   181  			exitStatus = 1
   182  		}
   183  	}
   184  
   185  	sort.Strings(lines)
   186  	for _, line := range lines {
   187  		fmt.Println(line)
   188  	}
   189  
   190  	os.Exit(exitStatus)
   191  }