-
-
Save roscopecoltran/c2350d16dc5134c3d5f8a7e6a05a7a40 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"fmt" | |
"go/ast" | |
"go/build" | |
"go/importer" | |
"go/parser" | |
"go/token" | |
"go/types" | |
"os" | |
"path/filepath" | |
"strconv" | |
"strings" | |
) | |
var ( | |
ctx *build.Context | |
allPackage map[string][]*PackageInfo | |
) | |
type PackageInfo struct { | |
Package *build.Package | |
Info *types.Info | |
} | |
func (p *PackageInfo) HasSymbol(s string) bool { | |
for _, o := range p.Info.Defs { | |
if o == nil { | |
continue | |
} | |
if o.Exported() && o.Name() == s { | |
return true | |
} | |
} | |
return false | |
} | |
type Typo struct { | |
Pos token.Position | |
Text string | |
} | |
func init() { | |
c := build.Default // copy | |
ctx = &c | |
ctx.CgoEnabled = false | |
ctx.GOPATH = "" | |
} | |
func getAllStdPackages(ctx *build.Context) (map[string][]*PackageInfo, error) { | |
allpkgs := map[string][]*PackageInfo{} | |
config := &types.Config{ | |
Importer: importer.Default(), | |
} | |
srcDir := filepath.Join(ctx.GOROOT, "src") | |
err := filepath.Walk(srcDir, func(path string, fi os.FileInfo, err error) error { | |
if err != nil { | |
return err | |
} | |
if !fi.IsDir() { | |
return nil | |
} | |
if n := fi.Name(); n == "internal" || n == "testdata" || n == "vendor" { | |
return filepath.SkipDir | |
} | |
pkg, err := ctx.ImportDir(path, 0) | |
if err != nil { | |
return nil | |
} | |
fset := token.NewFileSet() | |
pkgs, err := parser.ParseDir(fset, pkg.Dir, makeFilter(pkg), 0) | |
if err != nil { | |
return nil | |
} | |
for pn, p := range pkgs { | |
info := &types.Info{ | |
Defs: map[*ast.Ident]types.Object{}, | |
} | |
files := make([]*ast.File, 0, len(p.Files)) | |
for _, f := range p.Files { | |
files = append(files, f) | |
} | |
_, err := config.Check(pn, fset, files, info) | |
if err != nil { | |
return nil | |
} | |
allpkgs[pn] = append(allpkgs[pn], &PackageInfo{ | |
Package: pkg, | |
Info: info, | |
}) | |
} | |
return nil | |
}) | |
if err != nil { | |
return nil, err | |
} | |
return allpkgs, nil | |
} | |
func main() { | |
if pkgs, err := getAllStdPackages(ctx); err == nil { | |
allPackage = pkgs | |
} else { | |
fmt.Fprintln(os.Stderr, "Error:", err) | |
os.Exit(1) | |
} | |
typos, err := FindTypo(os.Args[1:]) | |
if err != nil { | |
fmt.Fprintln(os.Stderr, "Error:", err) | |
os.Exit(1) | |
} | |
for _, typo := range typos { | |
fmt.Println(strconv.Quote(typo.Text), "at", typo.Pos) | |
} | |
} | |
func FindTypo(paths []string) ([]*Typo, error) { | |
dir, err := os.Getwd() | |
if err != nil { | |
return nil, err | |
} | |
var typos []*Typo | |
for _, path := range paths { | |
t, err := findTypo(dir, path) | |
if err != nil { | |
return nil, err | |
} | |
typos = append(typos, t...) | |
} | |
return typos, nil | |
} | |
func findTypo(dir string, path string) ([]*Typo, error) { | |
pkg, err := ctx.Import(path, dir, build.IgnoreVendor) | |
if err != nil { | |
return nil, err | |
} | |
fset := token.NewFileSet() | |
pkgs, err := parser.ParseDir(fset, pkg.Dir, makeFilter(pkg), parser.ParseComments) | |
if err != nil { | |
return nil, err | |
} | |
var typos []*Typo | |
for _, p := range pkgs { | |
for _, f := range p.Files { | |
t, err := findTypoByFile(fset, f) | |
if err != nil { | |
return nil, err | |
} | |
typos = append(typos, t...) | |
} | |
} | |
return typos, nil | |
} | |
func makeFilter(pkg *build.Package) func(fi os.FileInfo) bool { | |
return func(fi os.FileInfo) bool { | |
if strings.HasSuffix(fi.Name(), "_test.go") { | |
return false | |
} | |
for _, ignored := range pkg.IgnoredGoFiles { | |
if ignored == fi.Name() { | |
return false | |
} | |
} | |
for _, cgofile := range pkg.CgoFiles { | |
if cgofile == fi.Name() { | |
return false | |
} | |
} | |
return true | |
} | |
} | |
func findTypoByFile(fset *token.FileSet, f *ast.File) ([]*Typo, error) { | |
var typos []*Typo | |
for _, cg := range f.Comments { | |
s := bufio.NewScanner(strings.NewReader(cg.Text())) | |
s.Split(bufio.ScanWords) | |
for s.Scan() { | |
str := strings.TrimRight(s.Text(), ".") | |
if typo, isTarget := hasTypo(str); typo && isTarget { | |
// 末尾のsを取り除いてもう一度やってみる | |
if strings.HasSuffix(str, "s") { | |
str = strings.TrimRight(str, "s") | |
if typo, isTarget := hasTypo(str); !typo && isTarget { | |
// sを取り除いたら大丈夫だった | |
continue | |
} | |
} | |
typos = append(typos, &Typo{ | |
Text: s.Text(), | |
Pos: fset.Position(cg.Pos()), | |
}) | |
} | |
} | |
if err := s.Err(); err != nil { | |
return nil, err | |
} | |
} | |
return typos, nil | |
} | |
func hasTypo(s string) (typo, target bool) { | |
expr, err := parser.ParseExpr(s) | |
if err != nil { | |
return false, false | |
} | |
pkg, symbol, ok := getPkgSymbol(expr) | |
if !ok { | |
return false, false | |
} | |
typo = !isExitSymbol(pkg, symbol) | |
return typo, true | |
} | |
func isExistPkg(pkg string) bool { | |
_, ok := allPackage[pkg] | |
return ok | |
} | |
func getPkgSymbol(expr ast.Expr) (pkg, symbol string, ok bool) { | |
selExpr, ok := expr.(*ast.SelectorExpr) | |
if !ok { | |
return "", "", false | |
} | |
if !selExpr.Sel.IsExported() { | |
return "", "", false | |
} | |
pkgIdent, ok := selExpr.X.(*ast.Ident) | |
if !ok { | |
return "", "", false | |
} | |
pkg = pkgIdent.Name | |
symbol = selExpr.Sel.Name | |
if !isExistPkg(pkgIdent.Name) { | |
return "", "", false | |
} | |
return pkg, symbol, true | |
} | |
func isExitSymbol(pkg, symbol string) bool { | |
ps, ok := allPackage[pkg] | |
if !ok { | |
return false | |
} | |
for _, p := range ps { | |
if p.HasSymbol(symbol) { | |
return true | |
} | |
} | |
return false | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ go run findtypo.go `go list std` | |
"user.NewContext" at /usr/local/go/src/context/context.go:105:2 | |
"user.FromContext" at /usr/local/go/src/context/context.go:105:2 | |
"template.Attributes." at /usr/local/go/src/crypto/x509/x509.go:2088:3 | |
"driver.Preparer" at /usr/local/go/src/database/sql/sql.go:1130:2 | |
"time.Format3339Nano" at /usr/local/go/src/database/sql/sql.go:2335:1 | |
"runtime.Memstats" at /usr/local/go/src/expvar/expvar.go:5:1 | |
"io.ReaderCloser" at /usr/local/go/src/net/http/server.go:842:1 | |
"syscall.Note" at /usr/local/go/src/os/signal/doc.go:5:1 | |
"runtime.BitVector" at /usr/local/go/src/reflect/type.go:3159:1 | |
"atomic.Loaduintptr" at /usr/local/go/src/runtime/signal_unix.go:32:1 | |
"atomic.Storeuintptr." at /usr/local/go/src/runtime/signal_unix.go:32:1 | |
"atomic.Or8" at /usr/local/go/src/runtime/mbitmap.go:312:1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment