From 2dff1e88eb92a374a989dd38e3402481f498ffa2 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Tue, 29 Dec 2015 19:06:21 -0500 Subject: [PATCH] oracle: referrers: fix crash when loading nonexistent files Files with //line directives may not exist. Print a helpful error instead of panicking when a file fails to load. + Test. Fixes issue #13459 Change-Id: I305d1380e66e64d20ea84a27c284f53c019fe5e6 Reviewed-on: https://go-review.googlesource.com/18209 Reviewed-by: Michael Matloob --- oracle/referrers.go | 30 +++++++++++++++++------ oracle/testdata/src/referrers/main.go | 8 ++++++ oracle/testdata/src/referrers/main.golden | 4 +++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/oracle/referrers.go b/oracle/referrers.go index 12b03162..65c90bc0 100644 --- a/oracle/referrers.go +++ b/oracle/referrers.go @@ -168,29 +168,33 @@ func (r *referrersResult) display(printf printfFunc) { // Show referring lines, like grep. type fileinfo struct { refs []*ast.Ident - linenums []int // line number of refs[i] - data chan []byte // file contents + linenums []int // line number of refs[i] + data chan interface{} // file contents or error } var fileinfos []*fileinfo fileinfosByName := make(map[string]*fileinfo) // First pass: start the file reads concurrently. + sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency for _, ref := range r.refs { posn := r.qpos.fset.Position(ref.Pos()) fi := fileinfosByName[posn.Filename] if fi == nil { - fi = &fileinfo{data: make(chan []byte)} + fi = &fileinfo{data: make(chan interface{})} fileinfosByName[posn.Filename] = fi fileinfos = append(fileinfos, fi) // First request for this file: // start asynchronous read. go func() { + sema <- struct{}{} // acquire token content, err := ioutil.ReadFile(posn.Filename) + <-sema // release token if err != nil { - content = []byte(fmt.Sprintf("error: %v", err)) + fi.data <- err + } else { + fi.data <- content } - fi.data <- content }() } fi.refs = append(fi.refs, ref) @@ -200,8 +204,20 @@ func (r *referrersResult) display(printf printfFunc) { // Second pass: print refs in original order. // One line may have several refs at different columns. for _, fi := range fileinfos { - content := <-fi.data // wait for I/O completion - lines := bytes.Split(content, []byte("\n")) + v := <-fi.data // wait for I/O completion + + // Print one item for all refs in a file that could not + // be loaded (perhaps due to //line directives). + if err, ok := v.(error); ok { + var suffix string + if more := len(fi.refs) - 1; more > 0 { + suffix = fmt.Sprintf(" (+ %d more refs in this file)", more) + } + printf(fi.refs[0], "%v%s", err, suffix) + continue + } + + lines := bytes.Split(v.([]byte), []byte("\n")) for i, ref := range fi.refs { printf(ref, "%s", lines[fi.linenums[i]-1]) } diff --git a/oracle/testdata/src/referrers/main.go b/oracle/testdata/src/referrers/main.go index 36cdb7a1..fef571ae 100644 --- a/oracle/testdata/src/referrers/main.go +++ b/oracle/testdata/src/referrers/main.go @@ -24,3 +24,11 @@ func main() { var s2 s s2.f = 1 } + +// Test //line directives: + +type U int // @referrers ref-type-U "U" + +//line nosuchfile.y:123 +var u1 U +var u2 U diff --git a/oracle/testdata/src/referrers/main.golden b/oracle/testdata/src/referrers/main.golden index 1f04be5b..d98d5109 100644 --- a/oracle/testdata/src/referrers/main.golden +++ b/oracle/testdata/src/referrers/main.golden @@ -36,3 +36,7 @@ _ = s{}.f // @referrers ref-field "f" s2.f = 1 +-------- @referrers ref-type-U -------- +2 references to type U int +open testdata/src/referrers/nosuchfile.y: no such file or directory (+ 1 more refs in this file) +