github.com/code-visible/golang@v0.0.0-20240214000051-0f9c587b0b32/sourcefile/file.go (about) 1 package sourcefile 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 "path/filepath" 8 9 "github.com/code-visible/golang/callhierarchy" 10 "github.com/code-visible/golang/node" 11 ) 12 13 type SourceFile struct { 14 ID string `json:"id"` 15 Name string `json:"name"` 16 Path string `json:"path"` 17 Pkg string `json:"pkg"` 18 Callables []*node.Callable `json:"callables"` 19 Abstracts []*node.Abstract `json:"abstracts"` 20 Calls []callhierarchy.Call `json:"calls"` 21 Deps []string `json:"deps"` 22 23 abstracts map[string]*node.Abstract 24 callables map[string]*node.Callable 25 parsed *ast.File 26 pkg *ast.Package 27 fset *token.FileSet 28 } 29 30 func NewSourceFile(pkg string, path string, file *ast.File, fset *token.FileSet) *SourceFile { 31 return &SourceFile{ 32 ID: fmt.Sprintf("%s/%s", pkg, filepath.Base(path)), 33 Name: filepath.Base(path), 34 Path: filepath.Dir(path), 35 Pkg: pkg, 36 Callables: nil, 37 Abstracts: nil, 38 Deps: nil, 39 abstracts: make(map[string]*node.Abstract), 40 callables: make(map[string]*node.Callable), 41 parsed: file, 42 pkg: nil, 43 fset: fset, 44 } 45 } 46 47 func (sf *SourceFile) EnumerateCallables() { 48 for _, decl := range sf.parsed.Decls { 49 fn, ok := decl.(*ast.FuncDecl) 50 if !ok { 51 continue 52 } 53 c := &node.Callable{ 54 ID: "", 55 Pos: sf.fset.Position(fn.Pos()).String(), 56 Name: fn.Name.Name, 57 Abstract: "", 58 Comment: fn.Doc.Text(), 59 File: sf.Name, 60 Pkg: sf.Pkg, 61 Typ: "", 62 Syscalls: make([]string, 0), 63 Parameters: make([]string, 0), 64 Results: make([]string, 0), 65 Description: "", 66 Method: false, 67 Private: false, 68 Orphan: false, 69 } 70 sf.callables[fn.Name.Name] = c 71 sf.Callables = append(sf.Callables, c) 72 } 73 } 74 75 func (sf *SourceFile) EnumerateAbstracts() { 76 for _, decl := range sf.parsed.Decls { 77 if decl, ok := decl.(*ast.GenDecl); ok { 78 for _, spec := range decl.Specs { 79 if spec, ok := spec.(*ast.TypeSpec); ok { 80 // TODO: 81 ab := &node.Abstract{ 82 ID: "", 83 Name: spec.Name.String(), 84 } 85 sf.abstracts[spec.Name.String()] = ab 86 sf.Abstracts = append(sf.Abstracts, ab) 87 } 88 } 89 } 90 } 91 } 92 93 func (sf *SourceFile) EnumerateCallHierarchy() { 94 var trace []ast.Node 95 ast.Inspect(sf.parsed, func(n ast.Node) bool { 96 if n == nil { 97 trace = trace[:len(trace)-1] 98 } else { 99 trace = append(trace, n) 100 } 101 102 if x, ok := n.(*ast.CallExpr); ok { 103 call := callhierarchy.Call{ 104 ID: "", 105 Caller: "universe", 106 Callee: "", 107 File: sf.Name, 108 Typ: "", 109 CallerPos: -1, 110 CalleePos: n.Pos(), 111 } 112 for i := len(trace) - 2; i >= 0; i-- { 113 if fnDecl, ok := trace[i].(*ast.FuncDecl); ok { 114 call.Caller = fnDecl.Name.Name 115 call.CallerPos = trace[i].Pos() 116 break 117 } 118 } 119 120 switch fnID := x.Fun.(type) { 121 case *ast.Ident: 122 call.Callee = fnID.Name 123 case *ast.SelectorExpr: 124 if scope, ok := fnID.X.(*ast.Ident); ok { 125 call.Callee = fmt.Sprintf("%s.%s", scope.Name, fnID.Sel.Name) 126 } else { 127 call.Callee = fmt.Sprintf("unknown.%s", fnID.Sel.Name) 128 } 129 default: 130 panic("parse call error, not covered case") 131 } 132 133 sf.Calls = append(sf.Calls, call) 134 } 135 136 return true 137 }) 138 }