238 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package nolintlint
 | |
| 
 | |
| import (
 | |
| 	"go/parser"
 | |
| 	"go/token"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 
 | |
| 	"github.com/golangci/golangci-lint/pkg/result"
 | |
| )
 | |
| 
 | |
| func TestLinter_Run(t *testing.T) {
 | |
| 	type issueWithReplacement struct {
 | |
| 		issue       string
 | |
| 		replacement *result.Replacement
 | |
| 	}
 | |
| 	testCases := []struct {
 | |
| 		desc     string
 | |
| 		needs    Needs
 | |
| 		excludes []string
 | |
| 		contents string
 | |
| 		expected []issueWithReplacement
 | |
| 	}{
 | |
| 		{
 | |
| 			desc:  "when no explanation is provided",
 | |
| 			needs: NeedsExplanation,
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| // example
 | |
| //nolint
 | |
| func foo() {
 | |
|   bad() //nolint
 | |
|   bad() //nolint //
 | |
|   bad() //nolint // 
 | |
|   good() //nolint // this is ok
 | |
| 	other() //nolintother
 | |
| }`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{issue: "directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:5:1"},
 | |
| 				{issue: "directive `//nolint` should provide explanation such as `//nolint // this is why` at testing.go:7:9"},
 | |
| 				{issue: "directive `//nolint //` should provide explanation such as `//nolint // this is why` at testing.go:8:9"},
 | |
| 				{issue: "directive `//nolint // ` should provide explanation such as `//nolint // this is why` at testing.go:9:9"},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			desc:  "when multiple directives on multiple lines",
 | |
| 			needs: NeedsExplanation,
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| // example
 | |
| //nolint // this is ok
 | |
| //nolint:dupl
 | |
| func foo() {}`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{issue: "directive `//nolint:dupl` should provide explanation such as `//nolint:dupl // this is why` at testing.go:6:1"},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			desc:     "when no explanation is needed for a specific linter",
 | |
| 			needs:    NeedsExplanation,
 | |
| 			excludes: []string{"lll"},
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
| 	thisIsAReallyLongLine() //nolint:lll
 | |
| }`,
 | |
| 		},
 | |
| 		{
 | |
| 			desc:  "when no specific linter is mentioned",
 | |
| 			needs: NeedsSpecific,
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
|   good() //nolint:my-linter
 | |
|   bad() //nolint
 | |
|   bad() //nolint // because
 | |
| }`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{issue: "directive `//nolint` should mention specific linter such as `//nolint:my-linter` at testing.go:6:9"},
 | |
| 				{issue: "directive `//nolint // because` should mention specific linter such as `//nolint:my-linter` at testing.go:7:9"},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "when machine-readable style isn't used",
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
|   bad() // nolint
 | |
|   bad() //   nolint
 | |
|   good() //nolint
 | |
| }`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{
 | |
| 					issue: "directive `// nolint` should be written without leading space as `//nolint` at testing.go:5:9",
 | |
| 					replacement: &result.Replacement{
 | |
| 						Inline: &result.InlineFix{
 | |
| 							StartCol:  10,
 | |
| 							Length:    1,
 | |
| 							NewString: "",
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				{
 | |
| 					issue: "directive `//   nolint` should be written without leading space as `//nolint` at testing.go:6:9",
 | |
| 					replacement: &result.Replacement{
 | |
| 						Inline: &result.InlineFix{
 | |
| 							StartCol:  10,
 | |
| 							Length:    3,
 | |
| 							NewString: "",
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "spaces are allowed in comma-separated list of linters",
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
|   good() //nolint:linter1,linter-two
 | |
|   bad() //nolint:linter1 linter2
 | |
|   good() //nolint: linter1,linter2
 | |
|   good() //nolint: linter1, linter2
 | |
| }`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{issue: "directive `//nolint:linter1 linter2` should match `//nolint[:<comma-separated-linters>] [// <explanation>]` at testing.go:6:9"}, //nolint:lll // this is a string
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "multi-line comments don't confuse parser",
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
|   //nolint:test
 | |
|   // something else
 | |
| }`,
 | |
| 		},
 | |
| 		{
 | |
| 			desc:  "needs unused without specific linter generates replacement",
 | |
| 			needs: NeedsUnused,
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
|   bad() //nolint
 | |
| }`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{
 | |
| 					issue: "directive `//nolint` is unused at testing.go:5:9",
 | |
| 					replacement: &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{
 | |
| 				{
 | |
| 					issue: "directive `//nolint:somelinter` is unused for linter \"somelinter\" at testing.go:5:9",
 | |
| 					replacement: &result.Replacement{
 | |
| 						Inline: &result.InlineFix{
 | |
| 							StartCol:  8,
 | |
| 							Length:    19,
 | |
| 							NewString: "",
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			desc:  "needs unused with multiple specific linters does not generate replacements",
 | |
| 			needs: NeedsUnused,
 | |
| 			contents: `
 | |
| package bar
 | |
| 
 | |
| func foo() {
 | |
|   bad() //nolint:linter1,linter2
 | |
| }`,
 | |
| 			expected: []issueWithReplacement{
 | |
| 				{
 | |
| 					issue: "directive `//nolint:linter1,linter2` is unused for linter \"linter1\" at testing.go:5:9",
 | |
| 				},
 | |
| 				{
 | |
| 					issue: "directive `//nolint:linter1,linter2` is unused for linter \"linter2\" at testing.go:5:9",
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range testCases {
 | |
| 		test := test
 | |
| 		t.Run(test.desc, func(t *testing.T) {
 | |
| 			t.Parallel()
 | |
| 
 | |
| 			linter, _ := NewLinter(test.needs, test.excludes)
 | |
| 
 | |
| 			fset := token.NewFileSet()
 | |
| 			expr, err := parser.ParseFile(fset, "testing.go", test.contents, parser.ParseComments)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			actualIssues, err := linter.Run(fset, expr)
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			actualIssuesWithReplacements := make([]issueWithReplacement, 0, len(actualIssues))
 | |
| 			for _, i := range actualIssues {
 | |
| 				actualIssuesWithReplacements = append(actualIssuesWithReplacements, issueWithReplacement{
 | |
| 					issue:       i.String(),
 | |
| 					replacement: i.Replacement(),
 | |
| 				})
 | |
| 			}
 | |
| 
 | |
| 			assert.ElementsMatch(t, test.expected, actualIssuesWithReplacements,
 | |
| 				"expected %s \nbut got %s", test.expected, actualIssuesWithReplacements)
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
