github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/misspell.go (about)

     1  package golinters
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/golangci/misspell"
    10  	"golang.org/x/tools/go/analysis"
    11  
    12  	"github.com/elek/golangci-lint/pkg/golinters/goanalysis"
    13  	"github.com/elek/golangci-lint/pkg/lint/linter"
    14  	"github.com/elek/golangci-lint/pkg/result"
    15  )
    16  
    17  func runMisspellOnFile(fileName string, r *misspell.Replacer, lintCtx *linter.Context) ([]result.Issue, error) {
    18  	var res []result.Issue
    19  	fileContent, err := lintCtx.FileCache.GetFileBytes(fileName)
    20  	if err != nil {
    21  		return nil, fmt.Errorf("can't get file %s contents: %s", fileName, err)
    22  	}
    23  
    24  	// use r.Replace, not r.ReplaceGo because r.ReplaceGo doesn't find
    25  	// issues inside strings: it searches only inside comments. r.Replace
    26  	// searches all words: it treats input as a plain text. A standalone misspell
    27  	// tool uses r.Replace by default.
    28  	_, diffs := r.Replace(string(fileContent))
    29  	for _, diff := range diffs {
    30  		text := fmt.Sprintf("`%s` is a misspelling of `%s`", diff.Original, diff.Corrected)
    31  		pos := token.Position{
    32  			Filename: fileName,
    33  			Line:     diff.Line,
    34  			Column:   diff.Column + 1,
    35  		}
    36  		replacement := &result.Replacement{
    37  			Inline: &result.InlineFix{
    38  				StartCol:  diff.Column,
    39  				Length:    len(diff.Original),
    40  				NewString: diff.Corrected,
    41  			},
    42  		}
    43  
    44  		res = append(res, result.Issue{
    45  			Pos:         pos,
    46  			Text:        text,
    47  			FromLinter:  misspellName,
    48  			Replacement: replacement,
    49  		})
    50  	}
    51  
    52  	return res, nil
    53  }
    54  
    55  const misspellName = "misspell"
    56  
    57  func NewMisspell() *goanalysis.Linter {
    58  	var mu sync.Mutex
    59  	var resIssues []goanalysis.Issue
    60  	var ruleErr error
    61  
    62  	analyzer := &analysis.Analyzer{
    63  		Name: misspellName,
    64  		Doc:  goanalysis.TheOnlyanalyzerDoc,
    65  	}
    66  	return goanalysis.NewLinter(
    67  		misspellName,
    68  		"Finds commonly misspelled English words in comments",
    69  		[]*analysis.Analyzer{analyzer},
    70  		nil,
    71  	).WithContextSetter(func(lintCtx *linter.Context) {
    72  		r := misspell.Replacer{
    73  			Replacements: misspell.DictMain,
    74  		}
    75  
    76  		// Figure out regional variations
    77  		settings := lintCtx.Settings().Misspell
    78  		locale := settings.Locale
    79  		switch strings.ToUpper(locale) {
    80  		case "":
    81  			// nothing
    82  		case "US":
    83  			r.AddRuleList(misspell.DictAmerican)
    84  		case "UK", "GB":
    85  			r.AddRuleList(misspell.DictBritish)
    86  		case "NZ", "AU", "CA":
    87  			ruleErr = fmt.Errorf("unknown locale: %q", locale)
    88  		}
    89  
    90  		if ruleErr == nil {
    91  			if len(settings.IgnoreWords) != 0 {
    92  				r.RemoveRule(settings.IgnoreWords)
    93  			}
    94  
    95  			r.Compile()
    96  		}
    97  
    98  		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
    99  			if ruleErr != nil {
   100  				return nil, ruleErr
   101  			}
   102  
   103  			var fileNames []string
   104  			for _, f := range pass.Files {
   105  				pos := pass.Fset.PositionFor(f.Pos(), false)
   106  				fileNames = append(fileNames, pos.Filename)
   107  			}
   108  
   109  			var res []goanalysis.Issue
   110  			for _, f := range fileNames {
   111  				issues, err := runMisspellOnFile(f, &r, lintCtx)
   112  				if err != nil {
   113  					return nil, err
   114  				}
   115  				for i := range issues {
   116  					res = append(res, goanalysis.NewIssue(&issues[i], pass))
   117  				}
   118  			}
   119  			if len(res) == 0 {
   120  				return nil, nil
   121  			}
   122  
   123  			mu.Lock()
   124  			resIssues = append(resIssues, res...)
   125  			mu.Unlock()
   126  
   127  			return nil, nil
   128  		}
   129  	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
   130  		return resIssues
   131  	}).WithLoadMode(goanalysis.LoadModeSyntax)
   132  }