github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/result/processors/skip_dirs.go (about)

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