1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements the Check function, which typechecks a package.
23 types map[ast.Expr]Type
26 func (c *checker) errorf(pos token.Pos, format string, args ...interface{}) string {
27 msg := fmt.Sprintf(format, args...)
28 c.Error(c.fset.Position(pos), msg)
32 // collectFields collects struct fields tok = token.STRUCT), interface methods
33 // (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC).
34 func (c *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) {
36 for _, field := range list.List {
38 if t, ok := ftype.(*ast.Ellipsis); ok {
42 typ := c.makeType(ftype, cycleOk)
45 assert(field.Tag.Kind == token.STRING)
46 tag, _ = strconv.Unquote(field.Tag.Value)
48 if len(field.Names) > 0 {
50 for _, name := range field.Names {
53 fields = append(fields, obj)
54 if tok == token.STRUCT {
55 tags = append(tags, tag)
62 tags = append(tags, tag)
65 obj := ast.NewObj(ast.Var, "")
67 fields = append(fields, obj)
69 utyp := Underlying(typ)
70 if typ, ok := utyp.(*Interface); ok {
71 // TODO(gri) This is not good enough. Check for double declarations!
72 fields = append(fields, typ.Methods...)
73 } else if _, ok := utyp.(*Bad); !ok {
74 // if utyp is Bad, don't complain (the root cause was reported before)
75 c.errorf(ftype.Pos(), "interface contains embedded non-interface type")
86 // makeType makes a new type for an AST type specification x or returns
87 // the type referred to by a type name x. If cycleOk is set, a type may
88 // refer to itself directly or indirectly; otherwise cycles are errors.
90 func (c *checker) makeType(x ast.Expr, cycleOk bool) (typ Type) {
92 fmt.Printf("makeType (cycleOk = %v)\n", cycleOk)
95 fmt.Printf("-> %T %v\n\n", typ, typ)
99 switch t := x.(type) {
107 // unresolved identifier (error has been reported before)
108 return &Bad{Msg: "unresolved identifier"}
110 if obj.Kind != ast.Typ {
111 msg := c.errorf(t.Pos(), "%s is not a type", t.Name)
112 return &Bad{Msg: msg}
114 c.checkObj(obj, cycleOk)
115 if !cycleOk && obj.Type.(*Name).Underlying == nil {
116 // TODO(gri) Enable this message again once its position
117 // is independent of the underlying map implementation.
118 // msg := c.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
119 msg := "illegal cycle"
120 return &Bad{Msg: msg}
122 return obj.Type.(Type)
125 return c.makeType(t.X, cycleOk)
127 case *ast.SelectorExpr:
128 // qualified identifier
129 // TODO (gri) eventually, this code belongs to expression
130 // type checking - here for the time being
131 if ident, ok := t.X.(*ast.Ident); ok {
132 if obj := ident.Obj; obj != nil {
133 if obj.Kind != ast.Pkg {
134 msg := c.errorf(ident.Pos(), "%s is not a package", obj.Name)
135 return &Bad{Msg: msg}
137 // TODO(gri) we have a package name but don't
138 // have the mapping from package name to package
139 // scope anymore (created in ast.NewPackage).
140 return &Bad{} // for now
143 // TODO(gri) can this really happen (the parser should have excluded this)?
144 msg := c.errorf(t.Pos(), "expected qualified identifier")
145 return &Bad{Msg: msg}
148 return &Pointer{Base: c.makeType(t.X, true)}
152 // TODO(gri) compute length
153 return &Array{Elt: c.makeType(t.Elt, cycleOk)}
155 return &Slice{Elt: c.makeType(t.Elt, true)}
157 case *ast.StructType:
158 fields, tags, _ := c.collectFields(token.STRUCT, t.Fields, cycleOk)
159 return &Struct{Fields: fields, Tags: tags}
162 params, _, _ := c.collectFields(token.FUNC, t.Params, true)
163 results, _, isVariadic := c.collectFields(token.FUNC, t.Results, true)
164 return &Func{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
166 case *ast.InterfaceType:
167 methods, _, _ := c.collectFields(token.INTERFACE, t.Methods, cycleOk)
169 return &Interface{Methods: methods}
172 return &Map{Key: c.makeType(t.Key, true), Elt: c.makeType(t.Key, true)}
175 return &Chan{Dir: t.Dir, Elt: c.makeType(t.Value, true)}
178 panic(fmt.Sprintf("unreachable (%T)", x))
181 // checkObj type checks an object.
182 func (c *checker) checkObj(obj *ast.Object, ref bool) {
184 // object has already been type checked
193 // TODO(gri) complete this
196 typ := &Name{Obj: obj}
197 obj.Type = typ // "mark" object so recursion terminates
198 typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref))
201 // TODO(gri) complete this
204 // TODO(gri) complete this
211 // Check typechecks a package.
212 // It augments the AST by assigning types to all ast.Objects and returns a map
213 // of types for all expression nodes in statements, and a scanner.ErrorList if
216 func Check(fset *token.FileSet, pkg *ast.Package) (types map[ast.Expr]Type, err os.Error) {
219 c.types = make(map[ast.Expr]Type)
221 for _, obj := range pkg.Scope.Objects {
222 c.checkObj(obj, false)
225 return c.types, c.GetError(scanner.NoMultiples)