func toSourcePos(fset *token.FileSet, node ast.Node) SourcePos { file := fset.File(node.Pos()) start := file.Offset(node.Pos()) end := file.Offset(node.End()) return SourcePos(fmt.Sprintf("%s:#%d,#%d", file.Name(), start, end)) }
func typeGoString(expr ast.Node) string { if expr == nil { return "" } // TODO: refactor using go/printer switch expr := expr.(type) { case *ast.Ident: return expr.String() case *ast.ArrayType: return "[]" + typeGoString(expr.Elt) case *ast.StarExpr: return "*" + typeGoString(expr.X) case *ast.SelectorExpr: return typeGoString(expr.X) + "." + expr.Sel.String() case *ast.FuncType: pre := "func(" + typeGoString(expr.Params) + ")" if expr.Results == nil || expr.Results.List == nil { return pre } post := typeGoString(expr.Results) if len(expr.Results.List) == 1 { return pre + " " + post } return pre + " (" + post + ")" case *ast.FieldList: if expr == nil { return "" } var buf bytes.Buffer for i, field := range expr.List { if i != 0 { buf.WriteString(", ") } if len(field.Names) == 0 { buf.WriteString(typeGoString(field.Type)) } for i, name := range field.Names { if i != 0 { buf.WriteString(", ") } buf.WriteString(name.String()) buf.WriteString(" ") buf.WriteString(typeGoString(field.Type)) } } return buf.String() case *ast.MapType: return "map[" + typeGoString(expr.Key) + "]" + typeGoString(expr.Value) case *ast.InterfaceType: return "interface{" + typeGoString(expr.Methods) + "}" case *ast.StructType: return "struct{" + typeGoString(expr.Fields) + "}" case *ast.ChanType: switch expr.Dir { case ast.SEND: return "chan<- " + typeGoString(expr.Value) case ast.RECV: return "<-chan " + typeGoString(expr.Value) default: return "chan " + typeGoString(expr.Value) } case *ast.Ellipsis: return "..." + typeGoString(expr.Elt) default: panic(fmt.Errorf("%#v", expr)) } }
func (v *typeVisitor) elementType(expr ast.Node) string { if expr == nil { return "" } p := v.astPackage f := v.astFile switch expr := expr.(type) { case *ast.Ident: name := expr.String() if name == "error" || isPrimitive(name) { return name } for _, f := range p.Files { if f.Scope.Lookup(name) != nil { return v.pkg.Name + "." + name } } // TODO: refactor for _, imp := range f.Imports { local := imp.Name if local == nil || local.String() != `.` { continue } importPath, _ := strconv.Unquote(imp.Path.Value) if local == nil && path.Base(importPath) != name { continue } buildPkg, err := build.Import(importPath, ".", build.FindOnly) if err != nil { log.Printf("%#v", err) continue } dir := buildPkg.Dir fset := token.NewFileSet() pkgs, err := parser.ParseDir(fset, dir, func(fi os.FileInfo) bool { return !fi.IsDir() && !strings.HasSuffix(fi.Name(), "_test.go") }, 0) if err != nil { log.Printf("%#v", err) continue } for _, pkg := range pkgs { for _, f := range pkg.Files { if f.Scope.Lookup(name) == nil { continue } break } break // break? } return importPath + "." + name } log.Printf("can't resolve name", name) return name case *ast.ArrayType: return v.elementType(expr.Elt) case *ast.StarExpr: return v.elementType(expr.X) case *ast.SelectorExpr: name := expr.X.(*ast.Ident).String() for _, imp := range f.Imports { local := imp.Name if local != nil && local.String() != name { continue } importPath, _ := strconv.Unquote(imp.Path.Value) if local == nil && path.Base(importPath) != name { continue } name = importPath break } return name + "." + expr.Sel.String() case *ast.FuncType: return strings.TrimSpace("func(" + v.elementType(expr.Params) + ") " + v.elementType(expr.Results)) case *ast.FieldList: if expr == nil { return "" } var buf bytes.Buffer for _, field := range expr.List { buf.WriteString(v.elementType(field.Type)) } return buf.String() case *ast.MapType: return "map[" + v.elementType(expr.Key) + "]" + v.elementType(expr.Value) case *ast.InterfaceType: return "interface {" + v.elementType(expr.Methods) + "}" case *ast.StructType: return "struct {" + v.elementType(expr.Fields) + "}" case *ast.ChanType: switch expr.Dir { case ast.SEND: return "chan<- " + v.elementType(expr.Value) case ast.RECV: return "<-chan " + v.elementType(expr.Value) default: return "chan " + v.elementType(expr.Value) } case *ast.Ellipsis: return "..." + v.elementType(expr.Elt) default: panic(fmt.Errorf("%#v", expr)) } }