doulei8861 2017-04-23 18:47
浏览 227

在Golang中进行多个接口转换的简便方法?

I'm trying to parse function calls from a code using go/ast package.

To do that, I first find all function calls like:

ast.Inspect(f, func(n ast.Node) bool {
    switch x := n.(type) {
    case *ast.FuncDecl:
        processFunction(x)
    }
    return true
})

And then, processFunction() looks like this:

func processFunction(e *ast.FuncDecl) {
    // Save wrapper function name
    f := e.Name.Name

    for _, expression := range e.Body.List {
        logrus.Printf("Current stmt: %#v", expression)
        pkg := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).X.(*ast.Ident).Name
        fn := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).Sel.Name
        fcall := fmt.Sprintf("%s.%s", pkg, fn)
        logrus.Printf("Yay. found my function call: ", fcall)
    }

}

The problem with this code is that if that particular hierarchy is not found in AST, the program panics. I know that we can do interface conversions gracefully via

x, ok := x.(type)

But, if I do each conversion like this, my code will be huge. Trying to use that in this fails of course.

    pkg, ok := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).X.(*ast.Ident).Name
    if !ok {
        continue
    }

Error:

./parser.go:41: assignment count mismatch: 2 = 1

Is there a concise way to do these series of conversions and also fail gracefully if this hierarchy is not found?

  • 写回答

1条回答 默认 最新

  • doutan8506 2017-04-23 22:00
    关注

    There is no way, that I know of, to chain type assertions. But you can simplify this specific example by extracting duplicate code into its own function.

    func fnSelExpr(s ast.Stmt) (*ast.SelectorExpr, bool) {
        if xs, ok := s.(*ast.ExprStmt); ok {
            if cx, ok := xs.X.(*ast.CallExpr); ok {
                return cx.Fun.(*ast.SelectorExpr)
            }
        }
        return nil, false
    }
    

    Then you can simplify your processFunction like this.

    func processFunction(e *ast.FuncDecl) {
        // Save wrapper function name
        f := e.Name.Name
    
        for _, expression := range e.Body.List {
            logrus.Printf("Current stmt: %#v", expression)
    
            sx, ok := fnSelExpr(expression)
            if !ok {
                continue
            }
    
            var pkg string
            if id, ok := sx.X.(*ast.Ident); ok {
                pkg = id.Name
            }
            fn := sx.Sel.Name
    
            fcall := fmt.Sprintf("%s.%s", pkg, fn)
            logrus.Printf("Yay. found my function call: ", fcall)
        }
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号