@@ -19,6 +19,7 @@ package main
1919import (
2020 "go/ast"
2121 "go/token"
22+ "go/types"
2223 "path/filepath"
2324 "strings"
2425)
@@ -66,6 +67,31 @@ func isGoBuiltinFunc(name string) bool {
6667 }
6768}
6869
70+ func (p * goParser ) inspectFile (ctx * fileContext , f * ast.File ) (map [string ]* Function , map [string ]* Struct , error ) {
71+ fileStructs := map [string ]* Struct {}
72+ fileFuncs := map [string ]* Function {}
73+ cont := true
74+ ast .Inspect (f , func (node ast.Node ) bool {
75+ if funcDecl , ok := node .(* ast.FuncDecl ); ok {
76+ // parse funcs
77+ f , ct := p .parseFunc (ctx , funcDecl )
78+ fileFuncs [f .Name ] = f
79+ cont = ct
80+ } else if typDecl , ok := node .(* ast.TypeSpec ); ok {
81+ // parse structs
82+ struName := typDecl .Name .Name
83+ struDecl , ok := typDecl .Type .(* ast.StructType )
84+ if ok {
85+ st , ct := p .parseStruct (ctx , struName , struDecl )
86+ fileStructs [struName ] = st
87+ cont = ct
88+ }
89+ }
90+ return cont
91+ })
92+ return fileFuncs , fileStructs , nil
93+ }
94+
6995// getOrSetFunc get a function in the map, or alloc and set a new one if not exists
7096func (p * goParser ) getOrSetFunc (pkg , name string ) * Function {
7197 pkgFuncs := p .processedPkgFunctions [pkg ]
@@ -127,7 +153,7 @@ func (p *goParser) seprateImports(impts []*ast.ImportSpec) (map[string]string, m
127153
128154// parseFunc parses all function declaration in one file
129155func (p * goParser ) parseFunc (ctx * fileContext , funcDecl * ast.FuncDecl ) (* Function , bool ) {
130-
156+ // funcObj := ctx.pkgTypeInfo.Defs[funcDecl.Name]
131157 var associatedStruct * Struct
132158 isMethod := funcDecl .Recv != nil
133159 if isMethod {
@@ -150,6 +176,7 @@ func (p *goParser) parseFunc(ctx *fileContext, funcDecl *ast.FuncDecl) (*Functio
150176 var functionCalls , methodCalls = map [string ]* Function {}, map [string ]* Function {}
151177
152178 ast .Inspect (funcDecl .Body , func (node ast.Node ) bool {
179+ // scope := ctx.pkgTypeInfo.Scopes[node]
153180 call , ok := node .(* ast.CallExpr )
154181 if ok {
155182 var funcName string
@@ -181,20 +208,40 @@ func (p *goParser) parseFunc(ctx *fileContext, funcDecl *ast.FuncDecl) (*Functio
181208 thirdPartyFunctionCalls [funcName ] = & ThirdPartyIdentity {PkgPath : impt , Identity : expr .Sel .Name }
182209 return true
183210 }
184- // WHY : skip sys imports?
185- if _ , ok := ctx .sysImports [ x .(* ast.Ident ).Name ]; ok {
211+ // NOTICE : skip sys imports?
212+ if ctx .IsSysImport ( x .(* ast.Ident ).Name ) {
186213 // internalFunctionCalls[funcName] = p.getOrSetFunc(impt, expr.Sel.Name)
187214 return true
188215 }
216+ // check if it's method calls
217+ sel , ok := ctx .pkgTypeInfo .Selections [expr ]
218+ if ok && (sel .Kind () == types .MethodExpr || sel .Kind () == types .MethodVal ) {
219+ // builtin or std libs, just ignore
220+ m := sel .Obj ()
221+ if m .Pkg () == nil || ctx .IsSysImport (m .Pkg ().Name ()) {
222+ return true
223+ }
224+ // try assert as named type
225+ obj := getTypeNamed (sel .Recv ())
226+ if obj == nil {
227+ return true
228+ }
189229
190- // Fallback must be method calls
191- // FIXME: get type info of object
192- f := p .getOrSetFunc (ctx .pkgPath , funcName )
193- f .IsMethod = true
194- f .AssociatedStruct = & Struct {Name : x .(* ast.Ident ).Name }
195- methodCalls [funcName ] = f
196- // TODO: seperate internal method and third-party method
197-
230+ mpkg := m .Pkg ().Path ()
231+ //NOTICE: use {structName.methodName} as method key
232+ mname := obj .Name () + "." + m .Name ()
233+ f := p .getOrSetFunc (mpkg , mname )
234+ f .AssociatedStruct = p .getOrSetStruct (mpkg , obj .Name ())
235+ f .IsMethod = true
236+
237+ if strings .HasPrefix (mpkg , p .modName ) {
238+ // internal pkg
239+ methodCalls [funcName ] = f
240+ } else {
241+ // external pkg
242+ thirdPartyMethodCalls [funcName ] = & ThirdPartyIdentity {mpkg , mname }
243+ }
244+ }
198245 return true
199246 case * ast.Ident :
200247 funcName = expr .Name
@@ -227,6 +274,22 @@ func (p *goParser) parseFunc(ctx *fileContext, funcDecl *ast.FuncDecl) (*Functio
227274 return f , true
228275}
229276
277+ func getTypeNamed (typ types.Type ) types.Object {
278+ if pt , ok := typ .(* types.Pointer ); ok {
279+ typ = pt .Elem ()
280+ }
281+ name , ok := typ .(* types.Named )
282+ if ok {
283+ return name .Obj ()
284+ }
285+ return nil
286+ }
287+
288+ func (ctx * fileContext ) IsSysImport (alias string ) bool {
289+ _ , ok := ctx .sysImports [alias ]
290+ return ok
291+ }
292+
230293// Struct holds the information about a struct
231294type Struct struct {
232295 Name string // Name of the struct
@@ -257,6 +320,7 @@ type fileContext struct {
257320 sysImports map [string ]string
258321 projectImports map [string ]string
259322 thirdPartyImports map [string ]string
323+ pkgTypeInfo * types.Info
260324}
261325
262326// parse a ast.StructType node and renturn allocated *Struct
@@ -287,7 +351,7 @@ func (p *goParser) parseStruct(ctx *fileContext, struName string, struDecl *ast.
287351 }
288352
289353 types := []ThirdPartyIdentity {}
290- isFunc := getTypeName (ctx .bs , fieldDecl .Type , & types )
354+ isFunc := getTypeName (ctx .fset , ctx . bs , fieldDecl .Type , & types )
291355
292356 for _ , ty := range types {
293357 if isFunc {
@@ -328,35 +392,35 @@ func (p *goParser) parseStruct(ctx *fileContext, struName string, struDecl *ast.
328392
329393// handle typ expr and return not-builtin type identity and return if the type if a func signature.
330394// ret is used to store results.
331- func getTypeName (file []byte , typ ast.Expr , ret * []ThirdPartyIdentity ) bool {
395+ func getTypeName (fset * token. FileSet , file []byte , typ ast.Expr , ret * []ThirdPartyIdentity ) bool {
332396 switch ty := typ .(type ) {
333397 case * ast.Ident :
334398 if ! isGoBuiltinFunc (ty .Name ) {
335399 * ret = append (* ret , ThirdPartyIdentity {Identity : ty .Name })
336400 }
337401 return false
338402 case * ast.StarExpr :
339- return getTypeName (file , ty .X , ret )
403+ return getTypeName (fset , file , ty .X , ret )
340404 case * ast.ArrayType :
341- return getTypeName (file , ty .Elt , ret )
405+ return getTypeName (fset , file , ty .Elt , ret )
342406 case * ast.MapType :
343- a := getTypeName (file , ty .Key , ret )
344- b := getTypeName (file , ty .Value , ret )
407+ a := getTypeName (fset , file , ty .Key , ret )
408+ b := getTypeName (fset , file , ty .Value , ret )
345409 return a || b
346410 case * ast.ChanType :
347- return getTypeName (file , ty .Value , ret )
411+ return getTypeName (fset , file , ty .Value , ret )
348412 case * ast.SelectorExpr :
349413 pkg , ok := ty .X .(* ast.Ident )
350414 if ok {
351415 * ret = append (* ret , ThirdPartyIdentity {Identity : ty .Sel .Name , PkgPath : pkg .Name })
352416 }
353417 return false
354418 case * ast.FuncType :
355- name := string (file [ty .Func : typ .End ()])
419+ name := string (file [fset . Position ( ty .Func ). Offset : fset . Position ( typ .End ()). Offset ])
356420 * ret = append (* ret , ThirdPartyIdentity {Identity : name })
357421 return true
358422 case * ast.InterfaceType :
359- name := string (file [ty .Interface : typ .End ()])
423+ name := string (file [fset . Position ( ty .Interface ). Offset : fset . Position ( typ .End ()). Offset ])
360424 * ret = append (* ret , ThirdPartyIdentity {Identity : name })
361425 return false
362426 }
0 commit comments