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 }