// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This file implements the Check function, which typechecks a package. package types import ( "fmt" "go/ast" "go/scanner" "go/token" "strconv" ) const debug = false type checker struct { fset *token.FileSet scanner.ErrorVector types map[ast.Expr]Type } func (c *checker) errorf(pos token.Pos, format string, args ...interface{}) string { msg := fmt.Sprintf(format, args...) c.Error(c.fset.Position(pos), msg) return msg } // collectFields collects struct fields tok = token.STRUCT), interface methods // (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC). func (c *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) { if list != nil { for _, field := range list.List { ftype := field.Type if t, ok := ftype.(*ast.Ellipsis); ok { ftype = t.Elt isVariadic = true } typ := c.makeType(ftype, cycleOk) tag := "" if field.Tag != nil { assert(field.Tag.Kind == token.STRING) tag, _ = strconv.Unquote(field.Tag.Value) } if len(field.Names) > 0 { // named fields for _, name := range field.Names { obj := name.Obj obj.Type = typ fields = append(fields, obj) if tok == token.STRUCT { tags = append(tags, tag) } } } else { // anonymous field switch tok { case token.STRUCT: tags = append(tags, tag) fallthrough case token.FUNC: obj := ast.NewObj(ast.Var, "") obj.Type = typ fields = append(fields, obj) case token.INTERFACE: utyp := Underlying(typ) if typ, ok := utyp.(*Interface); ok { // TODO(gri) This is not good enough. Check for double declarations! fields = append(fields, typ.Methods...) } else if _, ok := utyp.(*Bad); !ok { // if utyp is Bad, don't complain (the root cause was reported before) c.errorf(ftype.Pos(), "interface contains embedded non-interface type") } default: panic("unreachable") } } } } return } // makeType makes a new type for an AST type specification x or returns // the type referred to by a type name x. If cycleOk is set, a type may // refer to itself directly or indirectly; otherwise cycles are errors. // func (c *checker) makeType(x ast.Expr, cycleOk bool) (typ Type) { if debug { fmt.Printf("makeType (cycleOk = %v)\n", cycleOk) ast.Print(c.fset, x) defer func() { fmt.Printf("-> %T %v\n\n", typ, typ) }() } switch t := x.(type) { case *ast.BadExpr: return &Bad{} case *ast.Ident: // type name obj := t.Obj if obj == nil { // unresolved identifier (error has been reported before) return &Bad{Msg: "unresolved identifier"} } if obj.Kind != ast.Typ { msg := c.errorf(t.Pos(), "%s is not a type", t.Name) return &Bad{Msg: msg} } c.checkObj(obj, cycleOk) if !cycleOk && obj.Type.(*Name).Underlying == nil { // TODO(gri) Enable this message again once its position // is independent of the underlying map implementation. // msg := c.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name) msg := "illegal cycle" return &Bad{Msg: msg} } return obj.Type.(Type) case *ast.ParenExpr: return c.makeType(t.X, cycleOk) case *ast.SelectorExpr: // qualified identifier // TODO (gri) eventually, this code belongs to expression // type checking - here for the time being if ident, ok := t.X.(*ast.Ident); ok { if obj := ident.Obj; obj != nil { if obj.Kind != ast.Pkg { msg := c.errorf(ident.Pos(), "%s is not a package", obj.Name) return &Bad{Msg: msg} } // TODO(gri) we have a package name but don't // have the mapping from package name to package // scope anymore (created in ast.NewPackage). return &Bad{} // for now } } // TODO(gri) can this really happen (the parser should have excluded this)? msg := c.errorf(t.Pos(), "expected qualified identifier") return &Bad{Msg: msg} case *ast.StarExpr: return &Pointer{Base: c.makeType(t.X, true)} case *ast.ArrayType: if t.Len != nil { // TODO(gri) compute length return &Array{Elt: c.makeType(t.Elt, cycleOk)} } return &Slice{Elt: c.makeType(t.Elt, true)} case *ast.StructType: fields, tags, _ := c.collectFields(token.STRUCT, t.Fields, cycleOk) return &Struct{Fields: fields, Tags: tags} case *ast.FuncType: params, _, _ := c.collectFields(token.FUNC, t.Params, true) results, _, isVariadic := c.collectFields(token.FUNC, t.Results, true) return &Func{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} case *ast.InterfaceType: methods, _, _ := c.collectFields(token.INTERFACE, t.Methods, cycleOk) methods.Sort() return &Interface{Methods: methods} case *ast.MapType: return &Map{Key: c.makeType(t.Key, true), Elt: c.makeType(t.Key, true)} case *ast.ChanType: return &Chan{Dir: t.Dir, Elt: c.makeType(t.Value, true)} } panic(fmt.Sprintf("unreachable (%T)", x)) } // checkObj type checks an object. func (c *checker) checkObj(obj *ast.Object, ref bool) { if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad: // ignore case ast.Con: // TODO(gri) complete this case ast.Typ: typ := &Name{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) case ast.Var: // TODO(gri) complete this case ast.Fun: // TODO(gri) complete this default: panic("unreachable") } } // Check typechecks a package. // It augments the AST by assigning types to all ast.Objects and returns a map // of types for all expression nodes in statements, and a scanner.ErrorList if // there are errors. // func Check(fset *token.FileSet, pkg *ast.Package) (types map[ast.Expr]Type, err error) { var c checker c.fset = fset c.types = make(map[ast.Expr]Type) for _, obj := range pkg.Scope.Objects { c.checkObj(obj, false) } return c.types, c.GetError(scanner.NoMultiples) }