diff --git a/internal/x/tools/analysisflags/readme.md b/internal/x/tools/analysisflags/readme.md index 4d221d4ca50c..6035c2226568 100644 --- a/internal/x/tools/analysisflags/readme.md +++ b/internal/x/tools/analysisflags/readme.md @@ -5,4 +5,7 @@ This is just a copy of the code without any changes. ## History -- sync with https://github.com/golang/tools/blob/v0.28.0 +- https://github.com/golangci/golangci-lint/pull/6076 + - sync with https://github.com/golang/tools/blob/v0.37.0/go/analysis/internal/analysisflags +- https://github.com/golangci/golangci-lint/pull/5576 + - sync with https://github.com/golang/tools/blob/v0.28.0/go/analysis/internal/analysisflags diff --git a/internal/x/tools/analysisinternal/analysis.go b/internal/x/tools/analysisinternal/analysis.go index bb12600dac08..b613d167348a 100644 --- a/internal/x/tools/analysisinternal/analysis.go +++ b/internal/x/tools/analysisinternal/analysis.go @@ -8,25 +8,30 @@ package analysisinternal import ( "fmt" - "os" + "slices" "golang.org/x/tools/go/analysis" ) -// MakeReadFile returns a simple implementation of the Pass.ReadFile function. -func MakeReadFile(pass *analysis.Pass) func(filename string) ([]byte, error) { +// A ReadFileFunc is a function that returns the +// contents of a file, such as [os.ReadFile]. +type ReadFileFunc = func(filename string) ([]byte, error) + +// CheckedReadFile returns a wrapper around a Pass.ReadFile +// function that performs the appropriate checks. +func CheckedReadFile(pass *analysis.Pass, readFile ReadFileFunc) ReadFileFunc { return func(filename string) ([]byte, error) { if err := CheckReadable(pass, filename); err != nil { return nil, err } - return os.ReadFile(filename) + return readFile(filename) } } // CheckReadable enforces the access policy defined by the ReadFile field of [analysis.Pass]. func CheckReadable(pass *analysis.Pass, filename string) error { - if slicesContains(pass.OtherFiles, filename) || - slicesContains(pass.IgnoredFiles, filename) { + if slices.Contains(pass.OtherFiles, filename) || + slices.Contains(pass.IgnoredFiles, filename) { return nil } for _, f := range pass.Files { @@ -36,13 +41,3 @@ func CheckReadable(pass *analysis.Pass, filename string) error { } return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename) } - -// TODO(adonovan): use go1.21 slices.Contains. -func slicesContains[S ~[]E, E comparable](slice S, x E) bool { - for _, elem := range slice { - if elem == x { - return true - } - } - return false -} diff --git a/internal/x/tools/analysisinternal/readme.md b/internal/x/tools/analysisinternal/readme.md index f301cdbebb00..6c54592d9e26 100644 --- a/internal/x/tools/analysisinternal/readme.md +++ b/internal/x/tools/analysisinternal/readme.md @@ -5,4 +5,7 @@ This is just a copy of the code without any changes. ## History -- sync with https://github.com/golang/tools/blob/v0.28.0 +- https://github.com/golangci/golangci-lint/pull/6076 + - sync with https://github.com/golang/tools/blob/v0.37.0/internal/analysisinternal/ +- https://github.com/golangci/golangci-lint/pull/5576 + - sync with https://github.com/golang/tools/blob/v0.28.0/internal/analysisinternal/ diff --git a/internal/x/tools/diff/diff.go b/internal/x/tools/diff/diff.go index a13547b7a7e3..c12bdfd2acd6 100644 --- a/internal/x/tools/diff/diff.go +++ b/internal/x/tools/diff/diff.go @@ -7,6 +7,7 @@ package diff import ( "fmt" + "slices" "sort" "strings" ) @@ -64,7 +65,7 @@ func ApplyBytes(src []byte, edits []Edit) ([]byte, error) { // It may return a different slice. func validate(src string, edits []Edit) ([]Edit, int, error) { if !sort.IsSorted(editsSort(edits)) { - edits = append([]Edit(nil), edits...) + edits = slices.Clone(edits) SortEdits(edits) } diff --git a/internal/x/tools/diff/lcs/common.go b/internal/x/tools/diff/lcs/common.go index c3e82dd26839..27fa9ecbd5c5 100644 --- a/internal/x/tools/diff/lcs/common.go +++ b/internal/x/tools/diff/lcs/common.go @@ -51,7 +51,7 @@ func (l lcs) fix() lcs { // from the set of diagonals in l, find a maximal non-conflicting set // this problem may be NP-complete, but we use a greedy heuristic, // which is quadratic, but with a better data structure, could be D log D. - // indepedent is not enough: {0,3,1} and {3,0,2} can't both occur in an lcs + // independent is not enough: {0,3,1} and {3,0,2} can't both occur in an lcs // which has to have monotone x and y if len(l) == 0 { return nil diff --git a/internal/x/tools/diff/lcs/common_test.go b/internal/x/tools/diff/lcs/common_test.go index f19245e404ce..1a621f3f76ad 100644 --- a/internal/x/tools/diff/lcs/common_test.go +++ b/internal/x/tools/diff/lcs/common_test.go @@ -6,7 +6,8 @@ package lcs import ( "log" - "math/rand" + "math/rand/v2" + "slices" "strings" "testing" ) @@ -72,10 +73,8 @@ func check(t *testing.T, str string, lcs lcs, want []string) { got.WriteString(str[dd.X : dd.X+dd.Len]) } ans := got.String() - for _, w := range want { - if ans == w { - return - } + if slices.Contains(want, ans) { + return } t.Fatalf("str=%q lcs=%v want=%q got=%q", str, lcs, want, ans) } @@ -106,11 +105,11 @@ func lcslen(l lcs) int { } // return a random string of length n made of characters from s -func randstr(s string, n int) string { +func randstr(rng *rand.Rand, s string, n int) string { src := []rune(s) x := make([]rune, n) - for i := 0; i < n; i++ { - x[i] = src[rand.Intn(len(src))] + for i := range n { + x[i] = src[rng.Int64N(int64(len(src)))] } return string(x) } diff --git a/internal/x/tools/diff/lcs/doc.go b/internal/x/tools/diff/lcs/doc.go index 9029dd20b3d5..aa4b0fb5910e 100644 --- a/internal/x/tools/diff/lcs/doc.go +++ b/internal/x/tools/diff/lcs/doc.go @@ -139,7 +139,7 @@ computed labels. That is the worst case. Had the code noticed (x,y)=(u,v)=(3,3) from the edgegraph. The implementation looks for a number of special cases to try to avoid computing an extra forward path. If the two-sided algorithm has stop early (because D has become too large) it will have found a forward LCS and a -backwards LCS. Ideally these go with disjoint prefixes and suffixes of A and B, but disjointness may fail and the two +backwards LCS. Ideally these go with disjoint prefixes and suffixes of A and B, but disjointedness may fail and the two computed LCS may conflict. (An easy example is where A is a suffix of B, and shares a short prefix. The backwards LCS is all of A, and the forward LCS is a prefix of A.) The algorithm combines the two to form a best-effort LCS. In the worst case the forward partial LCS may have to diff --git a/internal/x/tools/diff/lcs/old.go b/internal/x/tools/diff/lcs/old.go index 4353da15ba9a..4c346706a756 100644 --- a/internal/x/tools/diff/lcs/old.go +++ b/internal/x/tools/diff/lcs/old.go @@ -105,7 +105,7 @@ func forward(e *editGraph) lcs { return ans } // from D to D+1 - for D := 0; D < e.limit; D++ { + for D := range e.limit { e.setForward(D+1, -(D + 1), e.getForward(D, -D)) if ok, ans := e.fdone(D+1, -(D + 1)); ok { return ans @@ -199,13 +199,14 @@ func (e *editGraph) bdone(D, k int) (bool, lcs) { } // run the backward algorithm, until success or up to the limit on D. +// (used only by tests) func backward(e *editGraph) lcs { e.setBackward(0, 0, e.ux) if ok, ans := e.bdone(0, 0); ok { return ans } // from D to D+1 - for D := 0; D < e.limit; D++ { + for D := range e.limit { e.setBackward(D+1, -(D + 1), e.getBackward(D, -D)-1) if ok, ans := e.bdone(D+1, -(D + 1)); ok { return ans @@ -299,7 +300,7 @@ func twosided(e *editGraph) lcs { e.setBackward(0, 0, e.ux) // from D to D+1 - for D := 0; D < e.limit; D++ { + for D := range e.limit { // just finished a backwards pass, so check if got, ok := e.twoDone(D, D); ok { return e.twolcs(D, D, got) @@ -376,10 +377,7 @@ func (e *editGraph) twoDone(df, db int) (int, bool) { if (df+db+e.delta)%2 != 0 { return 0, false // diagonals cannot overlap } - kmin := -db + e.delta - if -df > kmin { - kmin = -df - } + kmin := max(-df, -db+e.delta) kmax := db + e.delta if df < kmax { kmax = df diff --git a/internal/x/tools/diff/lcs/old_test.go b/internal/x/tools/diff/lcs/old_test.go index ddc3bde0ed27..035465fa34c1 100644 --- a/internal/x/tools/diff/lcs/old_test.go +++ b/internal/x/tools/diff/lcs/old_test.go @@ -7,7 +7,7 @@ package lcs import ( "fmt" "log" - "math/rand" + "math/rand/v2" "os" "strings" "testing" @@ -106,15 +106,16 @@ func TestRegressionOld003(t *testing.T) { } func TestRandOld(t *testing.T) { - rand.Seed(1) - for i := 0; i < 1000; i++ { + rng := rng(t) + + for i := range 1000 { // TODO(adonovan): use ASCII and bytesSeqs here? The use of // non-ASCII isn't relevant to the property exercised by the test. - a := []rune(randstr("abω", 16)) - b := []rune(randstr("abωc", 16)) + a := []rune(randstr(rng, "abω", 16)) + b := []rune(randstr(rng, "abωc", 16)) seq := runesSeqs{a, b} - const lim = 24 // large enough to get true lcs + const lim = 0 // make sure we get the lcs (24 was too small) _, forw := compute(seq, forward, lim) _, back := compute(seq, backward, lim) _, two := compute(seq, twosided, lim) @@ -158,7 +159,7 @@ func TestDiffAPI(t *testing.T) { } func BenchmarkTwoOld(b *testing.B) { - tests := genBench("abc", 96) + tests := genBench(rng(b), "abc", 96) for i := 0; i < b.N; i++ { for _, tt := range tests { _, two := compute(stringSeqs{tt.before, tt.after}, twosided, 100) @@ -170,7 +171,7 @@ func BenchmarkTwoOld(b *testing.B) { } func BenchmarkForwOld(b *testing.B) { - tests := genBench("abc", 96) + tests := genBench(rng(b), "abc", 96) for i := 0; i < b.N; i++ { for _, tt := range tests { _, two := compute(stringSeqs{tt.before, tt.after}, forward, 100) @@ -181,14 +182,21 @@ func BenchmarkForwOld(b *testing.B) { } } -func genBench(set string, n int) []struct{ before, after string } { +// rng returns a randomly initialized PRNG whose seeds are logged so +// that occasional test failures can be deterministically replayed. +func rng(tb testing.TB) *rand.Rand { + seed1, seed2 := rand.Uint64(), rand.Uint64() + tb.Logf("PRNG seeds: %d, %d", seed1, seed2) + return rand.New(rand.NewPCG(seed1, seed2)) +} + +func genBench(rng *rand.Rand, set string, n int) []struct{ before, after string } { // before and after for benchmarks. 24 strings of length n with // before and after differing at least once, and about 5% - rand.Seed(3) var ans []struct{ before, after string } - for i := 0; i < 24; i++ { + for range 24 { // maybe b should have an approximately known number of diffs - a := randstr(set, n) + a := randstr(rng, set, n) cnt := 0 bb := make([]rune, 0, n) for _, r := range a { diff --git a/internal/x/tools/diff/ndiff.go b/internal/x/tools/diff/ndiff.go index b429a69ee1f9..1c64d1ecdf60 100644 --- a/internal/x/tools/diff/ndiff.go +++ b/internal/x/tools/diff/ndiff.go @@ -72,7 +72,7 @@ func diffRunes(before, after []rune) []Edit { func runes(bytes []byte) []rune { n := utf8.RuneCount(bytes) runes := make([]rune, n) - for i := 0; i < n; i++ { + for i := range n { r, sz := utf8.DecodeRune(bytes) bytes = bytes[sz:] runes[i] = r diff --git a/internal/x/tools/diff/readme.md b/internal/x/tools/diff/readme.md index 4b979849897d..b28e41d9c0b8 100644 --- a/internal/x/tools/diff/readme.md +++ b/internal/x/tools/diff/readme.md @@ -5,4 +5,7 @@ This is just a copy of the code without any changes. ## History -- sync with https://github.com/golang/tools/blob/v0.28.0 +- https://github.com/golangci/golangci-lint/pull/6076 + - sync with https://github.com/golang/tools/blob/v0.37.0/internal/diff/ +- https://github.com/golangci/golangci-lint/pull/5576 + - sync with https://github.com/golang/tools/blob/v0.28.0/internal/diff/ diff --git a/internal/x/tools/diff/unified.go b/internal/x/tools/diff/unified.go index cfbda61020a0..9a786dbbef77 100644 --- a/internal/x/tools/diff/unified.go +++ b/internal/x/tools/diff/unified.go @@ -129,12 +129,12 @@ func toUnified(fromName, toName string, content string, edits []Edit, contextLin switch { case h != nil && start == last: - //direct extension + // direct extension case h != nil && start <= last+gap: - //within range of previous lines, add the joiners + // within range of previous lines, add the joiners addEqualLines(h, lines, last, start) default: - //need to start a new hunk + // need to start a new hunk if h != nil { // add the edge to the previous hunk addEqualLines(h, lines, last, last+contextLines) diff --git a/pkg/goanalysis/runner_checker.go b/pkg/goanalysis/runner_checker.go index 569002ed4b9f..e8fda9947f85 100644 --- a/pkg/goanalysis/runner_checker.go +++ b/pkg/goanalysis/runner_checker.go @@ -12,6 +12,7 @@ import ( "errors" "fmt" "go/types" + "os" "reflect" "time" @@ -160,7 +161,7 @@ func (act *action) analyze() { AllObjectFacts: act.AllObjectFacts, AllPackageFacts: act.AllPackageFacts, } - pass.ReadFile = analysisinternal.MakeReadFile(pass) + pass.ReadFile = analysisinternal.CheckedReadFile(pass, os.ReadFile) act.pass = pass act.runner.passToPkgGuard.Lock()