github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/lint/runner.go (about) 1 package lint 2 3 import ( 4 "context" 5 "fmt" 6 "runtime/debug" 7 "strings" 8 9 "github.com/hashicorp/go-multierror" 10 "github.com/pkg/errors" 11 gopackages "golang.org/x/tools/go/packages" 12 13 "github.com/golangci/golangci-lint/internal/errorutil" 14 "github.com/golangci/golangci-lint/pkg/config" 15 "github.com/golangci/golangci-lint/pkg/fsutils" 16 "github.com/golangci/golangci-lint/pkg/goutil" 17 "github.com/golangci/golangci-lint/pkg/lint/linter" 18 "github.com/golangci/golangci-lint/pkg/lint/lintersdb" 19 "github.com/golangci/golangci-lint/pkg/logutils" 20 "github.com/golangci/golangci-lint/pkg/packages" 21 "github.com/golangci/golangci-lint/pkg/result" 22 "github.com/golangci/golangci-lint/pkg/result/processors" 23 "github.com/golangci/golangci-lint/pkg/timeutils" 24 ) 25 26 type Runner struct { 27 Processors []processors.Processor 28 Log logutils.Log 29 } 30 31 func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env, es *lintersdb.EnabledSet, 32 lineCache *fsutils.LineCache, dbManager *lintersdb.Manager, pkgs []*gopackages.Package) (*Runner, error) { 33 skipFilesProcessor, err := processors.NewSkipFiles(cfg.Run.SkipFiles) 34 if err != nil { 35 return nil, err 36 } 37 38 skipDirs := cfg.Run.SkipDirs 39 if cfg.Run.UseDefaultSkipDirs { 40 skipDirs = append(skipDirs, packages.StdExcludeDirRegexps...) 41 } 42 skipDirsProcessor, err := processors.NewSkipDirs(skipDirs, log.Child(logutils.DebugKeySkipDirs), cfg.Run.Args) 43 if err != nil { 44 return nil, err 45 } 46 47 enabledLinters, err := es.GetEnabledLintersMap() 48 if err != nil { 49 return nil, errors.Wrap(err, "failed to get enabled linters") 50 } 51 52 // print deprecated messages 53 if !cfg.InternalCmdTest { 54 for name, lc := range enabledLinters { 55 if !lc.IsDeprecated() { 56 continue 57 } 58 59 var extra string 60 if lc.Deprecation.Replacement != "" { 61 extra = fmt.Sprintf(" Replaced by %s.", lc.Deprecation.Replacement) 62 } 63 64 log.Warnf("The linter '%s' is deprecated (since %s) due to: %s %s", name, lc.Deprecation.Since, lc.Deprecation.Message, extra) 65 } 66 } 67 68 return &Runner{ 69 Processors: []processors.Processor{ 70 processors.NewCgo(goenv), 71 72 // Must go after Cgo. 73 processors.NewFilenameUnadjuster(pkgs, log.Child(logutils.DebugKeyFilenameUnadjuster)), 74 75 // Must be before diff, nolint and exclude autogenerated processor at least. 76 processors.NewPathPrettifier(), 77 skipFilesProcessor, 78 skipDirsProcessor, // must be after path prettifier 79 80 processors.NewAutogeneratedExclude(), 81 82 // Must be before exclude because users see already marked output and configure excluding by it. 83 processors.NewIdentifierMarker(), 84 85 getExcludeProcessor(&cfg.Issues), 86 getExcludeRulesProcessor(&cfg.Issues, log, lineCache), 87 processors.NewNolint(log.Child(logutils.DebugKeyNolint), dbManager, enabledLinters), 88 89 processors.NewUniqByLine(cfg), 90 processors.NewDiff(cfg.Issues.Diff, cfg.Issues.DiffFromRevision, cfg.Issues.DiffPatchFilePath, cfg.Issues.WholeFiles), 91 processors.NewMaxPerFileFromLinter(cfg), 92 processors.NewMaxSameIssues(cfg.Issues.MaxSameIssues, log.Child(logutils.DebugKeyMaxSameIssues), cfg), 93 processors.NewMaxFromLinter(cfg.Issues.MaxIssuesPerLinter, log.Child(logutils.DebugKeyMaxFromLinter), cfg), 94 processors.NewSourceCode(lineCache, log.Child(logutils.DebugKeySourceCode)), 95 processors.NewPathShortener(), 96 getSeverityRulesProcessor(&cfg.Severity, log, lineCache), 97 processors.NewPathPrefixer(cfg.Output.PathPrefix), 98 processors.NewSortResults(cfg), 99 }, 100 Log: log, 101 }, nil 102 } 103 104 func (r *Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context, 105 lc *linter.Config) (ret []result.Issue, err error) { 106 defer func() { 107 if panicData := recover(); panicData != nil { 108 if pe, ok := panicData.(*errorutil.PanicError); ok { 109 err = fmt.Errorf("%s: %w", lc.Name(), pe) 110 111 // Don't print stacktrace from goroutines twice 112 r.Log.Errorf("Panic: %s: %s", pe, pe.Stack()) 113 } else { 114 err = fmt.Errorf("panic occurred: %s", panicData) 115 r.Log.Errorf("Panic stack trace: %s", debug.Stack()) 116 } 117 } 118 }() 119 120 issues, err := lc.Linter.Run(ctx, lintCtx) 121 122 if lc.DoesChangeTypes { 123 // Packages in lintCtx might be dirty due to the last analysis, 124 // which affects to the next analysis. 125 // To avoid this issue, we clear type information from the packages. 126 // See https://github.com/golangci/golangci-lint/pull/944. 127 // Currently, DoesChangeTypes is true only for `unused`. 128 lintCtx.ClearTypesInPackages() 129 } 130 131 if err != nil { 132 return nil, err 133 } 134 135 for i := range issues { 136 if issues[i].FromLinter == "" { 137 issues[i].FromLinter = lc.Name() 138 } 139 } 140 141 return issues, nil 142 } 143 144 type processorStat struct { 145 inCount int 146 outCount int 147 } 148 149 func (r Runner) processLintResults(inIssues []result.Issue) []result.Issue { 150 sw := timeutils.NewStopwatch("processing", r.Log) 151 152 var issuesBefore, issuesAfter int 153 statPerProcessor := map[string]processorStat{} 154 155 var outIssues []result.Issue 156 if len(inIssues) != 0 { 157 issuesBefore += len(inIssues) 158 outIssues = r.processIssues(inIssues, sw, statPerProcessor) 159 issuesAfter += len(outIssues) 160 } 161 162 // finalize processors: logging, clearing, no heavy work here 163 164 for _, p := range r.Processors { 165 p := p 166 sw.TrackStage(p.Name(), func() { 167 p.Finish() 168 }) 169 } 170 171 if issuesBefore != issuesAfter { 172 r.Log.Infof("Issues before processing: %d, after processing: %d", issuesBefore, issuesAfter) 173 } 174 r.printPerProcessorStat(statPerProcessor) 175 sw.PrintStages() 176 177 return outIssues 178 } 179 180 func (r Runner) printPerProcessorStat(stat map[string]processorStat) { 181 parts := make([]string, 0, len(stat)) 182 for name, ps := range stat { 183 if ps.inCount != 0 { 184 parts = append(parts, fmt.Sprintf("%s: %d/%d", name, ps.outCount, ps.inCount)) 185 } 186 } 187 if len(parts) != 0 { 188 r.Log.Infof("Processors filtering stat (out/in): %s", strings.Join(parts, ", ")) 189 } 190 } 191 192 func (r Runner) Run(ctx context.Context, linters []*linter.Config, lintCtx *linter.Context) ([]result.Issue, error) { 193 sw := timeutils.NewStopwatch("linters", r.Log) 194 defer sw.Print() 195 196 var ( 197 lintErrors *multierror.Error 198 issues []result.Issue 199 ) 200 201 for _, lc := range linters { 202 lc := lc 203 sw.TrackStage(lc.Name(), func() { 204 linterIssues, err := r.runLinterSafe(ctx, lintCtx, lc) 205 if err != nil { 206 lintErrors = multierror.Append(lintErrors, fmt.Errorf("can't run linter %s: %w", lc.Linter.Name(), err)) 207 r.Log.Warnf("Can't run linter %s: %v", lc.Linter.Name(), err) 208 209 return 210 } 211 issues = append(issues, linterIssues...) 212 }) 213 } 214 215 return r.processLintResults(issues), lintErrors.ErrorOrNil() 216 } 217 218 func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch, statPerProcessor map[string]processorStat) []result.Issue { 219 for _, p := range r.Processors { 220 var newIssues []result.Issue 221 var err error 222 p := p 223 sw.TrackStage(p.Name(), func() { 224 newIssues, err = p.Process(issues) 225 }) 226 227 if err != nil { 228 r.Log.Warnf("Can't process result by %s processor: %s", p.Name(), err) 229 } else { 230 stat := statPerProcessor[p.Name()] 231 stat.inCount += len(issues) 232 stat.outCount += len(newIssues) 233 statPerProcessor[p.Name()] = stat 234 issues = newIssues 235 } 236 237 if issues == nil { 238 issues = []result.Issue{} 239 } 240 } 241 242 return issues 243 } 244 245 func getExcludeProcessor(cfg *config.Issues) processors.Processor { 246 var excludeTotalPattern string 247 248 if len(cfg.ExcludePatterns) != 0 { 249 excludeTotalPattern = fmt.Sprintf("(%s)", strings.Join(cfg.ExcludePatterns, "|")) 250 } 251 252 var excludeProcessor processors.Processor 253 if cfg.ExcludeCaseSensitive { 254 excludeProcessor = processors.NewExcludeCaseSensitive(excludeTotalPattern) 255 } else { 256 excludeProcessor = processors.NewExclude(excludeTotalPattern) 257 } 258 259 return excludeProcessor 260 } 261 262 func getExcludeRulesProcessor(cfg *config.Issues, log logutils.Log, lineCache *fsutils.LineCache) processors.Processor { 263 var excludeRules []processors.ExcludeRule 264 for _, r := range cfg.ExcludeRules { 265 excludeRules = append(excludeRules, processors.ExcludeRule{ 266 BaseRule: processors.BaseRule{ 267 Text: r.Text, 268 Source: r.Source, 269 Path: r.Path, 270 Linters: r.Linters, 271 }, 272 }) 273 } 274 275 if cfg.UseDefaultExcludes { 276 for _, r := range config.GetExcludePatterns(cfg.IncludeDefaultExcludes) { 277 excludeRules = append(excludeRules, processors.ExcludeRule{ 278 BaseRule: processors.BaseRule{ 279 Text: r.Pattern, 280 Linters: []string{r.Linter}, 281 }, 282 }) 283 } 284 } 285 286 var excludeRulesProcessor processors.Processor 287 if cfg.ExcludeCaseSensitive { 288 excludeRulesProcessor = processors.NewExcludeRulesCaseSensitive( 289 excludeRules, 290 lineCache, 291 log.Child(logutils.DebugKeyExcludeRules), 292 ) 293 } else { 294 excludeRulesProcessor = processors.NewExcludeRules( 295 excludeRules, 296 lineCache, 297 log.Child(logutils.DebugKeyExcludeRules), 298 ) 299 } 300 301 return excludeRulesProcessor 302 } 303 304 func getSeverityRulesProcessor(cfg *config.Severity, log logutils.Log, lineCache *fsutils.LineCache) processors.Processor { 305 var severityRules []processors.SeverityRule 306 for _, r := range cfg.Rules { 307 severityRules = append(severityRules, processors.SeverityRule{ 308 Severity: r.Severity, 309 BaseRule: processors.BaseRule{ 310 Text: r.Text, 311 Source: r.Source, 312 Path: r.Path, 313 Linters: r.Linters, 314 }, 315 }) 316 } 317 318 var severityRulesProcessor processors.Processor 319 if cfg.CaseSensitive { 320 severityRulesProcessor = processors.NewSeverityRulesCaseSensitive( 321 cfg.Default, 322 severityRules, 323 lineCache, 324 log.Child(logutils.DebugKeySeverityRules), 325 ) 326 } else { 327 severityRulesProcessor = processors.NewSeverityRules( 328 cfg.Default, 329 severityRules, 330 lineCache, 331 log.Child(logutils.DebugKeySeverityRules), 332 ) 333 } 334 335 return severityRulesProcessor 336 }