// readFunc processes a func or method declaration. // func (r *reader) readFunc(fun *ast.FuncDecl) { // strip function body fun.Body = nil // determine if it should be associated with a type if fun.Recv != nil { // method recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type) if imp { // should not happen (incorrect AST); // don't show this method return } var typ *baseType if r.isVisible(recvTypeName) { // visible recv type: if not found, add it to r.types typ = r.lookupType(recvTypeName) } else { // invisible recv type: if not found, do not add it // (invisible embedded types are added before this // phase, so if the type doesn't exist yet, we don't // care about this method) typ = r.types[recvTypeName] } if typ != nil { // associate method with the type // (if the type is not exported, it may be embedded // somewhere so we need to collect the method anyway) typ.methods.set(fun) } // otherwise don't show the method // TODO(gri): There may be exported methods of non-exported types // that can be called because of exported values (consts, vars, or // function results) of that type. Could determine if that is the // case and then show those methods in an appropriate section. return } // perhaps a factory function // determine result type, if any if fun.Type.Results.NumFields() >= 1 { res := fun.Type.Results.List[0] if len(res.Names) <= 1 { // exactly one (named or anonymous) result associated // with the first type in result signature (there may // be more than one result) if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) { if typ := r.lookupType(n); typ != nil { // associate Func with typ typ.funcs.set(fun) return } } } } // just an ordinary function r.funcs.set(fun) }
func funcDeclToString(decl *ast.FuncDecl) string { var buffer bytes.Buffer var body *ast.BlockStmt body, decl.Body = decl.Body, nil printer.Fprint(&buffer, token.NewFileSet(), decl) decl.Body = body return buffer.String() }
// topLevelNodeToDecl converts an AST node to Go ast.Decl func topLevelNodeToDecl(node *parser.CallNode) ast.Decl { if node.Callee.(*parser.IdentNode).Ident != "defn" { panic("Top level node has to be defn") } switch node.Callee.(*parser.IdentNode).Ident { case "defn": decl := ast.FuncDecl{ Name: &ast.Ident{ NamePos: 2, Name: node.Args[0].(*parser.IdentNode).Ident, }, } fmt.Printf("SHOW ARGS: %+v\n", node.Args[1:]) // @TODO Fix this to support signature properly params := &ast.FieldList{ Opening: 1, Closing: 3, } params.List = make([]*ast.Field, 1) params.List[0] = &ast.Field{ Names: []*ast.Ident{ &ast.Ident{ Name: "lol", }, }, Type: &ast.Ident{ Name: "interface{}", }, } decl.Type = &ast.FuncType{ Func: 1, Params: params, Results: nil, } //decl.Body = nodeFnBody(node.Args[1:]) // TODO: Check argument lengths decl.Body = nodeFnBody(node.Args[1]) return &decl default: // The rest is normal function call probably //return nodeFnCall(node) fmt.Println("got nil %+v", node) return nil } }
// readFunc processes a func or method declaration. // func (r *reader) readFunc(fun *ast.FuncDecl) { // strip function body fun.Body = nil // associate methods with the receiver type, if any if fun.Recv != nil { // method if len(fun.Recv.List) == 0 { // should not happen (incorrect AST); (See issue 17788) // don't show this method return } recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type) if imp { // should not happen (incorrect AST); // don't show this method return } if typ := r.lookupType(recvTypeName); typ != nil { typ.methods.set(fun) } // otherwise ignore the method // TODO(gri): There may be exported methods of non-exported types // that can be called because of exported values (consts, vars, or // function results) of that type. Could determine if that is the // case and then show those methods in an appropriate section. return } // associate factory functions with the first visible result type, if any if fun.Type.Results.NumFields() >= 1 { res := fun.Type.Results.List[0] if len(res.Names) <= 1 { // exactly one (named or anonymous) result associated // with the first type in result signature (there may // be more than one result) if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) { if typ := r.lookupType(n); typ != nil { // associate function with typ typ.funcs.set(fun) return } } } } // just an ordinary function r.funcs.set(fun) }
func newFunc(decl *ast.FuncDecl) *Func { if !ast.IsExported(decl.Name.Name) { return nil } f := new(Func) f.Doc = doc.CommentText(decl.Doc) decl.Doc = nil f.Name = decl.Name.Name if decl.Recv != nil { f.Recv = recvAsString(decl.Recv.List[0].Type) } f.Decl = decl decl.Body = nil // remove body return f }
// oneLineFunc prints a function declaration as a single line. func (pkg *Package) oneLineFunc(decl *ast.FuncDecl) { decl.Doc = nil decl.Body = nil pkg.emit("", decl) }
// Track imports simplifies all imports into a single large package // with mangled names. In the process, it drops functions that are // never referred to. func TrackImports(pkgs map[string](map[string]*ast.File)) (main *ast.File) { // Let's first set of the package we're going to generate... main = new(ast.File) main.Name = ast.NewIdent("main") initstmts := []ast.Stmt{} // this is where we'll stash the init statements... todo := make(map[string]struct{}) todo["main.init"] = struct{}{} todo["main.main"] = struct{}{} done := make(map[string]struct{}) for len(todo) > 0 { for pkgfn := range todo { pkg := splitLast(pkgfn, ".")[0] // FIXME: Need to split after last "." only fn := splitLast(pkgfn, ".")[1] fmt.Println("Working on", fn, "in", pkg) if _, ok := done[pkg+".init"]; !ok && fn != "init" { // We still need to init this package! todo[pkg+".init"] = struct{}{} } // fmt.Println("Working on package", pkg, "function", fn) // We need to look in all this package's files... for _, f := range pkgs[pkg] { // FIXME: it'd be marginally faster to first check if the // function we want is in this particular file. On the other // hand, when there's only one file per package, that would be // slower... // First we'll track down the import declarations... sc := PackageScoping{ Imports: make(map[string]string), Globals: make(map[string]string), } for _, d := range f.Decls { if i, ok := d.(*ast.GenDecl); ok && i.Tok == token.IMPORT { for _, s := range i.Specs { ispec := s.(*ast.ImportSpec) // This should always be okay! path, _ := strconv.Unquote(string(ispec.Path.Value)) name := path if ispec.Name != nil { name = ispec.Name.Name } else { for _, f := range pkgs[path] { name = f.Name.Name } } sc.Imports[name] = path } } else if vdecl, ok := d.(*ast.GenDecl); ok && vdecl.Tok == token.VAR { for _, spec0 := range vdecl.Specs { spec := spec0.(*ast.ValueSpec) for _, n := range spec.Names { sc.Globals[n.Name] = pkg } } } else if tdecl, ok := d.(*ast.GenDecl); ok && vdecl.Tok == token.TYPE { for _, spec0 := range tdecl.Specs { spec := spec0.(*ast.TypeSpec) sc.Globals[spec.Name.Name] = pkg } } else if fdecl, ok := d.(*ast.FuncDecl); ok { sc.Globals[fdecl.Name.Name] = pkg } } // Now we'll go ahead and mangle things... for _, d := range f.Decls { if cdecl, ok := d.(*ast.GenDecl); ok && cdecl.Tok == token.CONST { fmt.Println("FIXME: I don't handle const yet at all... (ignoring)") } else if tdecl, ok := d.(*ast.GenDecl); ok && tdecl.Tok == token.TYPE { for _, spec0 := range tdecl.Specs { spec := spec0.(*ast.TypeSpec) if spec.Name.Name == fn { // fmt.Println("Got type declaration of", spec.Name) spec := *spec spec.Name = ast.NewIdent(fn) spec.Type = sc.MangleExpr(spec.Type) sc.MangleExpr(spec.Name) d := &ast.GenDecl{ Tok: token.TYPE, Specs: []ast.Spec{&spec}, } main.Decls = append(main.Decls, d) } } } else if vdecl, ok := d.(*ast.GenDecl); ok && vdecl.Tok == token.VAR { for _, spec0 := range vdecl.Specs { spec := spec0.(*ast.ValueSpec) for i, n := range spec.Names { if n.Name == fn { // fmt.Println("I got variable", fn) nnew := *n sc.MangleExpr(&nnew) vs := []ast.Expr(nil) if len(spec.Values) > i { vs = append(vs, spec.Values[i]) sc.MangleExpr(spec.Values[i]) } sc.MangleExpr(spec.Type) d := ast.GenDecl{ Tok: token.VAR, Specs: []ast.Spec{ &ast.ValueSpec{ Names: []*ast.Ident{&nnew}, Type: spec.Type, Values: vs, }, }, } main.Decls = append(main.Decls, &d) } } } } else if fdecl, ok := d.(*ast.FuncDecl); ok { if fdecl.Name.Name == fn { // first, let's update the name... but in a copy of the // function declaration fdecl := *fdecl fdecl.Name = ast.NewIdent(pkg + "_" + fn) if fdecl.Type.Params != nil { for _, f := range fdecl.Type.Params.List { sc.MangleExpr(f.Type) } } if fdecl.Type.Results != nil { for _, f := range fdecl.Type.Results.List { sc.MangleExpr(f.Type) } } sc.MangleStatement(fdecl.Body) // fmt.Println("Dumping out", pkg, fn) main.Decls = append(main.Decls, &fdecl) if fn == "init" && fdecl.Recv == nil { initstmts = append(initstmts, &ast.ExprStmt{&ast.CallExpr{Fun: fdecl.Name}}) } } } } // See what else we need to compile... for _, x := range sc.ToDo { if _, done := done[x]; !done { todo[x] = struct{}{} } } } delete(todo, pkgfn) done[pkgfn] = struct{}{} } } // Now we reverse the order, so that the declarations will be in // C-style order, not requiring forward declarations. newdecls := make([]ast.Decl, len(main.Decls)) for i := range main.Decls { newdecls[i] = main.Decls[len(main.Decls)-i-1] } main.Decls = newdecls mainfn := new(ast.FuncDecl) mainfn.Name = ast.NewIdent("main") mainfn.Type = &ast.FuncType{Params: &ast.FieldList{}} initstmts = append(initstmts, &ast.ExprStmt{&ast.CallExpr{Fun: ast.NewIdent("main_main")}}) mainfn.Body = &ast.BlockStmt{List: initstmts} main.Decls = append(main.Decls, mainfn) return main }