diff --git a/main.go b/main.go index 1877358e..9a219518 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( fzf "github.com/junegunn/fzf/src" "github.com/junegunn/fzf/src/protector" - "github.com/junegunn/fzf/src/util" ) var version string = "0.51" @@ -80,5 +79,5 @@ func main() { if err != nil { os.Stderr.WriteString(err.Error() + "\n") } - util.Exit(code) + os.Exit(code) } diff --git a/main_test.go b/main_test.go deleted file mode 100644 index 783f9e02..00000000 --- a/main_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/build" - "go/importer" - "go/parser" - "go/token" - "go/types" - "io/fs" - "os" - "os/exec" - "path/filepath" - "sort" - "strings" - "testing" -) - -func loadPackages(t *testing.T) []*build.Package { - // If GOROOT is not set, use `go env GOROOT` to determine it since it - // performs more work than just runtime.GOROOT(). For context, running - // the tests with the "-trimpath" flag causes GOROOT to not be set. - ctxt := &build.Default - if ctxt.GOROOT == "" { - cmd := exec.Command("go", "env", "GOROOT") - out, err := cmd.CombinedOutput() - out = bytes.TrimSpace(out) - if err != nil { - t.Fatalf("error running command: %q: %v\n%s", cmd.Args, err, out) - } - ctxt.GOROOT = string(out) - } - - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - var pkgs []*build.Package - seen := make(map[string]bool) - err = filepath.WalkDir(wd, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - name := d.Name() - if d.IsDir() { - if name == "" || name[0] == '.' || name[0] == '_' || name == "vendor" || name == "tmp" { - return filepath.SkipDir - } - return nil - } - if d.Type().IsRegular() && filepath.Ext(name) == ".go" && !strings.HasSuffix(name, "_test.go") { - dir := filepath.Dir(path) - if !seen[dir] { - pkg, err := ctxt.ImportDir(dir, build.ImportComment) - if err != nil { - return fmt.Errorf("%s: %s", dir, err) - } - if pkg.ImportPath == "" || pkg.ImportPath == "." { - importPath, err := filepath.Rel(wd, dir) - if err != nil { - t.Fatal(err) - } - pkg.ImportPath = filepath.ToSlash(filepath.Join("github.com/junegunn/fzf", importPath)) - } - - pkgs = append(pkgs, pkg) - seen[dir] = true - } - } - return nil - }) - if err != nil { - t.Fatal(err) - } - - sort.Slice(pkgs, func(i, j int) bool { - return pkgs[i].ImportPath < pkgs[j].ImportPath - }) - return pkgs -} - -var sourceImporter = importer.ForCompiler(token.NewFileSet(), "source", nil) - -func checkPackageForOsExit(t *testing.T, bpkg *build.Package, allowed map[string]int) (errOsExit bool) { - var files []*ast.File - fset := token.NewFileSet() - for _, name := range bpkg.GoFiles { - filename := filepath.Join(bpkg.Dir, name) - af, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) - if err != nil { - t.Fatal(err) - } - files = append(files, af) - } - - info := types.Info{ - Uses: make(map[*ast.Ident]types.Object), - } - conf := types.Config{ - Importer: sourceImporter, - } - _, err := conf.Check(bpkg.Name, fset, files, &info) - if err != nil { - t.Fatal(err) - } - - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - for id, obj := range info.Uses { - if obj.Pkg() != nil && obj.Pkg().Name() == "os" && obj.Name() == "Exit" { - pos := fset.Position(id.Pos()) - - name, err := filepath.Rel(wd, pos.Filename) - if err != nil { - t.Log(err) - name = pos.Filename - } - name = filepath.ToSlash(name) - - // Check if the usage is allowed - if allowed[name] > 0 { - allowed[name]-- - continue - } - - t.Errorf("os.Exit referenced at: %s:%d:%d", name, pos.Line, pos.Column) - errOsExit = true - } - } - return errOsExit -} - -// Enforce that src/util.Exit() is used instead of os.Exit by prohibiting -// references to it anywhere else in the fzf code base. -func TestOSExitNotAllowed(t *testing.T) { - if testing.Short() { - t.Skip("skipping: short test") - } - allowed := map[string]int{ - "main.go": 1, // os.Exit allowed 1 time in "main.go" - "src/util/atexit.go": 1, // os.Exit allowed 1 time in "atexit.go" - } - var errOsExit bool - for _, pkg := range loadPackages(t) { - t.Run(pkg.ImportPath, func(t *testing.T) { - if checkPackageForOsExit(t, pkg, allowed) { - errOsExit = true - } - }) - } - if t.Failed() && errOsExit { - var names []string - for name := range allowed { - names = append(names, fmt.Sprintf("%q", name)) - } - sort.Strings(names) - - const errMsg = ` -Test failed because os.Exit was referenced outside of the following files: - - %s - -Use github.com/junegunn/fzf/src/util.Exit() instead to exit the program. -This is enforced because calling os.Exit() prevents the functions -registered with util.AtExit() from running.` - - t.Errorf(errMsg, strings.Join(names, "\n ")) - } -} diff --git a/src/util/atexit.go b/src/util/atexit.go index a1844517..6212378b 100644 --- a/src/util/atexit.go +++ b/src/util/atexit.go @@ -1,7 +1,6 @@ package util import ( - "os" "sync" ) @@ -27,13 +26,3 @@ func RunAtExitFuncs() { } atExitFuncs = nil } - -// Exit executes any functions registered with AtExit() then exits the program -// with os.Exit(code). -// -// NOTE: It must be used instead of os.Exit() since calling os.Exit() terminates -// the program before any of the AtExit functions can run. -func Exit(code int) { - defer os.Exit(code) - RunAtExitFuncs() -} diff --git a/src/util/util_unix.go b/src/util/util_unix.go index 4410a9bf..5a67066b 100644 --- a/src/util/util_unix.go +++ b/src/util/util_unix.go @@ -61,7 +61,7 @@ func (x *Executor) Become(stdin *os.File, environ []string, command string) { shellPath, err := exec.LookPath(x.shell) if err != nil { fmt.Fprintf(os.Stderr, "fzf (become): %s\n", err.Error()) - Exit(127) + os.Exit(127) } args := append([]string{shellPath}, append(x.args, command)...) SetStdin(stdin)