1
0
mirror of https://github.com/golang/go synced 2024-11-07 15:26:11 -07:00

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>
This commit is contained in:
Rebecca Stambler 2018-07-12 12:52:18 -04:00 committed by Alan Donovan
parent 71d2908648
commit 6f8e8e14c8
4 changed files with 27 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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