Skip to content

Commit 7d55839

Browse files
committed
fix: undefined identity (cloudwego#20)
* fix: should ensure a Selection expr is func type * fix: igore variant func (closure)
1 parent 50e42ca commit 7d55839

2 files changed

Lines changed: 75 additions & 48 deletions

File tree

src/compress/golang/plugin/file.go

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,13 @@ func (p *goParser) parseFunc(ctx *fileContext, funcDecl *ast.FuncDecl) (*Functio
184184
if funcDecl.Body == nil {
185185
goto set_func
186186
}
187+
187188
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
188189
call, ok := node.(*ast.CallExpr)
189190
if ok {
190191
var funcName string
191192
switch expr := call.Fun.(type) {
192193
case *ast.SelectorExpr:
193-
funcName := ""
194194
// TODO: not the best but works, optimize it later.
195195
x := expr.X
196196
for {
@@ -204,28 +204,12 @@ func (p *goParser) parseFunc(ctx *fileContext, funcDecl *ast.FuncDecl) (*Functio
204204
}
205205
break
206206
}
207-
// fixme: in closure like func(importName StructX) { ... }, importName is not in projectImports
208207
funcName = x.(*ast.Ident).Name + "." + expr.Sel.Name
209-
// internal function calls
210-
if impt, ok := ctx.projectImports[x.(*ast.Ident).Name]; ok {
211-
functionCalls[funcName] = Identity{impt, expr.Sel.Name}
212-
return true
213-
}
214-
// third-party function calls
215-
if impt, ok := ctx.thirdPartyImports[x.(*ast.Ident).Name]; ok {
216-
thirdPartyFunctionCalls[funcName] = Identity{PkgPath: impt, Name: expr.Sel.Name}
217-
return true
218-
}
219-
// NOTICE: skip sys imports?
220-
if ctx.IsSysImport(x.(*ast.Ident).Name) {
221-
return true
222-
}
223208
// check if it's method calls
224-
sel, ok := ctx.pkgTypeInfo.Selections[expr]
225-
if ok && (sel.Kind() == types.MethodExpr || sel.Kind() == types.MethodVal) {
209+
if sel, ok := ctx.pkgTypeInfo.Selections[expr]; ok && (sel.Kind() == types.MethodExpr || sel.Kind() == types.MethodVal) {
226210
// builtin or std libs, just ignore
227-
m := sel.Obj().(*types.Func)
228-
if m == nil || m.Pkg() == nil || ctx.IsSysImport(m.Pkg().Name()) {
211+
m, ok := sel.Obj().(*types.Func)
212+
if !ok || m.Pkg() == nil || ctx.IsSysImport(m.Pkg().Name()) {
229213
return true
230214
}
231215
sig := m.Type().(*types.Signature)
@@ -244,13 +228,61 @@ func (p *goParser) parseFunc(ctx *fileContext, funcDecl *ast.FuncDecl) (*Functio
244228
// external pkg
245229
thirdPartyMethodCalls[funcName] = Identity{mpkg, mname}
246230
}
231+
return true
232+
}
233+
// check if it's a package reference
234+
if use, ok := ctx.pkgTypeInfo.Uses[x.(*ast.Ident)]; ok {
235+
pkg, ok := use.(*types.PkgName)
236+
if !ok || pkg.Imported() == nil {
237+
return true
238+
}
239+
// NOTICE: skip sys imports?
240+
if ctx.IsSysImport(pkg.Imported().Name()) {
241+
return true
242+
}
243+
typ, ok := ctx.pkgTypeInfo.Types[expr]
244+
if !ok {
245+
return true
246+
}
247+
// expr type must be func signature
248+
if _, ok := typ.Type.(*types.Signature); !ok {
249+
return true
250+
}
251+
// internal function calls
252+
if impt, ok := ctx.projectImports[pkg.Imported().Name()]; ok {
253+
functionCalls[funcName] = Identity{impt, expr.Sel.Name}
254+
return true
255+
}
256+
// third-party function calls
257+
if impt, ok := ctx.thirdPartyImports[pkg.Imported().Name()]; ok {
258+
thirdPartyFunctionCalls[funcName] = Identity{PkgPath: impt, Name: expr.Sel.Name}
259+
return true
260+
}
261+
return true
247262
}
248-
return true
249263
case *ast.Ident:
250264
funcName = expr.Name
251-
if !isGoBuiltinFunc(funcName) {
252-
functionCalls[funcName] = Identity{ctx.pkgPath, funcName}
265+
if isGoBuiltinFunc(funcName) {
266+
return true
267+
}
268+
typ, ok := ctx.pkgTypeInfo.Types[expr]
269+
if !ok {
270+
return true
271+
}
272+
// TODO: we can't handle variant func (closure) at present
273+
obj := ctx.pkgTypeInfo.Defs[expr]
274+
if _, isVar := obj.(*types.Var); isVar {
275+
return true
276+
}
277+
obj = ctx.pkgTypeInfo.Uses[expr]
278+
if _, isVar := obj.(*types.Var); isVar {
279+
return true
280+
}
281+
// expr type must be func signature
282+
if _, ok := typ.Type.(*types.Signature); !ok {
283+
return true
253284
}
285+
functionCalls[funcName] = Identity{ctx.pkgPath, funcName}
254286
return true
255287
}
256288
}
@@ -359,7 +391,6 @@ func (p *goParser) parseStruct(ctx *fileContext, struName string, struDecl *ast.
359391
fieldname = fieldDecl.Names[0].Name
360392
}
361393
p.collectTypes(ctx, fieldname, fieldDecl.Type, st, inlined)
362-
363394
return true
364395
})
365396
return st, true
@@ -370,8 +401,17 @@ func (p *goParser) collectTypes(ctx *fileContext, field string, typ ast.Expr, st
370401
isFunc := getTypeName(ctx.fset, ctx.bs, typ, &types)
371402

372403
for _, ty := range types {
404+
// regard func-typed field as a method on the struct
373405
if isFunc {
374-
mname := st.Name + "." + field
406+
// Fix: multiple types use the same mname
407+
// ex: type FuncMap map[func()]func()
408+
if len(types) > 1 {
409+
continue
410+
}
411+
mname := st.Name
412+
if field != "" {
413+
mname += "." + field
414+
}
375415
f := p.newFunc(ctx.pkgPath, mname)
376416
f.AssociatedStruct = &Identity{ctx.pkgPath, st.Name}
377417
f.IsMethod = true
@@ -424,36 +464,21 @@ func (p *goParser) parseInterface(ctx *fileContext, name string, decl *ast.Inter
424464
st.TypeKind = TypeKindInterface
425465
st.Content = string(ctx.GetRawContent(decl))
426466

427-
methods := map[string]Identity{}
428467
ast.Inspect(decl.Methods, func(n ast.Node) bool {
429468
fieldDecl, ok := n.(*ast.Field)
430469
if !ok {
431470
return true
432471
}
433-
fname := ""
434-
if len(fieldDecl.Names) > 0 {
435-
// TODO: combine all names
436-
fname = fieldDecl.Names[0].String()
437-
} else {
438-
fname = string(ctx.GetRawContent(fieldDecl.Type))
439-
}
440-
441-
types := []Identity{}
442-
isFunc := getTypeName(ctx.fset, ctx.bs, fieldDecl.Type, &types)
443-
if !isFunc {
444-
return true
472+
inlined := len(fieldDecl.Names) == 0
473+
fieldname := string(ctx.GetRawContent(fieldDecl.Type))
474+
if !inlined {
475+
// Fixme: join names?
476+
fieldname = fieldDecl.Names[0].Name
445477
}
446-
447-
mname := name + "." + fname
448-
f := p.newFunc(ctx.pkgPath, mname)
449-
f.IsMethod = true
450-
f.AssociatedStruct = &Identity{st.PkgPath, st.Name}
451-
f.FilePath = ctx.filePath
452-
methods[fname] = Identity{f.PkgPath, mname}
478+
p.collectTypes(ctx, fieldname, fieldDecl.Type, st, inlined)
453479
return true
454480
})
455481

456-
st.Methods = methods
457482
return st, true
458483
}
459484

src/compress/golang/plugin/go_ast_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package main
33
import (
44
"encoding/json"
55
"testing"
6+
7+
"github.com/davecgh/go-spew/spew"
68
)
79

810
func Test_goParser_ParseTilTheEnd(t *testing.T) {
@@ -66,7 +68,7 @@ func Test_goParser_ParseRepo(t *testing.T) {
6668
{
6769
name: "test",
6870
fields: fields{
69-
homePageDir: "/Users/bytedance/GOPATH/work/hertz",
71+
homePageDir: "/Users/admin/GOPATH/work/hertz",
7072
},
7173
},
7274
}
@@ -77,7 +79,7 @@ func Test_goParser_ParseRepo(t *testing.T) {
7779
if err != nil {
7880
t.Fatalf("goParser.ParseTilTheEnd() error = %v", err)
7981
}
80-
// spew.Dump(p)
82+
spew.Dump(p)
8183
out, fun := p.getMain(2)
8284
if fun.Name != "main" {
8385
t.Fail()

0 commit comments

Comments
 (0)