func (c *compiler) VisitFuncType(f *ast.FuncType) TypeValue { var fn_type types.Func if f.Params != nil && len(f.Params.List) > 0 { final_param_type := f.Params.List[len(f.Params.List)-1].Type if _, varargs := final_param_type.(*ast.Ellipsis); varargs { fn_type.IsVariadic = true } for i := 0; i < len(f.Params.List); i++ { namecount := len(f.Params.List[i].Names) typ := c.GetType(f.Params.List[i].Type) if namecount == 0 { arg := ast.NewObj(ast.Var, "_") arg.Type = typ fn_type.Params = append(fn_type.Params, arg) } else { args := make([]*ast.Object, namecount) for j := 0; j < namecount; j++ { ident := f.Params.List[i].Names[j] if ident != nil { args[j] = ident.Obj } else { args[j] = ast.NewObj(ast.Var, "_") } args[j].Type = typ } fn_type.Params = append(fn_type.Params, args...) } } } if f.Results != nil { for i := 0; i < len(f.Results.List); i++ { namecount := len(f.Results.List[i].Names) typ := c.GetType(f.Results.List[i].Type) if namecount > 0 { results := make([]*ast.Object, namecount) for j := 0; j < namecount; j++ { ident := f.Results.List[i].Names[j] if ident != nil { results[j] = ident.Obj } else { results[j] = ast.NewObj(ast.Var, "_") } results[j].Type = typ } fn_type.Results = append(fn_type.Results, results...) } else { result := ast.NewObj(ast.Var, "_") result.Type = typ fn_type.Results = append(fn_type.Results, result) } } } return TypeValue{&fn_type} }
// MethodOrEmbedSpec = Name [ Signature ] . // func (p *gcParser) parseMethodOrEmbedSpec() *ast.Object { name := p.parseName() if p.tok == '(' { obj := ast.NewObj(ast.Fun, name) obj.Type = p.parseSignature() return obj } // TODO lookup name and return that type return ast.NewObj(ast.Typ, "_") }
// MethodOrEmbedSpec = Name [ Signature ] . // func (p *gcParser) parseMethodOrEmbedSpec() *ast.Object { p.parseName() if p.tok == '(' { p.parseSignature() // TODO(gri) compute method object return ast.NewObj(ast.Fun, "_") } // TODO lookup name and return that type return ast.NewObj(ast.Typ, "_") }
func (p *parser) findIdent() *ast.Ident { pos := p.pos name := "_" var obj *ast.Object if p.tok == token.IDENT { name = string(p.lit) obj = p.funcScope.Lookup(name) p.next() } else { p.expect(token.IDENT) // use expect() error handling } if obj == nil { // No declaration found: either we are outside any function // (p.funcScope == nil) or the identifier is not declared // in any function. Try the file and package scope. obj = p.fileScope.Lookup(name) // file scope is nested in package scope if obj == nil { // No declaration found anywhere: track as // unresolved identifier in the package scope. obj = ast.NewObj(ast.Err, pos, name) p.pkgScope.Declare(obj) } } return &ast.Ident{pos, obj} }
func (p *parser) declareShortVar(decl *ast.AssignStmt, list []ast.Expr) { // Go spec: A short variable declaration may redeclare variables // provided they were originally declared in the same block with // the same type, and at least one of the non-blank variables is new. n := 0 // number of new variables for _, x := range list { if ident, isIdent := x.(*ast.Ident); isIdent { assert(ident.Obj == nil, "identifier already declared or resolved") obj := ast.NewObj(ast.Var, ident.Name) // remember corresponding assignment for other tools obj.Decl = decl ident.Obj = obj if ident.Name != "_" { if alt := p.topScope.Insert(obj); alt != nil { ident.Obj = alt // redeclaration } else { n++ // new declaration } } } else { p.errorExpected(x.Pos(), "identifier on left side of :=") } } if n == 0 && p.mode&DeclarationErrors != 0 { p.error(list[0].Pos(), "no new variables on left side of :=") } }
// MethodDecl = "func" Receiver Name Signature . // Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" [ FuncBody ]. // func (p *gcParser) parseMethodDecl() { // "func" already consumed p.expect('(') recv, _ := p.parseParameter() // receiver p.expect(')') // unexported method names in imports are qualified with their package. fn := ast.NewObj(ast.Fun, p.parseName()) fnType := p.parseSignature() fnType.Recv = recv fn.Type = fnType var recvType *Name if ptr, isptr := recv.Type.(*Pointer); isptr { recvType = ptr.Base.(*Name) } else { recvType = recv.Type.(*Name) } recvType.Methods = append(recvType.Methods, fn) recvType.Methods.Sort() if p.tok == '{' { p.parseFuncBody() } }
// Declare inserts a named object of the given kind in scope. func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast.Object { // a type may have been declared before - if it exists // already in the respective package scope, return that // type if kind == ast.Typ { if obj := scope.Lookup(name); obj != nil { assert(obj.Kind == ast.Typ) return obj } } // any other object must be a newly declared object - // create it and insert it into the package scope obj := ast.NewObj(kind, name) if scope.Insert(obj) != nil { p.errorf("already declared: %v %s", kind, obj.Name) } // a new type object is a named type and may be referred // to before the underlying type is known - set it up if kind == ast.Typ { obj.Type = &Name{Obj: obj} } return obj }
// collectMethods collects the method declarations from an AST File and // returns a mapping from receiver types to their method FuncDecl's. func (c *checker) collectMethods(file *ast.File) { for _, decl := range file.Decls { if funcdecl, ok := decl.(*ast.FuncDecl); ok && funcdecl.Recv != nil { recvField := funcdecl.Recv.List[0] var recv *ast.Ident switch typ := recvField.Type.(type) { case *ast.StarExpr: recv = typ.X.(*ast.Ident) case *ast.Ident: recv = typ case *ast.BadExpr: return } if recv.Obj == nil { // error reported elsewhere. return } if recv.Obj.Kind != ast.Typ { c.errorf(recv.Pos(), "%s is not a type", recv.Name) return } // The Obj field of the funcdecl wll be nil, so we'll have to // create a new one. funcdecl.Name.Obj = ast.NewObj(ast.Fun, funcdecl.Name.String()) funcdecl.Name.Obj.Decl = funcdecl c.methods[recv.Obj] = append(c.methods[recv.Obj], funcdecl.Name.Obj) } } }
func define(kind ast.ObjKind, name string) *ast.Object { obj := ast.NewObj(kind, name) if scope.Insert(obj) != nil { panic("types internal error: double declaration") } return obj }
func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { pkg := imports[path] if pkg == nil { // Guess the package name without importing it. Start with the last // element of the path. name := path[strings.LastIndex(path, "/")+1:] // Trim commonly used prefixes and suffixes containing illegal name // runes. name = strings.TrimSuffix(name, ".go") name = strings.TrimSuffix(name, "-go") name = strings.TrimPrefix(name, "go.") name = strings.TrimPrefix(name, "go-") name = strings.TrimPrefix(name, "biogo.") // It's also common for the last element of the path to contain an // extra "go" prefix, but not always. TODO: examine unresolved ids to // detect when trimming the "go" prefix is appropriate. pkg = ast.NewObj(ast.Pkg, name) pkg.Data = ast.NewScope(nil) imports[path] = pkg } return pkg, nil }
// ImportPath = string_lit . // func (p *gcParser) parsePkgId() *ast.Object { id, err := strconv.Unquote(p.expect(scanner.String)) if err != nil { p.error(err) } switch id { case "": // id == "" stands for the imported package id // (only known at time of package installation) id = p.id case "unsafe": // package unsafe is not in the imports map - handle explicitly return Unsafe } pkg := p.imports[id] if pkg == nil { scope = ast.NewScope(nil) pkg = ast.NewObj(ast.Pkg, "") pkg.Data = scope p.imports[id] = pkg } return pkg }
// ExportedName = ImportPath "." dotIdentifier . // func (p *gcParser) parseExportedName(kind ast.ObjKind) *ast.Object { pkg := p.parsePkgId() p.expect('.') name := p.parseDotIdent() // a type may have been declared before - if it exists // already in the respective package scope, return that // type scope := pkg.Data.(*ast.Scope) if kind == ast.Typ { if obj := scope.Lookup(name); obj != nil { assert(obj.Kind == ast.Typ) return obj } } // any other object must be a newly declared object - // create it and insert it into the package scope obj := ast.NewObj(kind, name) if scope.Insert(obj) != nil { p.errorf("already declared: %s", obj.Name) } // a new type object is a named type and may be referred // to before the underlying type is known - set it up if kind == ast.Typ { obj.Type = &Name{Obj: obj} } return obj }
func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec { if p.trace { defer un(trace(p, "ImportSpec")) } var ident *ast.Ident if p.tok == token.PERIOD { ident = &ast.Ident{p.pos, ast.NewObj(ast.Pkg, p.pos, ".")} p.next() } else if p.tok == token.IDENT { ident = p.parseIdent(ast.Pkg) // TODO(gri) Make sure the ident is not already declared in the // package scope. Also, cannot add the same name to // the package scope later. p.declIdent(p.fileScope, ident) } var path *ast.BasicLit if p.tok == token.STRING { path = &ast.BasicLit{p.pos, p.tok, p.lit} p.next() } else { p.expect(token.STRING) // use expect() error handling } p.expectSemi() return &ast.ImportSpec{doc, ident, path, p.lineComment} }
// Export = "PackageClause { Decl } "$$" . // PackageClause = "package" identifier [ "safe" ] "\n" . // func (p *gcParser) parseExport() *ast.Object { p.expectKeyword("package") name := p.expect(scanner.Ident) if p.tok != '\n' { // A package is safe if it was compiled with the -u flag, // which disables the unsafe package. // TODO(gri) remember "safe" package p.expectKeyword("safe") } p.expect('\n') assert(p.imports[p.id] == nil) pkg := ast.NewObj(ast.Pkg, name) pkg.Data = ast.NewScope(nil) p.imports[p.id] = pkg for p.tok != '$' && p.tok != scanner.EOF { p.parseDecl() } if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { // don't call next()/expect() since reading past the // export data may cause scanner errors (e.g. NUL chars) p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) } if n := p.scanner.ErrorCount; n != 0 { p.errorf("expected no scanner errors, got %d", n) } return pkg }
func init() { scope = ast.NewScope(nil) Universe = scope Bool = defType("bool") defType("byte") // TODO(gri) should be an alias for uint8 defType("complex64") Complex128 = defType("complex128") defType("float32") Float64 = defType("float64") defType("int8") defType("int16") defType("int32") defType("int64") String = defType("string") defType("uint8") defType("uint16") defType("uint32") defType("uint64") Int = defType("int") defType("uint") defType("uintptr") defConst("true") defConst("false") defConst("iota") defConst("nil") defFun("append") defFun("cap") defFun("close") defFun("complex") defFun("copy") defFun("delete") defFun("imag") defFun("len") defFun("make") defFun("new") defFun("panic") defFun("print") defFun("println") defFun("real") defFun("recover") scope = ast.NewScope(nil) Unsafe = ast.NewObj(ast.Pkg, "unsafe") Unsafe.Data = scope defType("Pointer") defFun("Alignof") defFun("New") defFun("NewArray") defFun("Offsetof") defFun("Reflect") defFun("Sizeof") defFun("Typeof") defFun("Unreflect") }
// findField returns the object with the given name if visible in the type's scope. // If no such object is found, an error is reported and a bad object is returned instead. func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) { // TODO(gri) This is simplistic at the moment and ignores anonymous fields. obj = typ.Scope.Lookup(name.Name) if obj == nil { tc.Errorf(name.Pos(), "%s not declared", name.Name) obj = ast.NewObj(ast.Bad, name.Name) } return }
func (p *parser) parseIdent(kind ast.ObjKind) *ast.Ident { obj := ast.NewObj(kind, p.pos, "_") if p.tok == token.IDENT { obj.Name = string(p.lit) p.next() } else { p.expect(token.IDENT) // use expect() error handling } return &ast.Ident{obj.Pos, obj} }
func importer(imports map[string]*ast.Object, path string) (*ast.Object, error) { pkg := imports[path] if pkg == nil { name := path[strings.LastIndex(path, "/")+1:] pkg = ast.NewObj(ast.Pkg, name) pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import imports[path] = pkg } return pkg, nil }
// poorMansImporter returns a (dummy) package object named // by the last path component of the provided package path // (as is the convention for packages). This is sufficient // to resolve package identifiers without doing an actual // import. It never returns an error. // func poorMansImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { pkg := imports[path] if pkg == nil { // note that strings.LastIndex returns -1 if there is no "/" pkg = ast.NewObj(ast.Pkg, path[strings.LastIndex(path, "/")+1:]) pkg.Data = ast.NewScope(nil) // required by ast.NewPackage for dot-import imports[path] = pkg } return pkg, nil }
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields. // It returns the newly allocated object. If an object with the same name already exists in scope, an error // is reported and the object is not inserted. // (Objects with _ name are always inserted into a scope without errors, but they cannot be found.) func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object { obj := ast.NewObj(kind, name.Name) obj.Decl = decl obj.N = n name.Obj = obj if alt := scope.Insert(obj); alt != obj { tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt)) } return obj }
// 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) if isVariadic { typ = &Slice{Elt: typ} } 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 }
// find returns the object with the given name if visible in the current scope hierarchy. // If no such object is found, an error is reported and a bad object is returned instead. func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) { for s := tc.topScope; s != nil && obj == nil; s = s.Outer { obj = s.Lookup(name.Name) } if obj == nil { tc.Errorf(name.Pos(), "%s not declared", name.Name) obj = ast.NewObj(ast.Bad, name.Name) } name.Obj = obj return }
// MethodSpec = ( identifier | ExportedName ) Signature . // func (p *gcParser) parseMethodSpec() *ast.Object { if p.tok == scanner.Ident { p.expect(scanner.Ident) } else { p.parseExportedName() } p.parseSignature() // TODO(gri) compute method object return ast.NewObj(ast.Fun, "_") }
func init() { // Universe scope Universe = ast.NewScope(nil) // unsafe package and its scope unsafe = ast.NewScope(nil) Unsafe = ast.NewObj(ast.Pkg, "unsafe") Unsafe.Data = unsafe // predeclared types for _, t := range Typ { def(ast.Typ, t.Name).Type = t } for _, t := range aliases { def(ast.Typ, t.Name).Type = t } // error type { res := ast.NewObj(ast.Var, "") res.Type = Typ[String] err := ast.NewObj(ast.Fun, "Error") err.Type = &Signature{Results: ObjList{res}} obj := def(ast.Typ, "error") obj.Type = &NamedType{Underlying: &Interface{Methods: ObjList{err}}, Obj: obj} } // predeclared constants for _, t := range predeclaredConstants { obj := def(ast.Con, t.name) obj.Type = Typ[t.kind] obj.Data = t.val } // predeclared functions for _, f := range predeclaredFunctions { def(ast.Fun, f.name).Type = f } universeIota = Universe.Lookup("iota") }
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields. // It returns the newly allocated object. If an object with the same name already exists in scope, an error // is reported and the object is not inserted. func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object { obj := ast.NewObj(kind, name.Name) obj.Decl = decl //obj.N = n name.Obj = obj if name.Name != "_" { if alt := scope.Insert(obj); alt != nil { tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String()) } } return obj }
func (v *refsSaver) importer() ast.Importer { return func(imports map[string]*ast.Object, pkgPath string) (*ast.Object, error) { if pkg, exists := imports[pkgPath]; exists { return pkg, nil } if !strings.HasPrefix(pkgPath, v.pkgPrefix) { return nil, errors.New("ignored") } pkg := ast.NewObj(ast.Pkg, path.Base(pkgPath)) imports[pkgPath] = pkg return pkg, nil } }
func (p *parser) makeIdentList(list *vector.Vector) []*ast.Ident { idents := make([]*ast.Ident, list.Len()) for i := 0; i < list.Len(); i++ { ident, isIdent := list.At(i).(*ast.Ident) if !isIdent { pos := list.At(i).(ast.Expr).Pos() p.errorExpected(pos, "identifier") idents[i] = &ast.Ident{pos, ast.NewObj(ast.Err, pos, "")} } idents[i] = ident } return idents }
func (c *compiler) VisitStructType(s *ast.StructType) TypeValue { var typ = new(types.Struct) if s.Fields != nil && s.Fields.List != nil { tags := make(map[*ast.Object]string) var i int = 0 for _, field := range s.Fields.List { fieldtype := c.GetType(field.Type) if field.Names != nil { for _, name := range field.Names { obj := name.Obj if obj == nil { obj = ast.NewObj(ast.Var, "_") } obj.Type = fieldtype typ.Fields = append(typ.Fields, obj) if field.Tag != nil { tags[obj] = field.Tag.Value } } i += len(field.Names) } else { obj := ast.NewObj(ast.Var, "_") obj.Type = fieldtype typ.Fields = append(typ.Fields, obj) if field.Tag != nil { tags[obj] = field.Tag.Value } i++ } } typ.Tags = make([]string, len(typ.Fields)) for i, field := range typ.Fields { // TODO unquote string? typ.Tags[i] = tags[field] } } return TypeValue{typ} }
func (p *parser) makeIdentList(list *vector.Vector) []*ast.Ident { idents := make([]*ast.Ident, len(*list)) for i, x := range *list { ident, isIdent := x.(*ast.Ident) if !isIdent { pos := x.(ast.Expr).Pos() p.errorExpected(pos, "identifier") ident = &ast.Ident{pos, ast.NewObj(ast.Err, pos, "_")} } idents[i] = ident } return idents }
// NewTestFuncDecl creates a new FuncDecl for starndard testing // without position. func NewTestFuncDecl(name string) *ast.FuncDecl { ident := ast.NewIdent(name) ident.Obj = ast.NewObj(ast.Fun, name) identVarT := ast.NewIdent("t") identVarT.Obj = ast.NewObj(ast.Var, "t") // params are params for func params := &ast.FieldList{ List: []*ast.Field{ { // t Names: []*ast.Ident{ identVarT, }, // *testing.T Type: &ast.StarExpr{ X: &ast.SelectorExpr{ X: ast.NewIdent("testing"), Sel: ast.NewIdent("T"), }, }, }, }, } funcType := &ast.FuncType{ Params: params, } return &ast.FuncDecl{ Name: ident, Type: funcType, Body: &ast.BlockStmt{}, } }