From 6f8e8e14c8c860b7da2c3d97a7c92c4bd57e503b Mon Sep 17 00:00:00 2001 From: Rebecca Stambler Date: Thu, 12 Jul 2018 12:52:18 -0400 Subject: [PATCH] go/types: record type information after detecting error The existing implementation stops recording type information once it encounters an error. This results in missing type information that is needed by various tools. This change handles a few commonly encountered cases by continuing to check subtrees after errors. Also, add tests for cases where the package fails to type-check. Updates #22467 Change-Id: I1bb48d4cb8ae5548dca63bdd785ea2f69329e92b Reviewed-on: https://go-review.googlesource.com/123578 Run-TryBot: Rebecca Stambler TryBot-Result: Gobot Gobot Reviewed-by: Alan Donovan --- src/go/types/api_test.go | 22 ++++++++++++++++++++-- src/go/types/assignments.go | 1 + src/go/types/call.go | 3 +++ src/go/types/expr.go | 6 +++--- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 1d3c32520a..700fde9231 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -26,7 +26,6 @@ func pkgFor(path, source string, info *Info) (*Package, error) { if err != nil { return nil, err } - conf := Config{Importer: importer.Default()} return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) } @@ -43,6 +42,20 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string { return pkg.Name() } +func maybeTypecheck(t *testing.T, path, source string, info *Info) string { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, path, source, 0) + if f == nil { // ignore errors unless f is nil + t.Fatalf("%s: unable to parse: %s", path, err) + } + conf := Config{ + Error: func(err error) {}, + Importer: importer.Default(), + } + pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, info) + return pkg.Name() +} + func TestValuesInfo(t *testing.T) { var tests = []struct { src string @@ -243,11 +256,16 @@ func TestTypesInfo(t *testing.T) { `<-ch`, `(string, bool)`, }, + + // tests for broken code that doesn't parse or type-check + {`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`}, + {`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`}, + {`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`}, } for _, test := range tests { info := Info{Types: make(map[ast.Expr]TypeAndValue)} - name := mustTypecheck(t, "TypesInfo", test.src, &info) + name := maybeTypecheck(t, "TypesInfo", test.src, &info) // look for expression type var typ Type diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index cb0fe3bc3a..6adef3b407 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -310,6 +310,7 @@ func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) { check.recordDef(ident, obj) } } else { + check.expr(&operand{}, lhs) check.errorf(lhs.Pos(), "cannot declare %s", lhs) } if obj == nil { diff --git a/src/go/types/call.go b/src/go/types/call.go index 1b40651b73..4e8544ad88 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -34,6 +34,9 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { check.conversion(x, T) } default: + for _, arg := range e.Args { + check.expr(&operand{}, arg) + } check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T) } x.expr = e diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 39ee6bcca3..60ac4a33ad 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -1094,6 +1094,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { continue } key, _ := kv.Key.(*ast.Ident) + check.expr(x, kv.Value) if key == nil { check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) continue @@ -1105,15 +1106,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { } fld := fields[i] check.recordUse(key, fld) + etyp := fld.typ + check.assignment(x, etyp, "struct literal") // 0 <= i < len(fields) if visited[i] { check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) continue } visited[i] = true - check.expr(x, kv.Value) - etyp := fld.typ - check.assignment(x, etyp, "struct literal") } } else { // no element must have a key