123 lines
2.6 KiB
Go
123 lines
2.6 KiB
Go
package processors
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"strings"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/result"
|
|
"golang.org/x/tools/go/loader"
|
|
)
|
|
|
|
type NolintProcessor struct {
|
|
prog *loader.Program
|
|
}
|
|
|
|
func NewNolintProcessor(prog *loader.Program) *NolintProcessor {
|
|
return &NolintProcessor{
|
|
prog: prog,
|
|
}
|
|
}
|
|
|
|
var _ Processor = NolintProcessor{}
|
|
|
|
type comment struct {
|
|
linters []string
|
|
line int
|
|
}
|
|
type fileComments []comment
|
|
type commentsCache map[string]fileComments
|
|
|
|
func (p NolintProcessor) Name() string {
|
|
return "nolint"
|
|
}
|
|
|
|
func (p NolintProcessor) Process(results []result.Result) ([]result.Result, error) {
|
|
var retResults []result.Result
|
|
parsedFilesCache := commentsCache{}
|
|
for _, res := range results {
|
|
pr, err := p.processResult(res, parsedFilesCache)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
retResults = append(retResults, *pr)
|
|
}
|
|
|
|
return retResults, nil
|
|
}
|
|
|
|
func (p NolintProcessor) processResult(r result.Result, parsedFilesCache commentsCache) (*result.Result, error) {
|
|
ret := r
|
|
ret.Issues = nil
|
|
for _, i := range r.Issues {
|
|
skip, err := p.shouldSkipIssue(&i, parsedFilesCache)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !skip {
|
|
ret.Issues = append(ret.Issues, i)
|
|
}
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func (p NolintProcessor) shouldSkipIssue(i *result.Issue, parsedFilesCache commentsCache) (bool, error) {
|
|
comments := parsedFilesCache[i.File]
|
|
if comments == nil {
|
|
file, err := parser.ParseFile(p.prog.Fset, i.File, nil, parser.ParseComments)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
comments = extractFileComments(p.prog.Fset, file.Comments...)
|
|
parsedFilesCache[i.File] = comments
|
|
}
|
|
|
|
for _, comment := range comments {
|
|
if comment.line != i.LineNumber {
|
|
continue
|
|
}
|
|
|
|
if len(comment.linters) == 0 {
|
|
return true, nil // skip all linters
|
|
}
|
|
|
|
for _, linter := range comment.linters {
|
|
if i.FromLinter == linter {
|
|
return true, nil
|
|
}
|
|
// TODO: check linter name
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func extractFileComments(fset *token.FileSet, comments ...*ast.CommentGroup) fileComments {
|
|
ret := fileComments{}
|
|
for _, g := range comments {
|
|
for _, c := range g.List {
|
|
text := strings.TrimLeft(c.Text, "/ ")
|
|
if strings.HasPrefix(text, "nolint") {
|
|
var linters []string
|
|
if strings.HasPrefix(text, "nolint:") {
|
|
text = strings.Split(text, " ")[0] // allow arbitrary text after this comment
|
|
for _, linter := range strings.Split(strings.TrimPrefix(text, "nolint:"), ",") {
|
|
linters = append(linters, strings.TrimSpace(linter))
|
|
}
|
|
}
|
|
pos := fset.Position(g.Pos())
|
|
ret = append(ret, comment{
|
|
linters: linters,
|
|
line: pos.Line,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|