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