]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: record type information after detecting error
authorRebecca Stambler <rstambler@golang.org>
Thu, 12 Jul 2018 16:52:18 +0000 (12:52 -0400)
committerAlan Donovan <adonovan@google.com>
Thu, 12 Jul 2018 22:12:51 +0000 (22:12 +0000)
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>
src/go/types/api_test.go
src/go/types/assignments.go
src/go/types/call.go
src/go/types/expr.go

index 1d3c32520a615b1a5cb9d6ad39d49b49a3b69858..700fde92313c652d838014313ef138be08a51edd 100644 (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
index cb0fe3bc3a775c5600b6c37629ea9931259281e8..6adef3b40707c58c96b8930312668cfd57ef1075 100644 (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 {
index 1b40651b73479eefe62c46bbb91ad0e91714c283..4e8544ad88cd683a1b8cf0c4a94edcc1b939ef2a 100644 (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
index 39ee6bcca3573d92c58ea13faf572414c82051e7..60ac4a33adaf83a14fde584f1bb62f263653bd9a 100644 (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