<p>I'm trying to add autocomplete functionality to my editor. What it would do, is simply write a templated out error check, like so:</p>
<p><code>
if err != nil {
return err
}
</code></p>
<p>However, to do this properly you need to know the return types of the current function.</p>
<p>Is there an existing tool to identify that? So far I've not found one, so I'm tempted to write my own. I also ask, because perhaps I'm taking an incorrect route to do this - perhaps theres a tool that already autocompletes error values?</p>
<p>Thoughts?</p>
<hr/>**评论:**<br/><br/>radu-matei: <pre><p>Hi!
I'm not going to discuss whether the approach is correct, I'm going to show you how to get a function's input and return types by parsing the source code using abstract syntax trees.</p>
<p>Usig this approach you can parse the entire source code, and when you find the node containing your target function (in this case it's called test), then you get all sorts of information on it, including the types of the params and return values.</p>
<p>See example below.</p>
<p>Please let me know if this helps!</p>
<p>Thanks!</p>
<p>file: main.go</p>
<pre><code>package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
"net/http"
)
func main() {
fs := token.NewFileSet()
f, err := parser.ParseFile(fs, "main.go", nil, parser.AllErrors)
if err != nil {
fmt.Printf("failed!, %v", err)
}
conf := types.Config{Importer: importer.Default()}
info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
if _, err := conf.Check("main.go", fs, []*ast.File{f}, info); err != nil {
log.Fatal(err) // type error
}
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.FuncDecl:
if x.Name.Name == "test" {
fmt.Printf("input: \n")
for _, p := range x.Type.Params.List {
tv, ok := info.Types[p.Type]
if !ok {
fmt.Printf("nil...\n")
return false
}
fmt.Printf("%v %v \n", p.Names, tv.Type)
}
fmt.Printf("output: \n")
for _, o := range x.Type.Results.List {
tv, ok := info.Types[o.Type]
if !ok {
fmt.Printf("nil...\n")
return false
}
fmt.Printf("%v %v \n", o.Names, tv.Type)
}
}
}
return true
})
}
func test(anotherint, n int, abc string, req *http.Request) (string, error) {
return "", nil
}
</code></pre>
<p>Output: </p>
<pre><code> go run main.go
input:
[anotherint n] int
[abc] string
[req] *net/http.Request
output:
[] string
[] error
</code></pre></pre>throwlikepollock: <pre><blockquote>
<p>Hi! I'm not going to discuss whether the approach is correct, I'm going to show you how to get a function's input and return types by parsing the source code using abstract syntax trees.</p>
</blockquote>
<p>Heya, I'm sorry if I was not clear in my post but I wasn't asking for help on how to do it, just if there was this functionality previously existing somewhere so I didn't waste my efforts writing it.</p>
<p>.. and then I waste your efforts writing it haha. My apologies. Regardless, I appreciate your example, thanks!</p></pre>TheMerovius: <pre><p>A friend of mine wrote <a href="https://github.com/stapelberg/expanderr" rel="nofollow">something similar</a> recently, which might interest you.</p></pre>carsncode: <pre><p>Please don't do this. <a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully" rel="nofollow">As Dave Cheney says</a>, "Don’t just check errors, handle them gracefully". Just returning the error object up the chain leaves you with a good chance that by the time something <em>actually does something useful</em> with the error, it lacks enough context to <em>actually be useful</em>. Yes, there will be <em>some cases</em> where you just return the error you received, but writing tools to do so in <em>every case</em> just automates a bad habit.</p></pre>throwlikepollock: <pre><p>I'm a bit lost. Isn't returning them what happens a large portion of the time?</p>
<p>I'm just trying to save typing from something I'm already doing. Returning wrapped and existing errors.</p>
<p>What am I supposed to do, consume the error and not handle it? I think you misunderstand what I'm doing here. It has nothing to do with how you handle the errors, I'm simply trying to save myself typing the same thing 400 times.</p>
<p><em>edit</em>: For example:</p>
<pre><code>if err != nil {
return somepackage.Struct{}, "", 0, errors.Wrap(err, "some context")
}
</code></pre>
<p>Yes, that example is a bit contrived. However, It sounds like you're recommending I type <code>return somepackage.Struct{}, "", 0,</code> repeatedly. Why does that matter?</p></pre>shovelpost: <pre><p>For me usually it is:</p>
<pre><code>return nil, fmt.Errorf("description %v", err)
</code></pre>
<p>What I mean is that I very rarely find myself returning <code>Struct{}</code></p>
<p>Nevertheless I am hoping for an addition to the language that will allow us to do</p>
<pre><code>return _, fmt.Errorf("description %v", err)
</code></pre>
<p>And <code>_</code> will be replaced with the default zero value, be it <code>nil</code> for pointers or <code>Struct{}</code>.</p></pre>throwlikepollock: <pre><p>oh god yes, <code>return _</code> would be amazing, never thought of that syntax before! Maybe Go 2 :)</p></pre>carsncode: <pre><p>I'm talking about what's in your original post:</p>
<blockquote>
<p>I'm trying to add autocomplete functionality to my editor. What it would do, is simply write a templated out error check, like so:
<code>if err != nil { return err }</code></p>
</blockquote>
<p>Automating that would be a Bad Thing™. Automating <em>good error handling</em>, to the extent that it's possible (because it will be <em>different in every error check</em>), could be valuable if its feasible.</p></pre>throwlikepollock: <pre><p>Yea, I can see where that led to confusion, but note that returning it blindly is not purely bad either. Eg, you don't want to blindly wrap everything. Then you'll be throwing away EOF/etc errors.</p>
<p>The problem of actual error values becomes more complicated as you go up the function stack. Parent functions have less and less context to understand what you may have wrapped, and what you might not have wrapped.</p>
<p>This is a case where I wish the <code>error</code> interface had some way of adding context to errors, without changing the value. Wrapping errors feels so terribly hacky. Like, a solution to a problem in Go that is caused by Go <em>(ie, a less than stellar design in this case)</em>.</p>
<p>I wish Go had error handling like in Rust, powered mainly by Enums. It's so nice in Rust. Data can go along with the error, and everything is understood, rather than the wishy washy stack trace that we're creating by hand in one big ass string <code>foo: bar: bar: bang: boom: etc</code>.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传