reduce mem. usage of unused and update staticcheck (#1063)
The primary improvement is in early clearing of
analyzed package's TypeInfo, facts, etc for
whole program analyzers (`unused`). Clear it when it
becomes unused and GC collects them early. Initially this
clearing was performed for all analyzers except `unused`.
Update staticcheck from v0.0.1-2019.2.3 to v0.0.1-2020.1.4
Also in this commit:
  * speed up loading packages from export data (2.5s -> 2.1s for std)
    by not using mutex for export data since it was allowed in
    x/tools#07722704da13
  * make an order of execution of linters stable
  * update renameio and robustio
  * use robustio in caching
Relates: #987, #994, #995, #1011
			
			
This commit is contained in:
		
							parent
							
								
									77e211ba75
								
							
						
					
					
						commit
						52c9b88c25
					
				
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -60,4 +60,4 @@ require ( | |||||||
| // See https://github.com/golangci/golangci-lint/issues/995 | // See https://github.com/golangci/golangci-lint/issues/995 | ||||||
| // Update only after mitigating this issue. | // Update only after mitigating this issue. | ||||||
| // TODO: Enable back tests with skip "Issue955" after update. | // TODO: Enable back tests with skip "Issue955" after update. | ||||||
| require honnef.co/go/tools v0.0.1-2019.2.3 | require honnef.co/go/tools v0.0.1-2020.1.3 | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.sum
									
									
									
									
									
								
							| @ -376,6 +376,7 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn | |||||||
| golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
|  | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20200228224639-71482053b885/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | golang.org/x/tools v0.0.0-20200228224639-71482053b885/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||||
| golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= | golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= | ||||||
| golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||||
| @ -420,6 +421,8 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||||
| honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= | ||||||
| honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | ||||||
|  | honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= | ||||||
|  | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||||
| mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= | mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= | ||||||
| mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= | mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= | ||||||
| mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= | mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								internal/cache/cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								internal/cache/cache.go
									
									
									
									
										vendored
									
									
								
							| @ -14,7 +14,6 @@ import ( | |||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| @ -24,6 +23,7 @@ import ( | |||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| 
 | 
 | ||||||
| 	"github.com/golangci/golangci-lint/internal/renameio" | 	"github.com/golangci/golangci-lint/internal/renameio" | ||||||
|  | 	"github.com/golangci/golangci-lint/internal/robustio" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // An ActionID is a cache action key, the hash of a complete description of a | // An ActionID is a cache action key, the hash of a complete description of a | ||||||
| @ -232,7 +232,7 @@ func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) { | |||||||
| 		return nil, entry, err | 		return nil, entry, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	data, err := ioutil.ReadFile(outputFile) | 	data, err := robustio.ReadFile(outputFile) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, entry, err | 		return nil, entry, err | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -22,8 +22,6 @@ import ( | |||||||
| 	"github.com/golangci/golangci-lint/internal/robustio" | 	"github.com/golangci/golangci-lint/internal/robustio" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const windowsOS = "windows" |  | ||||||
| 
 |  | ||||||
| func TestConcurrentReadsAndWrites(t *testing.T) { | func TestConcurrentReadsAndWrites(t *testing.T) { | ||||||
| 	dir, err := ioutil.TempDir("", "renameio") | 	dir, err := ioutil.TempDir("", "renameio") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -117,7 +115,7 @@ func TestConcurrentReadsAndWrites(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var minWriteSuccesses int64 = attempts | 	var minWriteSuccesses int64 = attempts | ||||||
| 	if runtime.GOOS == windowsOS { | 	if runtime.GOOS == "windows" { | ||||||
| 		// Windows produces frequent "Access is denied" errors under heavy rename load. | 		// Windows produces frequent "Access is denied" errors under heavy rename load. | ||||||
| 		// As long as those are the only errors and *some* of the writes succeed, we're happy. | 		// As long as those are the only errors and *some* of the writes succeed, we're happy. | ||||||
| 		minWriteSuccesses = attempts / 4 | 		minWriteSuccesses = attempts / 4 | ||||||
| @ -130,10 +128,18 @@ func TestConcurrentReadsAndWrites(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var minReadSuccesses int64 = attempts | 	var minReadSuccesses int64 = attempts | ||||||
| 	if runtime.GOOS == windowsOS { | 
 | ||||||
|  | 	switch runtime.GOOS { | ||||||
|  | 	case "windows": | ||||||
| 		// Windows produces frequent "Access is denied" errors under heavy rename load. | 		// Windows produces frequent "Access is denied" errors under heavy rename load. | ||||||
| 		// As long as those are the only errors and *some* of the writes succeed, we're happy. | 		// As long as those are the only errors and *some* of the reads succeed, we're happy. | ||||||
| 		minReadSuccesses = attempts / 4 | 		minReadSuccesses = attempts / 4 | ||||||
|  | 
 | ||||||
|  | 	case "darwin": | ||||||
|  | 		// The filesystem on macOS 10.14 occasionally fails with "no such file or | ||||||
|  | 		// directory" errors. See https://golang.org/issue/33041. The flake rate is | ||||||
|  | 		// fairly low, so ensure that at least 75% of attempts succeed. | ||||||
|  | 		minReadSuccesses = attempts - (attempts / 4) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if readSuccesses < minReadSuccesses { | 	if readSuccesses < minReadSuccesses { | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| //+build !nacl,!plan9,!windows,!js | // +build !plan9,!windows,!js | ||||||
| 
 | 
 | ||||||
| package renameio | package renameio | ||||||
| 
 | 
 | ||||||
| @ -19,6 +19,7 @@ func TestWriteFileModeAppliesUmask(t *testing.T) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Failed to create temporary directory: %v", err) | 		t.Fatalf("Failed to create temporary directory: %v", err) | ||||||
| 	} | 	} | ||||||
|  | 	defer os.RemoveAll(dir) | ||||||
| 
 | 
 | ||||||
| 	const mode = 0644 | 	const mode = 0644 | ||||||
| 	const umask = 0007 | 	const umask = 0007 | ||||||
| @ -29,7 +30,6 @@ func TestWriteFileModeAppliesUmask(t *testing.T) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Failed to write file: %v", err) | 		t.Fatalf("Failed to write file: %v", err) | ||||||
| 	} | 	} | ||||||
| 	defer os.RemoveAll(dir) |  | ||||||
| 
 | 
 | ||||||
| 	fi, err := os.Stat(file) | 	fi, err := os.Stat(file) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
							
								
								
									
										29
									
								
								internal/robustio/robustio_darwin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								internal/robustio/robustio_darwin.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | // Copyright 2019 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | package robustio | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const errFileNotFound = syscall.ENOENT | ||||||
|  | 
 | ||||||
|  | // isEphemeralError returns true if err may be resolved by waiting. | ||||||
|  | func isEphemeralError(err error) bool { | ||||||
|  | 	switch werr := err.(type) { | ||||||
|  | 	case *os.PathError: | ||||||
|  | 		err = werr.Err | ||||||
|  | 	case *os.LinkError: | ||||||
|  | 		err = werr.Err | ||||||
|  | 	case *os.SyscallError: | ||||||
|  | 		err = werr.Err | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	if errno, ok := err.(syscall.Errno); ok { | ||||||
|  | 		return errno == errFileNotFound | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								internal/robustio/robustio_flaky.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								internal/robustio/robustio_flaky.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | // Copyright 2019 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | // +build windows darwin | ||||||
|  | 
 | ||||||
|  | package robustio | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"math/rand" | ||||||
|  | 	"os" | ||||||
|  | 	"syscall" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const arbitraryTimeout = 500 * time.Millisecond | ||||||
|  | 
 | ||||||
|  | const ERROR_SHARING_VIOLATION = 32 | ||||||
|  | 
 | ||||||
|  | // retry retries ephemeral errors from f up to an arbitrary timeout | ||||||
|  | // to work around filesystem flakiness on Windows and Darwin. | ||||||
|  | func retry(f func() (err error, mayRetry bool)) error { | ||||||
|  | 	var ( | ||||||
|  | 		bestErr     error | ||||||
|  | 		lowestErrno syscall.Errno | ||||||
|  | 		start       time.Time | ||||||
|  | 		nextSleep   time.Duration = 1 * time.Millisecond | ||||||
|  | 	) | ||||||
|  | 	for { | ||||||
|  | 		err, mayRetry := f() | ||||||
|  | 		if err == nil || !mayRetry { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if errno, ok := err.(syscall.Errno); ok && (lowestErrno == 0 || errno < lowestErrno) { | ||||||
|  | 			bestErr = err | ||||||
|  | 			lowestErrno = errno | ||||||
|  | 		} else if bestErr == nil { | ||||||
|  | 			bestErr = err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if start.IsZero() { | ||||||
|  | 			start = time.Now() | ||||||
|  | 		} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		time.Sleep(nextSleep) | ||||||
|  | 		nextSleep += time.Duration(rand.Int63n(int64(nextSleep))) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return bestErr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // rename is like os.Rename, but retries ephemeral errors. | ||||||
|  | // | ||||||
|  | // On windows it wraps os.Rename, which (as of 2019-06-04) uses MoveFileEx with | ||||||
|  | // MOVEFILE_REPLACE_EXISTING. | ||||||
|  | // | ||||||
|  | // Windows also provides a different system call, ReplaceFile, | ||||||
|  | // that provides similar semantics, but perhaps preserves more metadata. (The | ||||||
|  | // documentation on the differences between the two is very sparse.) | ||||||
|  | // | ||||||
|  | // Empirical error rates with MoveFileEx are lower under modest concurrency, so | ||||||
|  | // for now we're sticking with what the os package already provides. | ||||||
|  | func rename(oldpath, newpath string) (err error) { | ||||||
|  | 	return retry(func() (err error, mayRetry bool) { | ||||||
|  | 		err = os.Rename(oldpath, newpath) | ||||||
|  | 		return err, isEphemeralError(err) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // readFile is like ioutil.ReadFile, but retries ephemeral errors. | ||||||
|  | func readFile(filename string) ([]byte, error) { | ||||||
|  | 	var b []byte | ||||||
|  | 	err := retry(func() (err error, mayRetry bool) { | ||||||
|  | 		b, err = ioutil.ReadFile(filename) | ||||||
|  | 
 | ||||||
|  | 		// Unlike in rename, we do not retry errFileNotFound here: it can occur | ||||||
|  | 		// as a spurious error, but the file may also genuinely not exist, so the | ||||||
|  | 		// increase in robustness is probably not worth the extra latency. | ||||||
|  | 
 | ||||||
|  | 		return err, isEphemeralError(err) && err != errFileNotFound | ||||||
|  | 	}) | ||||||
|  | 	return b, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func removeAll(path string) error { | ||||||
|  | 	return retry(func() (err error, mayRetry bool) { | ||||||
|  | 		err = os.RemoveAll(path) | ||||||
|  | 		return err, isEphemeralError(err) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
| @ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| //+build !windows | //+build !windows,!darwin | ||||||
| 
 | 
 | ||||||
| package robustio | package robustio | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,93 +5,22 @@ | |||||||
| package robustio | package robustio | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"io/ioutil" |  | ||||||
| 	"math/rand" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const arbitraryTimeout = 500 * time.Millisecond | const errFileNotFound = syscall.ERROR_FILE_NOT_FOUND | ||||||
| 
 |  | ||||||
| const ERROR_SHARING_VIOLATION = 32 |  | ||||||
| 
 |  | ||||||
| // retry retries ephemeral errors from f up to an arbitrary timeout |  | ||||||
| // to work around spurious filesystem errors on Windows |  | ||||||
| func retry(f func() (err error, mayRetry bool)) error { |  | ||||||
| 	var ( |  | ||||||
| 		bestErr     error |  | ||||||
| 		lowestErrno syscall.Errno |  | ||||||
| 		start       time.Time |  | ||||||
| 		nextSleep   time.Duration = 1 * time.Millisecond |  | ||||||
| 	) |  | ||||||
| 	for { |  | ||||||
| 		err, mayRetry := f() |  | ||||||
| 		if err == nil || !mayRetry { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if errno, ok := err.(syscall.Errno); ok && (lowestErrno == 0 || errno < lowestErrno) { |  | ||||||
| 			bestErr = err |  | ||||||
| 			lowestErrno = errno |  | ||||||
| 		} else if bestErr == nil { |  | ||||||
| 			bestErr = err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if start.IsZero() { |  | ||||||
| 			start = time.Now() |  | ||||||
| 		} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout { |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		time.Sleep(nextSleep) |  | ||||||
| 		nextSleep += time.Duration(rand.Int63n(int64(nextSleep))) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return bestErr |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // rename is like os.Rename, but retries ephemeral errors. |  | ||||||
| // |  | ||||||
| // It wraps os.Rename, which (as of 2019-06-04) uses MoveFileEx with |  | ||||||
| // MOVEFILE_REPLACE_EXISTING. |  | ||||||
| // |  | ||||||
| // Windows also provides a different system call, ReplaceFile, |  | ||||||
| // that provides similar semantics, but perhaps preserves more metadata. (The |  | ||||||
| // documentation on the differences between the two is very sparse.) |  | ||||||
| // |  | ||||||
| // Empirical error rates with MoveFileEx are lower under modest concurrency, so |  | ||||||
| // for now we're sticking with what the os package already provides. |  | ||||||
| func rename(oldpath, newpath string) (err error) { |  | ||||||
| 	return retry(func() (err error, mayRetry bool) { |  | ||||||
| 		err = os.Rename(oldpath, newpath) |  | ||||||
| 		return err, isEphemeralError(err) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // readFile is like ioutil.ReadFile, but retries ephemeral errors. |  | ||||||
| func readFile(filename string) ([]byte, error) { |  | ||||||
| 	var b []byte |  | ||||||
| 	err := retry(func() (err error, mayRetry bool) { |  | ||||||
| 		b, err = ioutil.ReadFile(filename) |  | ||||||
| 
 |  | ||||||
| 		// Unlike in rename, we do not retry ERROR_FILE_NOT_FOUND here: it can occur |  | ||||||
| 		// as a spurious error, but the file may also genuinely not exist, so the |  | ||||||
| 		// increase in robustness is probably not worth the extra latency. |  | ||||||
| 
 |  | ||||||
| 		return err, isEphemeralError(err) && err != syscall.ERROR_FILE_NOT_FOUND |  | ||||||
| 	}) |  | ||||||
| 	return b, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func removeAll(path string) error { |  | ||||||
| 	return retry(func() (err error, mayRetry bool) { |  | ||||||
| 		err = os.RemoveAll(path) |  | ||||||
| 		return err, isEphemeralError(err) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // isEphemeralError returns true if err may be resolved by waiting. | // isEphemeralError returns true if err may be resolved by waiting. | ||||||
| func isEphemeralError(err error) bool { | func isEphemeralError(err error) bool { | ||||||
|  | 	switch werr := err.(type) { | ||||||
|  | 	case *os.PathError: | ||||||
|  | 		err = werr.Err | ||||||
|  | 	case *os.LinkError: | ||||||
|  | 		err = werr.Err | ||||||
|  | 	case *os.SyscallError: | ||||||
|  | 		err = werr.Err | ||||||
|  | 	} | ||||||
| 	if errno, ok := err.(syscall.Errno); ok { | 	if errno, ok := err.(syscall.Errno); ok { | ||||||
| 		switch errno { | 		switch errno { | ||||||
| 		case syscall.ERROR_ACCESS_DENIED, | 		case syscall.ERROR_ACCESS_DENIED, | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ import ( | |||||||
| 
 | 
 | ||||||
| type Guard struct { | type Guard struct { | ||||||
| 	loadMutexes map[*packages.Package]*sync.Mutex | 	loadMutexes map[*packages.Package]*sync.Mutex | ||||||
| 	mutexForExportData sync.Mutex |  | ||||||
| 	mutex       sync.Mutex | 	mutex       sync.Mutex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -26,10 +25,6 @@ func (g *Guard) MutexForPkg(pkg *packages.Package) *sync.Mutex { | |||||||
| 	return g.loadMutexes[pkg] | 	return g.loadMutexes[pkg] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (g *Guard) MutexForExportData() *sync.Mutex { |  | ||||||
| 	return &g.mutexForExportData |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (g *Guard) Mutex() *sync.Mutex { | func (g *Guard) Mutex() *sync.Mutex { | ||||||
| 	return &g.mutex | 	return &g.mutex | ||||||
| } | } | ||||||
|  | |||||||
| @ -384,26 +384,6 @@ func extractDiagnostics(roots []*action) (retDiags []Diagnostic, retErrors []err | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NeedFacts reports whether any analysis required by the specified set |  | ||||||
| // needs facts.  If so, we must load the entire program from source. |  | ||||||
| func NeedFacts(analyzers []*analysis.Analyzer) bool { |  | ||||||
| 	seen := make(map[*analysis.Analyzer]bool) |  | ||||||
| 	var q []*analysis.Analyzer // for BFS |  | ||||||
| 	q = append(q, analyzers...) |  | ||||||
| 	for len(q) > 0 { |  | ||||||
| 		a := q[0] |  | ||||||
| 		q = q[1:] |  | ||||||
| 		if !seen[a] { |  | ||||||
| 			seen[a] = true |  | ||||||
| 			if len(a.FactTypes) > 0 { |  | ||||||
| 				return true |  | ||||||
| 			} |  | ||||||
| 			q = append(q, a.Requires...) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // An action represents one unit of analysis work: the application of | // An action represents one unit of analysis work: the application of | ||||||
| // one analysis to one package. Actions form a DAG, both within a | // one analysis to one package. Actions form a DAG, both within a | ||||||
| // package (as different analyzers are applied, either in sequence or | // package (as different analyzers are applied, either in sequence or | ||||||
| @ -964,7 +944,7 @@ func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struc | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (lp *loadingPackage) decUse() { | func (lp *loadingPackage) decUse(canClearTypes bool) { | ||||||
| 	lp.decUseMutex.Lock() | 	lp.decUseMutex.Lock() | ||||||
| 	defer lp.decUseMutex.Unlock() | 	defer lp.decUseMutex.Unlock() | ||||||
| 
 | 
 | ||||||
| @ -1007,11 +987,17 @@ func (lp *loadingPackage) decUse() { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if canClearTypes { | ||||||
|  | 		// canClearTypes is set to true if we can discard type | ||||||
|  | 		// information after the package and its dependents have been | ||||||
|  | 		// processed. This is the case when no whole program checkers (unused) are | ||||||
|  | 		// being run. | ||||||
| 		lp.pkg.Types = nil | 		lp.pkg.Types = nil | ||||||
|  | 	} | ||||||
| 	lp.pkg = nil | 	lp.pkg = nil | ||||||
| 
 | 
 | ||||||
| 	for _, imp := range lp.imports { | 	for _, imp := range lp.imports { | ||||||
| 		imp.decUse() | 		imp.decUse(canClearTypes) | ||||||
| 	} | 	} | ||||||
| 	lp.imports = nil | 	lp.imports = nil | ||||||
| 
 | 
 | ||||||
| @ -1047,12 +1033,8 @@ func (lp *loadingPackage) analyze(loadMode LoadMode, loadSem chan struct{}) { | |||||||
| 		<-loadSem | 		<-loadSem | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	defer func() { |  | ||||||
| 		if loadMode < LoadModeWholeProgram { |  | ||||||
| 	// Save memory on unused more fields. | 	// Save memory on unused more fields. | ||||||
| 			lp.decUse() | 	defer lp.decUse(loadMode < LoadModeWholeProgram) | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 
 | 
 | ||||||
| 	if err := lp.loadWithFacts(loadMode); err != nil { | 	if err := lp.loadWithFacts(loadMode); err != nil { | ||||||
| 		werr := errors.Wrapf(err, "failed to load package %s", lp.pkg.Name) | 		werr := errors.Wrapf(err, "failed to load package %s", lp.pkg.Name) | ||||||
| @ -1168,22 +1150,6 @@ func (lp *loadingPackage) loadFromSource(loadMode LoadMode) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (lp *loadingPackage) loadFromExportData() error { | func (lp *loadingPackage) loadFromExportData() error { | ||||||
| 	// Because gcexportdata.Read has the potential to create or |  | ||||||
| 	// modify the types.Package for each node in the transitive |  | ||||||
| 	// closure of dependencies of lpkg, all exportdata operations |  | ||||||
| 	// must be sequential. (Finer-grained locking would require |  | ||||||
| 	// changes to the gcexportdata API.) |  | ||||||
| 	// |  | ||||||
| 	// The exportMu lock guards the Package.Pkg field and the |  | ||||||
| 	// types.Package it points to, for each Package in the graph. |  | ||||||
| 	// |  | ||||||
| 	// Not all accesses to Package.Pkg need to be protected by this mutex: |  | ||||||
| 	// graph ordering ensures that direct dependencies of source |  | ||||||
| 	// packages are fully loaded before the importer reads their Pkg field. |  | ||||||
| 	mu := lp.loadGuard.MutexForExportData() |  | ||||||
| 	mu.Lock() |  | ||||||
| 	defer mu.Unlock() |  | ||||||
| 
 |  | ||||||
| 	pkg := lp.pkg | 	pkg := lp.pkg | ||||||
| 
 | 
 | ||||||
| 	// Call NewPackage directly with explicit name. | 	// Call NewPackage directly with explicit name. | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ func NewGocognit() *goanalysis.Linter { | |||||||
| 				return nil, nil | 				return nil, nil | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			sort.Slice(stats, func(i, j int) bool { | 			sort.SliceStable(stats, func(i, j int) bool { | ||||||
| 				return stats[i].Complexity > stats[j].Complexity | 				return stats[i].Complexity > stats[j].Complexity | ||||||
| 			}) | 			}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ func NewGocyclo() *goanalysis.Linter { | |||||||
| 				return nil, nil | 				return nil, nil | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			sort.Slice(stats, func(i, j int) bool { | 			sort.SliceStable(stats, func(i, j int) bool { | ||||||
| 				return stats[i].Complexity > stats[j].Complexity | 				return stats[i].Complexity > stats[j].Complexity | ||||||
| 			}) | 			}) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -55,7 +55,10 @@ func NewUnused() *goanalysis.Linter { | |||||||
| 		} | 		} | ||||||
| 		return issues | 		return issues | ||||||
| 	}).WithContextSetter(func(lintCtx *linter.Context) { | 	}).WithContextSetter(func(lintCtx *linter.Context) { | ||||||
| 		u.WholeProgram = lintCtx.Settings().Unused.CheckExported | 		if lintCtx.Settings().Unused.CheckExported { | ||||||
|  | 			lintCtx.Log.Infof("Using whole program analysis for unused, it can be memory-heavy") | ||||||
|  | 			u.WholeProgram = true | ||||||
|  | 		} | ||||||
| 	}).WithLoadMode(goanalysis.LoadModeWholeProgram) | 	}).WithLoadMode(goanalysis.LoadModeWholeProgram) | ||||||
| 	lnt.UseOriginalPackages() | 	lnt.UseOriginalPackages() | ||||||
| 	return lnt | 	return lnt | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ package lintersdb | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"sort" | 	"sort" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/golangci/golangci-lint/pkg/config" | 	"github.com/golangci/golangci-lint/pkg/config" | ||||||
| 	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis" | 	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis" | ||||||
| @ -92,6 +93,11 @@ func (es EnabledSet) Get(optimize bool) ([]*linter.Config, error) { | |||||||
| 		resultLinters = append(resultLinters, lc) | 		resultLinters = append(resultLinters, lc) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Make order of execution of linters (go/analysis metalinter and unused) stable. | ||||||
|  | 	sort.Slice(resultLinters, func(i, j int) bool { | ||||||
|  | 		return strings.Compare(resultLinters[i].Name(), resultLinters[j].Name()) <= 0 | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| 	return resultLinters, nil | 	return resultLinters, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -122,6 +128,10 @@ func (es EnabledSet) combineGoAnalysisLinters(linters map[string]*linter.Config) | |||||||
| 		delete(linters, lnt.Name()) | 		delete(linters, lnt.Name()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Make order of execution of go/analysis analyzers stable. | ||||||
|  | 	sort.Slice(goanalysisLinters, func(i, j int) bool { | ||||||
|  | 		return strings.Compare(goanalysisLinters[i].Name(), goanalysisLinters[j].Name()) <= 0 | ||||||
|  | 	}) | ||||||
| 	ml := goanalysis.NewMetaLinter(goanalysisLinters) | 	ml := goanalysis.NewMetaLinter(goanalysisLinters) | ||||||
| 
 | 
 | ||||||
| 	var presets []string | 	var presets []string | ||||||
|  | |||||||
| @ -98,6 +98,7 @@ func (cl *ContextLoader) makeBuildFlags() ([]string, error) { | |||||||
| 	if len(cl.cfg.Run.BuildTags) != 0 { | 	if len(cl.cfg.Run.BuildTags) != 0 { | ||||||
| 		// go help build | 		// go help build | ||||||
| 		buildFlags = append(buildFlags, "-tags", strings.Join(cl.cfg.Run.BuildTags, " ")) | 		buildFlags = append(buildFlags, "-tags", strings.Join(cl.cfg.Run.BuildTags, " ")) | ||||||
|  | 		cl.log.Infof("Using build tags: %v", cl.cfg.Run.BuildTags) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mod := cl.cfg.Run.ModulesDownloadMode | 	mod := cl.cfg.Run.ModulesDownloadMode | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Isaev Denis
						Isaev Denis