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 <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 1d3c325..700fde9 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -26,7 +26,6 @@
 	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 @@
 	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 @@
 			`<-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 cb0fe3b..6adef3b 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -310,6 +310,7 @@
 				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 1b40651..4e8544a 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -34,6 +34,9 @@
 				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 39ee6bc..60ac4a3 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -1094,6 +1094,7 @@
 						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 @@
 					}
 					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