import (
"bytes"
"flag"
+ "go/build"
+ "os"
+ "path/filepath"
"regexp"
"runtime"
"strings"
}
}
+type isDotSlashTest struct {
+ str string
+ result bool
+}
+
+var isDotSlashTests = []isDotSlashTest{
+ {``, false},
+ {`x`, false},
+ {`...`, false},
+ {`.../`, false},
+ {`...\`, false},
+
+ {`.`, true},
+ {`./`, true},
+ {`.\`, true},
+ {`./x`, true},
+ {`.\x`, true},
+
+ {`..`, true},
+ {`../`, true},
+ {`..\`, true},
+ {`../x`, true},
+ {`..\x`, true},
+}
+
+func TestIsDotSlashPath(t *testing.T) {
+ for _, test := range isDotSlashTests {
+ if result := isDotSlash(test.str); result != test.result {
+ t.Errorf("isDotSlash(%q) = %t; expected %t", test.str, result, test.result)
+ }
+ }
+}
+
type test struct {
name string
args []string // Arguments to "[go] doc".
}
}
+// Test the code to look up packages when the first argument starts with "./".
+// Our test case is in effect "cd src/text; doc ./template". This should get
+// text/template but before Issue 23383 was fixed would give html/template.
+func TestDotSlashLookup(t *testing.T) {
+ if testing.Short() {
+ t.Skip("scanning file system takes too long")
+ }
+ maybeSkip(t)
+ where := pwd()
+ defer func() {
+ if err := os.Chdir(where); err != nil {
+ t.Fatal(err)
+ }
+ }()
+ if err := os.Chdir(filepath.Join(build.Default.GOROOT, "src", "text")); err != nil {
+ t.Fatal(err)
+ }
+ var b bytes.Buffer
+ var flagSet flag.FlagSet
+ err := do(&b, &flagSet, []string{"./template"})
+ if err != nil {
+ t.Errorf("unexpected error %q from ./template", err)
+ }
+ // The output should contain information about the text/template package.
+ const want = `package template // import "text/template"`
+ output := b.String()
+ if !strings.HasPrefix(output, want) {
+ t.Fatalf("wrong package: %.*q...", len(want), output)
+ }
+}
+
type trimTest struct {
path string
prefix string
// is rand.Float64, we must scan both crypto/rand and math/rand
// to find the symbol, and the first call will return crypto/rand, true.
func parseArgs(args []string) (pkg *build.Package, path, symbol string, more bool) {
+ if len(args) == 0 {
+ // Easy: current directory.
+ return importDir(pwd()), "", "", false
+ }
+ arg := args[0]
+ // We have an argument. If it is a directory name beginning with . or ..,
+ // use the absolute path name. This discriminates "./errors" from "errors"
+ // if the current directory contains a non-standard errors package.
+ if isDotSlash(arg) {
+ arg = filepath.Join(pwd(), arg)
+ }
switch len(args) {
default:
usage()
- case 0:
- // Easy: current directory.
- return importDir(pwd()), "", "", false
case 1:
// Done below.
case 2:
// Package must be findable and importable.
- packagePath, ok := findPackage(args[0])
+ packagePath, ok := findPackage(arg)
if !ok {
return nil, args[0], args[1], false
}
- return importDir(packagePath), args[0], args[1], true
+ return importDir(packagePath), arg, args[1], true
}
// Usual case: one argument.
- arg := args[0]
// If it contains slashes, it begins with a package path.
// First, is it a complete package path as it is? If so, we are done.
// This avoids confusion over package paths that have other
return importDir(pwd()), "", arg, false
}
+// dotPaths lists all the dotted paths legal on Unix-like and
+// Windows-like file systems. We check them all, as the chance
+// of error is minute and even on Windows people will use ./
+// sometimes.
+var dotPaths = []string{
+ `./`,
+ `../`,
+ `.\`,
+ `..\`,
+}
+
+// isDotSlash reports whether the path begins with a reference
+// to the local . or .. directory.
+func isDotSlash(arg string) bool {
+ if arg == "." || arg == ".." {
+ return true
+ }
+ for _, dotPath := range dotPaths {
+ if strings.HasPrefix(arg, dotPath) {
+ return true
+ }
+ }
+ return false
+}
+
// importDir is just an error-catching wrapper for build.ImportDir.
func importDir(dir string) *build.Package {
pkg, err := build.ImportDir(dir, build.ImportComment)