Skip to content

Instantly share code, notes, and snippets.

@tenntenn
Last active October 26, 2018 14:21
Show Gist options
  • Save tenntenn/607e27638a3ec850c9a7c2dec334b5b7 to your computer and use it in GitHub Desktop.
Save tenntenn/607e27638a3ec850c9a7c2dec334b5b7 to your computer and use it in GitHub Desktop.
具象のerrorを返してるかチェックする
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"os"
)
func run(files []string) error {
fset := token.NewFileSet()
fs := make([]*ast.File, 0, len(files))
for _, fname := range files {
f, err := parser.ParseFile(fset, fname, nil, 0)
if err != nil {
return err
}
fs = append(fs, f)
}
config := types.Config{
Importer: importer.Default(),
}
info := &types.Info{
Uses: map[*ast.Ident]types.Object{},
}
if _, err := config.Check("main", fset, fs, info); err != nil {
return err
}
errTyp := types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
for _, f := range fs {
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.FuncType:
if n.Results == nil {
return false
}
for _, r := range n.Results.List {
ident, ok := r.Type.(*ast.Ident)
if !ok {
continue
}
o := info.Uses[ident]
if o == nil {
continue
}
if t := o.Type(); !types.Identical(t.Underlying(), errTyp) &&
types.Implements(t, errTyp) {
fmt.Println(fset.Position(ident.Pos()))
}
}
}
return true
})
}
return nil
}
func main() {
if err := run(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(1)
}
}
package main
import "fmt"
type Err string
func (err Err) Error() string {
return string(err)
}
func f() Err {
return Err("error")
}
func main() {
fmt.Println(f())
fmt.Println(func() Err {
return Err("error2")
})
fmt.Println(func() error {
return nil
})
}
@tenntenn
Copy link
Author

ちなみに、このパターンが検出漏れます
https://play.golang.org/p/f4YWYYmVFb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment