1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
// Copyright 2018 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 packages
import (
"cmp"
"fmt"
"iter"
"os"
"slices"
)
// Visit visits all the packages in the import graph whose roots are
// pkgs, calling the optional pre function the first time each package
// is encountered (preorder), and the optional post function after a
// package's dependencies have been visited (postorder).
// The boolean result of pre(pkg) determines whether
// the imports of package pkg are visited.
//
// Example:
//
// pkgs, err := Load(...)
// if err != nil { ... }
// Visit(pkgs, nil, func(pkg *Package) {
// log.Println(pkg)
// })
//
// In most cases, it is more convenient to use [Postorder]:
//
// for pkg := range Postorder(pkgs) {
// log.Println(pkg)
// }
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
seen := make(map[*Package]bool)
var visit func(*Package)
visit = func(pkg *Package) {
if !seen[pkg] {
seen[pkg] = true
if pre == nil || pre(pkg) {
for _, imp := range sorted(pkg.Imports) { // for determinism
visit(imp)
}
}
if post != nil {
post(pkg)
}
}
}
for _, pkg := range pkgs {
visit(pkg)
}
}
// PrintErrors prints to os.Stderr the accumulated errors of all
// packages in the import graph rooted at pkgs, dependencies first.
// PrintErrors returns the number of errors printed.
func PrintErrors(pkgs []*Package) int {
var n int
errModules := make(map[*Module]bool)
for pkg := range Postorder(pkgs) {
for _, err := range pkg.Errors {
fmt.Fprintln(os.Stderr, err)
n++
}
// Print pkg.Module.Error once if present.
mod := pkg.Module
if mod != nil && mod.Error != nil && !errModules[mod] {
errModules[mod] = true
fmt.Fprintln(os.Stderr, mod.Error.Err)
n++
}
}
return n
}
// Postorder returns an iterator over the the packages in
// the import graph whose roots are pkg.
// Packages are enumerated in dependencies-first order.
func Postorder(pkgs []*Package) iter.Seq[*Package] {
return func(yield func(*Package) bool) {
seen := make(map[*Package]bool)
var visit func(*Package) bool
visit = func(pkg *Package) bool {
if !seen[pkg] {
seen[pkg] = true
for _, imp := range sorted(pkg.Imports) { // for determinism
if !visit(imp) {
return false
}
}
if !yield(pkg) {
return false
}
}
return true
}
for _, pkg := range pkgs {
if !visit(pkg) {
break
}
}
}
}
// -- copied from golang.org.x/tools/gopls/internal/util/moremaps --
// sorted returns an iterator over the entries of m in key order.
func sorted[M ~map[K]V, K cmp.Ordered, V any](m M) iter.Seq2[K, V] {
// TODO(adonovan): use maps.Sorted if proposal #68598 is accepted.
return func(yield func(K, V) bool) {
keys := keySlice(m)
slices.Sort(keys)
for _, k := range keys {
if !yield(k, m[k]) {
break
}
}
}
}
// KeySlice returns the keys of the map M, like slices.Collect(maps.Keys(m)).
func keySlice[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
|