github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/golinters/whitespace.go (about) 1 package golinters 2 3 import ( 4 "go/token" 5 "sync" 6 7 "github.com/pkg/errors" 8 "github.com/ultraware/whitespace" 9 "golang.org/x/tools/go/analysis" 10 11 "github.com/golangci/golangci-lint/pkg/config" 12 "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" 13 "github.com/golangci/golangci-lint/pkg/lint/linter" 14 "github.com/golangci/golangci-lint/pkg/result" 15 ) 16 17 const whitespaceName = "whitespace" 18 19 //nolint:dupl 20 func NewWhitespace(settings *config.WhitespaceSettings) *goanalysis.Linter { 21 var mu sync.Mutex 22 var resIssues []goanalysis.Issue 23 24 var wsSettings whitespace.Settings 25 if settings != nil { 26 wsSettings = whitespace.Settings{ 27 MultiIf: settings.MultiIf, 28 MultiFunc: settings.MultiFunc, 29 } 30 } 31 32 analyzer := &analysis.Analyzer{ 33 Name: whitespaceName, 34 Doc: goanalysis.TheOnlyanalyzerDoc, 35 Run: goanalysis.DummyRun, 36 } 37 38 return goanalysis.NewLinter( 39 whitespaceName, 40 "Tool for detection of leading and trailing whitespace", 41 []*analysis.Analyzer{analyzer}, 42 nil, 43 ).WithContextSetter(func(lintCtx *linter.Context) { 44 analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { 45 issues, err := runWhitespace(lintCtx, pass, wsSettings) 46 if err != nil { 47 return nil, err 48 } 49 50 if len(issues) == 0 { 51 return nil, nil 52 } 53 54 mu.Lock() 55 resIssues = append(resIssues, issues...) 56 mu.Unlock() 57 58 return nil, nil 59 } 60 }).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { 61 return resIssues 62 }).WithLoadMode(goanalysis.LoadModeSyntax) 63 } 64 65 func runWhitespace(lintCtx *linter.Context, pass *analysis.Pass, wsSettings whitespace.Settings) ([]goanalysis.Issue, error) { 66 var messages []whitespace.Message 67 for _, file := range pass.Files { 68 messages = append(messages, whitespace.Run(file, pass.Fset, wsSettings)...) 69 } 70 71 if len(messages) == 0 { 72 return nil, nil 73 } 74 75 issues := make([]goanalysis.Issue, len(messages)) 76 for k, i := range messages { 77 issue := result.Issue{ 78 Pos: token.Position{ 79 Filename: i.Pos.Filename, 80 Line: i.Pos.Line, 81 }, 82 LineRange: &result.Range{From: i.Pos.Line, To: i.Pos.Line}, 83 Text: i.Message, 84 FromLinter: whitespaceName, 85 Replacement: &result.Replacement{}, 86 } 87 88 bracketLine, err := lintCtx.LineCache.GetLine(issue.Pos.Filename, issue.Pos.Line) 89 if err != nil { 90 return nil, errors.Wrapf(err, "failed to get line %s:%d", issue.Pos.Filename, issue.Pos.Line) 91 } 92 93 switch i.Type { 94 case whitespace.MessageTypeLeading: 95 issue.LineRange.To++ // cover two lines by the issue: opening bracket "{" (issue.Pos.Line) and following empty line 96 case whitespace.MessageTypeTrailing: 97 issue.LineRange.From-- // cover two lines by the issue: closing bracket "}" (issue.Pos.Line) and preceding empty line 98 issue.Pos.Line-- // set in sync with LineRange.From to not break fixer and other code features 99 case whitespace.MessageTypeAddAfter: 100 bracketLine += "\n" 101 } 102 issue.Replacement.NewLines = []string{bracketLine} 103 104 issues[k] = goanalysis.NewIssue(&issue, pass) 105 } 106 107 return issues, nil 108 }