github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/lint/linter.go (about)

     1  package lint
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"go/token"
     8  	"os"
     9  	"sync"
    10  )
    11  
    12  // ReadFile defines an abstraction for reading files.
    13  type ReadFile func(path string) (result []byte, err error)
    14  
    15  type disabledIntervalsMap = map[string][]DisabledInterval
    16  
    17  // Linter is used for linting set of files.
    18  type Linter struct {
    19  	reader ReadFile
    20  }
    21  
    22  // New creates a new Linter
    23  func New(reader ReadFile) Linter {
    24  	return Linter{reader: reader}
    25  }
    26  
    27  var (
    28  	genHdr = []byte("// Code generated ")
    29  	genFtr = []byte(" DO NOT EDIT.")
    30  )
    31  
    32  // Lint lints a set of files with the specified rule.
    33  func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-chan Failure, error) {
    34  	failures := make(chan Failure)
    35  
    36  	var wg sync.WaitGroup
    37  	for _, pkg := range packages {
    38  		wg.Add(1)
    39  		go func(pkg []string) {
    40  			if err := l.lintPackage(pkg, ruleSet, config, failures); err != nil {
    41  				fmt.Fprintln(os.Stderr, err)
    42  				os.Exit(1)
    43  			}
    44  			defer wg.Done()
    45  		}(pkg)
    46  	}
    47  
    48  	go func() {
    49  		wg.Wait()
    50  		close(failures)
    51  	}()
    52  
    53  	return failures, nil
    54  }
    55  
    56  func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config, failures chan Failure) error {
    57  	pkg := &Package{
    58  		fset:  token.NewFileSet(),
    59  		files: map[string]*File{},
    60  		mu:    sync.Mutex{},
    61  	}
    62  	for _, filename := range filenames {
    63  		content, err := l.reader(filename)
    64  		if err != nil {
    65  			return err
    66  		}
    67  		if isGenerated(content) && !config.IgnoreGeneratedHeader {
    68  			continue
    69  		}
    70  
    71  		file, err := NewFile(filename, content, pkg)
    72  		if err != nil {
    73  			return err
    74  		}
    75  		pkg.files[filename] = file
    76  	}
    77  
    78  	if len(pkg.files) == 0 {
    79  		return nil
    80  	}
    81  
    82  	pkg.lint(ruleSet, config, failures)
    83  
    84  	return nil
    85  }
    86  
    87  // isGenerated reports whether the source file is generated code
    88  // according the rules from https://golang.org/s/generatedcode.
    89  // This is inherited from the original go lint.
    90  func isGenerated(src []byte) bool {
    91  	sc := bufio.NewScanner(bytes.NewReader(src))
    92  	for sc.Scan() {
    93  		b := sc.Bytes()
    94  		if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
    95  			return true
    96  		}
    97  	}
    98  	return false
    99  }