github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/result/processors/nolint_test.go (about)

     1  package processors
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/mock"
    11  
    12  	"github.com/elek/golangci-lint/pkg/config"
    13  	"github.com/elek/golangci-lint/pkg/golinters"
    14  	"github.com/elek/golangci-lint/pkg/lint/lintersdb"
    15  	"github.com/elek/golangci-lint/pkg/logutils"
    16  	"github.com/elek/golangci-lint/pkg/result"
    17  )
    18  
    19  func newNolintFileIssue(line int, fromLinter string) result.Issue {
    20  	return result.Issue{
    21  		Pos: token.Position{
    22  			Filename: filepath.Join("testdata", "nolint.go"),
    23  			Line:     line,
    24  		},
    25  		FromLinter: fromLinter,
    26  	}
    27  }
    28  
    29  func newNolint2FileIssue(line int) result.Issue {
    30  	i := newNolintFileIssue(line, "errcheck")
    31  	i.Pos.Filename = filepath.Join("testdata", "nolint2.go")
    32  	return i
    33  }
    34  
    35  func newTestNolintProcessor(log logutils.Log) *Nolint {
    36  	return NewNolint(log, lintersdb.NewManager(nil, nil), nil)
    37  }
    38  
    39  func getMockLog() *logutils.MockLog {
    40  	log := logutils.NewMockLog()
    41  	log.On("Infof", mock.Anything, mock.Anything).Maybe()
    42  	return log
    43  }
    44  
    45  func TestNolint(t *testing.T) {
    46  	p := newTestNolintProcessor(getMockLog())
    47  	defer p.Finish()
    48  
    49  	// test inline comments
    50  	processAssertEmpty(t, p, newNolintFileIssue(3, "gofmt"))
    51  	processAssertEmpty(t, p, newNolintFileIssue(3, "gofmt")) // check cached is ok
    52  	processAssertSame(t, p, newNolintFileIssue(3, "gofmtA")) // check different name
    53  
    54  	processAssertEmpty(t, p, newNolintFileIssue(4, "gofmt"))
    55  	processAssertSame(t, p, newNolintFileIssue(4, "gofmtA")) // check different name
    56  
    57  	processAssertEmpty(t, p, newNolintFileIssue(5, "gofmt"))
    58  	processAssertEmpty(t, p, newNolintFileIssue(5, "govet"))
    59  	processAssertSame(t, p, newNolintFileIssue(5, "gofmtA")) // check different name
    60  
    61  	processAssertEmpty(t, p, newNolintFileIssue(6, "any"))
    62  	processAssertEmpty(t, p, newNolintFileIssue(7, "any"))
    63  
    64  	processAssertSame(t, p, newNolintFileIssue(1, "golint")) // no directive
    65  
    66  	// test preceding comments
    67  	processAssertEmpty(t, p, newNolintFileIssue(10, "any")) // preceding comment for var
    68  	processAssertEmpty(t, p, newNolintFileIssue(9, "any"))  // preceding comment for var itself
    69  
    70  	processAssertSame(t, p, newNolintFileIssue(14, "any"))  // preceding comment with extra \n
    71  	processAssertEmpty(t, p, newNolintFileIssue(12, "any")) // preceding comment with extra \n itself
    72  
    73  	processAssertSame(t, p, newNolintFileIssue(17, "any"))  // preceding comment on different column
    74  	processAssertEmpty(t, p, newNolintFileIssue(16, "any")) // preceding comment on different column itself
    75  
    76  	// preceding comment for func name and comment itself
    77  	for i := 19; i <= 23; i++ {
    78  		processAssertEmpty(t, p, newNolintFileIssue(i, "any"))
    79  	}
    80  
    81  	processAssertSame(t, p, newNolintFileIssue(24, "any")) // right after func
    82  
    83  	// preceding multiline comment: last line
    84  	for i := 25; i <= 30; i++ {
    85  		processAssertEmpty(t, p, newNolintFileIssue(i, "any"))
    86  	}
    87  
    88  	processAssertSame(t, p, newNolintFileIssue(31, "any")) // between funcs
    89  
    90  	// preceding multiline comment: first line
    91  	for i := 32; i <= 37; i++ {
    92  		processAssertEmpty(t, p, newNolintFileIssue(i, "any"))
    93  	}
    94  
    95  	processAssertSame(t, p, newNolintFileIssue(38, "any")) // between funcs
    96  
    97  	// preceding multiline comment: medium line
    98  	for i := 39; i <= 45; i++ {
    99  		processAssertEmpty(t, p, newNolintFileIssue(i, "any"))
   100  	}
   101  
   102  	// check bug with transitive expanding for next and next line
   103  	for i := 1; i <= 8; i++ {
   104  		processAssertSame(t, p, newNolint2FileIssue(i))
   105  	}
   106  	for i := 9; i <= 10; i++ {
   107  		processAssertEmpty(t, p, newNolint2FileIssue(i))
   108  	}
   109  
   110  	// check inline comment for function
   111  	for i := 11; i <= 13; i++ {
   112  		processAssertSame(t, p, newNolint2FileIssue(i))
   113  	}
   114  	processAssertEmpty(t, p, newNolint2FileIssue(14))
   115  	for i := 15; i <= 18; i++ {
   116  		processAssertSame(t, p, newNolint2FileIssue(i))
   117  	}
   118  
   119  	// variables block exclude
   120  	for i := 55; i <= 56; i++ {
   121  		processAssertSame(t, p, newNolint2FileIssue(i))
   122  	}
   123  }
   124  
   125  func TestNolintInvalidLinterName(t *testing.T) {
   126  	fileName := filepath.Join("testdata", "nolint_bad_names.go")
   127  	issues := []result.Issue{
   128  		{
   129  			Pos: token.Position{
   130  				Filename: fileName,
   131  				Line:     3,
   132  			},
   133  			FromLinter: "varcheck",
   134  		},
   135  		{
   136  			Pos: token.Position{
   137  				Filename: fileName,
   138  				Line:     6,
   139  			},
   140  			FromLinter: "deadcode",
   141  		},
   142  	}
   143  
   144  	log := getMockLog()
   145  	log.On("Warnf", "Found unknown linters in //nolint directives: %s", "bad1, bad2")
   146  
   147  	p := newTestNolintProcessor(log)
   148  	processAssertEmpty(t, p, issues...)
   149  	p.Finish()
   150  }
   151  
   152  func TestNolintInvalidLinterNameWithViolationOnTheSameLine(t *testing.T) {
   153  	log := getMockLog()
   154  	log.On("Warnf", "Found unknown linters in //nolint directives: %s", "foobar")
   155  	issues := []result.Issue{
   156  		{
   157  			Pos: token.Position{
   158  				Filename: filepath.Join("testdata", "nolint_apply_to_unknown.go"),
   159  				Line:     4,
   160  			},
   161  			FromLinter: "gofmt",
   162  		},
   163  	}
   164  
   165  	p := newTestNolintProcessor(log)
   166  	processedIssues, err := p.Process(issues)
   167  	p.Finish()
   168  
   169  	assert.Len(t, processedIssues, 1)
   170  	assert.Equal(t, issues, processedIssues)
   171  	assert.NoError(t, err)
   172  }
   173  
   174  func TestNolintAliases(t *testing.T) {
   175  	p := newTestNolintProcessor(getMockLog())
   176  	for _, line := range []int{47, 49, 51} {
   177  		line := line
   178  		t.Run(fmt.Sprintf("line-%d", line), func(t *testing.T) {
   179  			processAssertEmpty(t, p, newNolintFileIssue(line, "gosec"))
   180  		})
   181  	}
   182  	p.Finish()
   183  }
   184  
   185  func TestIgnoredRangeMatches(t *testing.T) {
   186  	var testcases = []struct {
   187  		doc      string
   188  		issue    result.Issue
   189  		linters  []string
   190  		expected bool
   191  	}{
   192  		{
   193  			doc: "unmatched line",
   194  			issue: result.Issue{
   195  				Pos: token.Position{
   196  					Line: 100,
   197  				},
   198  			},
   199  		},
   200  		{
   201  			doc: "matched line, all linters",
   202  			issue: result.Issue{
   203  				Pos: token.Position{
   204  					Line: 5,
   205  				},
   206  			},
   207  			expected: true,
   208  		},
   209  		{
   210  			doc: "matched line, unmatched linter",
   211  			issue: result.Issue{
   212  				Pos: token.Position{
   213  					Line: 5,
   214  				},
   215  			},
   216  			linters: []string{"vet"},
   217  		},
   218  		{
   219  			doc: "matched line and linters",
   220  			issue: result.Issue{
   221  				Pos: token.Position{
   222  					Line: 20,
   223  				},
   224  				FromLinter: "vet",
   225  			},
   226  			linters:  []string{"vet"},
   227  			expected: true,
   228  		},
   229  	}
   230  
   231  	for _, testcase := range testcases {
   232  		ir := ignoredRange{
   233  			col: 20,
   234  			Range: result.Range{
   235  				From: 5,
   236  				To:   20,
   237  			},
   238  			linters: testcase.linters,
   239  		}
   240  		assert.Equal(t, testcase.expected, ir.doesMatch(&testcase.issue), testcase.doc)
   241  	}
   242  }
   243  
   244  func TestNolintWholeFile(t *testing.T) {
   245  	fileName := filepath.Join("testdata", "nolint_whole_file.go")
   246  
   247  	p := newTestNolintProcessor(nil)
   248  	defer p.Finish()
   249  
   250  	processAssertEmpty(t, p, result.Issue{
   251  		Pos: token.Position{
   252  			Filename: fileName,
   253  			Line:     4,
   254  		},
   255  		FromLinter: "varcheck",
   256  	})
   257  	processAssertSame(t, p, result.Issue{
   258  		Pos: token.Position{
   259  			Filename: fileName,
   260  			Line:     4,
   261  		},
   262  		FromLinter: "deadcode",
   263  	})
   264  }
   265  
   266  func TestNolintUnused(t *testing.T) {
   267  	fileName := filepath.Join("testdata", "nolint_unused.go")
   268  
   269  	log := getMockLog()
   270  	log.On("Warnf", "Found unknown linters in //nolint directives: %s", "blah")
   271  
   272  	createProcessor := func(t *testing.T, log *logutils.MockLog, enabledLinters []string) *Nolint {
   273  		enabledSetLog := logutils.NewMockLog()
   274  		enabledSetLog.On("Infof", "Active %d linters: %s", len(enabledLinters), enabledLinters)
   275  		cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: enabledLinters}}
   276  		dbManager := lintersdb.NewManager(cfg, nil)
   277  		enabledLintersSet := lintersdb.NewEnabledSet(dbManager, lintersdb.NewValidator(dbManager), enabledSetLog, cfg)
   278  		enabledLintersMap, err := enabledLintersSet.GetEnabledLintersMap()
   279  		assert.NoError(t, err)
   280  		return NewNolint(log, dbManager, enabledLintersMap)
   281  	}
   282  
   283  	// the issue below is the nolintlint issue that would be generated for the test file
   284  	nolintlintIssueVarcheck := result.Issue{
   285  		Pos: token.Position{
   286  			Filename: fileName,
   287  			Line:     3,
   288  		},
   289  		FromLinter:           golinters.NolintlintName,
   290  		ExpectNoLint:         true,
   291  		ExpectedNoLintLinter: "varcheck",
   292  	}
   293  
   294  	// the issue below is another nolintlint issue that would be generated for the test file
   295  	nolintlintIssueVarcheckUnusedOK := result.Issue{
   296  		Pos: token.Position{
   297  			Filename: fileName,
   298  			Line:     5,
   299  		},
   300  		FromLinter:           golinters.NolintlintName,
   301  		ExpectNoLint:         true,
   302  		ExpectedNoLintLinter: "varcheck",
   303  	}
   304  
   305  	t.Run("when an issue does not occur, it is not removed from the nolintlint issues", func(t *testing.T) {
   306  		p := createProcessor(t, log, []string{"nolintlint", "varcheck"})
   307  		defer p.Finish()
   308  
   309  		processAssertSame(t, p, nolintlintIssueVarcheck)
   310  	})
   311  
   312  	t.Run("when an issue does not occur but nolintlint is nolinted, it is removed from the nolintlint issues", func(t *testing.T) {
   313  		p := createProcessor(t, log, []string{"nolintlint", "varcheck"})
   314  		defer p.Finish()
   315  
   316  		processAssertEmpty(t, p, nolintlintIssueVarcheckUnusedOK)
   317  	})
   318  
   319  	t.Run("when an issue occurs, it is removed from the nolintlint issues", func(t *testing.T) {
   320  		p := createProcessor(t, log, []string{"nolintlint", "varcheck"})
   321  		defer p.Finish()
   322  
   323  		processAssertEmpty(t, p, []result.Issue{{
   324  			Pos: token.Position{
   325  				Filename: fileName,
   326  				Line:     3,
   327  			},
   328  			FromLinter: "varcheck",
   329  		}, nolintlintIssueVarcheck}...)
   330  	})
   331  
   332  	t.Run("when a linter is not enabled, it is removed from the nolintlint unused issues", func(t *testing.T) {
   333  		enabledSetLog := logutils.NewMockLog()
   334  		enabledSetLog.On("Infof", "Active %d linters: %s", 1, []string{"nolintlint"})
   335  
   336  		cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: []string{"nolintlint"}}}
   337  		dbManager := lintersdb.NewManager(cfg, nil)
   338  		enabledLintersSet := lintersdb.NewEnabledSet(dbManager, lintersdb.NewValidator(dbManager), enabledSetLog, cfg)
   339  
   340  		enabledLintersMap, err := enabledLintersSet.GetEnabledLintersMap()
   341  		assert.NoError(t, err)
   342  		p := NewNolint(log, dbManager, enabledLintersMap)
   343  		defer p.Finish()
   344  
   345  		processAssertEmpty(t, p, nolintlintIssueVarcheck)
   346  	})
   347  }