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 }