Skip to content

Commit

Permalink
go/packages: use "=" instead of ":" for special queries
Browse files Browse the repository at this point in the history
This change updates the language accepted through the arguments
to packages.Load to make it more consistent. There are now two layers.
A pattern containing an "=" is considered to be a special query, and the
part of the pattern up to the first "=" is considered the query type. All
other patterns are to be interpreted as the build system interprets it.
For now two special queries exist. file= has the behavior that contains:
did: finding packages containing the given file. The query type pattern=
is used to pass through a pattern to be interpreted by the build system.
pattern= is a type of escaping to allow passing through patterns that
contain an "=" to be interpreted by the underlying buildsystem. To allow
for new query types to be introduced, packages.Load will report an
error if the qury type is not understood. We expect name= to be added in
an  upcoming change.

"contains:" changes to "file=". A new

Change-Id: I1b208d1c998c67d5556cdc872d7694273cedb7e4
Reviewed-on: https://go-review.googlesource.com/c/141681
Reviewed-by: Alan Donovan <adonovan@google.com>
  • Loading branch information
matloob committed Oct 15, 2018
1 parent 5e66757 commit 13ebad8
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 6 deletions.
26 changes: 23 additions & 3 deletions go/packages/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,29 @@ The Load function takes as input a list of patterns and return a list of Package
structs describing individual packages matched by those patterns.
The LoadMode controls the amount of detail in the loaded packages.
The patterns are used as arguments to the underlying build tool,
such as the go command or Bazel, and are interpreted according to
that tool's conventions.
Load passes most patterns directly to the underlying build tool,
but all patterns with the prefix "query=", where query is a
non-empty string of letters from [a-z], are reserved and may be
interpreted as query operators.
Only two query operators are currently supported, "file" and "pattern".
The query "file=path/to/file.go" matches the package or packages enclosing
the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
might returns the packages "fmt" and "fmt [fmt.test]".
The query "pattern=string" causes "string" to be passed directly to
the underlying build tool. In most cases this is unnecessary,
but an application can use Load("pattern=" + x) as an escaping mechanism
to ensure that x is not interpreted as a query operator if it contains '='.
A third query "name=identifier" will be added soon.
It will match packages whose package declaration contains the specified identifier.
For example, "name=rand" would match the packages "math/rand" and "crypto/rand",
and "name=main" would match all executables.
All other query operators are reserved for future use and currently
cause Load to report an error.
The Package struct provides basic information about the package, including
Expand Down
32 changes: 32 additions & 0 deletions go/packages/golist.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
// Determine files requested in contains patterns
var containFiles []string
restPatterns := make([]string, 0, len(patterns))
// Extract file= and other [querytype]= patterns. Report an error if querytype
// doesn't exist.
extractQueries:
for _, pattern := range patterns {
eqidx := strings.Index(pattern, "=")
if eqidx < 0 {
restPatterns = append(restPatterns, pattern)
} else {
query, value := pattern[:eqidx], pattern[eqidx+len("="):]
switch query {
case "file":
containFiles = append(containFiles, value)
case "pattern":
restPatterns = append(containFiles, value)
case "": // not a reserved query
restPatterns = append(restPatterns, pattern)
default:
for _, rune := range query {
if rune < 'a' || rune > 'z' { // not a reserved query
restPatterns = append(restPatterns, pattern)
continue extractQueries
}
}
// Reject all other patterns containing "="
return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
}
}
}
patterns = restPatterns
// Look for the deprecated contains: syntax.
// TODO(matloob): delete this around mid-October 2018.
restPatterns = restPatterns[:0]
for _, pattern := range patterns {
if strings.HasPrefix(pattern, "contains:") {
containFile := strings.TrimPrefix(pattern, "contains:")
Expand Down
46 changes: 43 additions & 3 deletions go/packages/packages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ func TestAbsoluteFilenames(t *testing.T) {
}
}

func TestContains(t *testing.T) {
func TestFile(t *testing.T) {
tmp, cleanup := makeTree(t, map[string]string{
"src/a/a.go": `package a; import "b"`,
"src/b/b.go": `package b; import "c"`,
Expand All @@ -1026,7 +1026,7 @@ func TestContains(t *testing.T) {
Dir: tmp,
Env: append(os.Environ(), "GOPATH="+tmp, "GO111MODULE=off"),
}
initial, err := packages.Load(cfg, "contains:src/b/b.go")
initial, err := packages.Load(cfg, "file=src/b/b.go")
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -1093,7 +1093,7 @@ func TestContains_FallbackSticks(t *testing.T) {
Dir: tmp,
Env: append(os.Environ(), "GOPATH="+tmp, "GO111MODULE=off"),
}
initial, err := packages.Load(cfg, "a", "contains:src/b/b.go")
initial, err := packages.Load(cfg, "a", "file=src/b/b.go")
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -1261,6 +1261,46 @@ func TestJSON(t *testing.T) {
}
}

func TestRejectInvalidQueries(t *testing.T) {
queries := []string{"=", "key=", "key=value", "file/a/b=c/..."}
cfg := &packages.Config{
Mode: packages.LoadImports,
Env: append(os.Environ(), "GO111MODULE=off"),
}
for _, q := range queries {
if _, err := packages.Load(cfg, q); err == nil {
t.Errorf("packages.Load(%q) succeeded. Expected \"invalid query type\" error", q)
} else if !strings.Contains(err.Error(), "invalid query type") {
t.Errorf("packages.Load(%q): got error %v, want \"invalid query type\" error", q, err)
}
}
}

func TestPatternPassthrough(t *testing.T) {
tmp, cleanup := makeTree(t, map[string]string{
"src/a/a.go": `package a;`,
})
defer cleanup()

cfg := &packages.Config{
Mode: packages.LoadImports,
Env: append(os.Environ(), "GOPATH="+tmp, "GO111MODULE=off"),
}
initial, err := packages.Load(cfg, "pattern=a")
if err != nil {
t.Fatal(err)
}

graph, _ := importGraph(initial)
wantGraph := `
* a
`[1:]
if graph != wantGraph {
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
}

}

func TestConfigDefaultEnv(t *testing.T) {
if runtime.GOOS == "windows" {
// TODO(jayconrod): write an equivalent batch script for windows.
Expand Down

0 comments on commit 13ebad8

Please sign in to comment.