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  }