You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

package.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package lint
  2. import (
  3. "go/ast"
  4. "go/token"
  5. "go/types"
  6. "sync"
  7. "golang.org/x/tools/go/gcexportdata"
  8. )
  9. // Package represents a package in the project.
  10. type Package struct {
  11. fset *token.FileSet
  12. files map[string]*File
  13. TypesPkg *types.Package
  14. TypesInfo *types.Info
  15. // sortable is the set of types in the package that implement sort.Interface.
  16. Sortable map[string]bool
  17. // main is whether this is a "main" package.
  18. main int
  19. mu sync.Mutex
  20. }
  21. var newImporter = func(fset *token.FileSet) types.ImporterFrom {
  22. return gcexportdata.NewImporter(fset, make(map[string]*types.Package))
  23. }
  24. var (
  25. trueValue = 1
  26. falseValue = 2
  27. notSet = 3
  28. )
  29. // IsMain returns if that's the main package.
  30. func (p *Package) IsMain() bool {
  31. if p.main == trueValue {
  32. return true
  33. } else if p.main == falseValue {
  34. return false
  35. }
  36. for _, f := range p.files {
  37. if f.isMain() {
  38. p.main = trueValue
  39. return true
  40. }
  41. }
  42. p.main = falseValue
  43. return false
  44. }
  45. // TypeCheck performs type checking for given package.
  46. func (p *Package) TypeCheck() error {
  47. p.mu.Lock()
  48. // If type checking has already been performed
  49. // skip it.
  50. if p.TypesInfo != nil || p.TypesPkg != nil {
  51. p.mu.Unlock()
  52. return nil
  53. }
  54. config := &types.Config{
  55. // By setting a no-op error reporter, the type checker does as much work as possible.
  56. Error: func(error) {},
  57. Importer: newImporter(p.fset),
  58. }
  59. info := &types.Info{
  60. Types: make(map[ast.Expr]types.TypeAndValue),
  61. Defs: make(map[*ast.Ident]types.Object),
  62. Uses: make(map[*ast.Ident]types.Object),
  63. Scopes: make(map[ast.Node]*types.Scope),
  64. }
  65. var anyFile *File
  66. var astFiles []*ast.File
  67. for _, f := range p.files {
  68. anyFile = f
  69. astFiles = append(astFiles, f.AST)
  70. }
  71. typesPkg, err := check(config, anyFile.AST.Name.Name, p.fset, astFiles, info)
  72. // Remember the typechecking info, even if config.Check failed,
  73. // since we will get partial information.
  74. p.TypesPkg = typesPkg
  75. p.TypesInfo = info
  76. p.mu.Unlock()
  77. return err
  78. }
  79. // check function encapsulates the call to go/types.Config.Check method and
  80. // recovers if the called method panics (see issue #59)
  81. func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast.File, info *types.Info) (p *types.Package, err error) {
  82. defer func() {
  83. if r := recover(); r != nil {
  84. err, _ = r.(error)
  85. p = nil
  86. return
  87. }
  88. }()
  89. return config.Check(n, fset, astFiles, info)
  90. }
  91. // TypeOf returns the type of an expression.
  92. func (p *Package) TypeOf(expr ast.Expr) types.Type {
  93. if p.TypesInfo == nil {
  94. return nil
  95. }
  96. return p.TypesInfo.TypeOf(expr)
  97. }
  98. type walker struct {
  99. nmap map[string]int
  100. has map[string]int
  101. }
  102. func (w *walker) Visit(n ast.Node) ast.Visitor {
  103. fn, ok := n.(*ast.FuncDecl)
  104. if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 {
  105. return w
  106. }
  107. // TODO(dsymonds): We could check the signature to be more precise.
  108. recv := receiverType(fn)
  109. if i, ok := w.nmap[fn.Name.Name]; ok {
  110. w.has[recv] |= i
  111. }
  112. return w
  113. }
  114. func (p *Package) scanSortable() {
  115. p.Sortable = make(map[string]bool)
  116. // bitfield for which methods exist on each type.
  117. const (
  118. Len = 1 << iota
  119. Less
  120. Swap
  121. )
  122. nmap := map[string]int{"Len": Len, "Less": Less, "Swap": Swap}
  123. has := make(map[string]int)
  124. for _, f := range p.files {
  125. ast.Walk(&walker{nmap, has}, f.AST)
  126. }
  127. for typ, ms := range has {
  128. if ms == Len|Less|Swap {
  129. p.Sortable[typ] = true
  130. }
  131. }
  132. }
  133. // receiverType returns the named type of the method receiver, sans "*",
  134. // or "invalid-type" if fn.Recv is ill formed.
  135. func receiverType(fn *ast.FuncDecl) string {
  136. switch e := fn.Recv.List[0].Type.(type) {
  137. case *ast.Ident:
  138. return e.Name
  139. case *ast.StarExpr:
  140. if id, ok := e.X.(*ast.Ident); ok {
  141. return id.Name
  142. }
  143. }
  144. // The parser accepts much more than just the legal forms.
  145. return "invalid-type"
  146. }
  147. func (p *Package) lint(rules []Rule, config Config, failures chan Failure) {
  148. p.scanSortable()
  149. var wg sync.WaitGroup
  150. for _, file := range p.files {
  151. wg.Add(1)
  152. go (func(file *File) {
  153. file.lint(rules, config, failures)
  154. defer wg.Done()
  155. })(file)
  156. }
  157. wg.Wait()
  158. }