github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/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/chenfeining/golangci-lint/pkg/config" 13 "github.com/chenfeining/golangci-lint/pkg/golinters" 14 "github.com/chenfeining/golangci-lint/pkg/lint/lintersdb" 15 "github.com/chenfeining/golangci-lint/pkg/logutils" 16 "github.com/chenfeining/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: 10, 132 }, 133 FromLinter: "errcheck", 134 }, 135 { 136 Pos: token.Position{ 137 Filename: fileName, 138 Line: 13, 139 }, 140 FromLinter: "errcheck", 141 }, 142 { 143 Pos: token.Position{ 144 Filename: fileName, 145 Line: 22, 146 }, 147 FromLinter: "ineffassign", 148 }, 149 } 150 151 log := getMockLog() 152 log.On("Warnf", "Found unknown linters in //nolint directives: %s", "bad1, bad2") 153 154 p := newTestNolintProcessor(log) 155 processAssertEmpty(t, p, issues...) 156 p.Finish() 157 } 158 159 func TestNolintInvalidLinterNameWithViolationOnTheSameLine(t *testing.T) { 160 log := getMockLog() 161 log.On("Warnf", "Found unknown linters in //nolint directives: %s", "foobar") 162 issues := []result.Issue{ 163 { 164 Pos: token.Position{ 165 Filename: filepath.Join("testdata", "nolint_apply_to_unknown.go"), 166 Line: 4, 167 }, 168 FromLinter: "gofmt", 169 }, 170 } 171 172 p := newTestNolintProcessor(log) 173 processedIssues, err := p.Process(issues) 174 p.Finish() 175 176 assert.Len(t, processedIssues, 1) 177 assert.Equal(t, issues, processedIssues) 178 assert.NoError(t, err) 179 } 180 181 func TestNolintAliases(t *testing.T) { 182 p := newTestNolintProcessor(getMockLog()) 183 for _, line := range []int{47, 49, 51} { 184 line := line 185 t.Run(fmt.Sprintf("line-%d", line), func(t *testing.T) { 186 processAssertEmpty(t, p, newNolintFileIssue(line, "gosec")) 187 }) 188 } 189 p.Finish() 190 } 191 192 func TestIgnoredRangeMatches(t *testing.T) { 193 var testcases = []struct { 194 doc string 195 issue result.Issue 196 linters []string 197 expected bool 198 }{ 199 { 200 doc: "unmatched line", 201 issue: result.Issue{ 202 Pos: token.Position{ 203 Line: 100, 204 }, 205 }, 206 }, 207 { 208 doc: "matched line, all linters", 209 issue: result.Issue{ 210 Pos: token.Position{ 211 Line: 5, 212 }, 213 }, 214 expected: true, 215 }, 216 { 217 doc: "matched line, unmatched linter", 218 issue: result.Issue{ 219 Pos: token.Position{ 220 Line: 5, 221 }, 222 }, 223 linters: []string{"vet"}, 224 }, 225 { 226 doc: "matched line and linters", 227 issue: result.Issue{ 228 Pos: token.Position{ 229 Line: 20, 230 }, 231 FromLinter: "vet", 232 }, 233 linters: []string{"vet"}, 234 expected: true, 235 }, 236 } 237 238 for _, testcase := range testcases { 239 ir := ignoredRange{ 240 col: 20, 241 Range: result.Range{ 242 From: 5, 243 To: 20, 244 }, 245 linters: testcase.linters, 246 } 247 assert.Equal(t, testcase.expected, ir.doesMatch(&testcase.issue), testcase.doc) 248 } 249 } 250 251 func TestNolintWholeFile(t *testing.T) { 252 fileName := filepath.Join("testdata", "nolint_whole_file.go") 253 254 p := newTestNolintProcessor(getMockLog()) 255 defer p.Finish() 256 257 processAssertEmpty(t, p, result.Issue{ 258 Pos: token.Position{ 259 Filename: fileName, 260 Line: 9, 261 }, 262 FromLinter: "errcheck", 263 }) 264 processAssertSame(t, p, result.Issue{ 265 Pos: token.Position{ 266 Filename: fileName, 267 Line: 14, 268 }, 269 FromLinter: "govet", 270 }) 271 } 272 273 func TestNolintUnused(t *testing.T) { 274 fileName := filepath.Join("testdata", "nolint_unused.go") 275 276 log := getMockLog() 277 log.On("Warnf", "Found unknown linters in //nolint directives: %s", "blah") 278 279 createProcessor := func(t *testing.T, log *logutils.MockLog, enabledLinters []string) *Nolint { 280 enabledSetLog := logutils.NewMockLog() 281 enabledSetLog.On("Infof", "Active %d linters: %s", len(enabledLinters), enabledLinters) 282 283 cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: enabledLinters}} 284 dbManager := lintersdb.NewManager(cfg, nil) 285 286 enabledLintersSet := lintersdb.NewEnabledSet(dbManager, lintersdb.NewValidator(dbManager), enabledSetLog, cfg) 287 288 enabledLintersMap, err := enabledLintersSet.GetEnabledLintersMap() 289 assert.NoError(t, err) 290 291 return NewNolint(log, dbManager, enabledLintersMap) 292 } 293 294 // the issue below is the nolintlint issue that would be generated for the test file 295 nolintlintIssueVarcheck := result.Issue{ 296 Pos: token.Position{ 297 Filename: fileName, 298 Line: 3, 299 }, 300 FromLinter: golinters.NoLintLintName, 301 ExpectNoLint: true, 302 ExpectedNoLintLinter: "varcheck", 303 } 304 305 // the issue below is another nolintlint issue that would be generated for the test file 306 nolintlintIssueVarcheckUnusedOK := result.Issue{ 307 Pos: token.Position{ 308 Filename: fileName, 309 Line: 5, 310 }, 311 FromLinter: golinters.NoLintLintName, 312 ExpectNoLint: true, 313 ExpectedNoLintLinter: "varcheck", 314 } 315 316 t.Run("when an issue does not occur, it is not removed from the nolintlint issues", func(t *testing.T) { 317 p := createProcessor(t, log, []string{"nolintlint", "varcheck"}) 318 defer p.Finish() 319 320 processAssertSame(t, p, nolintlintIssueVarcheck) 321 }) 322 323 t.Run("when an issue does not occur but nolintlint is nolinted, it is removed from the nolintlint issues", func(t *testing.T) { 324 p := createProcessor(t, log, []string{"nolintlint", "varcheck"}) 325 defer p.Finish() 326 327 processAssertEmpty(t, p, nolintlintIssueVarcheckUnusedOK) 328 }) 329 330 t.Run("when an issue occurs, it is removed from the nolintlint issues", func(t *testing.T) { 331 p := createProcessor(t, log, []string{"nolintlint", "varcheck"}) 332 defer p.Finish() 333 334 processAssertEmpty(t, p, []result.Issue{{ 335 Pos: token.Position{ 336 Filename: fileName, 337 Line: 3, 338 }, 339 FromLinter: "varcheck", 340 }, nolintlintIssueVarcheck}...) 341 }) 342 343 t.Run("when a linter is not enabled, it is removed from the nolintlint unused issues", func(t *testing.T) { 344 enabledSetLog := logutils.NewMockLog() 345 enabledSetLog.On("Infof", "Active %d linters: %s", 1, []string{"nolintlint"}) 346 347 cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: []string{"nolintlint"}}} 348 dbManager := lintersdb.NewManager(cfg, nil) 349 enabledLintersSet := lintersdb.NewEnabledSet(dbManager, lintersdb.NewValidator(dbManager), enabledSetLog, cfg) 350 351 enabledLintersMap, err := enabledLintersSet.GetEnabledLintersMap() 352 assert.NoError(t, err) 353 p := NewNolint(log, dbManager, enabledLintersMap) 354 defer p.Finish() 355 356 processAssertEmpty(t, p, nolintlintIssueVarcheck) 357 }) 358 }