diff --git a/src/build/incrementality.go b/src/build/incrementality.go index f7ba7ca4f..1b28552da 100644 --- a/src/build/incrementality.go +++ b/src/build/incrementality.go @@ -21,6 +21,7 @@ import ( "hash" "os" "path/filepath" + "slices" "sort" "github.com/thought-machine/please/src/core" @@ -156,7 +157,7 @@ func ruleHash(state *core.BuildState, target *core.BuildTarget, runtime bool) [] for dep := range target.DeclaredDependencies() { deps = append(deps, dep) } - sort.Sort(deps) + slices.SortFunc(deps, core.BuildLabel.Compare) for _, dep := range deps { h.Write([]byte(dep.String())) } diff --git a/src/cache/dir_cache.go b/src/cache/dir_cache.go index 86e3af0c8..02a367b8d 100644 --- a/src/cache/dir_cache.go +++ b/src/cache/dir_cache.go @@ -5,13 +5,14 @@ package cache import ( "archive/tar" "bufio" + "cmp" "compress/gzip" "encoding/base64" "fmt" "io" "os" "path/filepath" - "sort" + "slices" "strings" "sync" "time" @@ -445,12 +446,12 @@ func (cache *dirCache) clean(highWaterMark, lowWaterMark uint64) uint64 { return totalSize // Nothing to do, cache is small enough. } // OK, we need to slim it down a bit. We implement a simple LRU algorithm. - sort.Slice(entries, func(i, j int) bool { - diff := entries[i].Atime - entries[j].Atime + slices.SortFunc(entries, func(a, b cacheEntry) int { + diff := a.Atime - b.Atime if diff > -accessTimeGracePeriod && diff < accessTimeGracePeriod { - return entries[i].Size > entries[j].Size + return cmp.Compare(b.Size, a.Size) } - return entries[i].Atime < entries[j].Atime + return cmp.Compare(a.Atime, b.Atime) }) for _, entry := range entries { if _, marked := cache.isMarked(entry.Path); marked { diff --git a/src/cli/suggest.go b/src/cli/suggest.go index a638af4bd..89686bfe0 100644 --- a/src/cli/suggest.go +++ b/src/cli/suggest.go @@ -1,7 +1,8 @@ package cli import ( - "sort" + "cmp" + "slices" "strings" "github.com/texttheater/golang-levenshtein/levenshtein" @@ -17,7 +18,7 @@ func Suggest(needle string, haystack []string, maxSuggestionDistance int) []stri options = append(options, suggestion{s: straw, dist: distance}) } } - sort.Slice(options, func(i, j int) bool { return options[i].dist < options[j].dist }) + slices.SortFunc(options, func(a, b suggestion) int { return cmp.Compare(a.dist, b.dist) }) ret := make([]string, len(options)) for i, o := range options { ret[i] = o.s diff --git a/src/core/build_label.go b/src/core/build_label.go index 7f84911cf..703701d4b 100644 --- a/src/core/build_label.go +++ b/src/core/build_label.go @@ -310,23 +310,12 @@ func (label BuildLabel) Includes(that BuildLabel) bool { // Compare compares this build label to another one (suitable for using with slices.SortFunc) func (label BuildLabel) Compare(other BuildLabel) int { - if label.Subrepo != other.Subrepo { - if label.Subrepo < other.Subrepo { - return -1 - } - return 1 - } else if label.PackageName != other.PackageName { - if label.PackageName < other.PackageName { - return -1 - } - return 1 - } else if label.Name != other.Name { - if label.Name < other.Name { - return -1 - } - return 1 + if c := strings.Compare(label.Subrepo, other.Subrepo); c != 0 { + return c + } else if c := strings.Compare(label.PackageName, other.PackageName); c != 0 { + return c } - return 0 + return strings.Compare(label.Name, other.Name) } // Less returns true if this build label would sort less than another one. @@ -613,18 +602,9 @@ func LooksLikeABuildLabel(str string) bool { return strings.HasPrefix(str, "//") || strings.HasPrefix(str, ":") || (strings.HasPrefix(str, "@") && (strings.ContainsRune(str, ':') || strings.Contains(str, "//"))) } -// BuildLabels makes slices of build labels sortable. +// BuildLabels is a slice of build labels. type BuildLabels []BuildLabel -func (slice BuildLabels) Len() int { - return len(slice) -} -func (slice BuildLabels) Less(i, j int) bool { - return slice[i].Less(slice[j]) -} -func (slice BuildLabels) Swap(i, j int) { - slice[i], slice[j] = slice[j], slice[i] -} func (slice BuildLabels) String() string { s := make([]string, len(slice)) for i, l := range slice { diff --git a/src/core/build_target.go b/src/core/build_target.go index 069034724..fa387b35f 100644 --- a/src/core/build_target.go +++ b/src/core/build_target.go @@ -579,7 +579,7 @@ func (target *BuildTarget) Dependencies(graph *BuildGraph) ([]*BuildTarget, []Bu labels[i] = dep.Label } target.mutex.RUnlock() - ret := make(BuildTargets, 0, len(labels)) + ret := make([]*BuildTarget, 0, len(labels)) var unresolved []BuildLabel for _, l := range labels { depTarget := graph.Target(l) @@ -595,7 +595,7 @@ func (target *BuildTarget) Dependencies(graph *BuildGraph) ([]*BuildTarget, []Bu } } } - sort.Sort(ret) + slices.SortFunc(ret, compareTargets) return ret, unresolved } @@ -612,7 +612,7 @@ func (target *BuildTarget) ExternalDependencies(graph *BuildGraph) ([]*BuildTarg labels[i] = dep.Label } target.mutex.RUnlock() - ret := make(BuildTargets, 0, len(labels)) + ret := make([]*BuildTarget, 0, len(labels)) var unresolved []BuildLabel for _, l := range labels { depTarget := graph.Target(l) @@ -633,7 +633,7 @@ func (target *BuildTarget) ExternalDependencies(graph *BuildGraph) ([]*BuildTarg } } } - sort.Sort(ret) + slices.SortFunc(ret, compareTargets) return ret, unresolved } @@ -2064,17 +2064,9 @@ func (target *BuildTarget) CheckLicences(config *Configuration) (string, error) return "", nil } -// BuildTargets makes a slice of build targets sortable by their labels. -type BuildTargets []*BuildTarget - -func (slice BuildTargets) Len() int { - return len(slice) -} -func (slice BuildTargets) Less(i, j int) bool { - return slice[i].Label.Less(slice[j].Label) -} -func (slice BuildTargets) Swap(i, j int) { - slice[i], slice[j] = slice[j], slice[i] +// compareTargets compares two build targets by their labels (suitable for using with slices.SortFunc) +func compareTargets(a, b *BuildTarget) int { + return a.Label.Compare(b.Label) } const sourceListFileDir = "_plz" diff --git a/src/core/graph.go b/src/core/graph.go index ac7055c27..753876dfb 100644 --- a/src/core/graph.go +++ b/src/core/graph.go @@ -7,7 +7,6 @@ package core import ( "iter" "slices" - "sort" "github.com/thought-machine/please/src/cmap" ) @@ -141,11 +140,9 @@ func (graph *BuildGraph) SubrepoOrWait(subrepo string) (*Subrepo, <-chan struct{ } // AllTargets returns a consistently ordered slice of all the targets in the graph. -func (graph *BuildGraph) AllTargets() BuildTargets { +func (graph *BuildGraph) AllTargets() []*BuildTarget { targets := graph.targets.Values() - sort.Slice(targets, func(i, j int) bool { - return targets[i].Label.Less(targets[j].Label) - }) + slices.SortFunc(targets, compareTargets) return targets } diff --git a/src/core/package.go b/src/core/package.go index d348a28a3..d38a9ab70 100644 --- a/src/core/package.go +++ b/src/core/package.go @@ -3,7 +3,7 @@ package core import ( "fmt" "path/filepath" - "sort" + "slices" "strings" "sync" @@ -165,14 +165,14 @@ func (pkg *Package) MustRegisterOutput(state *BuildState, fileName string, targe // AllChildren returns all child targets of the given one. // The given target is included as well. func (pkg *Package) AllChildren(target *BuildTarget) []*BuildTarget { - ret := BuildTargets{} + ret := []*BuildTarget{} parent := target.Label.Parent() for _, t := range pkg.targets { if t.Label.Parent() == parent { ret = append(ret, t) } } - sort.Sort(ret) + slices.SortFunc(ret, compareTargets) return ret } diff --git a/src/core/state.go b/src/core/state.go index 82286c703..e0d75b5ef 100644 --- a/src/core/state.go +++ b/src/core/state.go @@ -13,7 +13,7 @@ import ( iofs "io/fs" "iter" "path/filepath" - "sort" + "slices" "strings" "sync" "sync/atomic" @@ -594,7 +594,7 @@ func (state *BuildState) expandOriginalPseudoTarget(label BuildLabel, justTests } } } - sort.Sort(ret) + slices.SortFunc(ret, BuildLabel.Compare) return ret } diff --git a/src/fs/sort.go b/src/fs/sort.go index af14ea4cb..9a802c435 100644 --- a/src/fs/sort.go +++ b/src/fs/sort.go @@ -1,23 +1,24 @@ package fs import ( - "sort" + "cmp" + "slices" "strings" ) // SortPaths sorts a list of filepaths in lexicographic order by component // (i.e. so all files in a directory sort after all subdirectories). func SortPaths(files []string) []string { - sort.Slice(files, func(i, j int) bool { - si, sj := commonPrefix(strings.Split(files[i], "/"), strings.Split(files[j], "/")) + slices.SortFunc(files, func(a, b string) int { + si, sj := commonPrefix(strings.Split(a, "/"), strings.Split(b, "/")) if len(si) == 1 && len(sj) > 1 { - return false // si is a leaf and sj is not + return 1 // si is a leaf and sj is not } else if len(sj) == 1 && len(si) > 1 { - return true // sj is a leaf and si is not + return -1 // sj is a leaf and si is not } else if len(si) == 0 || len(sj) == 0 { - return len(si) < len(sj) // one or the other is empty. + return cmp.Compare(len(si), len(sj)) // one or the other is empty. } - return si[0] < sj[0] + return strings.Compare(si[0], sj[0]) }) return files } diff --git a/src/gc/gc.go b/src/gc/gc.go index a84f5c25c..5d5166171 100644 --- a/src/gc/gc.go +++ b/src/gc/gc.go @@ -9,6 +9,7 @@ import ( "bytes" "fmt" "os" + "slices" "sort" "strings" @@ -140,7 +141,7 @@ func targetsToRemove(graph *core.BuildGraph, filter, targets, targetsToKeep []co } } } - sort.Sort(ret) + slices.SortFunc(ret, core.BuildLabel.Compare) sort.Strings(retSrcs) log.Notice("%d targets to remove", len(ret)) log.Notice("%d sources to remove", len(retSrcs)) diff --git a/src/output/shell_output.go b/src/output/shell_output.go index 5cd9d9f87..40ee53e85 100644 --- a/src/output/shell_output.go +++ b/src/output/shell_output.go @@ -9,7 +9,7 @@ import ( "math/rand" "os" "path/filepath" - "sort" + "slices" "strings" "time" @@ -153,7 +153,7 @@ func printTestResults(state *core.BuildState, failedTargets map[core.BuildLabel] for t := range failedTargets { targets = append(targets, t) } - sort.Sort(targets) + slices.SortFunc(targets, core.BuildLabel.Compare) for _, failed := range targets { target := state.Graph.TargetOrDie(failed) if target.Test.Results.Failures() == 0 && target.Test.Results.Errors() == 0 { diff --git a/src/parse/asp/main/main.go b/src/parse/asp/main/main.go index d32519764..d4c4d35e2 100644 --- a/src/parse/asp/main/main.go +++ b/src/parse/asp/main/main.go @@ -4,6 +4,7 @@ package main import ( + "cmp" "context" "fmt" iofs "io/fs" @@ -11,6 +12,7 @@ import ( "path/filepath" "reflect" "regexp" + "slices" "sort" "strings" "sync" @@ -158,8 +160,8 @@ func checkAST(stmts []*asp.Statement, parentScopes ...map[string]assignment) (er errs = append(errs, assign) } } - sort.Slice(errs, func(i, j int) bool { - return errs[i].Pos < errs[j].Pos + slices.SortFunc(errs, func(a, b assignment) int { + return cmp.Compare(a.Pos, b.Pos) }) return errs } diff --git a/src/query/changes.go b/src/query/changes.go index 584e17a17..b415062c4 100644 --- a/src/query/changes.go +++ b/src/query/changes.go @@ -4,7 +4,7 @@ import ( "bytes" "crypto/sha1" "path/filepath" - "sort" + "slices" "github.com/thought-machine/please/src/build" "github.com/thought-machine/please/src/core" @@ -83,7 +83,7 @@ func changedTargets(state *core.BuildState, files []string, changed map[*core.Bu ls = append(ls, l) } } - sort.Sort(ls) + slices.SortFunc(ls, core.BuildLabel.Compare) return ls } diff --git a/src/query/print.go b/src/query/print.go index 3955901c6..58771a809 100644 --- a/src/query/print.go +++ b/src/query/print.go @@ -1,13 +1,13 @@ package query import ( + "cmp" "encoding/json" "fmt" "io" "os" "reflect" "slices" - "sort" "strconv" "strings" "time" @@ -212,8 +212,8 @@ func (p *printer) printf(msg string, args ...interface{}) { fmt.Fprintf(p.w, msg, args...) } -func fields(structValue reflect.Value, fieldOrder map[string]int) orderedFields { - ret := make(orderedFields, structValue.NumField()) +func fields(structValue reflect.Value, fieldOrder map[string]int) []orderedField { + ret := make([]orderedField, structValue.NumField()) structType := structValue.Type() @@ -245,7 +245,7 @@ func (p *printer) PrintTarget() { fs = append(fs, fields(reflect.ValueOf(p.target.Test).Elem(), p.fieldOrder)...) } - sort.Sort(fs) + slices.SortFunc(fs, func(a, b orderedField) int { return cmp.Compare(a.order, b.order) }) for _, orderedField := range fs { p.printField(orderedField.field, orderedField.value) } @@ -423,7 +423,7 @@ func (p *printer) printSlice(v reflect.Value) string { // printMap prints the representation of a map field. func (p *printer) printMap(v reflect.Value) string { keys := v.MapKeys() - sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() }) + slices.SortFunc(keys, func(a, b reflect.Value) int { return strings.Compare(a.String(), b.String()) }) s := make([]string, len(keys)) indent := strings.Repeat(" ", p.indent+4) for i, key := range keys { @@ -457,9 +457,3 @@ type orderedField struct { field reflect.StructField value reflect.Value } - -type orderedFields []orderedField - -func (f orderedFields) Len() int { return len(f) } -func (f orderedFields) Swap(a, b int) { f[a], f[b] = f[b], f[a] } -func (f orderedFields) Less(a, b int) bool { return f[a].order < f[b].order } diff --git a/src/query/reverse_deps.go b/src/query/reverse_deps.go index f1f01e168..0510975c8 100644 --- a/src/query/reverse_deps.go +++ b/src/query/reverse_deps.go @@ -3,7 +3,7 @@ package query import ( "container/list" "fmt" - "sort" + "slices" "github.com/thought-machine/please/src/core" ) @@ -18,7 +18,7 @@ func ReverseDeps(state *core.BuildState, labels []core.BuildLabel, level int, hi ls = append(ls, target.Label) } } - sort.Sort(ls) + slices.SortFunc(ls, core.BuildLabel.Compare) for _, l := range ls { fmt.Println(l.String()) diff --git a/src/remote/utils.go b/src/remote/utils.go index d5fc6ff83..581579e2e 100644 --- a/src/remote/utils.go +++ b/src/remote/utils.go @@ -11,7 +11,7 @@ import ( iofs "io/fs" "os" "path/filepath" - "sort" + "slices" "strings" "time" @@ -508,9 +508,9 @@ func (b *dirBuilder) walk(name string, ch chan<- *uploadinfo.Entry) *pb.Digest { files := dir.Files dirs := dir.Directories syms := dir.Symlinks - sort.Slice(files, func(i, j int) bool { return files[i].Name < files[j].Name }) - sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name < dirs[j].Name }) - sort.Slice(syms, func(i, j int) bool { return syms[i].Name < syms[j].Name }) + slices.SortFunc(files, func(a, b *pb.FileNode) int { return strings.Compare(a.Name, b.Name) }) + slices.SortFunc(dirs, func(a, b *pb.DirectoryNode) int { return strings.Compare(a.Name, b.Name) }) + slices.SortFunc(syms, func(a, b *pb.SymlinkNode) int { return strings.Compare(a.Name, b.Name) }) // Ensure there are not duplicates in these slices. last := "" diff --git a/tools/build_langserver/lsp/BUILD b/tools/build_langserver/lsp/BUILD index d0be35f00..731b0609a 100644 --- a/tools/build_langserver/lsp/BUILD +++ b/tools/build_langserver/lsp/BUILD @@ -32,6 +32,7 @@ go_test( "lsp_test.go", "symbols_test.go", ], + flaky = True, data = ["test_data"], deps = [ ":lsp", diff --git a/tools/build_langserver/lsp/symbols.go b/tools/build_langserver/lsp/symbols.go index dad1c2dc4..352c8c850 100644 --- a/tools/build_langserver/lsp/symbols.go +++ b/tools/build_langserver/lsp/symbols.go @@ -1,7 +1,8 @@ package lsp import ( - "sort" + "cmp" + "slices" "strconv" "strings" @@ -46,7 +47,7 @@ func (h *Handler) symbols(params *lsp.DocumentSymbolParams) ([]*lsp.SymbolInform } return true }) - sort.Slice(syms, func(i, j int) bool { return compareRanges(syms[i].Location.Range, syms[j].Location.Range) }) + slices.SortFunc(syms, func(a, b *lsp.SymbolInformation) int { return compareRanges(a.Location.Range, b.Location.Range) }) return syms, nil } @@ -119,19 +120,20 @@ func rng(start, end asp.FilePosition) lsp.Range { return lsp.Range{Start: pos(start), End: pos(end)} } -// compareRanges compares two lsp.Ranges and returns true if the first starts before -// the second in a document. If equal the end positions are considered. -func compareRanges(a, b lsp.Range) bool { - if comparePositions(a.Start, b.Start) { - return true - } else if comparePositions(b.Start, a.Start) { - return false +// compareRanges compares two lsp.Ranges by where they start in a document, returning -1, 0 or +1 +// in the same manner as cmp.Compare. If equal the end positions are considered. +func compareRanges(a, b lsp.Range) int { + if c := comparePositions(a.Start, b.Start); c != 0 { + return c } return comparePositions(a.End, b.End) } -// comparePositions compares two lsp.Positions and returns true if the first -// is before the second in a document. -func comparePositions(a, b lsp.Position) bool { - return a.Line < b.Line || (a.Line == b.Line && a.Character < b.Character) +// comparePositions compares two lsp.Positions by their location in a document, returning -1, 0 or +1 +// in the same manner as cmp.Compare. +func comparePositions(a, b lsp.Position) int { + if c := cmp.Compare(a.Line, b.Line); c != 0 { + return c + } + return cmp.Compare(a.Character, b.Character) }