From e64f1a4c635f3f65c068836f76a56f6d50be7fb7 Mon Sep 17 00:00:00 2001 From: Rebecca Stambler Date: Thu, 17 Nov 2016 16:44:22 -0500 Subject: [PATCH] cmd/guru: add workaround to handle inconsistency between go/types and gc Add identical workaround from gorename to guru. Only affects queries that typecheck the code first. Fixes golang/go#16530 Change-Id: I718cfceb8d26868eea9128c8873b164333c50f53 Reviewed-on: https://go-review.googlesource.com/33359 Reviewed-by: Alan Donovan --- cmd/guru/callees.go | 2 +- cmd/guru/callers.go | 2 +- cmd/guru/callstack.go | 2 +- cmd/guru/guru.go | 48 ++++++++++++++++++++++ cmd/guru/guru_test.go | 1 + cmd/guru/peers.go | 2 +- cmd/guru/pointsto.go | 2 +- cmd/guru/testdata/src/softerrs/main.go | 15 +++++++ cmd/guru/testdata/src/softerrs/main.golden | 8 ++++ cmd/guru/whicherrs.go | 2 +- 10 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 cmd/guru/testdata/src/softerrs/main.go create mode 100644 cmd/guru/testdata/src/softerrs/main.golden diff --git a/cmd/guru/callees.go b/cmd/guru/callees.go index 6c6f70e1..077d7929 100644 --- a/cmd/guru/callees.go +++ b/cmd/guru/callees.go @@ -28,7 +28,7 @@ func callees(q *Query) error { } // Load/parse/type-check the program. - lprog, err := lconf.Load() + lprog, err := loadWithSoftErrors(&lconf) if err != nil { return err } diff --git a/cmd/guru/callers.go b/cmd/guru/callers.go index c02ffa6e..e51b6e50 100644 --- a/cmd/guru/callers.go +++ b/cmd/guru/callers.go @@ -27,7 +27,7 @@ func callers(q *Query) error { } // Load/parse/type-check the program. - lprog, err := lconf.Load() + lprog, err := loadWithSoftErrors(&lconf) if err != nil { return err } diff --git a/cmd/guru/callstack.go b/cmd/guru/callstack.go index a1870723..f1ca6a6c 100644 --- a/cmd/guru/callstack.go +++ b/cmd/guru/callstack.go @@ -35,7 +35,7 @@ func callstack(q *Query) error { } // Load/parse/type-check the program. - lprog, err := lconf.Load() + lprog, err := loadWithSoftErrors(&lconf) if err != nil { return err } diff --git a/cmd/guru/guru.go b/cmd/guru/guru.go index 3dedeb63..b83b7d65 100644 --- a/cmd/guru/guru.go +++ b/cmd/guru/guru.go @@ -21,6 +21,7 @@ import ( "io" "log" "path/filepath" + "strings" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/buildutil" @@ -255,6 +256,53 @@ func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos // ---------- Utilities ---------- +// loadWithSoftErrors calls lconf.Load, suppressing "soft" errors. (See Go issue 16530.) +// TODO(adonovan): Once the loader has an option to allow soft errors, +// replace calls to loadWithSoftErrors with loader calls with that parameter. +func loadWithSoftErrors(lconf *loader.Config) (*loader.Program, error) { + lconf.AllowErrors = true + + // Ideally we would just return conf.Load() here, but go/types + // reports certain "soft" errors that gc does not (Go issue 14596). + // As a workaround, we set AllowErrors=true and then duplicate + // the loader's error checking but allow soft errors. + // It would be nice if the loader API permitted "AllowErrors: soft". + prog, err := lconf.Load() + if err != nil { + return nil, err + } + var errpkgs []string + // Report hard errors in indirectly imported packages. + for _, info := range prog.AllPackages { + if containsHardErrors(info.Errors) { + errpkgs = append(errpkgs, info.Pkg.Path()) + } else { + // Enable SSA construction for packages containing only soft errors. + info.TransitivelyErrorFree = true + } + } + if errpkgs != nil { + var more string + if len(errpkgs) > 3 { + more = fmt.Sprintf(" and %d more", len(errpkgs)-3) + errpkgs = errpkgs[:3] + } + return nil, fmt.Errorf("couldn't load packages due to errors: %s%s", + strings.Join(errpkgs, ", "), more) + } + return prog, err +} + +func containsHardErrors(errors []error) bool { + for _, err := range errors { + if err, ok := err.(types.Error); ok && err.Soft { + continue + } + return true + } + return false +} + // allowErrors causes type errors to be silently ignored. // (Not suitable if SSA construction follows.) func allowErrors(lconf *loader.Config) { diff --git a/cmd/guru/guru_test.go b/cmd/guru/guru_test.go index 62dcd6d3..003183a6 100644 --- a/cmd/guru/guru_test.go +++ b/cmd/guru/guru_test.go @@ -230,6 +230,7 @@ func TestGuru(t *testing.T) { "testdata/src/reflection/main.go", "testdata/src/what/main.go", "testdata/src/whicherrs/main.go", + "testdata/src/softerrs/main.go", // JSON: // TODO(adonovan): most of these are very similar; combine them. "testdata/src/calls-json/main.go", diff --git a/cmd/guru/peers.go b/cmd/guru/peers.go index 35bd3c83..6e138bf0 100644 --- a/cmd/guru/peers.go +++ b/cmd/guru/peers.go @@ -31,7 +31,7 @@ func peers(q *Query) error { } // Load/parse/type-check the program. - lprog, err := lconf.Load() + lprog, err := loadWithSoftErrors(&lconf) if err != nil { return err } diff --git a/cmd/guru/pointsto.go b/cmd/guru/pointsto.go index 90e128c5..782277f3 100644 --- a/cmd/guru/pointsto.go +++ b/cmd/guru/pointsto.go @@ -34,7 +34,7 @@ func pointsto(q *Query) error { } // Load/parse/type-check the program. - lprog, err := lconf.Load() + lprog, err := loadWithSoftErrors(&lconf) if err != nil { return err } diff --git a/cmd/guru/testdata/src/softerrs/main.go b/cmd/guru/testdata/src/softerrs/main.go new file mode 100644 index 00000000..f7254b83 --- /dev/null +++ b/cmd/guru/testdata/src/softerrs/main.go @@ -0,0 +1,15 @@ +package main + +// Tests of various queries on a program containing only "soft" errors. +// See go.tools/guru/guru_test.go for explanation. +// See main.golden for expected query results. + +func _() { + var i int // "unused var" is a soft error +} + +func f() {} // @callers softerrs-callers-f "f" + +func main() { + f() // @describe softerrs-describe-f "f" +} diff --git a/cmd/guru/testdata/src/softerrs/main.golden b/cmd/guru/testdata/src/softerrs/main.golden new file mode 100644 index 00000000..ae95f46d --- /dev/null +++ b/cmd/guru/testdata/src/softerrs/main.golden @@ -0,0 +1,8 @@ +-------- @callers softerrs-callers-f -------- +softerrs.f is called from these 1 sites: + static function call from softerrs.main + +-------- @describe softerrs-describe-f -------- +reference to func f() +defined here + diff --git a/cmd/guru/whicherrs.go b/cmd/guru/whicherrs.go index 7cc7e5f8..37073c1d 100644 --- a/cmd/guru/whicherrs.go +++ b/cmd/guru/whicherrs.go @@ -37,7 +37,7 @@ func whicherrs(q *Query) error { } // Load/parse/type-check the program. - lprog, err := lconf.Load() + lprog, err := loadWithSoftErrors(&lconf) if err != nil { return err }