在Golang中进行多个接口转换的简洁方法是什么?

huangapple go评论71阅读模式
英文:

Concise way to do multiple interface conversions in Golang?

问题

我正在尝试使用go/ast包从代码中解析函数调用。

为了做到这一点,我首先找到所有的函数调用,如下所示:

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

然后,processFunction()函数如下所示:

func processFunction(e *ast.FuncDecl) {
    // 保存包装函数名
    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)
    }
}

这段代码的问题在于,如果在AST中找不到特定的层次结构,程序会发生崩溃。我知道我们可以通过以下方式优雅地进行接口转换:

x, ok := x.(type)

但是,如果我对每个转换都这样做,我的代码会变得很冗长。当然,在这里尝试使用这种方法会失败。

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

错误信息:

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

有没有一种简洁的方法来进行这些连续的转换,并且在找不到这个层次结构时能够优雅地处理错误?

英文:

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

得分: 1

我已经为您翻译了代码部分,如下所示:

// 无法使用*类型断言*来链接。但是,您可以通过将重复的代码提取到自己的函数中来简化此特定示例。

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
}

然后您可以像这样简化您的`processFunction`函数

func processFunction(e *ast.FuncDecl) {
    // 保存包装函数名称
    f := e.Name.Name

    for _, expression := range e.Body.List {
        logrus.Printf("当前语句: %#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("耶。找到了我的函数调用: ", fcall)
    }
}

希望对您有所帮助!

英文:

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)
	}
}

huangapple
  • 本文由 发表于 2017年4月24日 02:47:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/43575101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定