@@ -47,7 +47,7 @@ type NotSpecific struct {
4747}
4848
4949func (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
6060func (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
119118var 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
143142func (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