Skip to content

Commit be02979

Browse files
authored
fix: nolintlint comment analysis. (#1571)
1 parent a893212 commit be02979

File tree

3 files changed

+182
-142
lines changed

3 files changed

+182
-142
lines changed

pkg/golinters/nolintlint/nolintlint.go

+78-75
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type NotSpecific struct {
4747
}
4848

4949
func (i NotSpecific) Details() string {
50-
return fmt.Sprintf("directive `%s` should mention specific linter such as `//%s:my-linter`",
50+
return fmt.Sprintf("directive `%s` should mention specific linter such as `%s:my-linter`",
5151
i.fullDirective, i.directiveWithOptionalLeadingSpace)
5252
}
5353

@@ -58,7 +58,7 @@ type ParseError struct {
5858
}
5959

6060
func (i ParseError) Details() string {
61-
return fmt.Sprintf("directive `%s` should match `//%s[:<comma-separated-linters>] [// <explanation>]`",
61+
return fmt.Sprintf("directive `%s` should match `%s[:<comma-separated-linters>] [// <explanation>]`",
6262
i.fullDirective,
6363
i.directiveWithOptionalLeadingSpace)
6464
}
@@ -112,8 +112,7 @@ const (
112112
NeedsAll = NeedsMachineOnly | NeedsSpecific | NeedsExplanation
113113
)
114114

115-
// matches lines starting with the nolint directive
116-
var directiveOnlyPattern = regexp.MustCompile(`^\s*(nolint)(:\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*)?\b`)
115+
var commentPattern = regexp.MustCompile(`^//\s*(nolint)(:\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*)?\b`)
117116

118117
// matches a complete nolint directive
119118
var fullDirectivePattern = regexp.MustCompile(`^//\s*nolint(:\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*)?\s*(//.*)?\s*\n?$`)
@@ -142,98 +141,102 @@ var trailingBlankExplanation = regexp.MustCompile(`\s*(//\s*)?$`)
142141

143142
func (l Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) {
144143
var issues []Issue
144+
145145
for _, node := range nodes {
146146
if file, ok := node.(*ast.File); ok {
147147
for _, c := range file.Comments {
148-
text := c.Text()
149-
matches := directiveOnlyPattern.FindStringSubmatch(text)
150-
if len(matches) == 0 {
151-
continue
152-
}
153-
directive := matches[1]
148+
for _, comment := range c.List {
149+
if !commentPattern.MatchString(comment.Text) {
150+
continue
151+
}
154152

155-
// check for a space between the "//" and the directive
156-
leadingSpaceMatches := leadingSpacePattern.FindStringSubmatch(c.List[0].Text) // c.Text() doesn't have all leading space
157-
if len(leadingSpaceMatches) == 0 {
158-
continue
159-
}
160-
leadingSpace := leadingSpaceMatches[1]
153+
// check for a space between the "//" and the directive
154+
leadingSpaceMatches := leadingSpacePattern.FindStringSubmatch(comment.Text)
161155

162-
directiveWithOptionalLeadingSpace := directive
163-
if len(leadingSpace) > 0 {
164-
directiveWithOptionalLeadingSpace = " " + directive
165-
}
156+
var leadingSpace string
157+
if len(leadingSpaceMatches) > 0 {
158+
leadingSpace = leadingSpaceMatches[1]
159+
}
166160

167-
base := BaseIssue{
168-
fullDirective: c.List[0].Text,
169-
directiveWithOptionalLeadingSpace: directiveWithOptionalLeadingSpace,
170-
position: fset.Position(c.Pos()),
171-
}
161+
directiveWithOptionalLeadingSpace := comment.Text
162+
if len(leadingSpace) > 0 {
163+
split := strings.Split(strings.SplitN(comment.Text, ":", 2)[0], "//")
164+
directiveWithOptionalLeadingSpace = "// " + strings.TrimSpace(split[1])
165+
}
172166

173-
// check for, report and eliminate leading spaces so we can check for other issues
174-
if leadingSpace != "" && leadingSpace != " " {
175-
issues = append(issues, ExtraLeadingSpace{
176-
BaseIssue: base,
177-
})
178-
}
167+
base := BaseIssue{
168+
fullDirective: comment.Text,
169+
directiveWithOptionalLeadingSpace: directiveWithOptionalLeadingSpace,
170+
position: fset.Position(comment.Pos()),
171+
}
179172

180-
if (l.needs&NeedsMachineOnly) != 0 && strings.HasPrefix(directiveWithOptionalLeadingSpace, " ") {
181-
issues = append(issues, NotMachine{BaseIssue: base})
182-
}
173+
// check for, report and eliminate leading spaces so we can check for other issues
174+
if len(leadingSpace) > 1 {
175+
issues = append(issues, ExtraLeadingSpace{BaseIssue: base})
176+
}
183177

184-
fullMatches := fullDirectivePattern.FindStringSubmatch(c.List[0].Text)
185-
if len(fullMatches) == 0 {
186-
issues = append(issues, ParseError{BaseIssue: base})
187-
continue
188-
}
189-
lintersText, explanation := fullMatches[1], fullMatches[2]
190-
var linters []string
191-
if len(lintersText) > 0 {
192-
lls := strings.Split(lintersText[1:], ",")
193-
linters = make([]string, 0, len(lls))
194-
for _, ll := range lls {
195-
ll = strings.TrimSpace(ll)
196-
if ll != "" {
197-
linters = append(linters, ll)
198-
}
178+
if (l.needs&NeedsMachineOnly) != 0 && len(leadingSpace) > 0 {
179+
issues = append(issues, NotMachine{BaseIssue: base})
199180
}
200-
}
201-
if (l.needs & NeedsSpecific) != 0 {
202-
if len(linters) == 0 {
203-
issues = append(issues, NotSpecific{BaseIssue: base})
181+
182+
fullMatches := fullDirectivePattern.FindStringSubmatch(comment.Text)
183+
if len(fullMatches) == 0 {
184+
issues = append(issues, ParseError{BaseIssue: base})
185+
continue
204186
}
205-
}
206187

207-
// when detecting unused directives, we send all the directives through and filter them out in the nolint processor
208-
if l.needs&NeedsUnused != 0 {
209-
if len(linters) == 0 {
210-
issues = append(issues, UnusedCandidate{BaseIssue: base})
211-
} else {
212-
for _, linter := range linters {
213-
issues = append(issues, UnusedCandidate{BaseIssue: base, ExpectedLinter: linter})
188+
lintersText, explanation := fullMatches[1], fullMatches[2]
189+
var linters []string
190+
if len(lintersText) > 0 {
191+
lls := strings.Split(lintersText[1:], ",")
192+
linters = make([]string, 0, len(lls))
193+
for _, ll := range lls {
194+
ll = strings.TrimSpace(ll)
195+
if ll != "" {
196+
linters = append(linters, ll)
197+
}
214198
}
215199
}
216-
}
217200

218-
if (l.needs&NeedsExplanation) != 0 && (explanation == "" || strings.TrimSpace(explanation) == "//") {
219-
needsExplanation := len(linters) == 0 // if no linters are mentioned, we must have explanation
220-
// otherwise, check if we are excluding all of the mentioned linters
221-
for _, ll := range linters {
222-
if !l.excludeByLinter[ll] { // if a linter does require explanation
223-
needsExplanation = true
224-
break
201+
if (l.needs & NeedsSpecific) != 0 {
202+
if len(linters) == 0 {
203+
issues = append(issues, NotSpecific{BaseIssue: base})
225204
}
226205
}
227-
if needsExplanation {
228-
fullDirectiveWithoutExplanation := trailingBlankExplanation.ReplaceAllString(c.List[0].Text, "")
229-
issues = append(issues, NoExplanation{
230-
BaseIssue: base,
231-
fullDirectiveWithoutExplanation: fullDirectiveWithoutExplanation,
232-
})
206+
207+
// when detecting unused directives, we send all the directives through and filter them out in the nolint processor
208+
if (l.needs & NeedsUnused) != 0 {
209+
if len(linters) == 0 {
210+
issues = append(issues, UnusedCandidate{BaseIssue: base})
211+
} else {
212+
for _, linter := range linters {
213+
issues = append(issues, UnusedCandidate{BaseIssue: base, ExpectedLinter: linter})
214+
}
215+
}
216+
}
217+
218+
if (l.needs&NeedsExplanation) != 0 && (explanation == "" || strings.TrimSpace(explanation) == "//") {
219+
needsExplanation := len(linters) == 0 // if no linters are mentioned, we must have explanation
220+
// otherwise, check if we are excluding all of the mentioned linters
221+
for _, ll := range linters {
222+
if !l.excludeByLinter[ll] { // if a linter does require explanation
223+
needsExplanation = true
224+
break
225+
}
226+
}
227+
228+
if needsExplanation {
229+
fullDirectiveWithoutExplanation := trailingBlankExplanation.ReplaceAllString(comment.Text, "")
230+
issues = append(issues, NoExplanation{
231+
BaseIssue: base,
232+
fullDirectiveWithoutExplanation: fullDirectiveWithoutExplanation,
233+
})
234+
}
233235
}
234236
}
235237
}
236238
}
237239
}
240+
238241
return issues, nil
239242
}

0 commit comments

Comments
 (0)