77 "go/types"
88 "os"
99 "path/filepath"
10+ "regexp"
1011 "strings"
1112 "time"
1213
@@ -25,18 +26,20 @@ import (
2526)
2627
2728type ContextLoader struct {
28- cfg * config.Config
29- log logutils.Log
30- debugf logutils.DebugFunc
31- goenv * goutil.Env
29+ cfg * config.Config
30+ log logutils.Log
31+ debugf logutils.DebugFunc
32+ goenv * goutil.Env
33+ pkgTestIDRe * regexp.Regexp
3234}
3335
3436func NewContextLoader (cfg * config.Config , log logutils.Log , goenv * goutil.Env ) * ContextLoader {
3537 return & ContextLoader {
36- cfg : cfg ,
37- log : log ,
38- debugf : logutils .Debug ("loader" ),
39- goenv : goenv ,
38+ cfg : cfg ,
39+ log : log ,
40+ debugf : logutils .Debug ("loader" ),
41+ goenv : goenv ,
42+ pkgTestIDRe : regexp .MustCompile (`^(.*) \[(.*)\.test\]` ),
4043 }
4144}
4245
@@ -222,19 +225,65 @@ func (cl ContextLoader) loadPackages(ctx context.Context, loadMode packages.Load
222225 i , pkg .ID , pkg .GoFiles , pkg .CompiledGoFiles , syntaxFiles )
223226 }
224227
225- var retPkgs []* packages.Package
226228 for _ , pkg := range pkgs {
227229 for _ , err := range pkg .Errors {
228230 if strings .Contains (err .Msg , "no Go files" ) {
229231 return nil , errors .Wrapf (exitcodes .ErrNoGoFiles , "package %s" , pkg .PkgPath )
230232 }
231233 }
232- if ! shouldSkipPkg (pkg ) {
233- retPkgs = append (retPkgs , pkg )
234+ }
235+
236+ return cl .filterPackages (pkgs ), nil
237+ }
238+
239+ func (cl ContextLoader ) tryParseTestPackage (pkg * packages.Package ) (name , testName string , isTest bool ) {
240+ matches := cl .pkgTestIDRe .FindStringSubmatch (pkg .ID )
241+ if matches == nil {
242+ return "" , "" , false
243+ }
244+
245+ return matches [1 ], matches [2 ], true
246+ }
247+
248+ func (cl ContextLoader ) filterPackages (pkgs []* packages.Package ) []* packages.Package {
249+ packagesWithTests := map [string ]bool {}
250+ for _ , pkg := range pkgs {
251+ name , testName , isTest := cl .tryParseTestPackage (pkg )
252+ if ! isTest {
253+ continue
254+ }
255+ packagesWithTests [name ] = true
256+
257+ if name != testName {
258+ cl .log .Infof ("pkg ID=%s: %s != %s: %#v" , pkg .ID , name , testName , pkg )
259+ }
260+ }
261+
262+ cl .debugf ("package with tests: %#v" , packagesWithTests )
263+
264+ var retPkgs []* packages.Package
265+ for _ , pkg := range pkgs {
266+ if shouldSkipPkg (pkg ) {
267+ cl .debugf ("skip pkg ID=%s" , pkg .ID )
268+ continue
269+ }
270+
271+ _ , _ , isTest := cl .tryParseTestPackage (pkg )
272+ if ! isTest && packagesWithTests [pkg .PkgPath ] {
273+ // If tests loading is enabled,
274+ // for package with files a.go and a_test.go go/packages loads two packages:
275+ // 1. ID=".../a" GoFiles=[a.go]
276+ // 2. ID=".../a [.../a.test]" GoFiles=[a.go a_test.go]
277+ // We need only the second package, otherwise we can get warnings about unused variables/fields/functions
278+ // in a.go if they are used only in a_test.go.
279+ cl .debugf ("skip pkg ID=%s because we load it with test package" , pkg .ID )
280+ continue
234281 }
282+
283+ retPkgs = append (retPkgs , pkg )
235284 }
236285
237- return retPkgs , nil
286+ return retPkgs
238287}
239288
240289//nolint:gocyclo
0 commit comments