func (p *Parser) Parse(path string, cursor SourceLocation, unsaved []UnsavedDocument, options Options) (*Parsed, error) { if len(options.GoPath) == 0 { options.GoPath = os.Getenv("GOPATH") } path, uns := p.canonicalPaths(path, unsaved) var info types.Info info.Types = make(map[ast.Expr]types.Type) info.Objects = make(map[*ast.Ident]types.Object) fs := token.NewFileSet() started := time.Now() dname := filepath.Dir(path) f, astf, tpkg, err := p.importSourcePackage(fs, path, dname, uns, options, &info) errors, _ := err.(scanner.ErrorList) return &Parsed{ Info: &info, Package: tpkg, FileSet: fs, Ast: astf, File: f, Errors: errors, Duration: time.Now().Sub(started), }, nil }
// errorOrVoid filters the list of functions to only those that return only an // error or have no return value. func errorOrVoid(fns []*ast.FuncDecl, info types.Info) []*ast.FuncDecl { fds := []*ast.FuncDecl{} for _, fn := range fns { // look for functions with 0 or 1 return values res := fn.Type.Results if res.NumFields() > 1 { continue } // 0 return value is ok if res.NumFields() == 0 { fds = append(fds, fn) continue } // if 1 return value, look for those that return an error ret := res.List[0] // handle (a, b, c int) if len(ret.Names) > 1 { continue } t := info.TypeOf(ret.Type) if t != nil && t.String() == "error" { fds = append(fds, fn) } } return fds }
func functions(f *ast.File, info types.Info, fset *token.FileSet) ([]Function, error) { fns := exportedFuncs(f, fset) fns = errorOrVoid(fns, info) cmtMap := ast.NewCommentMap(fset, f, f.Comments) functions := make([]Function, len(fns)) for i, fn := range fns { fun := Function{Name: fn.Name.Name} fun.Comment = combine(cmtMap[fn]) // we only support null returns or error returns, so if there's a // return, it's an error. if len(fn.Type.Results.List) > 0 { fun.IsError = true } params := fn.Type.Params.List fun.Params = make([]Param, 0, len(params)) for _, field := range params { t := info.TypeOf(field.Type) pointer := false if p, ok := t.(*types.Pointer); ok { t = p.Elem() pointer = true } if b, ok := t.(*types.Basic); ok { if b.Kind() == types.UnsafePointer { log.Printf( "Can't create command for function %q because its parameter %q is an unsafe.Pointer.", fn.Name.Name, field.Names[0]) break } fieldCmt := combine(cmtMap[field]) // handle a, b, c int for _, name := range field.Names { nameCmt := combine(cmtMap[name]) if nameCmt == "" { nameCmt = fieldCmt } param := Param{ Name: name.Name, Type: b.Kind(), IsPointer: pointer, Comment: nameCmt, } fun.Params = append(fun.Params, param) } continue } } functions[i] = fun } return functions, nil }
func (c *compiler) typecheck(pkgpath string, fset *token.FileSet, files []*ast.File) (*types.Package, error) { config := &types.Config{ Sizeof: c.llvmtypes.Sizeof, Alignof: c.llvmtypes.Alignof, Offsetsof: c.llvmtypes.Offsetsof, } var info types.Info objectdata := make(map[types.Object]*ObjectData) info.Values = c.typeinfo.Values info.Types = c.typeinfo.Types info.Implicits = make(map[ast.Node]types.Object) info.Objects = make(map[*ast.Ident]types.Object) pkg, err := config.Check(pkgpath, fset, files, &info) if err != nil { return nil, err } for id, obj := range info.Objects { if obj == nil { continue } c.typeinfo.Objects[id] = obj objectdata[obj] = &ObjectData{Ident: id, Package: pkg} } for node, obj := range info.Implicits { id := ast.NewIdent(obj.Name()) c.typeinfo.Objects[id] = obj c.typeinfo.Implicits[node] = obj objectdata[obj] = &ObjectData{Ident: id, Package: pkg} } for _, pkg := range pkg.Imports() { assocObjectPackages(pkg, objectdata) } for object, data := range objectdata { if object, ok := object.(*types.TypeName); ok { // Add TypeNames to the LLVMTypeMap's TypeStringer. c.llvmtypes.pkgmap[object] = data.Package // Record exported types for generating runtime type information. // c.pkg is nil iff the package being checked is the package // being compiled. if c.pkg == nil && object.Pkg() == pkg && ast.IsExported(object.Name()) { c.exportedtypes = append(c.exportedtypes, object.Type()) } } c.objectdata[object] = data } return pkg, nil }
func (c *compiler) typecheck(pkgpath string, fset *token.FileSet, files []*ast.File) (*types.Package, error) { var errors string var imp = importer{compiler: c} config := &types.Config{ Error: func(err error) { if errors != "" { errors += "\n" } errors += err.Error() }, Import: imp.Import, Sizes: c.llvmtypes, } var info types.Info objectdata := make(map[types.Object]*ObjectData) info.Values = c.typeinfo.Values info.Types = c.typeinfo.Types info.Selections = c.typeinfo.Selections info.Implicits = make(map[ast.Node]types.Object) info.Objects = make(map[*ast.Ident]types.Object) pkg, err := config.Check(pkgpath, fset, files, &info) if err != nil { return nil, fmt.Errorf("%s", errors) } for id, obj := range info.Objects { if obj == nil { continue } c.typeinfo.Objects[id] = obj data := objectdata[obj] if data == nil { objectdata[obj] = &ObjectData{Ident: id} } else if data.Ident.Obj == nil { data.Ident = id } } for node, obj := range info.Implicits { id := ast.NewIdent(obj.Name()) c.typeinfo.Objects[id] = obj c.typeinfo.Implicits[node] = obj objectdata[obj] = &ObjectData{Ident: id} } for object, data := range objectdata { if object, ok := object.(*types.TypeName); ok { // Record exported types for generating runtime type information. // c.pkg is nil iff the package being checked is the package // being compiled. if c.pkg == nil && object.Pkg() == pkg && ast.IsExported(object.Name()) { c.exportedtypes = append(c.exportedtypes, object.Type()) } } c.objectdata[object] = data } return pkg, nil }