Fix false positives with unused identifiers
Issue #265: don't report unused warning when an identifier is used only in tests.
This commit is contained in:
parent
3345c7136f
commit
ccac35a87e
5
.github/ISSUE_TEMPLATE.md
vendored
5
.github/ISSUE_TEMPLATE.md
vendored
@ -2,5 +2,6 @@ Thank you for creating the issue!
|
|||||||
|
|
||||||
Please include the following information:
|
Please include the following information:
|
||||||
1. Version of golangci-lint: `golangci-lint --version` (or git commit if you don't use binary distribution)
|
1. Version of golangci-lint: `golangci-lint --version` (or git commit if you don't use binary distribution)
|
||||||
2. Go environment: `go version && go env`
|
2. Config file: `cat .golangci.yml`
|
||||||
3. Verbose output of running: `golangci-lint run -v`
|
3. Go environment: `go version && go env`
|
||||||
|
4. Verbose output of running: `golangci-lint run -v`
|
@ -7,6 +7,7 @@ import (
|
|||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ type ContextLoader struct {
|
|||||||
log logutils.Log
|
log logutils.Log
|
||||||
debugf logutils.DebugFunc
|
debugf logutils.DebugFunc
|
||||||
goenv *goutil.Env
|
goenv *goutil.Env
|
||||||
|
pkgTestIDRe *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContextLoader(cfg *config.Config, log logutils.Log, goenv *goutil.Env) *ContextLoader {
|
func NewContextLoader(cfg *config.Config, log logutils.Log, goenv *goutil.Env) *ContextLoader {
|
||||||
@ -37,6 +39,7 @@ func NewContextLoader(cfg *config.Config, log logutils.Log, goenv *goutil.Env) *
|
|||||||
log: log,
|
log: log,
|
||||||
debugf: logutils.Debug("loader"),
|
debugf: logutils.Debug("loader"),
|
||||||
goenv: goenv,
|
goenv: goenv,
|
||||||
|
pkgTestIDRe: regexp.MustCompile(`^(.*) \[(.*)\.test\]`),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,19 +225,65 @@ func (cl ContextLoader) loadPackages(ctx context.Context, loadMode packages.Load
|
|||||||
i, pkg.ID, pkg.GoFiles, pkg.CompiledGoFiles, syntaxFiles)
|
i, pkg.ID, pkg.GoFiles, pkg.CompiledGoFiles, syntaxFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
var retPkgs []*packages.Package
|
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
for _, err := range pkg.Errors {
|
for _, err := range pkg.Errors {
|
||||||
if strings.Contains(err.Msg, "no Go files") {
|
if strings.Contains(err.Msg, "no Go files") {
|
||||||
return nil, errors.Wrapf(exitcodes.ErrNoGoFiles, "package %s", pkg.PkgPath)
|
return nil, errors.Wrapf(exitcodes.ErrNoGoFiles, "package %s", pkg.PkgPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !shouldSkipPkg(pkg) {
|
}
|
||||||
retPkgs = append(retPkgs, pkg)
|
|
||||||
|
return cl.filterPackages(pkgs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cl ContextLoader) tryParseTestPackage(pkg *packages.Package) (name, testName string, isTest bool) {
|
||||||
|
matches := cl.pkgTestIDRe.FindStringSubmatch(pkg.ID)
|
||||||
|
if matches == nil {
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches[1], matches[2], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cl ContextLoader) filterPackages(pkgs []*packages.Package) []*packages.Package {
|
||||||
|
packagesWithTests := map[string]bool{}
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
name, testName, isTest := cl.tryParseTestPackage(pkg)
|
||||||
|
if !isTest {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
packagesWithTests[name] = true
|
||||||
|
|
||||||
|
if name != testName {
|
||||||
|
cl.log.Infof("pkg ID=%s: %s != %s: %#v", pkg.ID, name, testName, pkg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retPkgs, nil
|
cl.debugf("package with tests: %#v", packagesWithTests)
|
||||||
|
|
||||||
|
var retPkgs []*packages.Package
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if shouldSkipPkg(pkg) {
|
||||||
|
cl.debugf("skip pkg ID=%s", pkg.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, isTest := cl.tryParseTestPackage(pkg)
|
||||||
|
if !isTest && packagesWithTests[pkg.PkgPath] {
|
||||||
|
// If tests loading is enabled,
|
||||||
|
// for package with files a.go and a_test.go go/packages loads two packages:
|
||||||
|
// 1. ID=".../a" GoFiles=[a.go]
|
||||||
|
// 2. ID=".../a [.../a.test]" GoFiles=[a.go a_test.go]
|
||||||
|
// We need only the second package, otherwise we can get warnings about unused variables/fields/functions
|
||||||
|
// in a.go if they are used only in a_test.go.
|
||||||
|
cl.debugf("skip pkg ID=%s because we load it with test package", pkg.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
retPkgs = append(retPkgs, pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return retPkgs
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
|
@ -163,6 +163,12 @@ func TestDeadcodeNoFalsePositivesInMainPkg(t *testing.T) {
|
|||||||
checkNoIssuesRun(t, out, exitCode)
|
checkNoIssuesRun(t, out, exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIdentifierUsedOnlyInTests(t *testing.T) {
|
||||||
|
out, exitCode := runGolangciLint(t, "--no-config", "--disable-all", "-Eunused",
|
||||||
|
filepath.Join(testdataDir, "used_only_in_tests"))
|
||||||
|
checkNoIssuesRun(t, out, exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigFileIsDetected(t *testing.T) {
|
func TestConfigFileIsDetected(t *testing.T) {
|
||||||
checkGotConfig := func(out string, exitCode int) {
|
checkGotConfig := func(out string, exitCode int) {
|
||||||
assert.Equal(t, exitcodes.Success, exitCode, out)
|
assert.Equal(t, exitcodes.Success, exitCode, out)
|
||||||
|
5
test/testdata/used_only_in_tests/a.go
vendored
Normal file
5
test/testdata/used_only_in_tests/a.go
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
func f() bool {
|
||||||
|
return true
|
||||||
|
}
|
9
test/testdata/used_only_in_tests/a_test.go
vendored
Normal file
9
test/testdata/used_only_in_tests/a_test.go
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestF(t *testing.T) {
|
||||||
|
if !f() {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user