diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3faff1a..43295ed 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: '1.23.4' + go-version: '1.26.4' cache: true - run: make fmt diff --git a/.github/workflows/pr-auditor.yml b/.github/workflows/pr-auditor.yml index 2a20b66..bdeb515 100644 --- a/.github/workflows/pr-auditor.yml +++ b/.github/workflows/pr-auditor.yml @@ -14,7 +14,7 @@ jobs: repository: 'sourcegraph/devx-service' token: ${{ secrets.PR_AUDITOR_TOKEN }} - uses: actions/setup-go@v4 - with: { go-version: '1.22' } + with: { go-version: '1.26.4' } - run: 'go run ./cmd/pr-auditor' env: diff --git a/dag/dag_test.go b/dag/dag_test.go index 8a1ec34..0bb72cc 100644 --- a/dag/dag_test.go +++ b/dag/dag_test.go @@ -144,7 +144,7 @@ func TestAyclicGraphTransReduction_fullyConnected(t *testing.T) { const nodeCount = 200 nodes := make([]*counter, nodeCount) - for i := 0; i < nodeCount; i++ { + for i := range nodeCount { nodes[i] = &counter{Name: strconv.Itoa(i)} } @@ -358,24 +358,24 @@ func BenchmarkDAG(b *testing.B) { // create 4 layers of fully connected nodes // layer A - for i := 0; i < count; i++ { + for i := range count { g.Add(fmt.Sprintf("A%d", i)) } // layer B - for i := 0; i < count; i++ { + for i := range count { B := fmt.Sprintf("B%d", i) g.Add(B) - for j := 0; j < count; j++ { + for j := range count { g.Connect(BasicEdge(B, fmt.Sprintf("A%d", j))) } } // layer C - for i := 0; i < count; i++ { + for i := range count { c := fmt.Sprintf("C%d", i) g.Add(c) - for j := 0; j < count; j++ { + for j := range count { // connect them to previous layers so we have something that requires reduction g.Connect(BasicEdge(c, fmt.Sprintf("A%d", j))) g.Connect(BasicEdge(c, fmt.Sprintf("B%d", j))) @@ -383,10 +383,10 @@ func BenchmarkDAG(b *testing.B) { } // layer D - for i := 0; i < count; i++ { + for i := range count { d := fmt.Sprintf("D%d", i) g.Add(d) - for j := 0; j < count; j++ { + for j := range count { g.Connect(BasicEdge(d, fmt.Sprintf("A%d", j))) g.Connect(BasicEdge(d, fmt.Sprintf("B%d", j))) g.Connect(BasicEdge(d, fmt.Sprintf("C%d", j))) diff --git a/dag/dot.go b/dag/dot.go index 7e6d2af..d0de6a2 100644 --- a/dag/dot.go +++ b/dag/dot.go @@ -3,6 +3,7 @@ package dag import ( "bytes" "fmt" + "maps" "sort" "strings" ) @@ -95,12 +96,8 @@ func (v *marshalVertex) dot(g *marshalGraph, opts *DotOpts) []byte { } newAttrs := make(map[string]string) - for k, v := range attrs { - newAttrs[k] = v - } - for k, v := range node.Attrs { - newAttrs[k] = v - } + maps.Copy(newAttrs, attrs) + maps.Copy(newAttrs, node.Attrs) name = node.Name attrs = newAttrs diff --git a/dag/edge.go b/dag/edge.go index 8c78924..3b02c52 100644 --- a/dag/edge.go +++ b/dag/edge.go @@ -20,8 +20,8 @@ type basicEdge struct { S, T Vertex } -func (e *basicEdge) Hashcode() interface{} { - return [...]interface{}{e.S, e.T} +func (e *basicEdge) Hashcode() any { + return [...]any{e.S, e.T} } func (e *basicEdge) Source() Vertex { diff --git a/dag/graph.go b/dag/graph.go index ab1ae37..86a73d3 100644 --- a/dag/graph.go +++ b/dag/graph.go @@ -10,8 +10,8 @@ import ( type Graph struct { vertices Set edges Set - downEdges map[interface{}]Set - upEdges map[interface{}]Set + downEdges map[any]Set + upEdges map[any]Set } // Subgrapher allows a Vertex to be a Graph itself, by returning a Grapher. @@ -27,7 +27,7 @@ type Grapher interface { } // Vertex of the graph. -type Vertex interface{} +type Vertex any // NamedVertex is an optional interface that can be implemented by Vertex // to give it a human-friendly name that is used for outputting the graph. @@ -243,11 +243,11 @@ func (g *Graph) Connect(edge Edge) { func (g *Graph) Subsume(other *Graph) { // We're using Set.Filter just as a "visit each element" here, so we're // not doing anything with the result (which will always be empty). - other.vertices.Filter(func(i interface{}) bool { + other.vertices.Filter(func(i any) bool { g.Add(i) return false }) - other.edges.Filter(func(i interface{}) bool { + other.edges.Filter(func(i any) bool { g.Connect(i.(Edge)) return false }) @@ -342,10 +342,10 @@ func (g *Graph) init() { g.edges = make(Set) } if g.downEdges == nil { - g.downEdges = make(map[interface{}]Set) + g.downEdges = make(map[any]Set) } if g.upEdges == nil { - g.upEdges = make(map[interface{}]Set) + g.upEdges = make(map[any]Set) } } diff --git a/dag/graph_test.go b/dag/graph_test.go index 76c4764..6697dad 100644 --- a/dag/graph_test.go +++ b/dag/graph_test.go @@ -207,10 +207,10 @@ func TestGraphUpdownEdges(t *testing.T) { } type hashVertex struct { - code interface{} + code any } -func (v *hashVertex) Hashcode() interface{} { +func (v *hashVertex) Hashcode() any { return v.code } diff --git a/dag/marshal.go b/dag/marshal.go index a78032a..5ef5e4a 100644 --- a/dag/marshal.go +++ b/dag/marshal.go @@ -157,7 +157,7 @@ func newMarshalGraph(name string, g *Graph) *marshalGraph { func marshalVertexID(v Vertex) string { val := reflect.ValueOf(v) switch val.Kind() { - case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer: return strconv.Itoa(int(val.Pointer())) case reflect.Interface: // A vertex shouldn't contain another layer of interface, but handle diff --git a/dag/set.go b/dag/set.go index 390415e..d7dc49a 100644 --- a/dag/set.go +++ b/dag/set.go @@ -1,17 +1,19 @@ package dag +import "maps" + // Set is a set data structure. -type Set map[interface{}]interface{} +type Set map[any]any // Hashable is the interface used by set to get the hash code of a value. // If this isn't given, then the value of the item being added to the set // itself is used as the comparison value. type Hashable interface { - Hashcode() interface{} + Hashcode() any } // hashcode returns the hashcode used for set elements. -func hashcode(v interface{}) interface{} { +func hashcode(v any) any { if h, ok := v.(Hashable); ok { return h.Hashcode() } @@ -20,17 +22,17 @@ func hashcode(v interface{}) interface{} { } // Add adds an item to the set -func (s Set) Add(v interface{}) { +func (s Set) Add(v any) { s[hashcode(v)] = v } // Delete removes an item from the set. -func (s Set) Delete(v interface{}) { +func (s Set) Delete(v any) { delete(s, hashcode(v)) } // Include returns true/false of whether a value is in the set. -func (s Set) Include(v interface{}) bool { +func (s Set) Include(v any) bool { _, ok := s[hashcode(v)] return ok } @@ -72,7 +74,7 @@ func (s Set) Difference(other Set) Set { // Filter returns a set that contains the elements from the receiver // where the given callback returns true. -func (s Set) Filter(cb func(interface{}) bool) Set { +func (s Set) Filter(cb func(any) bool) Set { result := make(Set) for _, v := range s { @@ -90,12 +92,12 @@ func (s Set) Len() int { } // List returns the list of set elements. -func (s Set) List() []interface{} { +func (s Set) List() []any { if s == nil { return nil } - r := make([]interface{}, 0, len(s)) + r := make([]any, 0, len(s)) for _, v := range s { r = append(r, v) } @@ -106,8 +108,6 @@ func (s Set) List() []interface{} { // Copy returns a shallow copy of the set. func (s Set) Copy() Set { c := make(Set, len(s)) - for k, v := range s { - c[k] = v - } + maps.Copy(c, s) return c } diff --git a/dag/set_test.go b/dag/set_test.go index 721f246..6f29bd0 100644 --- a/dag/set_test.go +++ b/dag/set_test.go @@ -8,34 +8,34 @@ import ( func TestSetDifference(t *testing.T) { cases := []struct { Name string - A, B []interface{} - Expected []interface{} + A, B []any + Expected []any }{ { "same", - []interface{}{1, 2, 3}, - []interface{}{3, 1, 2}, - []interface{}{}, + []any{1, 2, 3}, + []any{3, 1, 2}, + []any{}, }, { "A has extra elements", - []interface{}{1, 2, 3}, - []interface{}{3, 2}, - []interface{}{1}, + []any{1, 2, 3}, + []any{3, 2}, + []any{1}, }, { "B has extra elements", - []interface{}{1, 2, 3}, - []interface{}{3, 2, 1, 4}, - []interface{}{}, + []any{1, 2, 3}, + []any{3, 2, 1, 4}, + []any{}, }, { "B is nil", - []interface{}{1, 2, 3}, + []any{1, 2, 3}, nil, - []interface{}{1, 2, 3}, + []any{1, 2, 3}, }, } @@ -68,22 +68,22 @@ func TestSetDifference(t *testing.T) { func TestSetFilter(t *testing.T) { cases := []struct { - Input []interface{} - Expected []interface{} + Input []any + Expected []any }{ { - []interface{}{1, 2, 3}, - []interface{}{1, 2, 3}, + []any{1, 2, 3}, + []any{1, 2, 3}, }, { - []interface{}{4, 5, 6}, - []interface{}{4}, + []any{4, 5, 6}, + []any{4}, }, { - []interface{}{7, 8, 9}, - []interface{}{}, + []any{7, 8, 9}, + []any{}, }, } @@ -98,7 +98,7 @@ func TestSetFilter(t *testing.T) { expected.Add(v) } - actual := input.Filter(func(v interface{}) bool { + actual := input.Filter(func(v any) bool { return v.(int) < 5 }) match := actual.Intersection(expected) @@ -131,7 +131,7 @@ func TestSetCopy(t *testing.T) { func makeSet(n int) Set { ret := make(Set, n) - for i := 0; i < n; i++ { + for i := range n { ret.Add(i) } return ret diff --git a/dag/tarjan.go b/dag/tarjan.go index fb4d4a7..2720fe5 100644 --- a/dag/tarjan.go +++ b/dag/tarjan.go @@ -1,5 +1,7 @@ package dag +import "slices" + // StronglyConnected returns the list of strongly connected components // within the Graph g. This information is primarily used by this package // for cycle detection, but strongly connected components have widespread @@ -55,13 +57,6 @@ func stronglyConnected(acct *sccAcct, g *Graph, v Vertex) int { return minIdx } -func min(a, b int) int { - if a <= b { - return a - } - return b -} - // sccAcct is used ot pass around accounting information for // the StronglyConnectedComponents algorithm type sccAcct struct { @@ -98,10 +93,5 @@ func (s *sccAcct) pop() Vertex { // inStack checks if a vertex is in the stack func (s *sccAcct) inStack(needle Vertex) bool { - for _, n := range s.Stack { - if n == needle { - return true - } - } - return false + return slices.Contains(s.Stack, needle) } diff --git a/dag/walk_test.go b/dag/walk_test.go index e2f26b1..828bb1f 100644 --- a/dag/walk_test.go +++ b/dag/walk_test.go @@ -15,8 +15,8 @@ func TestWalker_basic(t *testing.T) { g.Connect(BasicEdge(1, 2)) // Run it a bunch of times since it is timing dependent - for i := 0; i < 50; i++ { - var order []interface{} + for range 50 { + var order []any w := &Walker{Callback: walkCbRecord(&order)} w.Update(&g) @@ -26,7 +26,7 @@ func TestWalker_basic(t *testing.T) { } // Check - expected := []interface{}{1, 2} + expected := []any{1, 2} if !reflect.DeepEqual(order, expected) { t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) } @@ -40,8 +40,8 @@ func TestWalker_updateNilGraph(t *testing.T) { g.Connect(BasicEdge(1, 2)) // Run it a bunch of times since it is timing dependent - for i := 0; i < 50; i++ { - var order []interface{} + for range 50 { + var order []any w := &Walker{Callback: walkCbRecord(&order)} w.Update(&g) w.Update(nil) @@ -64,7 +64,7 @@ func TestWalker_error(t *testing.T) { g.Connect(BasicEdge(3, 4)) // Record function - var order []interface{} + var order []any recordF := walkCbRecord(&order) // Build a callback that delays until we close a channel @@ -85,7 +85,7 @@ func TestWalker_error(t *testing.T) { } // Check - expected := []interface{}{1} + expected := []any{1} if !reflect.DeepEqual(order, expected) { t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) } @@ -98,7 +98,7 @@ func TestWalker_newVertex(t *testing.T) { g.Connect(BasicEdge(1, 2)) // Record function - var order []interface{} + var order []any recordF := walkCbRecord(&order) done2 := make(chan int) @@ -132,7 +132,7 @@ func TestWalker_newVertex(t *testing.T) { } // Check - expected := []interface{}{1, 2, 3} + expected := []any{1, 2, 3} if !reflect.DeepEqual(order, expected) { t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) } @@ -145,7 +145,7 @@ func TestWalker_removeVertex(t *testing.T) { g.Connect(BasicEdge(1, 2)) // Record function - var order []interface{} + var order []any recordF := walkCbRecord(&order) var w *Walker @@ -168,7 +168,7 @@ func TestWalker_removeVertex(t *testing.T) { } // Check - expected := []interface{}{1} + expected := []any{1} if !reflect.DeepEqual(order, expected) { t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) } @@ -181,7 +181,7 @@ func TestWalker_newEdge(t *testing.T) { g.Connect(BasicEdge(1, 2)) // Record function - var order []interface{} + var order []any recordF := walkCbRecord(&order) var w *Walker @@ -208,7 +208,7 @@ func TestWalker_newEdge(t *testing.T) { } // Check - expected := []interface{}{1, 3, 2} + expected := []any{1, 3, 2} if !reflect.DeepEqual(order, expected) { t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) } @@ -224,7 +224,7 @@ func TestWalker_removeEdge(t *testing.T) { g.Connect(BasicEdge(3, 2)) // Record function - var order []interface{} + var order []any recordF := walkCbRecord(&order) // The way this works is that our original graph forces @@ -274,14 +274,14 @@ func TestWalker_removeEdge(t *testing.T) { } // Check - expected := []interface{}{1, 2, 3} + expected := []any{1, 2, 3} if !reflect.DeepEqual(order, expected) { t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected) } } // walkCbRecord is a test helper callback that just records the order called. -func walkCbRecord(order *[]interface{}) WalkFunc { +func walkCbRecord(order *[]any) WalkFunc { var l sync.Mutex return func(v Vertex) error { l.Lock() diff --git a/go.mod b/go.mod index 8f609ad..9bec572 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/sourcegraph/tf-dag -go 1.23.4 +go 1.26.4