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