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 <adonovan@google.com>
This commit is contained in:
Rebecca Stambler 2016-11-17 16:44:22 -05:00 committed by Alan Donovan
parent 4c6345e8dc
commit e64f1a4c63
10 changed files with 78 additions and 6 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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",

View File

@ -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
}

View File

@ -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
}

15
cmd/guru/testdata/src/softerrs/main.go vendored Normal file
View File

@ -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"
}

View File

@ -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

View File

@ -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
}