Skip to content

Commit

Permalink
go/types: record type information after detecting error
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
stamblerre authored and adonovan committed Jul 12, 2018
1 parent 71d2908 commit 6f8e8e1
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 5 deletions.
22 changes: 20 additions & 2 deletions src/go/types/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/go/types/assignments.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions src/go/types/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/go/types/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 6f8e8e1

Please sign in to comment.