github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/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/elek/golangci-lint/pkg/golinters/goanalysis"
    12  	"github.com/elek/golangci-lint/pkg/lint/linter"
    13  	"github.com/elek/golangci-lint/pkg/result"
    14  )
    15  
    16  func NewWhitespace() *goanalysis.Linter {
    17  	const linterName = "whitespace"
    18  	var mu sync.Mutex
    19  	var resIssues []goanalysis.Issue
    20  
    21  	analyzer := &analysis.Analyzer{
    22  		Name: linterName,
    23  		Doc:  goanalysis.TheOnlyanalyzerDoc,
    24  	}
    25  	return goanalysis.NewLinter(
    26  		linterName,
    27  		"Tool for detection of leading and trailing whitespace",
    28  		[]*analysis.Analyzer{analyzer},
    29  		nil,
    30  	).WithContextSetter(func(lintCtx *linter.Context) {
    31  		cfg := lintCtx.Cfg.LintersSettings.Whitespace
    32  		settings := whitespace.Settings{MultiIf: cfg.MultiIf, MultiFunc: cfg.MultiFunc}
    33  
    34  		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
    35  			var issues []whitespace.Message
    36  			for _, file := range pass.Files {
    37  				issues = append(issues, whitespace.Run(file, pass.Fset, settings)...)
    38  			}
    39  
    40  			if len(issues) == 0 {
    41  				return nil, nil
    42  			}
    43  
    44  			res := make([]goanalysis.Issue, len(issues))
    45  			for k, i := range issues {
    46  				issue := result.Issue{
    47  					Pos: token.Position{
    48  						Filename: i.Pos.Filename,
    49  						Line:     i.Pos.Line,
    50  					},
    51  					LineRange:   &result.Range{From: i.Pos.Line, To: i.Pos.Line},
    52  					Text:        i.Message,
    53  					FromLinter:  linterName,
    54  					Replacement: &result.Replacement{},
    55  				}
    56  
    57  				bracketLine, err := lintCtx.LineCache.GetLine(issue.Pos.Filename, issue.Pos.Line)
    58  				if err != nil {
    59  					return nil, errors.Wrapf(err, "failed to get line %s:%d", issue.Pos.Filename, issue.Pos.Line)
    60  				}
    61  
    62  				switch i.Type {
    63  				case whitespace.MessageTypeLeading:
    64  					issue.LineRange.To++ // cover two lines by the issue: opening bracket "{" (issue.Pos.Line) and following empty line
    65  				case whitespace.MessageTypeTrailing:
    66  					issue.LineRange.From-- // cover two lines by the issue: closing bracket "}" (issue.Pos.Line) and preceding empty line
    67  					issue.Pos.Line--       // set in sync with LineRange.From to not break fixer and other code features
    68  				case whitespace.MessageTypeAddAfter:
    69  					bracketLine += "\n"
    70  				}
    71  				issue.Replacement.NewLines = []string{bracketLine}
    72  
    73  				res[k] = goanalysis.NewIssue(&issue, pass)
    74  			}
    75  
    76  			mu.Lock()
    77  			resIssues = append(resIssues, res...)
    78  			mu.Unlock()
    79  
    80  			return nil, nil
    81  		}
    82  	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
    83  		return resIssues
    84  	}).WithLoadMode(goanalysis.LoadModeSyntax)
    85  }