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 }