diff options
Diffstat (limited to 'vendor/github.com/mgechev/revive/lint/package.go')
-rw-r--r-- | vendor/github.com/mgechev/revive/lint/package.go | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/vendor/github.com/mgechev/revive/lint/package.go b/vendor/github.com/mgechev/revive/lint/package.go new file mode 100644 index 0000000000..7b6046fd7e --- /dev/null +++ b/vendor/github.com/mgechev/revive/lint/package.go @@ -0,0 +1,178 @@ +package lint + +import ( + "go/ast" + "go/token" + "go/types" + "sync" + + "golang.org/x/tools/go/gcexportdata" +) + +// Package represents a package in the project. +type Package struct { + fset *token.FileSet + files map[string]*File + + TypesPkg *types.Package + TypesInfo *types.Info + + // sortable is the set of types in the package that implement sort.Interface. + Sortable map[string]bool + // main is whether this is a "main" package. + main int + mu sync.Mutex +} + +var newImporter = func(fset *token.FileSet) types.ImporterFrom { + return gcexportdata.NewImporter(fset, make(map[string]*types.Package)) +} + +var ( + trueValue = 1 + falseValue = 2 + notSet = 3 +) + +// IsMain returns if that's the main package. +func (p *Package) IsMain() bool { + if p.main == trueValue { + return true + } else if p.main == falseValue { + return false + } + for _, f := range p.files { + if f.isMain() { + p.main = trueValue + return true + } + } + p.main = falseValue + return false +} + +// TypeCheck performs type checking for given package. +func (p *Package) TypeCheck() error { + p.mu.Lock() + // If type checking has already been performed + // skip it. + if p.TypesInfo != nil || p.TypesPkg != nil { + p.mu.Unlock() + return nil + } + config := &types.Config{ + // By setting a no-op error reporter, the type checker does as much work as possible. + Error: func(error) {}, + Importer: newImporter(p.fset), + } + info := &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Scopes: make(map[ast.Node]*types.Scope), + } + var anyFile *File + var astFiles []*ast.File + for _, f := range p.files { + anyFile = f + astFiles = append(astFiles, f.AST) + } + + typesPkg, err := check(config, anyFile.AST.Name.Name, p.fset, astFiles, info) + + // Remember the typechecking info, even if config.Check failed, + // since we will get partial information. + p.TypesPkg = typesPkg + p.TypesInfo = info + p.mu.Unlock() + return err +} + +// check function encapsulates the call to go/types.Config.Check method and +// recovers if the called method panics (see issue #59) +func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.File, info *types.Info) (p *types.Package, err error) { + defer func() { + if r := recover(); r != nil { + err, _ = r.(error) + p = nil + return + } + }() + + return config.Check(n, fset, astFiles, info) +} + +// TypeOf returns the type of an expression. +func (p *Package) TypeOf(expr ast.Expr) types.Type { + if p.TypesInfo == nil { + return nil + } + return p.TypesInfo.TypeOf(expr) +} + +type walker struct { + nmap map[string]int + has map[string]int +} + +func (w *walker) Visit(n ast.Node) ast.Visitor { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 { + return w + } + // TODO(dsymonds): We could check the signature to be more precise. + recv := receiverType(fn) + if i, ok := w.nmap[fn.Name.Name]; ok { + w.has[recv] |= i + } + return w +} + +func (p *Package) scanSortable() { + p.Sortable = make(map[string]bool) + + // bitfield for which methods exist on each type. + const ( + Len = 1 << iota + Less + Swap + ) + nmap := map[string]int{"Len": Len, "Less": Less, "Swap": Swap} + has := make(map[string]int) + for _, f := range p.files { + ast.Walk(&walker{nmap, has}, f.AST) + } + for typ, ms := range has { + if ms == Len|Less|Swap { + p.Sortable[typ] = true + } + } +} + +// receiverType returns the named type of the method receiver, sans "*", +// or "invalid-type" if fn.Recv is ill formed. +func receiverType(fn *ast.FuncDecl) string { + switch e := fn.Recv.List[0].Type.(type) { + case *ast.Ident: + return e.Name + case *ast.StarExpr: + if id, ok := e.X.(*ast.Ident); ok { + return id.Name + } + } + // The parser accepts much more than just the legal forms. + return "invalid-type" +} + +func (p *Package) lint(rules []Rule, config Config, failures chan Failure) { + p.scanSortable() + var wg sync.WaitGroup + for _, file := range p.files { + wg.Add(1) + go (func(file *File) { + file.lint(rules, config, failures) + defer wg.Done() + })(file) + } + wg.Wait() +} |