aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/internal/search/search.go
diff options
context:
space:
mode:
authorBryan C. Mills <bcmills@google.com>2020-02-27 15:52:32 -0500
committerBryan C. Mills <bcmills@google.com>2020-02-28 19:05:54 +0000
commitd11e1f92fc578c5d2e604acfe9ea60d7afb84a0c (patch)
tree7903355bf2672af603635dd928b8d1aad8967d22 /src/cmd/go/internal/search/search.go
parent156c60709e7775a6f1baa9685155b5974fefdea9 (diff)
downloadgo-d11e1f92fc578c5d2e604acfe9ea60d7afb84a0c.tar.gz
go-d11e1f92fc578c5d2e604acfe9ea60d7afb84a0c.zip
cmd/go/internal/search: consolidate package-pattern predicates into Match methods
This change consolidates predicates currently scattered throughout various parts of the package and module loader into methods on the search.Match type. That not only makes them more concise, but also encourages consistency, both in the code and in reasoning about the kinds of patterns that need to be handled. (For example, the IsLocal predicate was previously two different calls, either of which could be easily forgotten at a given call site.) Factored out from CL 185344 and CL 185345. Updates #32917 Change-Id: Ifa450ffaf6101f673e0ed69ced001a487d6f9335 Reviewed-on: https://go-review.googlesource.com/c/go/+/221458 Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org> Reviewed-by: Jay Conrod <jayconrod@google.com>
Diffstat (limited to 'src/cmd/go/internal/search/search.go')
-rw-r--r--src/cmd/go/internal/search/search.go131
1 files changed, 77 insertions, 54 deletions
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
index d8bb5723fb..69d0e2d16f 100644
--- a/src/cmd/go/internal/search/search.go
+++ b/src/cmd/go/internal/search/search.go
@@ -18,8 +18,7 @@ import (
// A Match represents the result of matching a single package pattern.
type Match struct {
- Pattern string // the pattern itself
- Literal bool // whether it is a literal (no wildcards)
+ pattern string // the pattern itself
Pkgs []string // matching packages (dirs or import paths)
Errs []error // errors matching the patterns to packages, NOT errors loading those packages
@@ -29,43 +28,81 @@ type Match struct {
// match any packages.
}
+// NewMatch returns a Match describing the given pattern,
+// without resolving its packages or errors.
+func NewMatch(pattern string) *Match {
+ return &Match{pattern: pattern}
+}
+
+// Pattern returns the pattern to be matched.
+func (m *Match) Pattern() string { return m.pattern }
+
// AddError appends a MatchError wrapping err to m.Errs.
func (m *Match) AddError(err error) {
m.Errs = append(m.Errs, &MatchError{Match: m, Err: err})
}
+// Literal reports whether the pattern is free of wildcards and meta-patterns.
+//
+// A literal pattern must match at most one package.
+func (m *Match) IsLiteral() bool {
+ return !strings.Contains(m.pattern, "...") && !m.IsMeta()
+}
+
+// Local reports whether the pattern must be resolved from a specific root or
+// directory, such as a filesystem path or a single module.
+func (m *Match) IsLocal() bool {
+ return build.IsLocalImport(m.pattern) || filepath.IsAbs(m.pattern)
+}
+
+// Meta reports whether the pattern is a “meta-package” keyword that represents
+// multiple packages, such as "std", "cmd", or "all".
+func (m *Match) IsMeta() bool {
+ return IsMetaPackage(m.pattern)
+}
+
+// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
+func IsMetaPackage(name string) bool {
+ return name == "std" || name == "cmd" || name == "all"
+}
+
// A MatchError indicates an error that occurred while attempting to match a
// pattern.
type MatchError struct {
- *Match
- Err error
+ Match *Match
+ Err error
}
func (e *MatchError) Error() string {
- if e.Literal {
- return fmt.Sprintf("matching %s: %v", e.Pattern, e.Err)
+ if e.Match.IsLiteral() {
+ return fmt.Sprintf("%s: %v", e.Match.Pattern(), e.Err)
}
- return fmt.Sprintf("pattern %s: %v", e.Pattern, e.Err)
+ return fmt.Sprintf("pattern %s: %v", e.Match.Pattern(), e.Err)
}
func (e *MatchError) Unwrap() error {
return e.Err
}
-// MatchPackages returns all the packages that can be found
+// MatchPackages sets m.Pkgs to contain all the packages that can be found
// under the $GOPATH directories and $GOROOT matching pattern.
// The pattern is either "all" (all packages), "std" (standard packages),
// "cmd" (standard commands), or a path including "...".
-func MatchPackages(pattern string) *Match {
- m := &Match{
- Pattern: pattern,
- Literal: false,
+//
+// MatchPackages sets m.Errs to contain any errors encountered while processing
+// the match.
+func (m *Match) MatchPackages() {
+ m.Pkgs, m.Errs = nil, nil
+ if m.IsLocal() {
+ m.AddError(fmt.Errorf("internal error: MatchPackages: %s is not a valid package pattern", m.pattern))
+ return
}
+
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
- if !IsMetaPackage(pattern) {
- match = MatchPattern(pattern)
- treeCanMatch = TreeCanMatchPattern(pattern)
+ if !m.IsMeta() {
+ match = MatchPattern(m.pattern)
+ treeCanMatch = TreeCanMatchPattern(m.pattern)
}
have := map[string]bool{
@@ -76,12 +113,12 @@ func MatchPackages(pattern string) *Match {
}
for _, src := range cfg.BuildContext.SrcDirs() {
- if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
+ if (m.pattern == "std" || m.pattern == "cmd") && src != cfg.GOROOTsrc {
continue
}
src = filepath.Clean(src) + string(filepath.Separator)
root := src
- if pattern == "cmd" {
+ if m.pattern == "cmd" {
root += "cmd" + string(filepath.Separator)
}
err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
@@ -97,7 +134,7 @@ func MatchPackages(pattern string) *Match {
}
name := filepath.ToSlash(path[len(src):])
- if pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") {
+ if m.pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") {
// The name "std" is only the standard library.
// If the name is cmd, it's the root of the command tree.
want = false
@@ -141,7 +178,7 @@ func MatchPackages(pattern string) *Match {
// packages under cmd/vendor. At least as of
// March, 2017, there is one there for the
// vendored pprof tool.
- if pattern == "cmd" && pkg != nil && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
+ if m.pattern == "cmd" && pkg != nil && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
return nil
}
@@ -152,7 +189,6 @@ func MatchPackages(pattern string) *Match {
m.AddError(err)
}
}
- return m
}
var modRoot string
@@ -166,19 +202,20 @@ func SetModRoot(dir string) {
// use slash or backslash separators or a mix of both.
//
// MatchPackagesInFS scans the tree rooted at the directory that contains the
-// first "..." wildcard and returns a match with packages that
-func MatchPackagesInFS(pattern string) *Match {
- m := &Match{
- Pattern: pattern,
- Literal: false,
+// first "..." wildcard.
+func (m *Match) MatchPackagesInFS() {
+ m.Pkgs, m.Errs = nil, nil
+ if !m.IsLocal() {
+ m.AddError(fmt.Errorf("internal error: MatchPackagesInFS: %s is not a valid filesystem pattern", m.pattern))
+ return
}
// Clean the path and create a matching predicate.
// filepath.Clean removes "./" prefixes (and ".\" on Windows). We need to
// preserve these, since they are meaningful in MatchPattern and in
// returned import paths.
- cleanPattern := filepath.Clean(pattern)
- isLocal := strings.HasPrefix(pattern, "./") || (os.PathSeparator == '\\' && strings.HasPrefix(pattern, `.\`))
+ cleanPattern := filepath.Clean(m.pattern)
+ isLocal := strings.HasPrefix(m.pattern, "./") || (os.PathSeparator == '\\' && strings.HasPrefix(m.pattern, `.\`))
prefix := ""
if cleanPattern != "." && isLocal {
prefix = "./"
@@ -203,11 +240,11 @@ func MatchPackagesInFS(pattern string) *Match {
abs, err := filepath.Abs(dir)
if err != nil {
m.AddError(err)
- return m
+ return
}
if !hasFilepathPrefix(abs, modRoot) {
m.AddError(fmt.Errorf("directory %s is outside module root (%s)", abs, modRoot))
- return m
+ return
}
}
@@ -270,7 +307,6 @@ func MatchPackagesInFS(pattern string) *Match {
if err != nil {
m.AddError(err)
}
- return m
}
// TreeCanMatchPattern(pattern)(name) reports whether
@@ -361,7 +397,7 @@ func replaceVendor(x, repl string) string {
func WarnUnmatched(matches []*Match) {
for _, m := range matches {
if len(m.Pkgs) == 0 && len(m.Errs) == 0 {
- fmt.Fprintf(os.Stderr, "go: warning: %q matched no packages\n", m.Pattern)
+ fmt.Fprintf(os.Stderr, "go: warning: %q matched no packages\n", m.pattern)
}
}
}
@@ -378,17 +414,12 @@ func ImportPaths(patterns []string) []*Match {
func ImportPathsQuiet(patterns []string) []*Match {
var out []*Match
for _, a := range CleanPatterns(patterns) {
- if IsMetaPackage(a) {
- out = append(out, MatchPackages(a))
- continue
- }
-
- if build.IsLocalImport(a) || filepath.IsAbs(a) {
- var m *Match
- if strings.Contains(a, "...") {
- m = MatchPackagesInFS(a)
+ m := NewMatch(a)
+ if m.IsLocal() {
+ if m.IsLiteral() {
+ m.Pkgs = []string{a}
} else {
- m = &Match{Pattern: a, Literal: true, Pkgs: []string{a}}
+ m.MatchPackagesInFS()
}
// Change the file import path to a regular import path if the package
@@ -402,16 +433,13 @@ func ImportPathsQuiet(patterns []string) []*Match {
m.Pkgs[i] = bp.ImportPath
}
}
- out = append(out, m)
- continue
- }
-
- if strings.Contains(a, "...") {
- out = append(out, MatchPackages(a))
- continue
+ } else if m.IsLiteral() {
+ m.Pkgs = []string{a}
+ } else {
+ m.MatchPackages()
}
- out = append(out, &Match{Pattern: a, Literal: true, Pkgs: []string{a}})
+ out = append(out, m)
}
return out
}
@@ -463,11 +491,6 @@ func CleanPatterns(patterns []string) []string {
return out
}
-// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
-func IsMetaPackage(name string) bool {
- return name == "std" || name == "cmd" || name == "all"
-}
-
// hasPathPrefix reports whether the path s begins with the
// elements in prefix.
func hasPathPrefix(s, prefix string) bool {