github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/pkg/result/processors/skip_dirs.go (about)

     1  package processors
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/chenfeining/golangci-lint/pkg/fsutils"
    10  	"github.com/chenfeining/golangci-lint/pkg/logutils"
    11  	"github.com/chenfeining/golangci-lint/pkg/result"
    12  )
    13  
    14  type skipStat struct {
    15  	pattern string
    16  	count   int
    17  }
    18  
    19  type SkipDirs struct {
    20  	patterns         []*regexp.Regexp
    21  	log              logutils.Log
    22  	skippedDirs      map[string]*skipStat
    23  	absArgsDirs      []string
    24  	skippedDirsCache map[string]bool
    25  	pathPrefix       string
    26  }
    27  
    28  var _ Processor = (*SkipDirs)(nil)
    29  
    30  const goFileSuffix = ".go"
    31  
    32  func NewSkipDirs(patterns []string, log logutils.Log, runArgs []string, pathPrefix string) (*SkipDirs, error) {
    33  	var patternsRe []*regexp.Regexp
    34  	for _, p := range patterns {
    35  		p = fsutils.NormalizePathInRegex(p)
    36  		patternRe, err := regexp.Compile(p)
    37  		if err != nil {
    38  			return nil, fmt.Errorf("can't compile regexp %q: %w", p, err)
    39  		}
    40  		patternsRe = append(patternsRe, patternRe)
    41  	}
    42  
    43  	if len(runArgs) == 0 {
    44  		runArgs = append(runArgs, "./...")
    45  	}
    46  	var absArgsDirs []string
    47  	for _, arg := range runArgs {
    48  		base := filepath.Base(arg)
    49  		if base == "..." || strings.HasSuffix(base, goFileSuffix) {
    50  			arg = filepath.Dir(arg)
    51  		}
    52  
    53  		absArg, err := filepath.Abs(arg)
    54  		if err != nil {
    55  			return nil, fmt.Errorf("failed to abs-ify arg %q: %w", arg, err)
    56  		}
    57  		absArgsDirs = append(absArgsDirs, absArg)
    58  	}
    59  
    60  	return &SkipDirs{
    61  		patterns:         patternsRe,
    62  		log:              log,
    63  		skippedDirs:      map[string]*skipStat{},
    64  		absArgsDirs:      absArgsDirs,
    65  		skippedDirsCache: map[string]bool{},
    66  		pathPrefix:       pathPrefix,
    67  	}, nil
    68  }
    69  
    70  func (p *SkipDirs) Name() string {
    71  	return "skip_dirs"
    72  }
    73  
    74  func (p *SkipDirs) Process(issues []result.Issue) ([]result.Issue, error) {
    75  	if len(p.patterns) == 0 {
    76  		return issues, nil
    77  	}
    78  
    79  	return filterIssues(issues, p.shouldPassIssue), nil
    80  }
    81  
    82  func (p *SkipDirs) shouldPassIssue(i *result.Issue) bool {
    83  	if filepath.IsAbs(i.FilePath()) {
    84  		if !isSpecialAutogeneratedFile(i.FilePath()) {
    85  			p.log.Warnf("Got abs path %s in skip dirs processor, it should be relative", i.FilePath())
    86  		}
    87  		return true
    88  	}
    89  
    90  	issueRelDir := filepath.Dir(i.FilePath())
    91  
    92  	if toPass, ok := p.skippedDirsCache[issueRelDir]; ok {
    93  		if !toPass {
    94  			p.skippedDirs[issueRelDir].count++
    95  		}
    96  		return toPass
    97  	}
    98  
    99  	issueAbsDir, err := filepath.Abs(issueRelDir)
   100  	if err != nil {
   101  		p.log.Warnf("Can't abs-ify path %q: %s", issueRelDir, err)
   102  		return true
   103  	}
   104  
   105  	toPass := p.shouldPassIssueDirs(issueRelDir, issueAbsDir)
   106  	p.skippedDirsCache[issueRelDir] = toPass
   107  	return toPass
   108  }
   109  
   110  func (p *SkipDirs) shouldPassIssueDirs(issueRelDir, issueAbsDir string) bool {
   111  	for _, absArgDir := range p.absArgsDirs {
   112  		if absArgDir == issueAbsDir {
   113  			// we must not skip issues if they are from explicitly set dirs
   114  			// even if they match skip patterns
   115  			return true
   116  		}
   117  	}
   118  
   119  	// We use issueRelDir for matching: it's the relative to the current
   120  	// work dir path of directory of source file with the issue. It can lead
   121  	// to unexpected behavior if we're analyzing files out of current work dir.
   122  	// The alternative solution is to find relative to args path, but it has
   123  	// disadvantages (https://github.com/chenfeining/golangci-lint/pull/313).
   124  
   125  	path := fsutils.WithPathPrefix(p.pathPrefix, issueRelDir)
   126  	for _, pattern := range p.patterns {
   127  		if pattern.MatchString(path) {
   128  			ps := pattern.String()
   129  			if p.skippedDirs[issueRelDir] == nil {
   130  				p.skippedDirs[issueRelDir] = &skipStat{
   131  					pattern: ps,
   132  				}
   133  			}
   134  			p.skippedDirs[issueRelDir].count++
   135  			return false
   136  		}
   137  	}
   138  
   139  	return true
   140  }
   141  
   142  func (p *SkipDirs) Finish() {
   143  	for dir, stat := range p.skippedDirs {
   144  		p.log.Infof("Skipped %d issues from dir %s by pattern %s", stat.count, dir, stat.pattern)
   145  	}
   146  }