Skip to content

Commit 137268d

Browse files
author
Ryan Hitchman
committed
Deduplicate identical typecheck errors between platforms.
1 parent da44038 commit 137268d

File tree

2 files changed

+107
-2
lines changed

2 files changed

+107
-2
lines changed

test/typecheck/main.go

+71-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"go/build"
2525
"go/parser"
2626
"go/token"
27+
"io"
2728
"log"
2829
"os"
2930
"path/filepath"
@@ -60,6 +61,8 @@ var (
6061
"linux/arm64", "linux/ppc64le",
6162
"linux/s390x", "darwin/386",
6263
}
64+
darwinPlatString = "darwin/386,darwin/amd64"
65+
windowsPlatString = "windows/386,windows/amd64"
6366
)
6467

6568
type analyzer struct {
@@ -69,6 +72,7 @@ type analyzer struct {
6972
failed bool
7073
platform string
7174
donePaths map[string]interface{}
75+
errors []string
7276
}
7377

7478
func newAnalyzer(platform string) *analyzer {
@@ -120,11 +124,19 @@ func (a *analyzer) handleError(err error) {
120124
return
121125
}
122126
}
123-
// TODO(rmmh): dedup errors across platforms?
124-
fmt.Fprintf(os.Stderr, "%sERROR(%s) %s\n", logPrefix, a.platform, err)
127+
a.errors = append(a.errors, err.Error())
128+
if *serial {
129+
fmt.Fprintf(os.Stderr, "%sERROR(%s) %s\n", logPrefix, a.platform, err)
130+
}
125131
a.failed = true
126132
}
127133

134+
func (a *analyzer) dumpAndResetErrors() []string {
135+
es := a.errors
136+
a.errors = nil
137+
return es
138+
}
139+
128140
// collect extracts test metadata from a file.
129141
func (a *analyzer) collect(dir string) {
130142
if _, ok := a.donePaths[dir]; ok {
@@ -262,6 +274,51 @@ func (c *collector) handlePath(path string, info os.FileInfo, err error) error {
262274
return nil
263275
}
264276

277+
type analyzerResult struct {
278+
platform string
279+
dir string
280+
errors []string
281+
}
282+
283+
func dedupeErrors(out io.Writer, results chan analyzerResult, nDirs, nPlatforms int) {
284+
pkgRes := make(map[string][]analyzerResult)
285+
for done := 0; done < nDirs; {
286+
res := <-results
287+
pkgRes[res.dir] = append(pkgRes[res.dir], res)
288+
if len(pkgRes[res.dir]) != nPlatforms {
289+
continue // expect more results for dir
290+
}
291+
done++
292+
// Collect list of platforms for each error
293+
errPlats := map[string][]string{}
294+
for _, res := range pkgRes[res.dir] {
295+
for _, err := range res.errors {
296+
errPlats[err] = append(errPlats[err], res.platform)
297+
}
298+
}
299+
// Print each error (in the same order!) once.
300+
for _, res := range pkgRes[res.dir] {
301+
for _, err := range res.errors {
302+
if errPlats[err] == nil {
303+
continue // already printed
304+
}
305+
sort.Strings(errPlats[err])
306+
plats := strings.Join(errPlats[err], ",")
307+
if len(errPlats[err]) == len(crossPlatforms) {
308+
plats = "all"
309+
} else if plats == darwinPlatString {
310+
plats = "darwin"
311+
} else if plats == windowsPlatString {
312+
plats = "windows"
313+
}
314+
fmt.Fprintf(out, "%sERROR(%s) %s\n", logPrefix, plats, err)
315+
delete(errPlats, err)
316+
}
317+
}
318+
delete(pkgRes, res.dir)
319+
}
320+
}
321+
265322
func main() {
266323
flag.Parse()
267324
args := flag.Args()
@@ -296,6 +353,15 @@ func main() {
296353
var processedDirs int64
297354
var currentWork int64 // (dir_index << 8) | platform_index
298355
statuses := make([]int, len(ps))
356+
var results chan analyzerResult
357+
if !*serial {
358+
results = make(chan analyzerResult)
359+
wg.Add(1)
360+
go func() {
361+
dedupeErrors(os.Stderr, results, len(c.dirs), len(ps))
362+
wg.Done()
363+
}()
364+
}
299365
for i, p := range ps {
300366
wg.Add(1)
301367
fn := func(i int, p string) {
@@ -305,6 +371,9 @@ func main() {
305371
a.collect(dir)
306372
atomic.AddInt64(&processedDirs, 1)
307373
atomic.StoreInt64(&currentWork, int64(n<<8|i))
374+
if results != nil {
375+
results <- analyzerResult{p, dir, a.dumpAndResetErrors()}
376+
}
308377
}
309378
if a.failed {
310379
statuses[i] = 1

test/typecheck/main_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"bytes"
2021
"errors"
2122
"fmt"
2223
"go/ast"
@@ -155,3 +156,38 @@ func TestHandlePath(t *testing.T) {
155156
t.Error("should skip vendor")
156157
}
157158
}
159+
160+
func TestDedupeErrors(t *testing.T) {
161+
testcases := []struct {
162+
nPlatforms int
163+
results []analyzerResult
164+
expected string
165+
}{
166+
{1, []analyzerResult{}, ""},
167+
{1, []analyzerResult{{"linux/arm", "test", nil}}, ""},
168+
{1, []analyzerResult{
169+
{"linux/arm", "test", []string{"a"}}},
170+
"ERROR(linux/arm) a\n"},
171+
{3, []analyzerResult{
172+
{"linux/arm", "test", []string{"a"}},
173+
{"windows/386", "test", []string{"b"}},
174+
{"windows/amd64", "test", []string{"b", "c"}}},
175+
"ERROR(linux/arm) a\n" +
176+
"ERROR(windows) b\n" +
177+
"ERROR(windows/amd64) c\n"},
178+
}
179+
for _, tc := range testcases {
180+
out := &bytes.Buffer{}
181+
results := make(chan analyzerResult, len(tc.results))
182+
for _, res := range tc.results {
183+
results <- res
184+
}
185+
close(results)
186+
dedupeErrors(out, results, len(tc.results)/tc.nPlatforms, tc.nPlatforms)
187+
outString := out.String()
188+
if outString != tc.expected {
189+
t.Errorf("dedupeErrors(%v) = '%s', expected '%s'",
190+
tc.results, outString, tc.expected)
191+
}
192+
}
193+
}

0 commit comments

Comments
 (0)