eval: Fix lint errors.

This commit is contained in:
Qi Xiao 2019-04-18 22:15:34 +01:00
parent a8a6c71fbd
commit c8e767b2fd
21 changed files with 94 additions and 51 deletions

View File

@ -11,7 +11,13 @@ import (
) )
var ( var (
ErrArgs = errors.New("args error") // ErrArgs is thrown when a builtin function gets erroneous arguments.
//
// TODO(xiaq): Replace this single error type with multiple types that carry
// richer error information.
ErrArgs = errors.New("args error")
// ErrNoOptAccepted is thrown when a builtin function that does not accept
// any options gets passed options.
ErrNoOptAccepted = errors.New("function does not accept any options") ErrNoOptAccepted = errors.New("function does not accept any options")
) )
@ -73,6 +79,9 @@ type optionsPtr interface {
SetDefaultOptions() SetDefaultOptions()
} }
// Inputs is the type that the last parameter of a builtin function can take.
// When that is the case, it is a callback to get inputs. See the doc of
// BuiltinFn for details.
type Inputs func(func(interface{})) type Inputs func(func(interface{}))
var ( var (

View File

@ -9,8 +9,6 @@ import (
// Command and process control. // Command and process control.
var ErrNotInSameGroup = errors.New("not in the same process group")
func init() { func init() {
addBuiltinFns(map[string]interface{}{ addBuiltinFns(map[string]interface{}{
// Command resolution // Command resolution

View File

@ -3,6 +3,7 @@
package eval package eval
import ( import (
"errors"
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
@ -12,6 +13,10 @@ import (
"github.com/elves/elvish/sys" "github.com/elves/elvish/sys"
) )
// ErrNotInSameProcessGroup is thrown when the process IDs passed to fg are not
// in the same process group.
var ErrNotInSameProcessGroup = errors.New("not in the same process group")
func execFn(fm *Frame, args ...interface{}) error { func execFn(fm *Frame, args ...interface{}) error {
var argstrings []string var argstrings []string
if len(args) == 0 { if len(args) == 0 {
@ -47,7 +52,7 @@ func fg(pids ...int) error {
if i == 0 { if i == 0 {
thepgid = pgid thepgid = pgid
} else if pgid != thepgid { } else if pgid != thepgid {
return ErrNotInSameGroup return ErrNotInSameProcessGroup
} }
} }

View File

@ -177,17 +177,16 @@ func hasKey(container, key interface{}) (bool, error) {
// XXX(xiaq): Not all types that implement Lener have numerical indices // XXX(xiaq): Not all types that implement Lener have numerical indices
_, err := vals.ConvertListIndex(key, len) _, err := vals.ConvertListIndex(key, len)
return err == nil, nil return err == nil, nil
} else { }
var found bool var found bool
err := vals.IterateKeys(container, func(k interface{}) bool { err := vals.IterateKeys(container, func(k interface{}) bool {
if key == k { if key == k {
found = true found = true
}
return !found
})
if err == nil {
return found, nil
} }
return !found
})
if err == nil {
return found, nil
} }
return false, fmt.Errorf("couldn't get key or index of type '%s'", vals.Kind(container)) return false, fmt.Errorf("couldn't get key or index of type '%s'", vals.Kind(container))
} }

View File

@ -12,6 +12,7 @@ import (
// Filesystem. // Filesystem.
// ErrStoreNotConnected is thrown by dir-history when the store is not connected.
var ErrStoreNotConnected = errors.New("store not connected") var ErrStoreNotConnected = errors.New("store not connected")
func init() { func init() {

View File

@ -208,7 +208,7 @@ func toJSON(fm *Frame, inputs Inputs) error {
func fopen(fm *Frame, name string) (vals.File, error) { func fopen(fm *Frame, name string) (vals.File, error) {
// TODO support opening files for writing etc as well. // TODO support opening files for writing etc as well.
f, err := os.Open(name) f, err := os.Open(name)
return vals.File{f}, err return vals.NewFile(f), err
} }
func fclose(f vals.File) error { func fclose(f vals.File) error {
@ -217,7 +217,7 @@ func fclose(f vals.File) error {
func pipe() (vals.Pipe, error) { func pipe() (vals.Pipe, error) {
r, w, err := os.Pipe() r, w, err := os.Pipe()
return vals.Pipe{r, w}, err return vals.NewPipe(r, w), err
} }
func prclose(p vals.Pipe) error { func prclose(p vals.Pipe) error {

View File

@ -69,14 +69,12 @@ func resolve(fm *Frame, head string) string {
_, special := builtinSpecials[head] _, special := builtinSpecials[head]
if special { if special {
return "special" return "special"
} else {
explode, ns, name := ParseVariableRef(head)
if !explode && fm.ResolveVar(ns, name+FnSuffix) != nil {
return "$" + head + FnSuffix
} else {
return "(external " + parse.Quote(head) + ")"
}
} }
explode, ns, name := ParseVariableRef(head)
if !explode && fm.ResolveVar(ns, name+FnSuffix) != nil {
return "$" + head + FnSuffix
}
return "(external " + parse.Quote(head) + ")"
} }
func source(fm *Frame, fname string) error { func source(fm *Frame, fname string) error {

View File

@ -15,7 +15,8 @@ import (
// String operations. // String operations.
var ErrInput = errors.New("input error") // ErrInputOfEawkMustBeString is thrown when eawk gets a non-string input.
var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string")
func init() { func init() {
addBuiltinFns(map[string]interface{}{ addBuiltinFns(map[string]interface{}{
@ -145,7 +146,7 @@ func eawk(fm *Frame, f Callable, inputs Inputs) error {
line, ok := v.(string) line, ok := v.(string)
if !ok { if !ok {
broken = true broken = true
err = ErrInput err = ErrInputOfEawkMustBeString
return return
} }
args := []interface{}{line} args := []interface{}{line}

View File

@ -427,7 +427,7 @@ func (op *ifOp) invoke(fm *Frame) error {
for i, bodyOp := range op.bodyOps { for i, bodyOp := range op.bodyOps {
bodies[i] = bodyOp.execlambdaOp(fm) bodies[i] = bodyOp.execlambdaOp(fm)
} }
else_ := op.elseOp.execlambdaOp(fm) elseFn := op.elseOp.execlambdaOp(fm)
for i, condOp := range op.condOps { for i, condOp := range op.condOps {
condValues, err := condOp.exec(fm.fork("if cond")) condValues, err := condOp.exec(fm.fork("if cond"))
if err != nil { if err != nil {
@ -438,7 +438,7 @@ func (op *ifOp) invoke(fm *Frame) error {
} }
} }
if op.elseOp.body != nil { if op.elseOp.body != nil {
return else_.Call(fm.fork("if else"), NoArgs, NoOpts) return elseFn.Call(fm.fork("if else"), NoArgs, NoOpts)
} }
return nil return nil
} }
@ -624,7 +624,7 @@ func (op *tryOp) invoke(fm *Frame) error {
return err return err
} }
except := op.exceptOp.execlambdaOp(fm) except := op.exceptOp.execlambdaOp(fm)
else_ := op.elseOp.execlambdaOp(fm) elseFn := op.elseOp.execlambdaOp(fm)
finally := op.finallyOp.execlambdaOp(fm) finally := op.finallyOp.execlambdaOp(fm)
err = fm.fork("try body").Call(body, NoArgs, NoOpts) err = fm.fork("try body").Call(body, NoArgs, NoOpts)
@ -639,8 +639,8 @@ func (op *tryOp) invoke(fm *Frame) error {
err = fm.fork("try except").Call(except, NoArgs, NoOpts) err = fm.fork("try except").Call(except, NoArgs, NoOpts)
} }
} else { } else {
if else_ != nil { if elseFn != nil {
err = fm.fork("try else").Call(else_, NoArgs, NoOpts) err = fm.fork("try else").Call(elseFn, NoArgs, NoOpts)
} }
} }
if finally != nil { if finally != nil {

View File

@ -16,7 +16,8 @@ import (
// supplies does not match with what is required. // supplies does not match with what is required.
var ErrArityMismatch = errors.New("arity mismatch") var ErrArityMismatch = errors.New("arity mismatch")
// Closure is a closure defined in elvish script. // Closure is a closure defined in Elvish script. Each closure has its unique
// identity.
type Closure struct { type Closure struct {
ArgNames []string ArgNames []string
// The name for the rest argument. If empty, the function has fixed arity. // The name for the rest argument. If empty, the function has fixed arity.
@ -37,11 +38,12 @@ func (*Closure) Kind() string {
return "fn" return "fn"
} }
// Equal compares by identity. // Equal compares by address.
func (c *Closure) Equal(rhs interface{}) bool { func (c *Closure) Equal(rhs interface{}) bool {
return c == rhs return c == rhs
} }
// Hash returns the hash of the address of the closure.
func (c *Closure) Hash() uint32 { func (c *Closure) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(c)) return hash.Pointer(unsafe.Pointer(c))
} }
@ -51,6 +53,9 @@ func (c *Closure) Repr(int) string {
return fmt.Sprintf("<closure %p>", c) return fmt.Sprintf("<closure %p>", c)
} }
// Index supports the introspection of the closure. Supported keys are
// "arg-names", "rest-arg", "opt-names", "opt-defaults", "body", "def" and
// "src".
func (c *Closure) Index(k interface{}) (interface{}, bool) { func (c *Closure) Index(k interface{}) (interface{}, bool) {
switch k { switch k {
case "arg-names": case "arg-names":
@ -71,6 +76,7 @@ func (c *Closure) Index(k interface{}) (interface{}, bool) {
return nil, false return nil, false
} }
// IterateKeys calls f with all the valid keys that can be used for Index.
func (c *Closure) IterateKeys(f func(interface{}) bool) { func (c *Closure) IterateKeys(f func(interface{}) bool) {
util.Feed(f, "arg-names", "rest-arg", util.Feed(f, "arg-names", "rest-arg",
"opt-names", "opt-defaults", "body", "def", "src") "opt-names", "opt-defaults", "body", "def", "src")

View File

@ -7,12 +7,13 @@ import (
"github.com/elves/elvish/diag" "github.com/elves/elvish/diag"
) )
// CompilationError represents a compilation error and can pretty print it. // CompilationError represents a compilation error. It can be pretty-printed.
type CompilationError struct { type CompilationError struct {
Message string Message string
Context diag.SourceRange Context diag.SourceRange
} }
// Error returns a plain text representation of the compilation error.
func (ce *CompilationError) Error() string { func (ce *CompilationError) Error() string {
return fmt.Sprintf("compilation error: %d-%d in %s: %s", return fmt.Sprintf("compilation error: %d-%d in %s: %s",
ce.Context.Begin, ce.Context.End, ce.Context.Name, ce.Message) ce.Context.Begin, ce.Context.End, ce.Context.Name, ce.Message)
@ -23,7 +24,7 @@ func (ce *CompilationError) Range() diag.Ranging {
return ce.Context.Range() return ce.Context.Range()
} }
// PPrint pretty-prints a compilation error. // PPrint pretty-prints the compilation error.
func (ce *CompilationError) PPrint(indent string) string { func (ce *CompilationError) PPrint(indent string) string {
var buf bytes.Buffer var buf bytes.Buffer

View File

@ -134,10 +134,9 @@ func (op *pipelineOp) invoke(fm *Frame) error {
} }
}() }()
return nil return nil
} else {
wg.Wait()
return ComposeExceptionsFromPipeline(errors)
} }
wg.Wait()
return ComposeExceptionsFromPipeline(errors)
} }
func (cp *compiler) form(n *parse.Form) effectOpBody { func (cp *compiler) form(n *parse.Form) effectOpBody {
@ -344,9 +343,8 @@ func (op *formOp) invoke(fm *Frame) (errRet error) {
if headFn != nil { if headFn != nil {
return headFn.Call(fm, args, convertedOpts) return headFn.Call(fm, args, convertedOpts)
} else {
return op.spaceyAssignOp.exec(fm)
} }
return op.spaceyAssignOp.exec(fm)
} }
func allTrue(vs []interface{}) bool { func allTrue(vs []interface{}) bool {

View File

@ -175,9 +175,8 @@ func (op *elemOp) invoke(fm *Frame) ([]vars.Var, error) {
level := vars.ElementErrorLevel(err) level := vars.ElementErrorLevel(err)
if level < 0 { if level < 0 {
return nil, fm.errorpf(op.begin, op.end, "%s", err) return nil, fm.errorpf(op.begin, op.end, "%s", err)
} else {
return nil, fm.errorpf(op.begin, op.ends[level], "%s", err)
} }
return nil, fm.errorpf(op.begin, op.ends[level], "%s", err)
} }
return []vars.Var{elemVar}, nil return []vars.Var{elemVar}, nil
} }

View File

@ -35,10 +35,12 @@ func Cause(err error) error {
// exception. // exception.
var OK = &Exception{} var OK = &Exception{}
// Error returns the message of the cause of the exception.
func (exc *Exception) Error() string { func (exc *Exception) Error() string {
return exc.Cause.Error() return exc.Cause.Error()
} }
// PPrint pretty-prints the exception.
func (exc *Exception) PPrint(indent string) string { func (exc *Exception) PPrint(indent string) string {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@ -73,10 +75,13 @@ func (exc *Exception) PPrint(indent string) string {
return buf.String() return buf.String()
} }
// Kind returns "exception".
func (exc *Exception) Kind() string { func (exc *Exception) Kind() string {
return "exception" return "exception"
} }
// Repr returns a representation of the exception. It is lossy in that it does
// not preserve the stacktrace.
func (exc *Exception) Repr(indent int) string { func (exc *Exception) Repr(indent int) string {
if exc.Cause == nil { if exc.Cause == nil {
return "$ok" return "$ok"
@ -87,19 +92,23 @@ func (exc *Exception) Repr(indent int) string {
return "?(fail " + parse.Quote(exc.Cause.Error()) + ")" return "?(fail " + parse.Quote(exc.Cause.Error()) + ")"
} }
// Equal compares by identity. // Equal compares by address.
func (exc *Exception) Equal(rhs interface{}) bool { func (exc *Exception) Equal(rhs interface{}) bool {
return exc == rhs return exc == rhs
} }
// Hash returns the hash of the address.
func (exc *Exception) Hash() uint32 { func (exc *Exception) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(exc)) return hash.Pointer(unsafe.Pointer(exc))
} }
// Bool returns whether this exception has a nil cause; that is, it is $ok.
func (exc *Exception) Bool() bool { func (exc *Exception) Bool() bool {
return exc.Cause == nil return exc.Cause == nil
} }
// Index supports introspection of the exception. Currently the only supported
// key is "cause".
func (exc *Exception) Index(k interface{}) (interface{}, bool) { func (exc *Exception) Index(k interface{}) (interface{}, bool) {
// TODO: Access to Traceback // TODO: Access to Traceback
switch k { switch k {
@ -110,6 +119,7 @@ func (exc *Exception) Index(k interface{}) (interface{}, bool) {
} }
} }
// IterateKeys calls f with all the valid keys that can be used in Index.
func (exc *Exception) IterateKeys(f func(interface{}) bool) { func (exc *Exception) IterateKeys(f func(interface{}) bool) {
util.Feed(f, "cause") util.Feed(f, "cause")
} }
@ -120,6 +130,7 @@ type PipelineError struct {
Errors []*Exception Errors []*Exception
} }
// Repr returns a representation of the pipeline error, using the multi-error builtin.
func (pe PipelineError) Repr(indent int) string { func (pe PipelineError) Repr(indent int) string {
// TODO Make a more generalized ListReprBuilder and use it here. // TODO Make a more generalized ListReprBuilder and use it here.
b := new(bytes.Buffer) b := new(bytes.Buffer)
@ -137,6 +148,7 @@ func (pe PipelineError) Repr(indent int) string {
return b.String() return b.String()
} }
// Error returns a plain text representation of the pipeline error.
func (pe PipelineError) Error() string { func (pe PipelineError) Error() string {
b := new(bytes.Buffer) b := new(bytes.Buffer)
b.WriteString("(") b.WriteString("(")
@ -197,6 +209,7 @@ var flowNames = [...]string{
"return", "break", "continue", "return", "break", "continue",
} }
// Repr returns a representation of the flow "error".
func (f Flow) Repr(int) string { func (f Flow) Repr(int) string {
return "?(" + f.Error() + ")" return "?(" + f.Error() + ")"
} }
@ -208,6 +221,7 @@ func (f Flow) Error() string {
return flowNames[f] return flowNames[f]
} }
// PPrint pretty-prints the flow "error".
func (f Flow) PPrint(string) string { func (f Flow) PPrint(string) string {
return "\033[33;1m" + f.Error() + "\033[m" return "\033[33;1m" + f.Error() + "\033[m"
} }
@ -221,6 +235,8 @@ type ExternalCmdExit struct {
Pid int Pid int
} }
// NewExternalCmdExit constructs an error for representing a non-zero exit from
// an external command.
func NewExternalCmdExit(name string, ws syscall.WaitStatus, pid int) error { func NewExternalCmdExit(name string, ws syscall.WaitStatus, pid int) error {
if ws.Exited() && ws.ExitStatus() == 0 { if ws.Exited() && ws.ExitStatus() == 0 {
return nil return nil

View File

@ -14,8 +14,13 @@ import (
) )
var ( var (
// ErrExternalCmdOpts is thrown when an external command is passed Elvish
// options.
//
// TODO: Catch this kind of errors at compilation time.
ErrExternalCmdOpts = errors.New("external commands don't accept elvish options") ErrExternalCmdOpts = errors.New("external commands don't accept elvish options")
ErrCdNoArg = errors.New("implicit cd accepts no arguments") // ErrImplicitCdNoArg is thrown when an implicit cd form is passed arguments.
ErrImplicitCdNoArg = errors.New("implicit cd accepts no arguments")
) )
// ExternalCmd is an external command. // ExternalCmd is an external command.
@ -49,7 +54,7 @@ func (e ExternalCmd) Call(fm *Frame, argVals []interface{}, opts map[string]inte
if err == nil && stat.IsDir() { if err == nil && stat.IsDir() {
// implicit cd // implicit cd
if len(argVals) > 0 { if len(argVals) > 0 {
return ErrCdNoArg return ErrImplicitCdNoArg
} }
return fm.Chdir(e.Name) return fm.Chdir(e.Name)
} }

View File

@ -12,6 +12,7 @@ func (fm *Frame) Interrupts() <-chan struct{} {
return fm.intCh return fm.intCh
} }
// ErrInterrupted is thrown when the execution is interrupted by a signal.
var ErrInterrupted = errors.New("interrupted") var ErrInterrupted = errors.New("interrupted")
// IsInterrupted reports whether there has been an interrupt. // IsInterrupted reports whether there has been an interrupt.

View File

@ -9,6 +9,8 @@ import (
"github.com/elves/elvish/util" "github.com/elves/elvish/util"
) )
// RawOptions is the type of an argument a builtin function can take to declare
// that it wants to parse options itself. See the doc of BuiltinFn for details.
type RawOptions map[string]interface{} type RawOptions map[string]interface{}
// Takes a raw option map and a pointer to a struct, and populate the struct // Takes a raw option map and a pointer to a struct, and populate the struct

View File

@ -38,7 +38,7 @@ var (
BlackholeChan = make(chan interface{}) BlackholeChan = make(chan interface{})
// DevNull is /dev/null. // DevNull is /dev/null.
DevNull *os.File DevNull *os.File
// DevNullClosedInput is a port made up from DevNull and ClosedChan, // DevNullClosedChan is a port made up from DevNull and ClosedChan,
// suitable as placeholder input port. // suitable as placeholder input port.
DevNullClosedChan *Port DevNullClosedChan *Port
) )

View File

@ -5,12 +5,13 @@ import "syscall"
// Nop on Windows. // Nop on Windows.
func putSelfInFg() error { return nil } func putSelfInFg() error { return nil }
const DETACHED_PROCESS = 0x00000008 // The bitmask for CreationFlags in SysProcAttr to start a process in background.
const detachedProcess = 0x00000008
func makeSysProcAttr(bg bool) *syscall.SysProcAttr { func makeSysProcAttr(bg bool) *syscall.SysProcAttr {
flags := uint32(0) flags := uint32(0)
if bg { if bg {
flags |= DETACHED_PROCESS flags |= detachedProcess
} }
return &syscall.SysProcAttr{CreationFlags: flags} return &syscall.SysProcAttr{CreationFlags: flags}
} }

View File

@ -14,6 +14,8 @@ type PwdVariable struct {
var _ vars.Var = PwdVariable{} var _ vars.Var = PwdVariable{}
// Get returns the current working directory. It returns /unknown/pwd when it
// cannot be determined.
func (PwdVariable) Get() interface{} { func (PwdVariable) Get() interface{} {
pwd, err := os.Getwd() pwd, err := os.Getwd()
// TODO: Deprecate the $pwd variable. // TODO: Deprecate the $pwd variable.
@ -23,6 +25,7 @@ func (PwdVariable) Get() interface{} {
return pwd return pwd
} }
// Set changes the current working directory.
func (pwd PwdVariable) Set(v interface{}) error { func (pwd PwdVariable) Set(v interface{}) error {
path, ok := v.(string) path, ok := v.(string)
if !ok { if !ok {

View File

@ -69,8 +69,8 @@ func (t TestCase) Puts(vs ...interface{}) TestCase {
return t return t
} }
// Puts returns an altered Test that requires the source code to produce the // PutsStrings returns an altered Test that requires the source code to produce
// specified strings in the value channel when evaluated. // the specified strings in the value channel when evaluated.
func (t TestCase) PutsStrings(ss []string) TestCase { func (t TestCase) PutsStrings(ss []string) TestCase {
t.want.out = make([]interface{}, len(ss)) t.want.out = make([]interface{}, len(ss))
for i, s := range ss { for i, s := range ss {
@ -105,8 +105,8 @@ func Test(t *testing.T, tests ...TestCase) {
TestWithSetup(t, func(*Evaler) {}, tests...) TestWithSetup(t, func(*Evaler) {}, tests...)
} }
// Test runs test cases. For each test case, a new Evaler is created with // TestWithSetup runs test cases. For each test case, a new Evaler is created
// NewEvaler and passed to the setup function. // with NewEvaler and passed to the setup function.
func TestWithSetup(t *testing.T, setup func(*Evaler), tests ...TestCase) { func TestWithSetup(t *testing.T, setup func(*Evaler), tests ...TestCase) {
for _, tt := range tests { for _, tt := range tests {
ev := NewEvaler() ev := NewEvaler()