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  }