Commit 6f8e8e14 authored by Rebecca Stambler's avatar Rebecca Stambler Committed by Alan Donovan

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: default avatarAlan Donovan <adonovan@google.com>
parent 71d29086
......@@ -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
......
......@@ -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 {
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment