Skip to content

Commit

Permalink
fix: method handler 不能解析
Browse files Browse the repository at this point in the history
  • Loading branch information
link-duan committed Apr 6, 2023
1 parent 7f3ec20 commit ccbf09c
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 99 deletions.
28 changes: 19 additions & 9 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"

"github.com/gotomicro/eapi/spec"
"github.com/gotomicro/eapi/utils"
"golang.org/x/tools/go/packages"
)

Expand Down Expand Up @@ -212,6 +213,23 @@ func (c *Context) MatchCall(n ast.Node, rule *CallRule, callback func(call *ast.
return
}

func (c *Context) GetFuncFromAstNode(n ast.Node) *types.Func {
var obj interface{}
switch handlerArg := n.(type) {
case *ast.Ident:
obj = c.Package().TypesInfo.ObjectOf(handlerArg)
case *ast.SelectorExpr:
obj = c.Package().TypesInfo.ObjectOf(handlerArg.Sel)
default:
return nil
}
fn, ok := obj.(*types.Func)
if !ok {
return nil
}
return fn
}

type CallInfo struct {
Type string
Method string
Expand Down Expand Up @@ -298,15 +316,7 @@ func (c *Context) parseCallInfoByIdent(ident *ast.Ident) (info *CallInfo) {
if !ok {
return nil
}
info.Method = fn.Name()

sign := fn.Type().(*types.Signature)
if sign.Recv() != nil {
info.Type = sign.Recv().Type().String()
} else {
info.Type = fn.Pkg().Path()
}

info.Type, info.Method = utils.GetFuncInfo(fn)
return
}

Expand Down
47 changes: 11 additions & 36 deletions plugins/echo/echo.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package echo

import (
"fmt"
"go/ast"
"go/token"
"go/types"
"os"
"path"
"regexp"
"strings"

"github.com/gotomicro/eapi"
"github.com/gotomicro/eapi/plugins/common"
"github.com/gotomicro/eapi/utils"
"github.com/knadh/koanf"
)

Expand Down Expand Up @@ -152,8 +155,12 @@ func (e *Plugin) parseAPI(ctx *eapi.Context, callExpr *ast.CallExpr, comment *ea
if handlerFn == nil {
return
}

handlerDef := ctx.GetDefinition(handlerFn.Pkg().Path(), handlerFn.Name())
typeName, methodName := utils.GetFuncInfo(handlerFn)
handlerDef := ctx.GetDefinition(typeName, methodName)
if handlerDef == nil {
fmt.Fprintf(os.Stderr, "handler function %s.%s not found\n", typeName, methodName)
return
}
handlerFnDef, ok := handlerDef.(*eapi.FuncDefinition)
if !ok {
return
Expand Down Expand Up @@ -182,46 +189,14 @@ func (e *Plugin) parseAPI(ctx *eapi.Context, callExpr *ast.CallExpr, comment *ea

func (e *Plugin) getHandlerFn(ctx *eapi.Context, callExpr *ast.CallExpr) (handlerFn *types.Func) {
handlerArg := callExpr.Args[len(callExpr.Args)-1]

if call, ok := handlerArg.(*ast.CallExpr); ok {
nestedCall := e.unwrapCall(call)
nestedCall := utils.UnwrapCall(call)
if len(nestedCall.Args) <= 0 {
return
}
handlerArg = nestedCall.Args[0]
}

var handler interface{}
switch handlerArg := handlerArg.(type) {
case *ast.Ident:
handler = ctx.Package().TypesInfo.Uses[handlerArg]
case *ast.SelectorExpr:
handler = ctx.Package().TypesInfo.Uses[handlerArg.Sel]
default:
return
}

handlerFn, ok := handler.(*types.Func)
if !ok {
return nil
}
return
}

// unwrap and returns the first nested call
// e.g. unwrapCall(`a(b(c(d)), b1(c1))`) return `c(d)`
func (e *Plugin) unwrapCall(callExpr *ast.CallExpr) *ast.CallExpr {
if len(callExpr.Args) == 0 {
return callExpr
}

arg0 := callExpr.Args[0]
arg0Call, ok := arg0.(*ast.CallExpr)
if ok {
return e.unwrapCall(arg0Call)
}

return callExpr
return ctx.GetFuncFromAstNode(handlerArg)
}

var (
Expand Down
44 changes: 6 additions & 38 deletions plugins/gin/gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

analyzer "github.com/gotomicro/eapi"
"github.com/gotomicro/eapi/plugins/common"
"github.com/gotomicro/eapi/utils"
"github.com/knadh/koanf"
)

Expand Down Expand Up @@ -167,10 +168,10 @@ func (e *Plugin) parseAPI(ctx *analyzer.Context, callExpr *ast.CallExpr, comment
if handlerFn == nil {
return
}

handlerDef := ctx.GetDefinition(handlerFn.Pkg().Path(), handlerFn.Name())
typeName, methodName := utils.GetFuncInfo(handlerFn)
handlerDef := ctx.GetDefinition(typeName, methodName)
if handlerDef == nil {
fmt.Fprintf(os.Stderr, "handler function %s.%s not found\n", handlerFn.Pkg().Path(), handlerFn.Name())
fmt.Fprintf(os.Stderr, "handler function %s.%s not found\n", typeName, methodName)
return
}
handlerFnDef, ok := handlerDef.(*analyzer.FuncDefinition)
Expand Down Expand Up @@ -198,49 +199,16 @@ func (e *Plugin) parseAPI(ctx *analyzer.Context, callExpr *ast.CallExpr, comment
return
}

// unwrap and returns the first nested call
// e.g. unwrapCall(`a(b(c(d)), b1(c1))`) return `c(d)`
func (e *Plugin) unwrapCall(callExpr *ast.CallExpr) *ast.CallExpr {
if len(callExpr.Args) == 0 {
return callExpr
}

arg0 := callExpr.Args[0]
arg0Call, ok := arg0.(*ast.CallExpr)
if ok {
return e.unwrapCall(arg0Call)
}

return callExpr
}

func (e *Plugin) getHandlerFn(ctx *analyzer.Context, callExpr *ast.CallExpr) (handlerFn *types.Func) {
handlerArg := callExpr.Args[len(callExpr.Args)-1]

if call, ok := handlerArg.(*ast.CallExpr); ok {
nestedCall := e.unwrapCall(call)
nestedCall := utils.UnwrapCall(call)
if len(nestedCall.Args) <= 0 {
return
}
handlerArg = nestedCall.Args[0]
}

var handler interface{}
switch handlerArg := handlerArg.(type) {
case *ast.Ident:
handler = ctx.Package().TypesInfo.Uses[handlerArg]
case *ast.SelectorExpr:
handler = ctx.Package().TypesInfo.Uses[handlerArg.Sel]
default:
return
}

handlerFn, ok := handler.(*types.Func)
if !ok {
return
}

return
return ctx.GetFuncFromAstNode(handlerArg)
}

var (
Expand Down
51 changes: 36 additions & 15 deletions test/testdata/gin/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,19 @@
"title": "GormDeletedAt",
"type": "string"
},
"server_pkg_shop.GoodsInfoPathParams": {
"ext": {
"type": "object"
},
"properties": {
"guid": {
"description": "Goods Guid",
"type": "integer"
}
},
"title": "ShopGoodsInfoPathParams",
"type": "object"
},
"server_pkg_view.ErrCode": {
"description": "\u003ctable\u003e\u003ctr\u003e\u003cth\u003eValue\u003c/th\u003e\u003cth\u003eKey\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeNotFound\u003c/td\u003e\u003ctd\u003eResource not found\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeCancled\u003c/td\u003e\u003ctd\u003eRequest canceld\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeUnknown\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeInvalidArgument\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e",
"enum": [
Expand Down Expand Up @@ -448,19 +461,6 @@
},
"title": "ViewSelfRefType",
"type": "object"
},
"server_pkg_shop.GoodsInfoPathParams": {
"title": "ShopGoodsInfoPathParams",
"type": "object",
"ext": {
"type": "object"
},
"properties": {
"guid": {
"description": "Goods Guid",
"type": "integer"
}
}
}
},
"securitySchemes": {
Expand All @@ -485,6 +485,27 @@
},
"openapi": "3.1.0",
"paths": {
"/api/controller/goods/{guid}": {
"delete": {
"operationId": "controller.Delete",
"parameters": [
{
"description": "Goods Guid",
"in": "path",
"name": "guid",
"required": true,
"schema": {
"title": "guid",
"type": "string"
}
}
],
"responses": {},
"tags": [
"Shop"
]
}
},
"/api/goods": {
"post": {
"description": "GoodsCreate 创建商品接口",
Expand Down Expand Up @@ -626,10 +647,10 @@
"operationId": "shop.GoodsInfo",
"parameters": [
{
"description": "Goods Guid",
"in": "path",
"name": "guid",
"required": true,
"description": "Goods Guid",
"schema": {
"description": "Goods Guid",
"type": "integer"
Expand Down Expand Up @@ -712,4 +733,4 @@
}
}
}
}
}
14 changes: 14 additions & 0 deletions test/testdata/gin/pkg/controller/goods.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package controller

import "github.com/gin-gonic/gin"

type GoodsController struct{}

func NewGoodsController() *GoodsController {
return &GoodsController{}
}

func (s *GoodsController) Delete(c *gin.Context) {
// Goods Guid
_ = c.Param("guid")
}
7 changes: 6 additions & 1 deletion test/testdata/gin/router.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package router

import (
"server/pkg/controller"
"server/pkg/handler"
"server/pkg/shop"

Expand Down Expand Up @@ -33,9 +34,13 @@ func ServeHttp() *gin.Engine {
g.POST("/goods", handler.Handler(shop.GoodsCreate))
g.POST("/goods/:guid/down", shop.GoodsDown)
}
g = g.Group("/v2")
g := g.Group("/v2")
g.GET("/goods/:guid", shop.GoodsInfo)
}

// controller style
goodsController := controller.NewGoodsController()
g.DELETE("/controller/goods/:guid", goodsController.Delete)

return r
}
29 changes: 29 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
package utils

import (
"go/ast"
"go/types"
"os"
"strings"
)

func Debug() bool {
return os.Getenv("DEBUG") == "true" || strings.HasSuffix(os.Args[0], ".test")
}

// UnwrapCall unwrap and returns the first nested call
// e.g. unwrapCall(`a(b(c(d)), b1(c1))`) return `c(d)`
func UnwrapCall(callExpr *ast.CallExpr) *ast.CallExpr {
if len(callExpr.Args) == 0 {
return callExpr
}

arg0 := callExpr.Args[0]
arg0Call, ok := arg0.(*ast.CallExpr)
if ok {
return UnwrapCall(arg0Call)
}

return callExpr
}

func GetFuncInfo(fn *types.Func) (typeName, methodName string) {
sign := fn.Type().(*types.Signature)
if sign.Recv() != nil {
typeName = sign.Recv().Type().String()
} else {
typeName = fn.Pkg().Path()
}
methodName = fn.Name()
return
}

0 comments on commit ccbf09c

Please sign in to comment.