diff --git a/_js/README.md b/_js/README.md index 9a6d696f..529a93bb 100644 --- a/_js/README.md +++ b/_js/README.md @@ -7,7 +7,7 @@ It is transpiled from Go to JS using https://github.com/gopherjs/gopherjs. ### Sample usage -``` +```javascript const sh = require('mvdan-sh') const syntax = sh.syntax diff --git a/_js/main.go b/_js/main.go index 793d05be..7935e028 100644 --- a/_js/main.go +++ b/_js/main.go @@ -157,7 +157,7 @@ func throw(err error) { js.Global.Call("$throw", js.MakeFullWrapper(err)) } -// streamReader is an io.Reader wrapper for Node's stream.Readable. See +// streamReader is an [io.Reader] wrapper for Node's stream.Readable. See // https://nodejs.org/api/stream.html#stream_class_stream_readable // TODO: support https://streams.spec.whatwg.org/#rs-class too? type streamReader struct { diff --git a/cmd/shfmt/main.go b/cmd/shfmt/main.go index 37e40898..d1f6e0c8 100644 --- a/cmd/shfmt/main.go +++ b/cmd/shfmt/main.go @@ -88,7 +88,7 @@ var ( func init() { // TODO: the flag package has constructors like newBoolValue; - // if we had access to something like that, we could use flag.Value everywhere, + // if we had access to something like that, we could use [flag.Value] everywhere, // and avoid this monstrosity of a type switch. for _, f := range allFlags { switch f := f.(type) { @@ -356,7 +356,7 @@ func walkPath(path string, entry fs.DirEntry) error { // and we first want to tell if we should skip a path entirely. // // TODO: Should the call to Find with the language name check "ignore" too, then? - // Otherwise a [[bash]] section with ignore=true is effectively never used. + // Otherwise, a [[bash]] section with ignore=true is effectively never used. // // TODO: Should there be a way to explicitly turn off ignore rules when walking? // Perhaps swapping the default to --apply-ignore=auto and allowing --apply-ignore=false? diff --git a/expand/environ.go b/expand/environ.go index 17645119..05d8b55f 100644 --- a/expand/environ.go +++ b/expand/environ.go @@ -120,7 +120,7 @@ func (v Variable) Resolve(env Environ) (string, Variable) { } // FuncEnviron wraps a function mapping variable names to their string values, -// and implements Environ. Empty strings returned by the function will be +// and implements [Environ]. Empty strings returned by the function will be // treated as unset variables. All variables will be exported. // // Note that the returned Environ's Each method will be a no-op. @@ -140,7 +140,7 @@ func (f funcEnviron) Get(name string) Variable { func (f funcEnviron) Each(func(name string, vr Variable) bool) {} -// ListEnviron returns an Environ with the supplied variables, in the form +// ListEnviron returns an [Environ] with the supplied variables, in the form // "key=value". All variables will be exported. The last value in pairs is used // if multiple values are present. // @@ -150,7 +150,7 @@ func ListEnviron(pairs ...string) Environ { return listEnvironWithUpper(runtime.GOOS == "windows", pairs...) } -// listEnvironWithUpper implements ListEnviron, but letting the tests specify +// listEnvironWithUpper implements [ListEnviron], but letting the tests specify // whether to uppercase all names or not. func listEnvironWithUpper(upper bool, pairs ...string) Environ { list := slices.Clone(pairs) diff --git a/expand/expand.go b/expand/expand.go index bc60e21d..a8c0fea8 100644 --- a/expand/expand.go +++ b/expand/expand.go @@ -39,7 +39,7 @@ type Config struct { Env Environ // CmdSubst expands a command substitution node, writing its standard - // output to the provided io.Writer. + // output to the provided [io.Writer]. // // If nil, encountering a command substitution will result in an // UnexpectedCommandError. @@ -58,9 +58,9 @@ type Config struct { // Deprecated: use ReadDir2 instead. ReadDir func(string) ([]fs.FileInfo, error) - // ReadDir is used for file path globbing. - // If nil, and ReadDir is nil as well, globbing is disabled. - // Use os.ReadDir to use the filesystem directly. + // ReadDir2 is used for file path globbing. + // If nil, and [ReadDir] is nil as well, globbing is disabled. + // Use [os.ReadDir] to use the filesystem directly. ReadDir2 func(string) ([]fs.DirEntry, error) // GlobStar corresponds to the shell option that allows globbing with @@ -164,7 +164,7 @@ func (cfg *Config) envSet(name, value string) error { return wenv.Set(name, Variable{Kind: String, Str: value}) } -// Literal expands a single shell word. It is similar to Fields, but the result +// Literal expands a single shell word. It is similar to [Fields], but the result // is a single string. This is the behavior when a word is used as the value in // a shell variable assignment, for example. // @@ -781,7 +781,7 @@ func (cfg *Config) expandUser(field string) (prefix, rest string) { if name == "" { // Current user; try via "HOME", otherwise fall back to the // system's appropriate home dir env var. Don't use os/user, as - // that's overkill. We can't use os.UserHomeDir, because we want + // that's overkill. We can't use [os.UserHomeDir], because we want // to use cfg.Env, and we always want to check "HOME" first. if vr := cfg.Env.Get("HOME"); vr.IsSet() { @@ -883,15 +883,15 @@ func (cfg *Config) glob(base, pat string) ([]string, error) { match = filepath.Join(base, match) } match = pathJoin2(match, part) - // We can't use ReadDir2 on the parent and match the directory + // We can't use [Config.ReadDir2] on the parent and match the directory // entry by name, because short paths on Windows break that. - // Our only option is to ReadDir2 on the directory entry itself, + // Our only option is to [Config.ReadDir2] on the directory entry itself, // which can be wasteful if we only want to see if it exists, // but at least it's correct in all scenarios. if _, err := cfg.ReadDir2(match); err != nil { if isWindowsErrPathNotFound(err) { - // Unfortunately, os.File.Readdir on a regular file on - // Windows returns an error that satisfies ErrNotExist. + // Unfortunately, [os.File.Readdir] on a regular file on + // Windows returns an error that satisfies [fs.ErrNotExist]. // Luckily, it returns a special "path not found" rather // than the normal "file not found" for missing files, // so we can use that knowledge to work around the bug. @@ -977,10 +977,10 @@ func (cfg *Config) globDir(base, dir string, rx *regexp.Regexp, matchHidden bool // No filtering. } else if mode := info.Type(); mode&os.ModeSymlink != 0 { // We need to know if the symlink points to a directory. - // This requires an extra syscall, as ReadDir on the parent directory + // This requires an extra syscall, as [Config.ReadDir] on the parent directory // does not follow symlinks for each of the directory entries. // ReadDir is somewhat wasteful here, as we only want its error result, - // but we could try to reuse its result as per the TODO in Config.glob. + // but we could try to reuse its result as per the TODO in [Config.glob]. if _, err := cfg.ReadDir2(filepath.Join(fullDir, info.Name())); err != nil { continue } diff --git a/fileutil/file.go b/fileutil/file.go index d69a2b9b..ae82a64e 100644 --- a/fileutil/file.go +++ b/fileutil/file.go @@ -49,8 +49,8 @@ const ( ConfNotScript ScriptConfidence = iota // ConfIfShebang describes files which might be shell scripts, depending - // on the shebang line in the file's contents. Since CouldBeScript only - // works on fs.FileInfo, the answer in this case can't be final. + // on the shebang line in the file's contents. Since [CouldBeScript] only + // works on [fs.FileInfo], the answer in this case can't be final. ConfIfShebang // ConfIsScript describes files which are definitely shell scripts, @@ -60,7 +60,7 @@ const ( // CouldBeScript is a shortcut for CouldBeScript2(fs.FileInfoToDirEntry(info)). // -// Deprecated: prefer CouldBeScript2, which usually requires fewer syscalls. +// Deprecated: prefer [CouldBeScript2], which usually requires fewer syscalls. func CouldBeScript(info fs.FileInfo) ScriptConfidence { return CouldBeScript2(fs.FileInfoToDirEntry(info)) } diff --git a/interp/api.go b/interp/api.go index dfc78a3d..3b7913e1 100644 --- a/interp/api.go +++ b/interp/api.go @@ -75,7 +75,7 @@ type Runner struct { // execHandler is responsible for executing programs. It must not be nil. execHandler ExecHandlerFunc - // execMiddlewares grows with calls to ExecHandlers, + // execMiddlewares grows with calls to [ExecHandlers], // and is used to construct execHandler when Reset is first called. // The slice is needed to preserve the relative order of middlewares. execMiddlewares []func(ExecHandlerFunc) ExecHandlerFunc @@ -379,12 +379,12 @@ func ExecHandlers(middlewares ...func(next ExecHandlerFunc) ExecHandlerFunc) Run } } -// TODO: consider porting the middleware API in ExecHandlers to OpenHandler, +// TODO: consider porting the middleware API in [ExecHandlers] to [OpenHandler], // ReadDirHandler, and StatHandler. -// TODO(v4): now that ExecHandlers allows calling a next handler with changed -// arguments, one of the two advantages of CallHandler is gone. The other is the -// ability to work with builtins; if we make ExecHandlers work with builtins, we +// TODO(v4): now that [ExecHandlers] allows calling a next handler with changed +// arguments, one of the two advantages of [CallHandler] is gone. The other is the +// ability to work with builtins; if we make [ExecHandlers] work with builtins, we // could join both APIs. // OpenHandler sets file open handler. See [OpenHandlerFunc] for more info. @@ -457,7 +457,7 @@ func stdinFile(r io.Reader) (*os.File, error) { // Note that providing a non-nil standard input other than [os.File] will require // an [os.Pipe] and spawning a goroutine to copy into it, // as an [os.File] is the only way to share a reader with subprocesses. -// See [os/exec.Cmd.Stdin]. +// See os/[exec.Cmd.Stdin]. func StdIO(in io.Reader, out, err io.Writer) RunnerOption { return func(r *Runner) error { stdin, _err := stdinFile(in) @@ -632,7 +632,7 @@ var bashOptsTable = [...]bashOpt{ // know which option we're after at compile time. First come the shell options, // then the bash options. const ( - // These correspond to indexes in shellOptsTable + // These correspond to indexes in [shellOptsTable] optAllExport = iota optErrExit optNoExec @@ -642,7 +642,7 @@ const ( optPipeFail // These correspond to indexes (offset by the above seven items) of - // supported options in bashOptsTable + // supported options in [bashOptsTable] optExpandAliases optGlobStar optNoCaseGlob @@ -690,7 +690,7 @@ func (r *Runner) Reset() { readDirHandler: r.readDirHandler, statHandler: r.statHandler, - // These can be set by functions like Dir or Params, but + // These can be set by functions like [Dir] or [Params], but // builtins can overwrite them; reset the fields to whatever the // constructor set up. Dir: r.origDir, @@ -772,15 +772,15 @@ func IsExitStatus(err error) (status uint8, ok bool) { return 0, false } -// Run interprets a node, which can be a *File, *Stmt, or Command. If a non-nil +// Run interprets a node, which can be a [*File], [*Stmt], or [Command]. If a non-nil // error is returned, it will typically contain a command's exit status, which -// can be retrieved with IsExitStatus. +// can be retrieved with [IsExitStatus]. // // Run can be called multiple times synchronously to interpret programs -// incrementally. To reuse a Runner without keeping the internal shell state, +// incrementally. To reuse a [Runner] without keeping the internal shell state, // call Reset. // -// Calling Run on an entire *File implies an exit, meaning that an exit trap may +// Calling Run on an entire [*File] implies an exit, meaning that an exit trap may // run. func (r *Runner) Run(ctx context.Context, node syntax.Node) error { if !r.didReset { @@ -825,12 +825,12 @@ func (r *Runner) Exited() bool { return r.shellExited } -// Subshell makes a copy of the given Runner, suitable for use concurrently +// Subshell makes a copy of the given [Runner], suitable for use concurrently // with the original. The copy will have the same environment, including // variables and functions, but they can all be modified without affecting the // original. // -// Subshell is not safe to use concurrently with Run. Orchestrating this is +// Subshell is not safe to use concurrently with [Run]. Orchestrating this is // left up to the caller; no locking is performed. // // To replace e.g. stdin/out/err, do StdIO(r.stdin, r.stdout, r.stderr)(r) on @@ -840,7 +840,7 @@ func (r *Runner) Subshell() *Runner { r.Reset() } // Keep in sync with the Runner type. Manually copy fields, to not copy - // sensitive ones like errgroup.Group, and to do deep copies of slices. + // sensitive ones like [errgroup.Group], and to do deep copies of slices. r2 := &Runner{ Dir: r.Dir, Params: r.Params, diff --git a/interp/builtin.go b/interp/builtin.go index fcfcea94..7117bca6 100644 --- a/interp/builtin.go +++ b/interp/builtin.go @@ -947,7 +947,7 @@ func (r *Runner) readLine(ctx context.Context, raw bool) ([]byte, error) { // [cancelreader.NewReader] may fail under some circumstances, such as r.stdin being // a regular file on Linux, in which case epoll returns an "operation not permitted" error // given that regular files can always be read immediately. Polling them makes no sense. - // As such, if cancelreader fails, fall back to no cancellation, meaning this is best-effort. + // As such, if [cancelreader] fails, fall back to no cancellation, meaning this is best-effort. // // TODO: it would be nice if the cancelreader library classified errors so that we could // safely handle "this file does not need polling" by skipping the polling as we do below diff --git a/interp/handler.go b/interp/handler.go index c617328e..8de56394 100644 --- a/interp/handler.go +++ b/interp/handler.go @@ -134,8 +134,9 @@ func DefaultExecHandler(killTimeout time.Duration) ExecHandlerFunc { switch err := err.(type) { case *exec.ExitError: - // Windows and Plan9 do not have support for syscall.WaitStatus - // with methods like Signaled and Signal, so for those, waitStatus is a no-op. + // Windows and Plan9 do not have support for [syscall.WaitStatus] + // with methods like Signaled and Signal, so for those, [waitStatus] is a no-op. + // Note: [waitStatus] is an alias [syscall.WaitStatus] if status, ok := err.Sys().(waitStatus); ok && status.Signaled() { if ctx.Err() != nil { return ctx.Err() @@ -209,7 +210,7 @@ func LookPath(env expand.Environ, file string) (string, error) { return LookPathDir(env.Get("PWD").String(), env, file) } -// LookPathDir is similar to [os/exec.LookPath], with the difference that it uses the +// LookPathDir is similar to os/[exec.LookPath], with the difference that it uses the // provided environment. env is used to fetch relevant environment variables // such as PWD and PATH. // @@ -218,7 +219,7 @@ func LookPathDir(cwd string, env expand.Environ, file string) (string, error) { return lookPathDir(cwd, env, file, findExecutable) } -// findAny defines a function to pass to lookPathDir. +// findAny defines a function to pass to [lookPathDir]. type findAny = func(dir string, file string, exts []string) (string, error) func lookPathDir(cwd string, env expand.Environ, file string, find findAny) (string, error) { @@ -254,7 +255,7 @@ func lookPathDir(cwd string, env expand.Environ, file string, find findAny) (str return "", fmt.Errorf("%q: executable file not found in $PATH", file) } -// scriptFromPathDir is similar to LookPathDir, with the difference that it looks +// scriptFromPathDir is similar to [LookPathDir], with the difference that it looks // for both executable and non-executable files. func scriptFromPathDir(cwd string, env expand.Environ, file string) (string, error) { return lookPathDir(cwd, env, file, findFile) diff --git a/interp/interp_test.go b/interp/interp_test.go index d2e5e4c1..32e2b3e6 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -26,7 +26,7 @@ import ( "mvdan.cc/sh/v3/syntax" ) -// runnerRunTimeout is the context timeout used by any tests calling Runner.Run. +// runnerRunTimeout is the context timeout used by any tests calling [Runner.Run]. // The timeout saves us from hangs or burning too much CPU if there are bugs. // All the test cases are designed to be inexpensive and stop in a very short // amount of time, so 5s should be plenty even for busy machines. diff --git a/interp/runner.go b/interp/runner.go index 4ddfeb6b..59573a5d 100644 --- a/interp/runner.go +++ b/interp/runner.go @@ -27,7 +27,7 @@ import ( const ( // shellReplyPS3Var, or PS3, is a special variable in Bash used by the select command, - // while the shell is awaiting for input. the default value is shellDefaultPS3 + // while the shell is awaiting for input. the default value is [shellDefaultPS3] shellReplyPS3Var = "PS3" // shellDefaultPS3, or #?, is PS3's default value shellDefaultPS3 = "#? " @@ -78,7 +78,7 @@ func (r *Runner) fillExpandConfig(ctx context.Context) { dir := os.TempDir() // We can't atomically create a random unused temporary FIFO. - // Similar to os.CreateTemp, + // Similar to [os.CreateTemp], // keep trying new random paths until one does not exist. // We use a uint64 because a uint32 easily runs into retries. var path string @@ -217,7 +217,7 @@ func (r *Runner) pattern(word *syntax.Word) string { return str } -// expandEnviron exposes Runner's variables to the expand package. +// expandEnviron exposes [Runner]'s variables to the expand package. type expandEnv struct { r *Runner } @@ -987,7 +987,7 @@ func (r *Runner) call(ctx context.Context, pos syntax.Pos, args []string) { r.inFunc = true // Functions run in a nested scope. - // Note that Runner.exec below does something similar. + // Note that [Runner.exec] below does something similar. origEnv := r.writeEnv r.writeEnv = &overlayEnviron{parent: r.writeEnv, funcScope: true} diff --git a/interp/test.go b/interp/test.go index 68109f20..ac20d724 100644 --- a/interp/test.go +++ b/interp/test.go @@ -179,7 +179,7 @@ func (r *Runner) unTest(ctx context.Context, op syntax.UnTestOperator, x string) f = r.stderr } if f, ok := f.(interface{ Fd() uintptr }); ok { - // Support Fd methods such as the one on *os.File. + // Support [os.File.Fd] methods such as the one on [*os.File]. return term.IsTerminal(int(f.Fd())) } // TODO: allow term.IsTerminal here too if running in the diff --git a/interp/unix_test.go b/interp/unix_test.go index c3dc33bf..ddb28645 100644 --- a/interp/unix_test.go +++ b/interp/unix_test.go @@ -54,7 +54,7 @@ func TestRunnerTerminalStdIO(t *testing.T) { r, _ := interp.New(interp.StdIO(secondaryReader, secondary, secondary)) go func() { - // To mimic os/exec.Cmd.Start, use a goroutine. + // To mimic os/[exec.Cmd.Start], use a goroutine. if err := r.Run(context.Background(), file); err != nil { t.Error(err) } diff --git a/interp/vars.go b/interp/vars.go index f7417bd7..1fee77ca 100644 --- a/interp/vars.go +++ b/interp/vars.go @@ -276,7 +276,7 @@ func stringIndex(index syntax.ArithmExpr) bool { return false } -// TODO: make assignVal and setVar consistent with the WriteEnviron interface +// TODO: make assignVal and [setVar] consistent with the [expand.WriteEnviron] interface func (r *Runner) assignVal(as *syntax.Assign, valType string) expand.Variable { prev := r.lookupVar(as.Name.Value) diff --git a/syntax/lexer.go b/syntax/lexer.go index b90e8e1c..f518cf19 100644 --- a/syntax/lexer.go +++ b/syntax/lexer.go @@ -397,7 +397,7 @@ func (p *Parser) extendedGlob() bool { func (p *Parser) peekBytes(s string) bool { peekEnd := int(p.bsp) + len(s) // TODO: This should loop for slow readers, e.g. those providing one byte at - // a time. Use a loop and test it with testing/iotest.OneByteReader. + // a time. Use a loop and test it with [testing/iotest.OneByteReader]. if peekEnd > len(p.bs) { p.fill() } @@ -818,7 +818,7 @@ func (p *Parser) newLit(r rune) { w := utf8.RuneLen(r) p.litBs = append(p.litBuf[:0], p.bs[p.bsp-uint(w):p.bsp]...) default: - // don't let r == utf8.RuneSelf go to the second case as RuneLen + // don't let r == utf8.RuneSelf go to the second case as [utf8.RuneLen] // would return -1 p.litBs = p.litBuf[:0] } diff --git a/syntax/nodes.go b/syntax/nodes.go index c900c074..0aea3b43 100644 --- a/syntax/nodes.go +++ b/syntax/nodes.go @@ -12,11 +12,11 @@ import ( // Node represents a syntax tree node. type Node interface { // Pos returns the position of the first character of the node. Comments - // are ignored, except if the node is a *File. + // are ignored, except if the node is a [*File]. Pos() Pos // End returns the position of the character immediately after the node. // If the character is a newline, the line number won't cross into the - // next line. Comments are ignored, except if the node is a *File. + // next line. Comments are ignored, except if the node is a [*File]. End() Pos } diff --git a/syntax/printer.go b/syntax/printer.go index b42f6f1b..37876e19 100644 --- a/syntax/printer.go +++ b/syntax/printer.go @@ -89,7 +89,7 @@ func Minify(enabled bool) PrinterOption { // newlines must still appear, such as those following comments or around // here-documents. // -// Print's trailing newline when given a *File is not affected by this option. +// Print's trailing newline when given a [*File] is not affected by this option. func SingleLine(enabled bool) PrinterOption { return func(p *Printer) { p.singleLine = enabled } } @@ -114,9 +114,9 @@ func NewPrinter(opts ...PrinterOption) *Printer { // Print "pretty-prints" the given syntax tree node to the given writer. Writes // to w are buffered. // -// The node types supported at the moment are *File, *Stmt, *Word, *Assign, any -// Command node, and any WordPart node. A trailing newline will only be printed -// when a *File is used. +// The node types supported at the moment are [*File], [*Stmt], [*Word], [*Assign], any +// [Command] node, and any WordPart node. A trailing newline will only be printed +// when a [*File] is used. func (p *Printer) Print(w io.Writer, node Node) error { p.reset() diff --git a/syntax/typedjson/json.go b/syntax/typedjson/json.go index 2400edea..4c9ee72f 100644 --- a/syntax/typedjson/json.go +++ b/syntax/typedjson/json.go @@ -71,7 +71,7 @@ func encodeValue(val reflect.Value) (reflect.Value, string) { enc.Elem().Field(0).SetString(tname) return enc, "" case reflect.Struct: - // Construct a new struct with an optional Type, Pos and End, + // Construct a new struct with an optional Type, [syntax.Node.Pos] and [syntax.Node.End], // and then all the visible fields which aren't positions. typ := val.Type() fields := []reflect.StructField{typeField, posField, endField} @@ -279,7 +279,7 @@ func decodeValue(val reflect.Value, enc any) error { fval := val.FieldByName(name) switch name { case "Type", "Pos", "End": - // Type is already used above. Pos and End came from method calls. + // Type is already used above. [syntax.Node.Pos] and [syntax.Node.End] came from method calls. continue } if !fval.IsValid() {