func (tc *typechecker) resolve(obj *ast.Object) { // check for declaration cycles if tc.cyclemap[obj] { tc.Errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name) obj.Kind = ast.Bad return } tc.cyclemap[obj] = true defer func() { tc.cyclemap[obj] = false, false }() // resolve non-type objects typ, _ := obj.Type.(*Type) if typ == nil { switch obj.Kind { case ast.Bad: // ignore case ast.Con: tc.declConst(obj) case ast.Var: tc.declVar(obj) obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false) case ast.Fun: obj.Type = NewType(Function) t := obj.Decl.(*ast.FuncDecl).Type tc.declSignature(obj.Type.(*Type), nil, t.Params, t.Results) default: // type objects have non-nil types when resolve is called if debug { fmt.Printf("kind = %s\n", obj.Kind) } panic("unreachable") } return } // resolve type objects if typ.Form == Unresolved { tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false) // provide types for all methods for _, obj := range typ.Scope.Objects { if obj.Kind == ast.Fun { assert(obj.Type == nil) obj.Type = NewType(Method) f := obj.Decl.(*ast.FuncDecl) t := f.Type tc.declSignature(obj.Type.(*Type), f.Recv, t.Params, t.Results) } } } }
func objDoc(fset *token.FileSet, pkg *ast.Package, tabIndent bool, tabWidth int, obj *ast.Object) *Doc { decl := obj.Decl kind := obj.Kind.String() tp := fset.Position(obj.Pos()) objSrc := "" pkgName := "" if pkg != nil && pkg.Name != "builtin" { pkgName = pkg.Name } if obj.Kind == ast.Pkg { pkgName = "" doc := "" // special-case `package name` is generated as a TypeSpec if v, ok := obj.Decl.(*ast.TypeSpec); ok && v.Doc != nil { doc = "/*\n" + v.Doc.Text() + "\n*/\n" } objSrc = doc + "package " + obj.Name } else if af, ok := pkg.Files[tp.Filename]; ok { switch decl.(type) { case *ast.TypeSpec, *ast.ValueSpec, *ast.Field: line := tp.Line - 1 for _, cg := range af.Comments { cgp := fset.Position(cg.End()) if cgp.Filename == tp.Filename && cgp.Line == line { switch v := decl.(type) { case *ast.TypeSpec: v.Doc = cg case *ast.ValueSpec: v.Doc = cg case *ast.Field: pkgName = "" kind = "field" } break } } } } if objSrc == "" { objSrc, _ = printSrc(fset, decl, tabIndent, tabWidth) } return &Doc{ Src: objSrc, Pkg: pkgName, Name: obj.Name, Kind: kind, Fn: tp.Filename, Row: tp.Line - 1, Col: tp.Column - 1, } }
// object typechecks an object by assigning it a type; obj.Type must be nil. // Callers must check obj.Type before calling object; this eliminates a call // for each identifier that has been typechecked already, a common scenario. // func (check *checker) object(obj *ast.Object, cycleOk bool) { assert(obj.Type == nil) switch obj.Kind { case ast.Bad, ast.Pkg: // nothing to do case ast.Con, ast.Var: // The obj.Data field for constants and variables is initialized // to the respective (hypothetical, for variables) iota value by // the parser. The object's fields can be in one of the following // states: // Type != nil => the constant value is Data // Type == nil => the object is not typechecked yet, and Data can be: // Data is int => Data is the value of iota for this declaration // Data == nil => the object's expression is being evaluated if obj.Data == nil { check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) obj.Type = Typ[Invalid] return } spec := obj.Decl.(*ast.ValueSpec) iota := obj.Data.(int) obj.Data = nil // determine initialization expressions values := spec.Values if len(values) == 0 && obj.Kind == ast.Con { values = check.initexprs[spec] } check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota) case ast.Typ: typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) // typecheck associated method signatures if obj.Data != nil { scope := obj.Data.(*ast.Scope) switch t := typ.Underlying.(type) { case *Struct: // struct fields must not conflict with methods for _, f := range t.Fields { if m := scope.Lookup(f.Name); m != nil { check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name) // ok to continue } } case *Interface: // methods cannot be associated with an interface type for _, m := range scope.Objects { recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) // ok to continue } } // typecheck method signatures for _, obj := range scope.Objects { mdecl := obj.Decl.(*ast.FuncDecl) sig := check.typ(mdecl.Type, cycleOk).(*Signature) params, _ := check.collectParams(mdecl.Recv, false) sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter obj.Type = sig check.later(obj, sig, mdecl.Body) } } case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) // methods are typechecked when their receivers are typechecked if fdecl.Recv == nil { sig := check.typ(fdecl.Type, cycleOk).(*Signature) if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) { check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") // ok to continue } obj.Type = sig check.later(obj, sig, fdecl.Body) } default: panic("unreachable") } }
// object typechecks an object by assigning it a type; obj.Type must be nil. // Callers must check obj.Type before calling object; this eliminates a call // for each identifier that has been typechecked already, a common scenario. // func (check *checker) object(obj *ast.Object, cycleOk bool) { assert(obj.Type == nil) switch obj.Kind { case ast.Bad, ast.Pkg: // nothing to do case ast.Con, ast.Var: // The obj.Data field for constants and variables is initialized // to the respective (hypothetical, for variables) iota value by // the parser. The object's fields can be in one of the following // states: // Type != nil => the constant value is Data // Type == nil => the object is not typechecked yet, and Data can be: // Data is int => Data is the value of iota for this declaration // Data == nil => the object's expression is being evaluated if obj.Data == nil { check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) obj.Type = Typ[Invalid] return } spec := obj.Decl.(*ast.ValueSpec) iota := obj.Data.(int) obj.Data = nil // determine initialization expressions values := spec.Values if len(values) == 0 && obj.Kind == ast.Con { values = check.initexprs[spec] } check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota) case ast.Typ: typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) // typecheck associated method signatures if obj.Data != nil { scope := obj.Data.(*ast.Scope) switch t := typ.Underlying.(type) { case *Struct: // struct fields must not conflict with methods for _, f := range t.Fields { if m := scope.Lookup(f.Name); m != nil { check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name) } } // ok to continue case *Interface: // methods cannot be associated with an interface type for _, m := range scope.Objects { recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) } // ok to continue } // typecheck method signatures for _, m := range scope.Objects { mdecl := m.Decl.(*ast.FuncDecl) // TODO(gri) At the moment, the receiver is type-checked when checking // the method body. Also, we don't properly track if the receiver is // a pointer (i.e., currently, method sets are too large). FIX THIS. mtyp := check.typ(mdecl.Type, cycleOk).(*Signature) m.Type = mtyp } } case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) if fdecl.Recv != nil { // This will ensure that the method base type is // type-checked check.collectFields(token.FUNC, fdecl.Recv, true) } ftyp := check.typ(fdecl.Type, cycleOk).(*Signature) obj.Type = ftyp check.function(ftyp, fdecl.Body) default: panic("unreachable") } }
// obj type checks an object. func (check *checker) obj(obj *ast.Object, cycleOk bool) { if trace { fmt.Printf("obj(%s)\n", obj.Name) } if obj.Type != nil { // object has already been type checked return } switch obj.Kind { case ast.Bad, ast.Pkg: // nothing to do case ast.Con: if obj.Data == nil { check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name) return } spec, ok := obj.Decl.(*ast.ValueSpec) assert(ok) // The Data stored with the constant is the value of iota for that // ast.ValueSpec. Use it for the evaluation of the initialization // expressions. iota := obj.Data.(int) obj.Data = nil check.decl(spec.Pos(), obj, spec.Names, spec.Type, check.specValues(spec), iota) case ast.Var: // TODO(gri) missing cycle detection spec, ok := obj.Decl.(*ast.ValueSpec) if !ok { // TODO(gri) the assertion fails for "x, y := 1, 2, 3" it seems fmt.Printf("var = %s\n", obj.Name) } assert(ok) check.decl(spec.Pos(), obj, spec.Names, spec.Type, spec.Values, 0) case ast.Typ: typ := &NamedType{Obj: obj} obj.Type = typ // "mark" object so recursion terminates typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk)) // collect associated methods, if any if obj.Data != nil { scope := obj.Data.(*ast.Scope) // struct fields must not conflict with methods if t, ok := typ.Underlying.(*Struct); ok { for _, f := range t.Fields { if m := scope.Lookup(f.Name); m != nil { check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name) } } } // collect methods methods := make(ObjList, len(scope.Objects)) i := 0 for _, m := range scope.Objects { methods[i] = m i++ } methods.Sort() typ.Methods = methods // methods cannot be associated with an interface type // (do this check after sorting for reproducible error positions - needed for testing) if _, ok := typ.Underlying.(*Interface); ok { for _, m := range methods { recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) } } } case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) ftyp := check.typ(fdecl.Type, cycleOk).(*Signature) obj.Type = ftyp if fdecl.Recv != nil { // TODO(gri) handle method receiver } check.stmt(fdecl.Body) default: panic("unreachable") } }