Update nolintlint to fix nolint formatting and remove unused nolint statements (#1573)
Also allow multiple ranges to satisfy a nolint statement as having been used.
This commit is contained in:
		
							parent
							
								
									df9278efd2
								
							
						
					
					
						commit
						aeb9830329
					
				@ -72,6 +72,7 @@ func NewNoLintLint() *goanalysis.Linter {
 | 
				
			|||||||
					Pos:                  i.Position(),
 | 
										Pos:                  i.Position(),
 | 
				
			||||||
					ExpectNoLint:         expectNoLint,
 | 
										ExpectNoLint:         expectNoLint,
 | 
				
			||||||
					ExpectedNoLintLinter: expectedNolintLinter,
 | 
										ExpectedNoLintLinter: expectedNolintLinter,
 | 
				
			||||||
 | 
										Replacement:          i.Replacement(),
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				res = append(res, goanalysis.NewIssue(issue, pass))
 | 
									res = append(res, goanalysis.NewIssue(issue, pass))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,16 +8,21 @@ import (
 | 
				
			|||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unicode"
 | 
						"unicode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golangci/golangci-lint/pkg/result"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BaseIssue struct {
 | 
					type BaseIssue struct {
 | 
				
			||||||
	fullDirective                     string
 | 
						fullDirective                     string
 | 
				
			||||||
	directiveWithOptionalLeadingSpace string
 | 
						directiveWithOptionalLeadingSpace string
 | 
				
			||||||
	position                          token.Position
 | 
						position                          token.Position
 | 
				
			||||||
 | 
						replacement                       *result.Replacement
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b BaseIssue) Position() token.Position {
 | 
					func (b BaseIssue) Position() token.Position { return b.position }
 | 
				
			||||||
	return b.position
 | 
					
 | 
				
			||||||
 | 
					func (b BaseIssue) Replacement() *result.Replacement {
 | 
				
			||||||
 | 
						return b.replacement
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ExtraLeadingSpace struct {
 | 
					type ExtraLeadingSpace struct {
 | 
				
			||||||
@ -85,7 +90,7 @@ type UnusedCandidate struct {
 | 
				
			|||||||
func (i UnusedCandidate) Details() string {
 | 
					func (i UnusedCandidate) Details() string {
 | 
				
			||||||
	details := fmt.Sprintf("directive `%s` is unused", i.fullDirective)
 | 
						details := fmt.Sprintf("directive `%s` is unused", i.fullDirective)
 | 
				
			||||||
	if i.ExpectedLinter != "" {
 | 
						if i.ExpectedLinter != "" {
 | 
				
			||||||
		details += fmt.Sprintf(" for linter %s", i.ExpectedLinter)
 | 
							details += fmt.Sprintf(" for linter %q", i.ExpectedLinter)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return details
 | 
						return details
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -100,6 +105,7 @@ type Issue interface {
 | 
				
			|||||||
	Details() string
 | 
						Details() string
 | 
				
			||||||
	Position() token.Position
 | 
						Position() token.Position
 | 
				
			||||||
	String() string
 | 
						String() string
 | 
				
			||||||
 | 
						Replacement() *result.Replacement
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Needs uint
 | 
					type Needs uint
 | 
				
			||||||
@ -115,7 +121,7 @@ const (
 | 
				
			|||||||
var commentPattern = regexp.MustCompile(`^//\s*(nolint)(:\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*)?\b`)
 | 
					var commentPattern = regexp.MustCompile(`^//\s*(nolint)(:\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*)?\b`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// matches a complete nolint directive
 | 
					// matches a complete nolint directive
 | 
				
			||||||
var fullDirectivePattern = regexp.MustCompile(`^//\s*nolint(:\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*)?\s*(//.*)?\s*\n?$`)
 | 
					var fullDirectivePattern = regexp.MustCompile(`^//\s*nolint(?::(\s*[\w-]+\s*(?:,\s*[\w-]+\s*)*))?\s*(//.*)?\s*\n?$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Linter struct {
 | 
					type Linter struct {
 | 
				
			||||||
	excludes        []string // lists individual linters that don't require explanations
 | 
						excludes        []string // lists individual linters that don't require explanations
 | 
				
			||||||
@ -164,19 +170,34 @@ func (l Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) {
 | 
				
			|||||||
						directiveWithOptionalLeadingSpace = "// " + strings.TrimSpace(split[1])
 | 
											directiveWithOptionalLeadingSpace = "// " + strings.TrimSpace(split[1])
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										pos := fset.Position(comment.Pos())
 | 
				
			||||||
 | 
										end := fset.Position(comment.End())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					base := BaseIssue{
 | 
										base := BaseIssue{
 | 
				
			||||||
						fullDirective:                     comment.Text,
 | 
											fullDirective:                     comment.Text,
 | 
				
			||||||
						directiveWithOptionalLeadingSpace: directiveWithOptionalLeadingSpace,
 | 
											directiveWithOptionalLeadingSpace: directiveWithOptionalLeadingSpace,
 | 
				
			||||||
						position:                          fset.Position(comment.Pos()),
 | 
											position:                          pos,
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// check for, report and eliminate leading spaces so we can check for other issues
 | 
										// check for, report and eliminate leading spaces so we can check for other issues
 | 
				
			||||||
					if len(leadingSpace) > 1 {
 | 
										if len(leadingSpace) > 0 {
 | 
				
			||||||
						issues = append(issues, ExtraLeadingSpace{BaseIssue: base})
 | 
											machineReadableReplacement := &result.Replacement{
 | 
				
			||||||
					}
 | 
												Inline: &result.InlineFix{
 | 
				
			||||||
 | 
													StartCol:  pos.Column - 1,
 | 
				
			||||||
 | 
													Length:    len(leadingSpace) + 2,
 | 
				
			||||||
 | 
													NewString: "//",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (l.needs&NeedsMachineOnly) != 0 && len(leadingSpace) > 0 {
 | 
											if (l.needs & NeedsMachineOnly) != 0 {
 | 
				
			||||||
						issues = append(issues, NotMachine{BaseIssue: base})
 | 
												issue := NotMachine{BaseIssue: base}
 | 
				
			||||||
 | 
												issue.BaseIssue.replacement = machineReadableReplacement
 | 
				
			||||||
 | 
												issues = append(issues, issue)
 | 
				
			||||||
 | 
											} else if len(leadingSpace) > 1 {
 | 
				
			||||||
 | 
												issue := ExtraLeadingSpace{BaseIssue: base}
 | 
				
			||||||
 | 
												issue.BaseIssue.replacement = machineReadableReplacement
 | 
				
			||||||
 | 
												issues = append(issues, issue)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					fullMatches := fullDirectivePattern.FindStringSubmatch(comment.Text)
 | 
										fullMatches := fullDirectivePattern.FindStringSubmatch(comment.Text)
 | 
				
			||||||
@ -188,7 +209,7 @@ func (l Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) {
 | 
				
			|||||||
					lintersText, explanation := fullMatches[1], fullMatches[2]
 | 
										lintersText, explanation := fullMatches[1], fullMatches[2]
 | 
				
			||||||
					var linters []string
 | 
										var linters []string
 | 
				
			||||||
					if len(lintersText) > 0 {
 | 
										if len(lintersText) > 0 {
 | 
				
			||||||
						lls := strings.Split(lintersText[1:], ",")
 | 
											lls := strings.Split(lintersText, ",")
 | 
				
			||||||
						linters = make([]string, 0, len(lls))
 | 
											linters = make([]string, 0, len(lls))
 | 
				
			||||||
						for _, ll := range lls {
 | 
											for _, ll := range lls {
 | 
				
			||||||
							ll = strings.TrimSpace(ll)
 | 
												ll = strings.TrimSpace(ll)
 | 
				
			||||||
@ -206,11 +227,34 @@ func (l Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					// when detecting unused directives, we send all the directives through and filter them out in the nolint processor
 | 
										// when detecting unused directives, we send all the directives through and filter them out in the nolint processor
 | 
				
			||||||
					if (l.needs & NeedsUnused) != 0 {
 | 
										if (l.needs & NeedsUnused) != 0 {
 | 
				
			||||||
 | 
											removeNolintCompletely := &result.Replacement{
 | 
				
			||||||
 | 
												Inline: &result.InlineFix{
 | 
				
			||||||
 | 
													StartCol:  pos.Column - 1,
 | 
				
			||||||
 | 
													Length:    end.Column - pos.Column,
 | 
				
			||||||
 | 
													NewString: "",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						if len(linters) == 0 {
 | 
											if len(linters) == 0 {
 | 
				
			||||||
							issues = append(issues, UnusedCandidate{BaseIssue: base})
 | 
												issue := UnusedCandidate{BaseIssue: base}
 | 
				
			||||||
 | 
												issue.replacement = removeNolintCompletely
 | 
				
			||||||
 | 
												issues = append(issues, issue)
 | 
				
			||||||
						} else {
 | 
											} else {
 | 
				
			||||||
							for _, linter := range linters {
 | 
												for i, linter := range linters {
 | 
				
			||||||
								issues = append(issues, UnusedCandidate{BaseIssue: base, ExpectedLinter: linter})
 | 
													issue := UnusedCandidate{BaseIssue: base, ExpectedLinter: linter}
 | 
				
			||||||
 | 
													replacement := removeNolintCompletely
 | 
				
			||||||
 | 
													if len(linters) > 1 {
 | 
				
			||||||
 | 
														otherLinters := append(append([]string(nil), linters[0:i]...), linters[i+1:]...)
 | 
				
			||||||
 | 
														replacement = &result.Replacement{
 | 
				
			||||||
 | 
															Inline: &result.InlineFix{
 | 
				
			||||||
 | 
																StartCol:  (pos.Column - 1) + len("//") + len(leadingSpace) + len("nolint:"),
 | 
				
			||||||
 | 
																Length:    len(lintersText) - 1,
 | 
				
			||||||
 | 
																NewString: strings.Join(otherLinters, ","),
 | 
				
			||||||
 | 
															},
 | 
				
			||||||
 | 
														}
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
													issue.replacement = replacement
 | 
				
			||||||
 | 
													issues = append(issues, issue)
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,16 +7,22 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"github.com/stretchr/testify/require"
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golangci/golangci-lint/pkg/result"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//nolint:funlen
 | 
					//nolint:funlen
 | 
				
			||||||
func TestNoLintLint(t *testing.T) {
 | 
					func TestNoLintLint(t *testing.T) {
 | 
				
			||||||
 | 
						type issueWithReplacement struct {
 | 
				
			||||||
 | 
							issue       string
 | 
				
			||||||
 | 
							replacement *result.Replacement
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		desc     string
 | 
							desc     string
 | 
				
			||||||
		needs    Needs
 | 
							needs    Needs
 | 
				
			||||||
		excludes []string
 | 
							excludes []string
 | 
				
			||||||
		contents string
 | 
							contents string
 | 
				
			||||||
		expected []string
 | 
							expected []issueWithReplacement
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			desc:  "when no explanation is provided",
 | 
								desc:  "when no explanation is provided",
 | 
				
			||||||
@ -33,11 +39,11 @@ func foo() {
 | 
				
			|||||||
  good() //nolint // this is ok
 | 
					  good() //nolint // this is ok
 | 
				
			||||||
	other() //nolintother
 | 
						other() //nolintother
 | 
				
			||||||
}`,
 | 
					}`,
 | 
				
			||||||
			expected: []string{
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
				"directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:5:1",
 | 
									{"directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:5:1", nil},
 | 
				
			||||||
				"directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:7:9",
 | 
									{"directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:7:9", nil},
 | 
				
			||||||
				"directive `//nolint //` should provide explanation such as `//nolint // this is why` at testing.go:8:9",
 | 
									{"directive `//nolint //` should provide explanation such as `//nolint // this is why` at testing.go:8:9", nil},
 | 
				
			||||||
				"directive `//nolint // ` should provide explanation such as `//nolint // this is why` at testing.go:9:9",
 | 
									{"directive `//nolint // ` should provide explanation such as `//nolint // this is why` at testing.go:9:9", nil},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -50,8 +56,8 @@ package bar
 | 
				
			|||||||
//nolint // this is ok
 | 
					//nolint // this is ok
 | 
				
			||||||
//nolint:dupl
 | 
					//nolint:dupl
 | 
				
			||||||
func foo() {}`,
 | 
					func foo() {}`,
 | 
				
			||||||
			expected: []string{
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
				"directive `//nolint:dupl` should provide explanation such as `//nolint:dupl // this is why` at testing.go:6:1",
 | 
									{"directive `//nolint:dupl` should provide explanation such as `//nolint:dupl // this is why` at testing.go:6:1", nil},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -76,9 +82,9 @@ func foo() {
 | 
				
			|||||||
  bad() //nolint
 | 
					  bad() //nolint
 | 
				
			||||||
  bad() // nolint // because
 | 
					  bad() // nolint // because
 | 
				
			||||||
}`,
 | 
					}`,
 | 
				
			||||||
			expected: []string{
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
				"directive `//nolint` should mention specific linter such as `//nolint:my-linter` at testing.go:6:9",
 | 
									{"directive `//nolint` should mention specific linter such as `//nolint:my-linter` at testing.go:6:9", nil},
 | 
				
			||||||
				"directive `// nolint // because` should mention specific linter such as `// nolint:my-linter` at testing.go:7:9",
 | 
									{"directive `// nolint // because` should mention specific linter such as `// nolint:my-linter` at testing.go:7:9", nil},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -91,8 +97,17 @@ func foo() {
 | 
				
			|||||||
  bad() // nolint
 | 
					  bad() // nolint
 | 
				
			||||||
  good() //nolint
 | 
					  good() //nolint
 | 
				
			||||||
}`,
 | 
					}`,
 | 
				
			||||||
			expected: []string{
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
				"directive `// nolint` should be written without leading space as `//nolint` at testing.go:5:9",
 | 
									{
 | 
				
			||||||
 | 
										"directive `// nolint` should be written without leading space as `//nolint` at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  8,
 | 
				
			||||||
 | 
												Length:    3,
 | 
				
			||||||
 | 
												NewString: "//",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -104,8 +119,17 @@ func foo() {
 | 
				
			|||||||
  bad() //  nolint
 | 
					  bad() //  nolint
 | 
				
			||||||
  good() // nolint
 | 
					  good() // nolint
 | 
				
			||||||
}`,
 | 
					}`,
 | 
				
			||||||
			expected: []string{
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
				"directive `//  nolint` should not have more than one leading space at testing.go:5:9",
 | 
									{
 | 
				
			||||||
 | 
										"directive `//  nolint` should not have more than one leading space at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  8,
 | 
				
			||||||
 | 
												Length:    4,
 | 
				
			||||||
 | 
												NewString: "//",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -119,8 +143,8 @@ func foo() {
 | 
				
			|||||||
  good() // nolint: linter1,linter2
 | 
					  good() // nolint: linter1,linter2
 | 
				
			||||||
  good() // nolint: linter1, linter2
 | 
					  good() // nolint: linter1, linter2
 | 
				
			||||||
}`,
 | 
					}`,
 | 
				
			||||||
			expected: []string{
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
				"directive `// nolint:linter1 linter2` should match `// nolint[:<comma-separated-linters>] [// <explanation>]` at testing.go:6:9", //nolint:lll // this is a string
 | 
									{"directive `// nolint:linter1 linter2` should match `// nolint[:<comma-separated-linters>] [// <explanation>]` at testing.go:6:9", nil}, //nolint:lll // this is a string
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -133,6 +157,92 @@ func foo() {
 | 
				
			|||||||
  // something else
 | 
					  // something else
 | 
				
			||||||
}`,
 | 
					}`,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								desc:  "needs unused without specific linter generates replacement",
 | 
				
			||||||
 | 
								needs: NeedsUnused,
 | 
				
			||||||
 | 
								contents: `
 | 
				
			||||||
 | 
					package bar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func foo() {
 | 
				
			||||||
 | 
					  bad() //nolint
 | 
				
			||||||
 | 
					}`,
 | 
				
			||||||
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"directive `//nolint` is unused at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  8,
 | 
				
			||||||
 | 
												Length:    8,
 | 
				
			||||||
 | 
												NewString: "",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								desc:  "needs unused with one specific linter generates replacement",
 | 
				
			||||||
 | 
								needs: NeedsUnused,
 | 
				
			||||||
 | 
								contents: `
 | 
				
			||||||
 | 
					package bar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func foo() {
 | 
				
			||||||
 | 
					  bad() //nolint:somelinter
 | 
				
			||||||
 | 
					}`,
 | 
				
			||||||
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"directive `//nolint:somelinter` is unused for linter \"somelinter\" at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  8,
 | 
				
			||||||
 | 
												Length:    19,
 | 
				
			||||||
 | 
												NewString: "",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								desc:  "needs unused with multiple specific linter generates replacement for each linter",
 | 
				
			||||||
 | 
								needs: NeedsUnused,
 | 
				
			||||||
 | 
								contents: `
 | 
				
			||||||
 | 
					package bar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func foo() {
 | 
				
			||||||
 | 
					  bad() //nolint:linter1,linter2,linter3
 | 
				
			||||||
 | 
					}`,
 | 
				
			||||||
 | 
								expected: []issueWithReplacement{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"directive `//nolint:linter1,linter2,linter3` is unused for linter \"linter1\" at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  17,
 | 
				
			||||||
 | 
												Length:    22,
 | 
				
			||||||
 | 
												NewString: "linter2,linter3",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"directive `//nolint:linter1,linter2,linter3` is unused for linter \"linter2\" at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  17,
 | 
				
			||||||
 | 
												Length:    22,
 | 
				
			||||||
 | 
												NewString: "linter1,linter3",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"directive `//nolint:linter1,linter2,linter3` is unused for linter \"linter3\" at testing.go:5:9",
 | 
				
			||||||
 | 
										&result.Replacement{
 | 
				
			||||||
 | 
											Inline: &result.InlineFix{
 | 
				
			||||||
 | 
												StartCol:  17,
 | 
				
			||||||
 | 
												Length:    22,
 | 
				
			||||||
 | 
												NewString: "linter1,linter2",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range testCases {
 | 
						for _, test := range testCases {
 | 
				
			||||||
@ -149,12 +259,16 @@ func foo() {
 | 
				
			|||||||
			actualIssues, err := linter.Run(fset, expr)
 | 
								actualIssues, err := linter.Run(fset, expr)
 | 
				
			||||||
			require.NoError(t, err)
 | 
								require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			actualIssueStrs := make([]string, 0, len(actualIssues))
 | 
								actualIssuesWithReplacements := make([]issueWithReplacement, 0, len(actualIssues))
 | 
				
			||||||
			for _, i := range actualIssues {
 | 
								for _, i := range actualIssues {
 | 
				
			||||||
				actualIssueStrs = append(actualIssueStrs, i.String())
 | 
									actualIssuesWithReplacements = append(actualIssuesWithReplacements, issueWithReplacement{
 | 
				
			||||||
 | 
										issue:       i.String(),
 | 
				
			||||||
 | 
										replacement: i.Replacement(),
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			assert.ElementsMatch(t, test.expected, actualIssueStrs, "expected %s \nbut got %s", test.expected, actualIssues)
 | 
								assert.ElementsMatch(t, test.expected, actualIssuesWithReplacements,
 | 
				
			||||||
 | 
									"expected %s \nbut got %s", test.expected, actualIssuesWithReplacements)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ type Range struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Replacement struct {
 | 
					type Replacement struct {
 | 
				
			||||||
	NeedOnlyDelete bool     // need to delete all lines of the issue without replacement with new lines
 | 
						NeedOnlyDelete bool     // need to delete all lines of the issue without replacement with new lines
 | 
				
			||||||
	NewLines       []string // is NeedDelete is false it's the replacement lines
 | 
						NewLines       []string // if NeedDelete is false it's the replacement lines
 | 
				
			||||||
	Inline         *InlineFix
 | 
						Inline         *InlineFix
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,8 @@ type ignoredRange struct {
 | 
				
			|||||||
	linters                []string
 | 
						linters                []string
 | 
				
			||||||
	matchedIssueFromLinter map[string]bool
 | 
						matchedIssueFromLinter map[string]bool
 | 
				
			||||||
	result.Range
 | 
						result.Range
 | 
				
			||||||
	col int
 | 
						col           int
 | 
				
			||||||
 | 
						originalRange *ignoredRange // pre-expanded range (used to match nolintlint issues)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (i *ignoredRange) doesMatch(issue *result.Issue) bool {
 | 
					func (i *ignoredRange) doesMatch(issue *result.Issue) bool {
 | 
				
			||||||
@ -163,7 +164,11 @@ func (p *Nolint) shouldPassIssue(i *result.Issue) (bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, ir := range fd.ignoredRanges {
 | 
						for _, ir := range fd.ignoredRanges {
 | 
				
			||||||
		if ir.doesMatch(i) {
 | 
							if ir.doesMatch(i) {
 | 
				
			||||||
 | 
								nolintDebugf("found ignored range for issue %v: %v", i, ir)
 | 
				
			||||||
			ir.matchedIssueFromLinter[i.FromLinter] = true
 | 
								ir.matchedIssueFromLinter[i.FromLinter] = true
 | 
				
			||||||
 | 
								if ir.originalRange != nil {
 | 
				
			||||||
 | 
									ir.originalRange.matchedIssueFromLinter[i.FromLinter] = true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			return false, nil
 | 
								return false, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -199,9 +204,14 @@ func (e *rangeExpander) Visit(node ast.Node) ast.Visitor {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	expandedRange := *foundRange
 | 
						expandedRange := *foundRange
 | 
				
			||||||
 | 
						// store the original unexpanded range for matching nolintlint issues
 | 
				
			||||||
 | 
						if expandedRange.originalRange == nil {
 | 
				
			||||||
 | 
							expandedRange.originalRange = foundRange
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if expandedRange.To < nodeEndLine {
 | 
						if expandedRange.To < nodeEndLine {
 | 
				
			||||||
		expandedRange.To = nodeEndLine
 | 
							expandedRange.To = nodeEndLine
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nolintDebugf("found range is %v for node %#v [%d;%d], expanded range is %v",
 | 
						nolintDebugf("found range is %v for node %#v [%d;%d], expanded range is %v",
 | 
				
			||||||
		*foundRange, node, nodeStartLine, nodeEndLine, expandedRange)
 | 
							*foundRange, node, nodeStartLine, nodeEndLine, expandedRange)
 | 
				
			||||||
	e.expandedRanges = append(e.expandedRanges, expandedRange)
 | 
						e.expandedRanges = append(e.expandedRanges, expandedRange)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								test/testdata/fix/in/nolintlint.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/testdata/fix/in/nolintlint.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					//args: -Enolintlint -Elll
 | 
				
			||||||
 | 
					//config: linters-settings.nolintlint.allow-leading-space=false
 | 
				
			||||||
 | 
					package p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func nolintlint() {
 | 
				
			||||||
 | 
						run() // nolint:bob // leading space should be dropped
 | 
				
			||||||
 | 
						run() //  nolint:bob // leading spaces should be dropped
 | 
				
			||||||
 | 
						// note that the next lines will retain trailing whitespace when fixed
 | 
				
			||||||
 | 
						run() //nolint // nolint should be dropped
 | 
				
			||||||
 | 
						run() //nolint:lll // nolint should be dropped
 | 
				
			||||||
 | 
						run() //nolint:alice,lll,bob // enabled linter should be dropped
 | 
				
			||||||
 | 
						run() //nolint:alice,lll,bob,nolintlint // enabled linter should not be dropped when nolintlint is nolinted
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								test/testdata/fix/out/nolintlint.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/testdata/fix/out/nolintlint.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					//args: -Enolintlint -Elll
 | 
				
			||||||
 | 
					//config: linters-settings.nolintlint.allow-leading-space=false
 | 
				
			||||||
 | 
					package p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func nolintlint() {
 | 
				
			||||||
 | 
						run() //nolint:bob // leading space should be dropped
 | 
				
			||||||
 | 
						run() //nolint:bob // leading spaces should be dropped
 | 
				
			||||||
 | 
						// note that the next lines will retain trailing whitespace when fixed
 | 
				
			||||||
 | 
						run() 
 | 
				
			||||||
 | 
						run() 
 | 
				
			||||||
 | 
						run() //nolint:alice,bob // enabled linter should be dropped
 | 
				
			||||||
 | 
						run() //nolint:alice,lll,bob,nolintlint // enabled linter should not be dropped when nolintlint is nolinted
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								test/testdata/nolintlint.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								test/testdata/nolintlint.go
									
									
									
									
										vendored
									
									
								
							@ -1,7 +1,7 @@
 | 
				
			|||||||
//args: -Enolintlint
 | 
					//args: -Enolintlint -Emisspell
 | 
				
			||||||
//config: linters-settings.nolintlint.require-explanation=true
 | 
					//config: linters-settings.nolintlint.require-explanation=true
 | 
				
			||||||
//config: linters-settings.nolintlint.require-specific=true
 | 
					//config: linters-settings.nolintlint.require-specific=true
 | 
				
			||||||
//config: linters-settings.nolintlint.allowing-leading-space=false
 | 
					//config: linters-settings.nolintlint.allow-leading-space=false
 | 
				
			||||||
package testdata
 | 
					package testdata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "fmt"
 | 
					import "fmt"
 | 
				
			||||||
@ -10,4 +10,11 @@ func Foo() {
 | 
				
			|||||||
	fmt.Println("not specific")         //nolint // ERROR "directive `.*` should mention specific linter such as `//nolint:my-linter`"
 | 
						fmt.Println("not specific")         //nolint // ERROR "directive `.*` should mention specific linter such as `//nolint:my-linter`"
 | 
				
			||||||
	fmt.Println("not machine readable") // nolint // ERROR "directive `.*`  should be written as `//nolint`"
 | 
						fmt.Println("not machine readable") // nolint // ERROR "directive `.*`  should be written as `//nolint`"
 | 
				
			||||||
	fmt.Println("extra spaces")         //  nolint:deadcode // because // ERROR "directive `.*` should not have more than one leading space"
 | 
						fmt.Println("extra spaces")         //  nolint:deadcode // because // ERROR "directive `.*` should not have more than one leading space"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// test expanded range
 | 
				
			||||||
 | 
						//nolint:misspell // deliberate misspelling to trigger nolintlint
 | 
				
			||||||
 | 
						func() {
 | 
				
			||||||
 | 
							mispell := true
 | 
				
			||||||
 | 
							fmt.Println(mispell)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user