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 }