aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2018-08-20 04:32:00 +0000
committerBrad Fitzpatrick <bradfitz@golang.org>2018-08-20 04:32:00 +0000
commit673a05e4dfcbaa3a64f5a5eb38444aff593714e0 (patch)
treebeb34cc06ddb43412865c751b4842d3c113cc53e
parent807e7f2420c683384dc9c6db498808ba1b7aab17 (diff)
parente0faedbb5344eb6f8f704005fe88961cdc6cf5f8 (diff)
downloadgo-673a05e4dfcbaa3a64f5a5eb38444aff593714e0.tar.gz
go-673a05e4dfcbaa3a64f5a5eb38444aff593714e0.zip
[release-branch.go1.11] Merge branch 'master' into release-branch.go1.11
Change-Id: I4f09f847c7304e37df8388b45aa8d6281a677de3
-rw-r--r--doc/code.html51
-rw-r--r--doc/go1.11.html18
-rw-r--r--src/cmd/compile/internal/gc/reflect.go23
-rw-r--r--src/cmd/compile/internal/gc/ssa.go4
-rw-r--r--src/cmd/go/alldocs.go163
-rw-r--r--src/cmd/go/go_test.go90
-rw-r--r--src/cmd/go/internal/get/get.go49
-rw-r--r--src/cmd/go/internal/imports/scan.go14
-rw-r--r--src/cmd/go/internal/list/list.go39
-rw-r--r--src/cmd/go/internal/load/flag.go50
-rw-r--r--src/cmd/go/internal/load/pkg.go192
-rw-r--r--src/cmd/go/internal/load/search.go9
-rw-r--r--src/cmd/go/internal/modcmd/download.go2
-rw-r--r--src/cmd/go/internal/modcmd/fix.go65
-rw-r--r--src/cmd/go/internal/modcmd/mod.go1
-rw-r--r--src/cmd/go/internal/modcmd/tidy.go3
-rw-r--r--src/cmd/go/internal/modcmd/why.go24
-rw-r--r--src/cmd/go/internal/modconv/convert_test.go52
-rw-r--r--src/cmd/go/internal/modfetch/codehost/vcs.go14
-rw-r--r--src/cmd/go/internal/modfetch/coderepo_test.go7
-rw-r--r--src/cmd/go/internal/modfetch/repo.go15
-rw-r--r--src/cmd/go/internal/modfile/rule.go8
-rw-r--r--src/cmd/go/internal/modget/get.go13
-rw-r--r--src/cmd/go/internal/modload/help.go77
-rw-r--r--src/cmd/go/internal/modload/import.go4
-rw-r--r--src/cmd/go/internal/modload/init.go17
-rw-r--r--src/cmd/go/internal/modload/load.go202
-rw-r--r--src/cmd/go/internal/modload/query.go6
-rw-r--r--src/cmd/go/internal/search/search.go119
-rw-r--r--src/cmd/go/internal/work/build.go10
-rw-r--r--src/cmd/go/main.go1
-rw-r--r--src/cmd/go/testdata/script/gcflags_patterns.txt71
-rw-r--r--src/cmd/go/testdata/script/mod_enabled.txt9
-rw-r--r--src/cmd/go/testdata/script/mod_fs_patterns.txt12
-rw-r--r--src/cmd/go/testdata/script/mod_get_commit.txt4
-rw-r--r--src/cmd/go/testdata/script/mod_list_dir.txt5
-rw-r--r--src/cmd/go/testdata/script/mod_patterns.txt55
-rw-r--r--src/cmd/go/testdata/script/mod_run_internal.txt46
-rw-r--r--src/cmd/go/testdata/script/mod_run_path.txt15
-rw-r--r--src/cmd/go/testdata/script/mod_tidy.txt7
-rw-r--r--src/cmd/go/testdata/script/mod_vcs_missing.txt11
-rw-r--r--src/cmd/vet/print.go14
-rw-r--r--src/cmd/vet/testdata/print.go4
-rw-r--r--src/go/doc/comment.go14
-rw-r--r--src/go/printer/nodes.go58
-rw-r--r--src/go/printer/testdata/alignment.golden33
-rw-r--r--src/go/printer/testdata/alignment.input40
-rw-r--r--src/os/file_plan9.go7
-rw-r--r--src/runtime/runtime2.go10
-rw-r--r--src/runtime/sys_darwin_amd64.s4
-rw-r--r--src/runtime/sys_darwin_arm64.s4
-rw-r--r--test/closure4.go21
52 files changed, 1088 insertions, 698 deletions
diff --git a/doc/code.html b/doc/code.html
index 4e8c54a1c5..b6d41ef68c 100644
--- a/doc/code.html
+++ b/doc/code.html
@@ -44,18 +44,16 @@ control repositories.
<h3 id="Workspaces">Workspaces</h3>
<p>
-A workspace is a directory hierarchy with three directories at its root:
+A workspace is a directory hierarchy with two directories at its root:
</p>
<ul>
-<li><code>src</code> contains Go source files,
-<li><code>pkg</code> contains package objects, and
+<li><code>src</code> contains Go source files, and
<li><code>bin</code> contains executable commands.
</ul>
<p>
-The <code>go</code> tool builds source packages and installs the resulting
-binaries to the <code>pkg</code> and <code>bin</code> directories.
+The <code>go</code> tool builds and installs binaries to the <code>bin</code> directory.
</p>
<p>
@@ -72,10 +70,6 @@ To give you an idea of how a workspace looks in practice, here's an example:
bin/
hello # command executable
outyet # command executable
-pkg/
- linux_amd64/
- github.com/golang/example/
- stringutil.a # package object
src/
<a href="https://github.com/golang/example/">github.com/golang/example/</a>
.git/ # Git repository metadata
@@ -374,9 +368,8 @@ $ <b>go build</b>
</pre>
<p>
-This won't produce an output file. To do that, you must use <code>go
-install</code>, which places the package object inside the <code>pkg</code>
-directory of the workspace.
+This won't produce an output file.
+Instead it saves the compiled package in the local build cache.
</p>
<p>
@@ -400,9 +393,7 @@ func main() {
</pre>
<p>
-Whenever the <code>go</code> tool installs a package or binary, it also
-installs whatever dependencies it has.
-So when you install the <code>hello</code> program
+Install the <code>hello</code> program:
</p>
<pre>
@@ -410,10 +401,6 @@ $ <b>go install github.com/user/hello</b>
</pre>
<p>
-the <code>stringutil</code> package will be installed as well, automatically.
-</p>
-
-<p>
Running the new version of the program, you should see a new, reversed message:
</p>
@@ -429,10 +416,6 @@ After the steps above, your workspace should look like this:
<pre>
bin/
hello # command executable
-pkg/
- linux_amd64/ # this will reflect your OS and architecture
- github.com/user/
- stringutil.a # package object
src/
github.com/user/
hello/
@@ -441,22 +424,6 @@ src/
reverse.go # package source
</pre>
-<p>
-Note that <code>go install</code> placed the <code>stringutil.a</code> object
-in a directory inside <code>pkg/linux_amd64</code> that mirrors its source
-directory.
-This is so that future invocations of the <code>go</code> tool can find the
-package object and avoid recompiling the package unnecessarily.
-The <code>linux_amd64</code> part is there to aid in cross-compilation,
-and will reflect the operating system and architecture of your system.
-</p>
-
-<p>
-Go command executables are statically linked; the package objects need not
-be present to run Go programs.
-</p>
-
-
<h3 id="PackageNames">Package names</h3>
<p>
@@ -597,12 +564,6 @@ tree should now look like this:
<pre>
bin/
hello # command executable
-pkg/
- linux_amd64/
- github.com/golang/example/
- stringutil.a # package object
- github.com/user/
- stringutil.a # package object
src/
github.com/golang/example/
.git/ # Git repository metadata
diff --git a/doc/go1.11.html b/doc/go1.11.html
index a1249db475..fae1c5ff14 100644
--- a/doc/go1.11.html
+++ b/doc/go1.11.html
@@ -167,7 +167,18 @@ Do not send CLs removing the interior tags from such phrases.
<h3 id="gopackages">Package loading</h3>
<p>
- TODO: Note about go/build versus golang.org/x/tools/go/packages.
+ The new package
+ <a href="https://godoc.org/golang.org/x/tools/go/packages"><code>golang.org/x/tools/go/packages</code></a>
+ provides a simple API for locating and loading packages of Go source code.
+ Although not yet part of the standard library, for many tasks it
+ effectively replaces the <a href="/pkg/go/build"><code>go/build</code></a>
+ package, whose API is unable to fully support modules.
+ Because it runs an external query command such as
+ <a href="/cmd/go/#hdr-List_packages"><code>go list</code></a>
+ to obtain information about Go packages, it enables the construction of
+ analysis tools that work equally well with alternative build systems
+ such as <a href="https://bazel.build">Bazel</a>
+ and <a href="https://buckbuild.com">Buck</a>.
</p>
<h3 id="gocache">Build cache requirement</h3>
@@ -262,9 +273,8 @@ func f(v interface{}) {
This is useful, for example, to call <code>String</code> methods
when paused at a breakpoint.
- <!-- TODO(austin): Make sure methods calls are actually supported by Delve -->
- This is currently only supported by Delve.
+ This is currently only supported by Delve (version 1.1.0 and up).
</p>
<h3 id="test">Test</h3>
@@ -793,7 +803,7 @@ for k := range m {
<!-- CL 101715 was reverted -->
-<dl id="runtime"><dt><a href="/pkg/runtime/">runtime</a></dt>
+<dl id="runtime-again"><dt><a href="/pkg/runtime/">runtime</a></dt>
<dd>
<p><!-- CL 70993 -->
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index b9124b6317..935c3b0503 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -34,8 +34,9 @@ type ptabEntry struct {
// runtime interface and reflection data structures
var (
- signatsetmu sync.Mutex // protects signatset
+ signatmu sync.Mutex // protects signatset and signatslice
signatset = make(map[*types.Type]struct{})
+ signatslice []*types.Type
itabs []itabEntry
ptabs []ptabEntry
@@ -960,9 +961,9 @@ func typesymprefix(prefix string, t *types.Type) *types.Sym {
// This function is for looking up type-related generated functions
// (e.g. eq and hash). Make sure they are indeed generated.
- signatsetmu.Lock()
+ signatmu.Lock()
addsignat(t)
- signatsetmu.Unlock()
+ signatmu.Unlock()
//print("algsym: %s -> %+S\n", p, s);
@@ -974,9 +975,9 @@ func typenamesym(t *types.Type) *types.Sym {
Fatalf("typenamesym %v", t)
}
s := typesym(t)
- signatsetmu.Lock()
+ signatmu.Lock()
addsignat(t)
- signatsetmu.Unlock()
+ signatmu.Unlock()
return s
}
@@ -1447,7 +1448,10 @@ func itabsym(it *obj.LSym, offset int64) *obj.LSym {
// addsignat ensures that a runtime type descriptor is emitted for t.
func addsignat(t *types.Type) {
- signatset[t] = struct{}{}
+ if _, ok := signatset[t]; !ok {
+ signatset[t] = struct{}{}
+ signatslice = append(signatslice, t)
+ }
}
func addsignats(dcls []*Node) {
@@ -1462,14 +1466,15 @@ func addsignats(dcls []*Node) {
func dumpsignats() {
// Process signatset. Use a loop, as dtypesym adds
// entries to signatset while it is being processed.
- signats := make([]typeAndStr, len(signatset))
- for len(signatset) > 0 {
+ signats := make([]typeAndStr, len(signatslice))
+ for len(signatslice) > 0 {
signats = signats[:0]
// Transfer entries to a slice and sort, for reproducible builds.
- for t := range signatset {
+ for _, t := range signatslice {
signats = append(signats, typeAndStr{t: t, short: typesymname(t), regular: t.String()})
delete(signatset, t)
}
+ signatslice = signatslice[:0]
sort.Sort(typesByString(signats))
for _, ts := range signats {
t := ts.t
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 553713a1e9..af43da6275 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -3515,6 +3515,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
break
}
closure = s.expr(fn)
+ if thearch.LinkArch.Family == sys.Wasm {
+ // TODO(neelance): On other architectures this should be eliminated by the optimization steps
+ s.nilCheck(closure)
+ }
case OCALLMETH:
if fn.Op != ODOTMETH {
Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 1292596697..c67e3f5a1c 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -40,6 +40,7 @@
// cache build and test caching
// environment environment variables
// filetype file types
+// go.mod the go.mod file
// gopath GOPATH environment variable
// gopath-get legacy GOPATH go get
// goproxy module proxy protocol
@@ -622,24 +623,25 @@
// to -f '{{.ImportPath}}'. The struct being passed to the template is:
//
// type Package struct {
-// Dir string // directory containing package sources
-// ImportPath string // import path of package in dir
-// ImportComment string // path in import comment on package statement
-// Name string // package name
-// Doc string // package documentation string
-// Target string // install path
-// Shlib string // the shared library that contains this package (only set when -linkshared)
-// Goroot bool // is this package in the Go root?
-// Standard bool // is this package part of the standard Go library?
-// Stale bool // would 'go install' do anything for this package?
-// StaleReason string // explanation for Stale==true
-// Root string // Go root or Go path dir containing this package
-// ConflictDir string // this directory shadows Dir in $GOPATH
-// BinaryOnly bool // binary-only package: cannot be recompiled from sources
-// ForTest string // package is only for use in named test
-// DepOnly bool // package is only a dependency, not explicitly listed
-// Export string // file containing export data (when using -export)
-// Module *Module // info about package's containing module, if any (can be nil)
+// Dir string // directory containing package sources
+// ImportPath string // import path of package in dir
+// ImportComment string // path in import comment on package statement
+// Name string // package name
+// Doc string // package documentation string
+// Target string // install path
+// Shlib string // the shared library that contains this package (only set when -linkshared)
+// Goroot bool // is this package in the Go root?
+// Standard bool // is this package part of the standard Go library?
+// Stale bool // would 'go install' do anything for this package?
+// StaleReason string // explanation for Stale==true
+// Root string // Go root or Go path dir containing this package
+// ConflictDir string // this directory shadows Dir in $GOPATH
+// BinaryOnly bool // binary-only package: cannot be recompiled from sources
+// ForTest string // package is only for use in named test
+// Export string // file containing export data (when using -export)
+// Module *Module // info about package's containing module, if any (can be nil)
+// Match []string // command-line patterns matching this package
+// DepOnly bool // package is only a dependency, not explicitly listed
//
// // Source files
// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@@ -722,7 +724,8 @@
// The -compiled flag causes list to set CompiledGoFiles to the Go source
// files presented to the compiler. Typically this means that it repeats
// the files listed in GoFiles and then also adds the Go code generated
-// by processing CgoFiles and SwigFiles.
+// by processing CgoFiles and SwigFiles. The Imports list contains the
+// union of all imports from both GoFiles and CompiledGoFiles.
//
// The -deps flag causes list to iterate over not just the named packages
// but also all their dependencies. It visits them in a depth-first post-order
@@ -842,7 +845,7 @@
// module paths match the pattern.
// A query of the form path@version specifies the result of that query,
// which is not limited to active modules.
-// See 'go help module' for more about module queries.
+// See 'go help modules' for more about module queries.
//
// The template function "module" takes a single string argument
// that must be a module path or query and returns the specified
@@ -873,7 +876,6 @@
//
// download download modules to local cache
// edit edit go.mod from tools or scripts
-// fix make go.mod semantically consistent
// graph print module requirement graph
// init initialize new module in current directory
// tidy add missing and remove unused modules
@@ -912,7 +914,7 @@
// Dir string // absolute path to cached source root directory
// }
//
-// See 'go help module' for more about module queries.
+// See 'go help modules' for more about module queries.
//
//
// Edit go.mod from tools or scripts
@@ -997,52 +999,6 @@
// by invoking 'go mod edit' with -require, -exclude, and so on.
//
//
-// Make go.mod semantically consistent
-//
-// Usage:
-//
-// go mod fix
-//
-// Fix updates go.mod to use canonical version identifiers and
-// to be semantically consistent. For example, consider this go.mod file:
-//
-// module M
-//
-// require (
-// A v1
-// B v1.0.0
-// C v1.0.0
-// D v1.2.3
-// E dev
-// )
-//
-// exclude D v1.2.3
-//
-// First, fix rewrites non-canonical version identifiers to semver form, so
-// A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the latest
-// commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1.
-//
-// Next, fix updates requirements to respect exclusions, so the requirement
-// on the excluded D v1.2.3 is updated to use the next available version of D,
-// perhaps D v1.2.4 or D v1.3.0.
-//
-// Finally, fix removes redundant or misleading requirements.
-// For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0, then go.mod's
-// requirement of B v1.0.0 is misleading (superseded by A's need for v1.2.0),
-// and its requirement of C v1.0.0 is redundant (implied by A's need for the
-// same version), so both will be removed. If module M contains packages
-// that directly import packages from B or C, then the requirements will be
-// kept but updated to the actual versions being used.
-//
-// Although fix runs the fix-up operation in isolation, the fix-up also
-// runs automatically any time a go command uses the module graph,
-// to update go.mod to reflect reality. Because the module graph defines
-// the meaning of import statements, any commands that load packages
-// also use and therefore fix the module graph. For example,
-// go build, go get, go install, go list, go test, go mod graph, go mod tidy,
-// and other commands all effectively imply go mod fix.
-//
-//
// Print module requirement graph
//
// Usage:
@@ -1619,6 +1575,73 @@
// command.
//
//
+// The go.mod file
+//
+// A module version is defined by a tree of source files, with a go.mod
+// file in its root. When the go command is run, it looks in the current
+// directory and then successive parent directories to find the go.mod
+// marking the root of the main (current) module.
+//
+// The go.mod file itself is line-oriented, with // comments but
+// no /* */ comments. Each line holds a single directive, made up of a
+// verb followed by arguments. For example:
+//
+// module my/thing
+// require other/thing v1.0.2
+// require new/thing v2.3.4
+// exclude old/thing v1.2.3
+// replace bad/thing v1.4.5 => good/thing v1.4.5
+//
+// The verbs are module, to define the module path; require, to require
+// a particular module at a given version or later; exclude, to exclude
+// a particular module version from use; and replace, to replace a module
+// version with a different module version. Exclude and replace apply only
+// in the main module's go.mod and are ignored in dependencies.
+// See https://research.swtch.com/vgo-mvs for details.
+//
+// The leading verb can be factored out of adjacent lines to create a block,
+// like in Go imports:
+//
+// require (
+// new/thing v2.3.4
+// old/thing v1.2.3
+// )
+//
+// The go.mod file is designed both to be edited directly and to be
+// easily updated by tools. The 'go mod edit' command can be used to
+// parse and edit the go.mod file from programs and tools.
+// See 'go help mod edit'.
+//
+// The go command automatically updates go.mod each time it uses the
+// module graph, to make sure go.mod always accurately reflects reality
+// and is properly formatted.
+//
+// The update rewrites non-canonical version identifiers to semver form,
+// so A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the
+// latest commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1.
+//
+// The update modifies requirements to respect exclusions, so the
+// requirement on the excluded D v1.2.3 is updated to use the next
+// available version of D, perhaps D v1.2.4 or D v1.3.0.
+//
+// The update removes redundant or misleading requirements.
+// For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0,
+// then go.mod's requirement of B v1.0.0 is misleading (superseded by
+// A's need for v1.2.0), and its requirement of C v1.0.0 is redundant
+// (implied by A's need for the same version), so both will be removed.
+// If module M contains packages that directly import packages from B or
+// C, then the requirements will be kept but updated to the actual
+// versions being used.
+//
+// Finally, the update reformats the go.mod in a canonical formatting, so
+// that future mechanical changes will result in minimal diffs.
+//
+// Because the module graph defines the meaning of import statements, any
+// commands that load packages also use and therefore update go.mod,
+// including go build, go get, go install, go list, go test, go mod graph,
+// go mod tidy, and go mod why.
+//
+//
// GOPATH environment variable
//
// The Go path is used to resolve import statements.
@@ -2094,7 +2117,7 @@
// The go.mod file can also specify replacements and excluded versions
// that only apply when building the module directly; they are ignored
// when the module is incorporated into a larger build.
-// For more about the go.mod file, see https://research.swtch.com/vgo-module.
+// For more about the go.mod file, see 'go help go.mod'.
//
// To start a new module, simply create a go.mod file in the root of the
// module's directory tree, containing only a module statement.
@@ -2349,8 +2372,6 @@
// about how source code in version control systems is mapped to
// module file trees.
//
-// TODO: Add documentation to go command.
-//
// Module downloading and verification
//
// The go command maintains, in the main module's root directory alongside
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index debe4867e6..85cae90f87 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -1291,6 +1291,7 @@ func TestGetGitDefaultBranch(t *testing.T) {
tg.grepStdout(`\* another-branch`, "not on correct default branch")
}
+// Security issue. Don't disable. See golang.org/issue/22125.
func TestAccidentalGitCheckout(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil {
@@ -1301,13 +1302,17 @@ func TestAccidentalGitCheckout(t *testing.T) {
defer tg.cleanup()
tg.parallel()
tg.tempDir("src")
+
tg.setenv("GOPATH", tg.path("."))
tg.runFail("get", "-u", "vcs-test.golang.org/go/test1-svn-git")
tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason")
- tg.runFail("get", "-u", "vcs-test.golang.org/go/test2-svn-git/test2main")
- tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason")
+ if _, err := os.Stat(tg.path("SrC")); err == nil {
+ // This case only triggers on a case-insensitive file system.
+ tg.runFail("get", "-u", "vcs-test.golang.org/go/test2-svn-git/test2main")
+ tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason")
+ }
}
func TestErrorMessageForSyntaxErrorInTestGoFileSaysFAIL(t *testing.T) {
@@ -3527,24 +3532,43 @@ func TestImportLocal(t *testing.T) {
}
func TestGoGetInsecure(t *testing.T) {
- testenv.MustHaveExternalNetwork(t)
+ test := func(t *testing.T, modules bool) {
+ testenv.MustHaveExternalNetwork(t)
- tg := testgo(t)
- defer tg.cleanup()
- tg.makeTempdir()
- tg.setenv("GOPATH", tg.path("."))
- tg.failSSH()
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.makeTempdir()
+ tg.failSSH()
- const repo = "insecure.go-get-issue-15410.appspot.com/pkg/p"
+ if modules {
+ tg.setenv("GOPATH", tg.path("gp"))
+ tg.tempFile("go.mod", "module m")
+ tg.cd(tg.path("."))
+ tg.setenv("GO111MODULE", "on")
+ } else {
+ tg.setenv("GOPATH", tg.path("."))
+ tg.setenv("GO111MODULE", "off")
+ }
- // Try go get -d of HTTP-only repo (should fail).
- tg.runFail("get", "-d", repo)
+ const repo = "insecure.go-get-issue-15410.appspot.com/pkg/p"
- // Try again with -insecure (should succeed).
- tg.run("get", "-d", "-insecure", repo)
+ // Try go get -d of HTTP-only repo (should fail).
+ tg.runFail("get", "-d", repo)
+
+ // Try again with -insecure (should succeed).
+ tg.run("get", "-d", "-insecure", repo)
+
+ // Try updating without -insecure (should fail).
+ tg.runFail("get", "-d", "-u", "-f", repo)
- // Try updating without -insecure (should fail).
- tg.runFail("get", "-d", "-u", "-f", repo)
+ if modules {
+ tg.run("list", "-m", "...")
+ tg.grepStdout("insecure.go-get-issue", "should find insecure module")
+ }
+ }
+
+ t.Run("gopath", func(t *testing.T) { test(t, false) })
+ t.Run("modules", func(t *testing.T) { test(t, true) })
}
func TestGoGetUpdateInsecure(t *testing.T) {
@@ -4187,9 +4211,10 @@ func TestGoGetUpdateWithWildcard(t *testing.T) {
tg.setenv("GOPATH", tg.path("."))
const aPkgImportPath = "github.com/tmwh/go-get-issue-14450/a"
tg.run("get", aPkgImportPath)
- tg.run("get", "-u", ".../")
- tg.grepStderrNot("cannot find package", "did not update packages given wildcard path")
+ tg.runFail("get", "-u", ".../")
+ tg.grepStderr("cannot find package.*d-dependency/e", "should have detected e missing")
+ // Even though get -u failed, the source for others should be downloaded.
var expectedPkgPaths = []string{
"src/github.com/tmwh/go-get-issue-14450/b",
"src/github.com/tmwh/go-get-issue-14450-b-dependency/c",
@@ -5647,37 +5672,6 @@ func TestRelativePkgdir(t *testing.T) {
tg.run("build", "-i", "-pkgdir=.", "runtime")
}
-func TestGcflagsPatterns(t *testing.T) {
- skipIfGccgo(t, "gccgo has no standard packages")
- tg := testgo(t)
- defer tg.cleanup()
- tg.setenv("GOPATH", "")
- tg.setenv("GOCACHE", "off")
-
- tg.run("build", "-n", "-v", "-gcflags= \t\r\n -e", "fmt")
- tg.grepStderr("^# fmt", "did not rebuild fmt")
- tg.grepStderrNot("^# reflect", "incorrectly rebuilt reflect")
-
- tg.run("build", "-n", "-v", "-gcflags=-e", "fmt", "reflect")
- tg.grepStderr("^# fmt", "did not rebuild fmt")
- tg.grepStderr("^# reflect", "did not rebuild reflect")
- tg.grepStderrNot("^# runtime", "incorrectly rebuilt runtime")
-
- tg.run("build", "-n", "-x", "-v", "-gcflags= \t\r\n reflect \t\r\n = \t\r\n -N", "fmt")
- tg.grepStderr("^# fmt", "did not rebuild fmt")
- tg.grepStderr("^# reflect", "did not rebuild reflect")
- tg.grepStderr("compile.* -N .*-p reflect", "did not build reflect with -N flag")
- tg.grepStderrNot("compile.* -N .*-p fmt", "incorrectly built fmt with -N flag")
-
- tg.run("test", "-c", "-n", "-gcflags=-N", "-ldflags=-X=x.y=z", "strings")
- tg.grepStderr("compile.* -N .*compare_test.go", "did not compile strings_test package with -N flag")
- tg.grepStderr("link.* -X=x.y=z", "did not link strings.test binary with -X flag")
-
- tg.run("test", "-c", "-n", "-gcflags=strings=-N", "-ldflags=strings=-X=x.y=z", "strings")
- tg.grepStderr("compile.* -N .*compare_test.go", "did not compile strings_test package with -N flag")
- tg.grepStderr("link.* -X=x.y=z", "did not link strings.test binary with -X flag")
-}
-
func TestGoTestMinusN(t *testing.T) {
// Intent here is to verify that 'go test -n' works without crashing.
// This reuses flag_test.go, but really any test would do.
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index fd97c6dcb6..e4148bceb0 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -163,9 +163,8 @@ func runGet(cmd *base.Command, args []string) {
if *getT {
mode |= load.GetTestDeps
}
- args = downloadPaths(args)
- for _, arg := range args {
- download(arg, nil, &stk, mode)
+ for _, pkg := range downloadPaths(args) {
+ download(pkg, nil, &stk, mode)
}
base.ExitIfErrors()
@@ -184,8 +183,7 @@ func runGet(cmd *base.Command, args []string) {
// This leads to duplicated loads of the standard packages.
load.ClearCmdCache()
- args = load.ImportPaths(args)
- load.PackagesForBuild(args)
+ pkgs := load.PackagesForBuild(args)
// Phase 3. Install.
if *getD {
@@ -195,7 +193,7 @@ func runGet(cmd *base.Command, args []string) {
return
}
- work.InstallPackages(args)
+ work.InstallPackages(args, pkgs)
}
// downloadPaths prepares the list of paths to pass to download.
@@ -203,34 +201,21 @@ func runGet(cmd *base.Command, args []string) {
// for a particular pattern, downloadPaths leaves it in the result list,
// in the hope that we can figure out the repository from the
// initial ...-free prefix.
-func downloadPaths(args []string) []string {
- for _, arg := range args {
+func downloadPaths(patterns []string) []string {
+ for _, arg := range patterns {
if strings.Contains(arg, "@") {
base.Fatalf("go: cannot use path@version syntax in GOPATH mode")
}
}
-
- args = load.ImportPathsForGoGet(args)
- var out []string
- for _, a := range args {
- if strings.Contains(a, "...") {
- var expand []string
- // Use matchPackagesInFS to avoid printing
- // warnings. They will be printed by the
- // eventual call to importPaths instead.
- if build.IsLocalImport(a) {
- expand = search.MatchPackagesInFS(a)
- } else {
- expand = search.MatchPackages(a)
- }
- if len(expand) > 0 {
- out = append(out, expand...)
- continue
- }
+ var pkgs []string
+ for _, m := range search.ImportPathsQuiet(patterns) {
+ if len(m.Pkgs) == 0 && strings.Contains(m.Pattern, "...") {
+ pkgs = append(pkgs, m.Pattern)
+ } else {
+ pkgs = append(pkgs, m.Pkgs...)
}
- out = append(out, a)
}
- return out
+ return pkgs
}
// downloadCache records the import paths we have already
@@ -255,7 +240,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
}
load1 := func(path string, mode int) *load.Package {
if parent == nil {
- return load.LoadPackage(path, stk)
+ return load.LoadPackageNoFlags(path, stk)
}
return load.LoadImport(path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
}
@@ -311,9 +296,9 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
// for p has been replaced in the package cache.
if wildcardOkay && strings.Contains(arg, "...") {
if build.IsLocalImport(arg) {
- args = search.MatchPackagesInFS(arg)
+ args = search.MatchPackagesInFS(arg).Pkgs
} else {
- args = search.MatchPackages(arg)
+ args = search.MatchPackages(arg).Pkgs
}
isWildcard = true
}
@@ -344,7 +329,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files))
// The imports might have changed, so reload again.
- p = load.ReloadPackage(arg, stk)
+ p = load.ReloadPackageNoFlags(arg, stk)
if p.Error != nil {
base.Errorf("%s", p.Error)
return
diff --git a/src/cmd/go/internal/imports/scan.go b/src/cmd/go/internal/imports/scan.go
index 095bb64a8d..d944e95724 100644
--- a/src/cmd/go/internal/imports/scan.go
+++ b/src/cmd/go/internal/imports/scan.go
@@ -37,6 +37,7 @@ func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]stri
imports := make(map[string]bool)
testImports := make(map[string]bool)
numFiles := 0
+Files:
for _, name := range files {
r, err := os.Open(name)
if err != nil {
@@ -48,6 +49,19 @@ func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]stri
if err != nil {
return nil, nil, fmt.Errorf("reading %s: %v", name, err)
}
+
+ // import "C" is implicit requirement of cgo tag.
+ // When listing files on the command line (explicitFiles=true)
+ // we do not apply build tag filtering but we still do apply
+ // cgo filtering, so no explicitFiles check here.
+ // Why? Because we always have, and it's not worth breaking
+ // that behavior now.
+ for _, path := range list {
+ if path == `"C"` && !tags["cgo"] && !tags["*"] {
+ continue Files
+ }
+ }
+
if !explicitFiles && !ShouldBuild(data, tags) {
continue
}
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index e75270fa55..186b006c12 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -47,24 +47,25 @@ syntax of package template. The default output is equivalent
to -f '{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
- Dir string // directory containing package sources
- ImportPath string // import path of package in dir
- ImportComment string // path in import comment on package statement
- Name string // package name
- Doc string // package documentation string
- Target string // install path
- Shlib string // the shared library that contains this package (only set when -linkshared)
- Goroot bool // is this package in the Go root?
- Standard bool // is this package part of the standard Go library?
- Stale bool // would 'go install' do anything for this package?
- StaleReason string // explanation for Stale==true
- Root string // Go root or Go path dir containing this package
- ConflictDir string // this directory shadows Dir in $GOPATH
- BinaryOnly bool // binary-only package: cannot be recompiled from sources
- ForTest string // package is only for use in named test
- DepOnly bool // package is only a dependency, not explicitly listed
- Export string // file containing export data (when using -export)
- Module *Module // info about package's containing module, if any (can be nil)
+ Dir string // directory containing package sources
+ ImportPath string // import path of package in dir
+ ImportComment string // path in import comment on package statement
+ Name string // package name
+ Doc string // package documentation string
+ Target string // install path
+ Shlib string // the shared library that contains this package (only set when -linkshared)
+ Goroot bool // is this package in the Go root?
+ Standard bool // is this package part of the standard Go library?
+ Stale bool // would 'go install' do anything for this package?
+ StaleReason string // explanation for Stale==true
+ Root string // Go root or Go path dir containing this package
+ ConflictDir string // this directory shadows Dir in $GOPATH
+ BinaryOnly bool // binary-only package: cannot be recompiled from sources
+ ForTest string // package is only for use in named test
+ Export string // file containing export data (when using -export)
+ Module *Module // info about package's containing module, if any (can be nil)
+ Match []string // command-line patterns matching this package
+ DepOnly bool // package is only a dependency, not explicitly listed
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@@ -268,7 +269,7 @@ A pattern containing "..." specifies the active modules whose
module paths match the pattern.
A query of the form path@version specifies the result of that query,
which is not limited to active modules.
-See 'go help module' for more about module queries.
+See 'go help modules' for more about module queries.
The template function "module" takes a single string argument
that must be a module path or query and returns the specified
diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go
index d9177b0de3..7534e65f54 100644
--- a/src/cmd/go/internal/load/flag.go
+++ b/src/cmd/go/internal/load/flag.go
@@ -6,7 +6,6 @@ package load
import (
"cmd/go/internal/base"
- "cmd/go/internal/search"
"cmd/go/internal/str"
"fmt"
"strings"
@@ -92,52 +91,3 @@ func (f *PerPackageFlag) For(p *Package) []string {
}
return flags
}
-
-var (
- cmdlineMatchers []func(*Package) bool
- cmdlineMatcherLiterals []func(*Package) bool
-)
-
-// SetCmdlinePatterns records the set of patterns given on the command line,
-// for use by the PerPackageFlags.
-func SetCmdlinePatterns(args []string) {
- setCmdlinePatterns(args, base.Cwd)
-}
-
-func setCmdlinePatterns(args []string, cwd string) {
- if len(args) == 0 {
- args = []string{"."}
- }
- cmdlineMatchers = nil // allow reset for testing
- cmdlineMatcherLiterals = nil
- for _, arg := range args {
- cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd))
- }
- for _, arg := range args {
- if !strings.Contains(arg, "...") && !search.IsMetaPackage(arg) {
- cmdlineMatcherLiterals = append(cmdlineMatcherLiterals, MatchPackage(arg, cwd))
- }
- }
-}
-
-// isCmdlinePkg reports whether p is a package listed on the command line.
-func isCmdlinePkg(p *Package) bool {
- for _, m := range cmdlineMatchers {
- if m(p) {
- return true
- }
- }
- return false
-}
-
-// isCmdlinePkgLiteral reports whether p is a package listed as
-// a literal package argument on the command line
-// (as opposed to being the result of expanding a wildcard).
-func isCmdlinePkgLiteral(p *Package) bool {
- for _, m := range cmdlineMatcherLiterals {
- if m(p) {
- return true
- }
- }
- return false
-}
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index bef27b33ad..ec2fa730c6 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -35,10 +35,11 @@ var (
ModBinDir func() string // return effective bin directory
ModLookup func(path string) (dir, realPath string, err error) // lookup effective meaning of import
ModPackageModuleInfo func(path string) *modinfo.ModulePublic // return module info for Package struct
- ModImportPaths func(args []string) []string // expand import paths
+ ModImportPaths func(args []string) []*search.Match // expand import paths
ModPackageBuildInfo func(main string, deps []string) string // return module info to embed in binary
ModInfoProg func(info string) []byte // wrap module info in .go code for binary
ModImportFromFiles func([]string) // update go.mod to add modules for imports in these files
+ ModDirImportPath func(string) string // return effective import path for directory
)
var IgnoreImports bool // control whether we ignore imports in packages
@@ -60,15 +61,17 @@ type PackagePublic struct {
Doc string `json:",omitempty"` // package documentation string
Target string `json:",omitempty"` // installed target for this package (may be executable)
Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared)
- Goroot bool `json:",omitempty"` // is this package found in the Go root?
- Standard bool `json:",omitempty"` // is this package part of the standard Go library?
Root string `json:",omitempty"` // Go root or Go path dir containing this package
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
- BinaryOnly bool `json:",omitempty"` // package cannot be recompiled
ForTest string `json:",omitempty"` // package is only for use in named test
- DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed
Export string `json:",omitempty"` // file containing export data (set by go list -export)
Module *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any
+ Match []string `json:",omitempty"` // command-line patterns matching this package
+ Goroot bool `json:",omitempty"` // is this package found in the Go root?
+ Standard bool `json:",omitempty"` // is this package part of the standard Go library?
+ DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed
+ BinaryOnly bool `json:",omitempty"` // package cannot be recompiled
+ Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
// Stale and StaleReason remain here *only* for the list command.
// They are only initialized in preparation for list execution.
@@ -107,7 +110,7 @@ type PackagePublic struct {
Deps []string `json:",omitempty"` // all (recursively) imported dependencies
// Error information
- Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
+ // Incomplete is above, packed into the other bools
Error *PackageError `json:",omitempty"` // error loading this package (not dependencies)
DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies
@@ -374,15 +377,17 @@ func ClearPackageCachePartial(args []string) {
}
}
-// reloadPackage is like loadPackage but makes sure
+// ReloadPackageNoFlags is like LoadPackageNoFlags but makes sure
// not to use the package cache.
-func ReloadPackage(arg string, stk *ImportStack) *Package {
+// It is only for use by GOPATH-based "go get".
+// TODO(rsc): When GOPATH-based "go get" is removed, delete this function.
+func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package {
p := packageCache[arg]
if p != nil {
delete(packageCache, p.Dir)
delete(packageCache, p.ImportPath)
}
- return LoadPackage(arg, stk)
+ return LoadPackageNoFlags(arg, stk)
}
// dirToImportPath returns the pseudo-import path we use for a package
@@ -431,6 +436,9 @@ const (
// but possibly a local import path (an absolute file system path or one beginning
// with ./ or ../). A local relative path is interpreted relative to srcDir.
// It returns a *Package describing the package found in that directory.
+// LoadImport does not set tool flags and should only be used by
+// this package, as part of a bigger load operation, and by GOPATH-based "go get".
+// TODO(rsc): When GOPATH-based "go get" is removed, unexport this function.
func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package {
stk.Push(path)
defer stk.Pop()
@@ -560,11 +568,11 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
}
// Checked on every import because the rules depend on the code doing the importing.
- if perr := disallowInternal(srcDir, parentPath, p, stk); perr != p {
+ if perr := disallowInternal(srcDir, parent, parentPath, p, stk); perr != p {
return setErrorPos(perr, importPos)
}
if mode&ResolveImport != 0 {
- if perr := disallowVendor(srcDir, parentPath, origPath, p, stk); perr != p {
+ if perr := disallowVendor(srcDir, parent, parentPath, origPath, p, stk); perr != p {
return setErrorPos(perr, importPos)
}
}
@@ -925,7 +933,7 @@ func reusePackage(p *Package, stk *ImportStack) *Package {
// is allowed to import p.
// If the import is allowed, disallowInternal returns the original package p.
// If not, it returns a new package containing just an appropriate error.
-func disallowInternal(srcDir, importerPath string, p *Package, stk *ImportStack) *Package {
+func disallowInternal(srcDir string, importer *Package, importerPath string, p *Package, stk *ImportStack) *Package {
// golang.org/s/go14internal:
// An import of a path containing the element “internal”
// is disallowed if the importing code is outside the tree
@@ -983,10 +991,16 @@ func disallowInternal(srcDir, importerPath string, p *Package, stk *ImportStack)
return p
}
} else {
- // p is in a module, so make it available based on the import path instead
+ // p is in a module, so make it available based on the importer's import path instead
// of the file path (https://golang.org/issue/23970).
- parent := p.ImportPath[:i]
- if str.HasPathPrefix(importerPath, parent) {
+ if importerPath == "." {
+ // The importer is a list of command-line files.
+ // Pretend that the import path is the import path of the
+ // directory containing them.
+ importerPath = ModDirImportPath(importer.Dir)
+ }
+ parentOfInternal := p.ImportPath[:i]
+ if str.HasPathPrefix(importerPath, parentOfInternal) {
return p
}
}
@@ -1024,7 +1038,7 @@ func findInternal(path string) (index int, ok bool) {
// is allowed to import p as path.
// If the import is allowed, disallowVendor returns the original package p.
// If not, it returns a new package containing just an appropriate error.
-func disallowVendor(srcDir, importerPath, path string, p *Package, stk *ImportStack) *Package {
+func disallowVendor(srcDir string, importer *Package, importerPath, path string, p *Package, stk *ImportStack) *Package {
// The stack includes p.ImportPath.
// If that's the only thing on the stack, we started
// with a name given on the command line, not an
@@ -1033,26 +1047,18 @@ func disallowVendor(srcDir, importerPath, path string, p *Package, stk *ImportSt
return p
}
- if p.Standard && ModPackageModuleInfo != nil && importerPath != "" {
- // Modules must not import vendor packages in the standard library,
- // but the usual vendor visibility check will not catch them
- // because the module loader presents them with an ImportPath starting
- // with "golang_org/" instead of "vendor/".
- if mod := ModPackageModuleInfo(importerPath); mod != nil {
- dir := p.Dir
- if relDir, err := filepath.Rel(p.Root, p.Dir); err == nil {
- dir = relDir
- }
- if _, ok := FindVendor(filepath.ToSlash(dir)); ok {
- perr := *p
- perr.Error = &PackageError{
- ImportStack: stk.Copy(),
- Err: "use of vendored package " + path + " not allowed",
- }
- perr.Incomplete = true
- return &perr
- }
+ // Modules must not import vendor packages in the standard library,
+ // but the usual vendor visibility check will not catch them
+ // because the module loader presents them with an ImportPath starting
+ // with "golang_org/" instead of "vendor/".
+ if p.Standard && !importer.Standard && strings.HasPrefix(p.ImportPath, "golang_org") {
+ perr := *p
+ perr.Error = &PackageError{
+ ImportStack: stk.Copy(),
+ Err: "use of vendored package " + path + " not allowed",
}
+ perr.Incomplete = true
+ return &perr
}
if perr := disallowVendorVisibility(srcDir, p, stk); perr != p {
@@ -1185,27 +1191,6 @@ var foldPath = make(map[string]string)
func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
p.copyBuild(bp)
- // Decide whether p was listed on the command line.
- // Given that load is called while processing the command line,
- // you might think we could simply pass a flag down into load
- // saying whether we are loading something named on the command
- // line or something to satisfy an import. But the first load of a
- // package named on the command line may be as a dependency
- // of an earlier package named on the command line, not when we
- // get to that package during command line processing.
- // For example "go test fmt reflect" will load reflect as a dependency
- // of fmt before it attempts to load as a command-line argument.
- // Because loads are cached, the later load will be a no-op,
- // so it is important that the first load can fill in CmdlinePkg correctly.
- // Hence the call to a separate matching check here.
- p.Internal.CmdlinePkg = isCmdlinePkg(p)
- p.Internal.CmdlinePkgLiteral = isCmdlinePkgLiteral(p)
-
- p.Internal.Asmflags = BuildAsmflags.For(p)
- p.Internal.Gcflags = BuildGcflags.For(p)
- p.Internal.Ldflags = BuildLdflags.For(p)
- p.Internal.Gccgoflags = BuildGccgoflags.For(p)
-
// The localPrefix is the path we interpret ./ imports relative to.
// Synthesized main packages sometimes override this.
if p.Internal.Local {
@@ -1740,11 +1725,31 @@ func ClearCmdCache() {
}
}
+// LoadPackage loads the package named by arg.
+func LoadPackage(arg string, stk *ImportStack) *Package {
+ p := loadPackage(arg, stk)
+ setToolFlags(p)
+ return p
+}
+
+// LoadPackageNoFlags is like LoadPackage
+// but does not guarantee that the build tool flags are set in the result.
+// It is only for use by GOPATH-based "go get"
+// and is only appropriate for preliminary loading of packages.
+// A real load using LoadPackage or (more likely)
+// Packages, PackageAndErrors, or PackagesForBuild
+// must be done before passing the package to any build
+// steps, so that the tool flags can be set properly.
+// TODO(rsc): When GOPATH-based "go get" is removed, delete this function.
+func LoadPackageNoFlags(arg string, stk *ImportStack) *Package {
+ return loadPackage(arg, stk)
+}
+
// loadPackage is like loadImport but is used for command-line arguments,
// not for paths found in import statements. In addition to ordinary import paths,
// loadPackage accepts pseudo-paths beginning with cmd/ to denote commands
// in the Go command directory, as well as paths to those directories.
-func LoadPackage(arg string, stk *ImportStack) *Package {
+func loadPackage(arg string, stk *ImportStack) *Package {
if build.IsLocalImport(arg) {
dir := arg
if !filepath.IsAbs(dir) {
@@ -1829,54 +1834,64 @@ func Packages(args []string) []*Package {
// *Package for every argument, even the ones that
// cannot be loaded at all.
// The packages that fail to load will have p.Error != nil.
-func PackagesAndErrors(args []string) []*Package {
- if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
- return []*Package{GoFilesPackage(args)}
+func PackagesAndErrors(patterns []string) []*Package {
+ if len(patterns) > 0 && strings.HasSuffix(patterns[0], ".go") {
+ return []*Package{GoFilesPackage(patterns)}
}
- args = ImportPaths(args)
+ matches := ImportPaths(patterns)
var (
pkgs []*Package
stk ImportStack
- seenArg = make(map[string]bool)
seenPkg = make(map[*Package]bool)
)
- for _, arg := range args {
- if seenArg[arg] {
- continue
- }
- seenArg[arg] = true
- pkg := LoadPackage(arg, &stk)
- if seenPkg[pkg] {
- continue
+ for _, m := range matches {
+ for _, pkg := range m.Pkgs {
+ p := loadPackage(pkg, &stk)
+ p.Match = append(p.Match, m.Pattern)
+ p.Internal.CmdlinePkg = true
+ if m.Literal {
+ // Note: do not set = m.Literal unconditionally
+ // because maybe we'll see p matching both
+ // a literal and also a non-literal pattern.
+ p.Internal.CmdlinePkgLiteral = true
+ }
+ if seenPkg[p] {
+ continue
+ }
+ seenPkg[p] = true
+ pkgs = append(pkgs, p)
}
- seenPkg[pkg] = true
- pkgs = append(pkgs, pkg)
}
+ // Now that CmdlinePkg is set correctly,
+ // compute the effective flags for all loaded packages
+ // (not just the ones matching the patterns but also
+ // their dependencies).
+ setToolFlags(pkgs...)
+
return pkgs
}
-func ImportPaths(args []string) []string {
- if cmdlineMatchers == nil {
- SetCmdlinePatterns(search.CleanImportPaths(args))
+func setToolFlags(pkgs ...*Package) {
+ for _, p := range PackageList(pkgs) {
+ p.Internal.Asmflags = BuildAsmflags.For(p)
+ p.Internal.Gcflags = BuildGcflags.For(p)
+ p.Internal.Ldflags = BuildLdflags.For(p)
+ p.Internal.Gccgoflags = BuildGccgoflags.For(p)
}
+}
+
+func ImportPaths(args []string) []*search.Match {
if ModInit(); cfg.ModulesEnabled {
return ModImportPaths(args)
}
return search.ImportPaths(args)
}
-func ImportPathsForGoGet(args []string) []string {
- if cmdlineMatchers == nil {
- SetCmdlinePatterns(search.CleanImportPaths(args))
- }
- return search.ImportPathsNoDotExpansion(args)
-}
-
-// packagesForBuild is like 'packages' but fails if any of
-// the packages or their dependencies have errors
+// PackagesForBuild is like Packages but exits
+// if any of the packages or their dependencies have errors
// (cannot be built).
func PackagesForBuild(args []string) []*Package {
pkgs := PackagesAndErrors(args)
@@ -1924,7 +1939,6 @@ func PackagesForBuild(args []string) []*Package {
func GoFilesPackage(gofiles []string) *Package {
ModInit()
- // TODO: Remove this restriction.
for _, f := range gofiles {
if !strings.HasSuffix(f, ".go") {
base.Fatalf("named files must be .go files")
@@ -1976,6 +1990,11 @@ func GoFilesPackage(gofiles []string) *Package {
}
bp, err := ctxt.ImportDir(dir, 0)
+ if ModDirImportPath != nil {
+ // Use the effective import path of the directory
+ // for deciding visibility during pkg.load.
+ bp.ImportPath = ModDirImportPath(dir)
+ }
pkg := new(Package)
pkg.Internal.Local = true
pkg.Internal.CmdlineFiles = true
@@ -1985,6 +2004,7 @@ func GoFilesPackage(gofiles []string) *Package {
pkg.Internal.LocalPrefix = dirToImportPath(dir)
pkg.ImportPath = "command-line-arguments"
pkg.Target = ""
+ pkg.Match = gofiles
if pkg.Name == "main" {
_, elem := filepath.Split(gofiles[0])
@@ -1999,5 +2019,7 @@ func GoFilesPackage(gofiles []string) *Package {
}
}
+ setToolFlags(pkg)
+
return pkg
}
diff --git a/src/cmd/go/internal/load/search.go b/src/cmd/go/internal/load/search.go
index d379c7b021..cf09c7b0a8 100644
--- a/src/cmd/go/internal/load/search.go
+++ b/src/cmd/go/internal/load/search.go
@@ -6,7 +6,6 @@ package load
import (
"path/filepath"
- "runtime"
"strings"
"cmd/go/internal/search"
@@ -28,13 +27,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
}
dir = filepath.Join(cwd, dir)
if pattern == "" {
- return func(p *Package) bool {
- // TODO(rsc): This is wrong. See golang.org/issue/25878.
- if runtime.GOOS != "windows" {
- return p.Dir == dir
- }
- return strings.EqualFold(p.Dir, dir)
- }
+ return func(p *Package) bool { return p.Dir == dir }
}
matchPath := search.MatchPattern(pattern)
return func(p *Package) bool {
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index 0a457a56f2..2f072d73cf 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -41,7 +41,7 @@ corresponding to this Go struct:
Dir string // absolute path to cached source root directory
}
-See 'go help module' for more about module queries.
+See 'go help modules' for more about module queries.
`,
}
diff --git a/src/cmd/go/internal/modcmd/fix.go b/src/cmd/go/internal/modcmd/fix.go
deleted file mode 100644
index bfb51456a6..0000000000
--- a/src/cmd/go/internal/modcmd/fix.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// go mod fix
-
-package modcmd
-
-import (
- "cmd/go/internal/base"
- "cmd/go/internal/modload"
-)
-
-var cmdFix = &base.Command{
- UsageLine: "go mod fix",
- Short: "make go.mod semantically consistent",
- Long: `
-Fix updates go.mod to use canonical version identifiers and
-to be semantically consistent. For example, consider this go.mod file:
-
- module M
-
- require (
- A v1
- B v1.0.0
- C v1.0.0
- D v1.2.3
- E dev
- )
-
- exclude D v1.2.3
-
-First, fix rewrites non-canonical version identifiers to semver form, so
-A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the latest
-commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1.
-
-Next, fix updates requirements to respect exclusions, so the requirement
-on the excluded D v1.2.3 is updated to use the next available version of D,
-perhaps D v1.2.4 or D v1.3.0.
-
-Finally, fix removes redundant or misleading requirements.
-For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0, then go.mod's
-requirement of B v1.0.0 is misleading (superseded by A's need for v1.2.0),
-and its requirement of C v1.0.0 is redundant (implied by A's need for the
-same version), so both will be removed. If module M contains packages
-that directly import packages from B or C, then the requirements will be
-kept but updated to the actual versions being used.
-
-Although fix runs the fix-up operation in isolation, the fix-up also
-runs automatically any time a go command uses the module graph,
-to update go.mod to reflect reality. Because the module graph defines
-the meaning of import statements, any commands that load packages
-also use and therefore fix the module graph. For example,
-go build, go get, go install, go list, go test, go mod graph, go mod tidy,
-and other commands all effectively imply go mod fix.
- `,
- Run: runFix,
-}
-
-func runFix(cmd *base.Command, args []string) {
- if len(args) != 0 {
- base.Fatalf("go mod fix: fix takes no arguments")
- }
- modload.LoadBuildList() // writes go.mod
-}
diff --git a/src/cmd/go/internal/modcmd/mod.go b/src/cmd/go/internal/modcmd/mod.go
index c1d0c13a10..f150cc9728 100644
--- a/src/cmd/go/internal/modcmd/mod.go
+++ b/src/cmd/go/internal/modcmd/mod.go
@@ -21,7 +21,6 @@ See 'go help modules' for an overview of module functionality.
Commands: []*base.Command{
cmdDownload,
cmdEdit,
- cmdFix,
cmdGraph,
cmdInit,
cmdTidy,
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index 54f47e764f..f2063a9ea6 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -44,10 +44,11 @@ func runTidy(cmd *base.Command, args []string) {
// LoadALL adds missing modules.
// Remove unused modules.
- used := map[module.Version]bool{modload.Target: true}
+ used := make(map[module.Version]bool)
for _, pkg := range modload.LoadALL() {
used[modload.PackageModule(pkg)] = true
}
+ used[modload.Target] = true // note: LoadALL initializes Target
inGoMod := make(map[string]bool)
for _, r := range modload.ModFile().Require {
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index 6923685599..03e0a039bc 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -100,20 +100,22 @@ func runWhy(cmd *base.Command, args []string) {
sep = "\n"
}
} else {
- pkgs := modload.ImportPaths(args) // resolve to packages
- loadALL() // rebuild graph, from main module (not from named packages)
+ matches := modload.ImportPaths(args) // resolve to packages
+ loadALL() // rebuild graph, from main module (not from named packages)
sep := ""
- for _, path := range pkgs {
- why := modload.Why(path)
- if why == "" {
- vendoring := ""
- if *whyVendor {
- vendoring = " to vendor"
+ for _, m := range matches {
+ for _, path := range m.Pkgs {
+ why := modload.Why(path)
+ if why == "" {
+ vendoring := ""
+ if *whyVendor {
+ vendoring = " to vendor"
+ }
+ why = "(main module does not need" + vendoring + " package " + path + ")\n"
}
- why = "(main module does not need" + vendoring + " package " + path + ")\n"
+ fmt.Printf("%s# %s\n%s", sep, path, why)
+ sep = "\n"
}
- fmt.Printf("%s# %s\n%s", sep, path, why)
- sep = "\n"
}
}
}
diff --git a/src/cmd/go/internal/modconv/convert_test.go b/src/cmd/go/internal/modconv/convert_test.go
index f430380871..ad27abb8ef 100644
--- a/src/cmd/go/internal/modconv/convert_test.go
+++ b/src/cmd/go/internal/modconv/convert_test.go
@@ -61,28 +61,36 @@ func TestConvertLegacyConfig(t *testing.T) {
vers string
gomod string
}{
- {
- // Gopkg.lock parsing.
- "github.com/golang/dep", "v0.4.0",
- `module github.com/golang/dep
-
- require (
- github.com/Masterminds/semver v0.0.0-20170726230514-a93e51b5a57e
- github.com/Masterminds/vcs v1.11.1
- github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
- github.com/boltdb/bolt v1.3.1
- github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
- github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
- github.com/jmank88/nuts v0.3.0
- github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
- github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
- github.com/pkg/errors v0.8.0
- github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
- golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
- golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
- golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
- )`,
- },
+ /*
+ Different versions of git seem to find or not find
+ github.com/Masterminds/semver's a93e51b5a57e,
+ which is an unmerged pull request.
+ We'd rather not provide access to unmerged pull requests,
+ so the line is removed from the golden file here,
+ but some git commands still find it somehow.
+
+ {
+ // Gopkg.lock parsing.
+ "github.com/golang/dep", "v0.4.0",
+ `module github.com/golang/dep
+
+ require (
+ github.com/Masterminds/vcs v1.11.1
+ github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
+ github.com/boltdb/bolt v1.3.1
+ github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
+ github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
+ github.com/jmank88/nuts v0.3.0
+ github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
+ github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
+ github.com/pkg/errors v0.8.0
+ github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
+ golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
+ golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
+ golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
+ )`,
+ },
+ */
// TODO: https://github.com/docker/distribution uses vendor.conf
diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go
index 03def8e082..9e862a0ef8 100644
--- a/src/cmd/go/internal/modfetch/codehost/vcs.go
+++ b/src/cmd/go/internal/modfetch/codehost/vcs.go
@@ -22,6 +22,17 @@ import (
"cmd/go/internal/str"
)
+// A VCSError indicates an error using a version control system.
+// The implication of a VCSError is that we know definitively where
+// to get the code, but we can't access it due to the error.
+// The caller should report this error instead of continuing to probe
+// other possible module paths.
+type VCSError struct {
+ Err error
+}
+
+func (e *VCSError) Error() string { return e.Err.Error() }
+
func NewRepo(vcs, remote string) (Repo, error) {
type key struct {
vcs string
@@ -33,6 +44,9 @@ func NewRepo(vcs, remote string) (Repo, error) {
}
c := vcsRepoCache.Do(key{vcs, remote}, func() interface{} {
repo, err := newVCSRepo(vcs, remote)
+ if err != nil {
+ err = &VCSError{err}
+ }
return cached{repo, err}
}).(cached)
diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go
index c46705105d..79b82786cb 100644
--- a/src/cmd/go/internal/modfetch/coderepo_test.go
+++ b/src/cmd/go/internal/modfetch/coderepo_test.go
@@ -228,10 +228,9 @@ var codeRepoTests = []struct {
path: "swtch.com/testmod",
rev: "v1.0.0",
version: "v1.0.0",
- name: "v1.0.0",
- short: "v1.0.0",
- time: time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC),
- gomod: "module \"swtch.com/testmod\"\n",
+ // NO name or short - we intentionally ignore those in the proxy protocol
+ time: time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC),
+ gomod: "module \"swtch.com/testmod\"\n",
},
{
// redirect to googlesource
diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go
index 003479461c..0ea8c1f0e3 100644
--- a/src/cmd/go/internal/modfetch/repo.go
+++ b/src/cmd/go/internal/modfetch/repo.go
@@ -216,7 +216,11 @@ func lookup(path string) (r Repo, err error) {
return lookupProxy(path)
}
- rr, err := get.RepoRootForImportPath(path, get.PreferMod, web.Secure)
+ security := web.Secure
+ if get.Insecure {
+ security = web.Insecure
+ }
+ rr, err := get.RepoRootForImportPath(path, get.PreferMod, security)
if err != nil {
// We don't know where to find code for a module with this path.
return nil, err
@@ -237,6 +241,9 @@ func lookup(path string) (r Repo, err error) {
func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) {
code, err := codehost.NewRepo(rr.VCS, rr.Repo)
if err != nil {
+ if _, ok := err.(*codehost.VCSError); ok {
+ return nil, err
+ }
return nil, fmt.Errorf("lookup %s: %v", rr.Root, err)
}
return code, nil
@@ -254,7 +261,11 @@ func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) {
// Note: Because we are converting a code reference from a legacy
// version control system, we ignore meta tags about modules
// and use only direct source control entries (get.IgnoreMod).
- rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, web.Secure)
+ security := web.Secure
+ if get.Insecure {
+ security = web.Insecure
+ }
+ rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, security)
if err != nil {
return nil, nil, err
}
diff --git a/src/cmd/go/internal/modfile/rule.go b/src/cmd/go/internal/modfile/rule.go
index f669575c86..e11f0a6e31 100644
--- a/src/cmd/go/internal/modfile/rule.go
+++ b/src/cmd/go/internal/modfile/rule.go
@@ -250,7 +250,7 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
arrow = 1
}
if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" {
- fmt.Fprintf(errs, "%s:%d: usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", f.Syntax.Name, line.Start.Line, verb, verb)
+ fmt.Fprintf(errs, "%s:%d: usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory\n", f.Syntax.Name, line.Start.Line, verb, verb)
return
}
s, err := parseString(&args[0])
@@ -287,11 +287,11 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
nv := ""
if len(args) == arrow+2 {
if !IsDirectoryPath(ns) {
- fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)", f.Syntax.Name, line.Start.Line)
+ fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)\n", f.Syntax.Name, line.Start.Line)
return
}
if filepath.Separator == '/' && strings.Contains(ns, `\`) {
- fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)", f.Syntax.Name, line.Start.Line)
+ fmt.Fprintf(errs, "%s:%d: replacement directory appears to be Windows path (on a non-windows system)\n", f.Syntax.Name, line.Start.Line)
return
}
}
@@ -303,7 +303,7 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
return
}
if IsDirectoryPath(ns) {
- fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version", f.Syntax.Name, line.Start.Line, ns)
+ fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version\n", f.Syntax.Name, line.Start.Line, ns)
return
}
}
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index f4a92686a5..90a5bd8130 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -229,7 +229,7 @@ func runGet(cmd *base.Command, args []string) {
// and a list of install targets (for the "go install" at the end).
var tasks []*task
var install []string
- for _, arg := range search.CleanImportPaths(args) {
+ for _, arg := range search.CleanPatterns(args) {
// Argument is module query path@vers, or else path with implicit @latest.
path := arg
vers := ""
@@ -519,8 +519,9 @@ func runGet(cmd *base.Command, args []string) {
// Note that 'go get -u' without any arguments results in len(install) == 1:
// search.CleanImportPaths returns "." for empty args.
work.BuildInit()
- var pkgs []string
- for _, p := range load.PackagesAndErrors(install) {
+ pkgs := load.PackagesAndErrors(install)
+ var todo []*load.Package
+ for _, p := range pkgs {
// Ignore "no Go source files" errors for 'go get' operations on modules.
if p.Error != nil {
if len(args) == 0 && getU != "" && strings.HasPrefix(p.Error.Err, "no Go files") {
@@ -534,14 +535,14 @@ func runGet(cmd *base.Command, args []string) {
continue
}
}
- pkgs = append(pkgs, p.ImportPath)
+ todo = append(todo, p)
}
// If -d was specified, we're done after the download: no build.
// (The load.PackagesAndErrors is what did the download
// of the named packages and their dependencies.)
- if len(pkgs) > 0 && !*getD {
- work.InstallPackages(pkgs)
+ if len(todo) > 0 && !*getD {
+ work.InstallPackages(install, todo)
}
}
}
diff --git a/src/cmd/go/internal/modload/help.go b/src/cmd/go/internal/modload/help.go
index 64c70b7d7b..9a12b24482 100644
--- a/src/cmd/go/internal/modload/help.go
+++ b/src/cmd/go/internal/modload/help.go
@@ -6,8 +6,7 @@ package modload
import "cmd/go/internal/base"
-// TODO(rsc): The links out to research.swtch.com here should all be
-// replaced eventually with links to proper documentation.
+// TODO(rsc): The "module code layout" section needs to be written.
var HelpModules = &base.Command{
UsageLine: "modules",
@@ -81,7 +80,7 @@ depends on specific versions of golang.org/x/text and gopkg.in/yaml.v2:
The go.mod file can also specify replacements and excluded versions
that only apply when building the module directly; they are ignored
when the module is incorporated into a larger build.
-For more about the go.mod file, see https://research.swtch.com/vgo-module.
+For more about the go.mod file, see 'go help go.mod'.
To start a new module, simply create a go.mod file in the root of the
module's directory tree, containing only a module statement.
@@ -336,8 +335,6 @@ For now, see https://research.swtch.com/vgo-module for information
about how source code in version control systems is mapped to
module file trees.
-TODO: Add documentation to go command.
-
Module downloading and verification
The go command maintains, in the main module's root directory alongside
@@ -381,3 +378,73 @@ top-level vendor directory is used; vendor directories in other locations
are still ignored.
`,
}
+
+var HelpGoMod = &base.Command{
+ UsageLine: "go.mod",
+ Short: "the go.mod file",
+ Long: `
+A module version is defined by a tree of source files, with a go.mod
+file in its root. When the go command is run, it looks in the current
+directory and then successive parent directories to find the go.mod
+marking the root of the main (current) module.
+
+The go.mod file itself is line-oriented, with // comments but
+no /* */ comments. Each line holds a single directive, made up of a
+verb followed by arguments. For example:
+
+ module my/thing
+ require other/thing v1.0.2
+ require new/thing v2.3.4
+ exclude old/thing v1.2.3
+ replace bad/thing v1.4.5 => good/thing v1.4.5
+
+The verbs are module, to define the module path; require, to require
+a particular module at a given version or later; exclude, to exclude
+a particular module version from use; and replace, to replace a module
+version with a different module version. Exclude and replace apply only
+in the main module's go.mod and are ignored in dependencies.
+See https://research.swtch.com/vgo-mvs for details.
+
+The leading verb can be factored out of adjacent lines to create a block,
+like in Go imports:
+
+ require (
+ new/thing v2.3.4
+ old/thing v1.2.3
+ )
+
+The go.mod file is designed both to be edited directly and to be
+easily updated by tools. The 'go mod edit' command can be used to
+parse and edit the go.mod file from programs and tools.
+See 'go help mod edit'.
+
+The go command automatically updates go.mod each time it uses the
+module graph, to make sure go.mod always accurately reflects reality
+and is properly formatted.
+
+The update rewrites non-canonical version identifiers to semver form,
+so A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the
+latest commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1.
+
+The update modifies requirements to respect exclusions, so the
+requirement on the excluded D v1.2.3 is updated to use the next
+available version of D, perhaps D v1.2.4 or D v1.3.0.
+
+The update removes redundant or misleading requirements.
+For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0,
+then go.mod's requirement of B v1.0.0 is misleading (superseded by
+A's need for v1.2.0), and its requirement of C v1.0.0 is redundant
+(implied by A's need for the same version), so both will be removed.
+If module M contains packages that directly import packages from B or
+C, then the requirements will be kept but updated to the actual
+versions being used.
+
+Finally, the update reformats the go.mod in a canonical formatting, so
+that future mechanical changes will result in minimal diffs.
+
+Because the module graph defines the meaning of import statements, any
+commands that load packages also use and therefore update go.mod,
+including go build, go get, go install, go list, go test, go mod graph,
+go mod tidy, and go mod why.
+ `,
+}
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index 3b954f18fe..78ae83e4bf 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -14,6 +14,7 @@ import (
"strings"
"cmd/go/internal/cfg"
+ "cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module"
"cmd/go/internal/par"
"cmd/go/internal/search"
@@ -133,6 +134,9 @@ func Import(path string) (m module.Version, dir string, err error) {
m, _, err = QueryPackage(path, "latest", Allowed)
if err != nil {
+ if _, ok := err.(*codehost.VCSError); ok {
+ return module.Version{}, "", err
+ }
return module.Version{}, "", &ImportMissingError{ImportPath: path}
}
return m, "", &ImportMissingError{ImportPath: path, Module: m}
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 5e9db0f9ea..f995bad13b 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -150,8 +150,20 @@ func Init() {
ModRoot = cwd
} else {
ModRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
- if ModRoot == "" && !MustUseModules {
- return
+ if !MustUseModules {
+ if ModRoot == "" {
+ return
+ }
+ if search.InDir(ModRoot, os.TempDir()) == "." {
+ // If you create /tmp/go.mod for experimenting,
+ // then any tests that create work directories under /tmp
+ // will find it and get modules when they're not expecting them.
+ // It's a bit of a peculiar thing to disallow but quite mysterious
+ // when it happens. See golang.org/issue/26708.
+ ModRoot = ""
+ fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
+ return
+ }
}
}
@@ -163,6 +175,7 @@ func Init() {
load.ModPackageBuildInfo = PackageBuildInfo
load.ModInfoProg = ModInfoProg
load.ModImportFromFiles = ImportFromFiles
+ load.ModDirImportPath = DirImportPath
search.SetModRoot(ModRoot)
}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 389c643db2..285daa8f4f 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -27,6 +27,7 @@ import (
"cmd/go/internal/par"
"cmd/go/internal/search"
"cmd/go/internal/semver"
+ "cmd/go/internal/str"
)
// buildList is the list of modules to use for building packages.
@@ -50,24 +51,46 @@ var loaded *loader
// ImportPaths returns the set of packages matching the args (patterns),
// adding modules to the build list as needed to satisfy new imports.
-func ImportPaths(args []string) []string {
+func ImportPaths(patterns []string) []*search.Match {
InitMod()
- cleaned := search.CleanImportPaths(args)
+ var matches []*search.Match
+ for _, pattern := range search.CleanPatterns(patterns) {
+ m := &search.Match{
+ Pattern: pattern,
+ Literal: !strings.Contains(pattern, "...") && !search.IsMetaPackage(pattern),
+ }
+ if m.Literal {
+ m.Pkgs = []string{pattern}
+ }
+ matches = append(matches, m)
+ }
+
+ fsDirs := make([][]string, len(matches))
loaded = newLoader()
- var paths []string
- loaded.load(func() []string {
- var roots []string
- paths = nil
- for _, pkg := range cleaned {
+ updateMatches := func(iterating bool) {
+ for i, m := range matches {
switch {
- case build.IsLocalImport(pkg) || filepath.IsAbs(pkg):
- list := []string{pkg}
- if strings.Contains(pkg, "...") {
- // TODO: Where is the go.mod cutoff?
- list = warnPattern(pkg, search.AllPackagesInFS(pkg))
+ case build.IsLocalImport(m.Pattern) || filepath.IsAbs(m.Pattern):
+ // Evaluate list of file system directories on first iteration.
+ if fsDirs[i] == nil {
+ var dirs []string
+ if m.Literal {
+ dirs = []string{m.Pattern}
+ } else {
+ dirs = search.MatchPackagesInFS(m.Pattern).Pkgs
+ }
+ fsDirs[i] = dirs
}
- for _, pkg := range list {
+
+ // Make a copy of the directory list and translate to import paths.
+ // Note that whether a directory corresponds to an import path
+ // changes as the build list is updated, and a directory can change
+ // from not being in the build list to being in it and back as
+ // the exact version of a particular module increases during
+ // the loader iterations.
+ m.Pkgs = str.StringList(fsDirs[i])
+ for i, pkg := range m.Pkgs {
dir := pkg
if !filepath.IsAbs(dir) {
dir = filepath.Join(cwd, pkg)
@@ -93,38 +116,67 @@ func ImportPaths(args []string) []string {
} else if path := pathInModuleCache(dir); path != "" {
pkg = path
} else {
- base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
- continue
+ pkg = ""
+ if !iterating {
+ base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
+ }
}
- roots = append(roots, pkg)
- paths = append(paths, pkg)
+ info, err := os.Stat(dir)
+ if err != nil || !info.IsDir() {
+ // If the directory does not exist,
+ // don't turn it into an import path
+ // that will trigger a lookup.
+ pkg = ""
+ if !iterating {
+ if err != nil {
+ base.Errorf("go: no such directory %v", m.Pattern)
+ } else {
+ base.Errorf("go: %s is not a directory", m.Pattern)
+ }
+ }
+ }
+ m.Pkgs[i] = pkg
}
- case pkg == "all":
+ case strings.Contains(m.Pattern, "..."):
+ m.Pkgs = matchPackages(m.Pattern, loaded.tags, true, buildList)
+
+ case m.Pattern == "all":
loaded.testAll = true
- // TODO: Don't print warnings multiple times.
- roots = append(roots, warnPattern("all", matchPackages("...", loaded.tags, false, []module.Version{Target}))...)
- paths = append(paths, "all") // will expand after load completes
-
- case search.IsMetaPackage(pkg): // std, cmd
- list := search.AllPackages(pkg)
- roots = append(roots, list...)
- paths = append(paths, list...)
-
- case strings.Contains(pkg, "..."):
- // TODO: Don't we need to reevaluate this one last time once the build list stops changing?
- list := warnPattern(pkg, matchPackages(pkg, loaded.tags, true, buildList))
- roots = append(roots, list...)
- paths = append(paths, list...)
-
- default:
- roots = append(roots, pkg)
- paths = append(paths, pkg)
+ if iterating {
+ // Enumerate the packages in the main module.
+ // We'll load the dependencies as we find them.
+ m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target})
+ } else {
+ // Starting with the packages in the main module,
+ // enumerate the full list of "all".
+ m.Pkgs = loaded.computePatternAll(m.Pkgs)
+ }
+
+ case search.IsMetaPackage(m.Pattern): // std, cmd
+ if len(m.Pkgs) == 0 {
+ m.Pkgs = search.MatchPackages(m.Pattern).Pkgs
+ }
+ }
+ }
+ }
+
+ loaded.load(func() []string {
+ var roots []string
+ updateMatches(true)
+ for _, m := range matches {
+ for _, pkg := range m.Pkgs {
+ if pkg != "" {
+ roots = append(roots, pkg)
+ }
}
}
return roots
})
+ // One last pass to finalize wildcards.
+ updateMatches(false)
+
// A given module path may be used as itself or as a replacement for another
// module, but not both at the same time. Otherwise, the aliasing behavior is
// too subtle (see https://golang.org/issue/26607), and we don't want to
@@ -142,33 +194,10 @@ func ImportPaths(args []string) []string {
}
}
base.ExitIfErrors()
-
WriteGoMod()
- // Process paths to produce final paths list.
- // Remove duplicates and expand "all".
- have := make(map[string]bool)
- var final []string
- for _, path := range paths {
- if have[path] {
- continue
- }
- have[path] = true
- if path == "all" {
- for _, pkg := range loaded.pkgs {
- if e, ok := pkg.err.(*ImportMissingError); ok && e.Module.Path == "" {
- continue // Package doesn't actually exist, so don't report it.
- }
- if !have[pkg.path] {
- have[pkg.path] = true
- final = append(final, pkg.path)
- }
- }
- continue
- }
- final = append(final, path)
- }
- return final
+ search.WarnUnmatched(matches)
+ return matches
}
// pathInModuleCache returns the import path of the directory dir,
@@ -219,6 +248,28 @@ func ImportFromFiles(gofiles []string) {
WriteGoMod()
}
+// DirImportPath returns the effective import path for dir,
+// provided it is within the main module, or else returns ".".
+func DirImportPath(dir string) string {
+ if !filepath.IsAbs(dir) {
+ dir = filepath.Join(cwd, dir)
+ } else {
+ dir = filepath.Clean(dir)
+ }
+
+ if dir == ModRoot {
+ return Target.Path
+ }
+ if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
+ suffix := filepath.ToSlash(dir[len(ModRoot):])
+ if strings.HasPrefix(suffix, "/vendor/") {
+ return strings.TrimPrefix(suffix, "/vendor/")
+ }
+ return Target.Path + suffix
+ }
+ return "."
+}
+
// LoadBuildList loads and returns the build list from go.mod.
// The loading of the build list happens automatically in ImportPaths:
// LoadBuildList need only be called if ImportPaths is not
@@ -581,6 +632,35 @@ func (ld *loader) doPkg(item interface{}) {
}
}
+// computePatternAll returns the list of packages matching pattern "all",
+// starting with a list of the import paths for the packages in the main module.
+func (ld *loader) computePatternAll(paths []string) []string {
+ seen := make(map[*loadPkg]bool)
+ var all []string
+ var walk func(*loadPkg)
+ walk = func(pkg *loadPkg) {
+ if seen[pkg] {
+ return
+ }
+ seen[pkg] = true
+ if pkg.testOf == nil {
+ all = append(all, pkg.path)
+ }
+ for _, p := range pkg.imports {
+ walk(p)
+ }
+ if p := pkg.test; p != nil {
+ walk(p)
+ }
+ }
+ for _, path := range paths {
+ walk(ld.pkg(path, false))
+ }
+ sort.Strings(all)
+
+ return all
+}
+
// scanDir is like imports.ScanDir but elides known magic imports from the list,
// so that we do not go looking for packages that don't really exist.
//
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
index bd3141865c..3b550f1db7 100644
--- a/src/cmd/go/internal/modload/query.go
+++ b/src/cmd/go/internal/modload/query.go
@@ -6,6 +6,7 @@ package modload
import (
"cmd/go/internal/modfetch"
+ "cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module"
"cmd/go/internal/semver"
"fmt"
@@ -223,6 +224,11 @@ func QueryPackage(path, query string, allowed func(module.Version) bool) (module
for p := path; p != "."; p = pathpkg.Dir(p) {
info, err := Query(p, query, allowed)
if err != nil {
+ if _, ok := err.(*codehost.VCSError); ok {
+ // A VCSError means we know where to find the code,
+ // we just can't. Abort search.
+ return module.Version{}, nil, err
+ }
if finalErr == errMissing {
finalErr = err
}
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
index b020f600c1..60ae73696b 100644
--- a/src/cmd/go/internal/search/search.go
+++ b/src/cmd/go/internal/search/search.go
@@ -17,32 +17,22 @@ import (
"strings"
)
-// AllPackages returns all the packages that can be found
+// 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)
+ Pkgs []string // matching packages (dirs or import paths)
+}
+
+// MatchPackages returns 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 AllPackages(pattern string) []string {
- pkgs := MatchPackages(pattern)
- if len(pkgs) == 0 {
- fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
- }
- return pkgs
-}
-
-// AllPackagesInFS is like allPackages but is passed a pattern
-// beginning ./ or ../, meaning it should scan the tree rooted
-// at the given directory. There are ... in the pattern too.
-func AllPackagesInFS(pattern string) []string {
- pkgs := MatchPackagesInFS(pattern)
- if len(pkgs) == 0 {
- fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+func MatchPackages(pattern string) *Match {
+ m := &Match{
+ Pattern: pattern,
+ Literal: false,
}
- return pkgs
-}
-
-// MatchPackages returns a list of package paths matching pattern
-// (see go help packages for pattern syntax).
-func MatchPackages(pattern string) []string {
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !IsMetaPackage(pattern) {
@@ -56,7 +46,6 @@ func MatchPackages(pattern string) []string {
if !cfg.BuildContext.CgoEnabled {
have["runtime/cgo"] = true // ignore during walk
}
- var pkgs []string
for _, src := range cfg.BuildContext.SrcDirs() {
if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
@@ -123,11 +112,11 @@ func MatchPackages(pattern string) []string {
return nil
}
- pkgs = append(pkgs, name)
+ m.Pkgs = append(m.Pkgs, name)
return nil
})
}
- return pkgs
+ return m
}
var modRoot string
@@ -136,10 +125,16 @@ func SetModRoot(dir string) {
modRoot = dir
}
-// MatchPackagesInFS returns a list of package paths matching pattern,
-// which must begin with ./ or ../
-// (see go help packages for pattern syntax).
-func MatchPackagesInFS(pattern string) []string {
+// MatchPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+// (See go help packages for pattern syntax.)
+func MatchPackagesInFS(pattern string) *Match {
+ m := &Match{
+ Pattern: pattern,
+ Literal: false,
+ }
+
// Find directory to begin the scan.
// Could be smarter but this one optimization
// is enough for now, since ... is usually at the
@@ -168,7 +163,6 @@ func MatchPackagesInFS(pattern string) []string {
}
}
- var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
return nil
@@ -218,10 +212,10 @@ func MatchPackagesInFS(pattern string) []string {
}
return nil
}
- pkgs = append(pkgs, name)
+ m.Pkgs = append(m.Pkgs, name)
return nil
})
- return pkgs
+ return m
}
// TreeCanMatchPattern(pattern)(name) reports whether
@@ -308,36 +302,53 @@ func replaceVendor(x, repl string) string {
return strings.Join(elem, "/")
}
-// ImportPaths returns the import paths to use for the given command line.
-func ImportPaths(args []string) []string {
- args = CleanImportPaths(args)
- var out []string
- for _, a := range args {
+// WarnUnmatched warns about patterns that didn't match any packages.
+func WarnUnmatched(matches []*Match) {
+ for _, m := range matches {
+ if len(m.Pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "go: warning: %q matched no packages\n", m.Pattern)
+ }
+ }
+}
+
+// ImportPaths returns the matching paths to use for the given command line.
+// It calls ImportPathsQuiet and then WarnUnmatched.
+func ImportPaths(patterns []string) []*Match {
+ matches := ImportPathsQuiet(patterns)
+ WarnUnmatched(matches)
+ return matches
+}
+
+// ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches.
+func ImportPathsQuiet(patterns []string) []*Match {
+ var out []*Match
+ for _, a := range CleanPatterns(patterns) {
if IsMetaPackage(a) {
- out = append(out, AllPackages(a)...)
+ out = append(out, MatchPackages(a))
continue
}
if strings.Contains(a, "...") {
if build.IsLocalImport(a) {
- out = append(out, AllPackagesInFS(a)...)
+ out = append(out, MatchPackagesInFS(a))
} else {
- out = append(out, AllPackages(a)...)
+ out = append(out, MatchPackages(a))
}
continue
}
- out = append(out, a)
+ out = append(out, &Match{Pattern: a, Literal: true, Pkgs: []string{a}})
}
return out
}
-// CleanImportPaths returns the import paths to use for the given
-// command line, but it does no wildcard expansion.
-func CleanImportPaths(args []string) []string {
- if len(args) == 0 {
+// CleanPatterns returns the patterns to use for the given
+// command line. It canonicalizes the patterns but does not
+// evaluate any matches.
+func CleanPatterns(patterns []string) []string {
+ if len(patterns) == 0 {
return []string{"."}
}
var out []string
- for _, a := range args {
+ for _, a := range patterns {
// Arguments are supposed to be import paths, but
// as a courtesy to Windows developers, rewrite \ to /
// in command-line arguments. Handles .\... and so on.
@@ -359,22 +370,6 @@ func CleanImportPaths(args []string) []string {
return out
}
-// ImportPathsNoDotExpansion returns the import paths to use for the given
-// command line, but it does no ... expansion.
-// TODO(rsc): Delete once old go get is gone.
-func ImportPathsNoDotExpansion(args []string) []string {
- args = CleanImportPaths(args)
- var out []string
- for _, a := range args {
- if IsMetaPackage(a) {
- out = append(out, AllPackages(a)...)
- continue
- }
- out = append(out, a)
- }
- 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"
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 891f81e116..ed41ce5d07 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -414,7 +414,7 @@ func libname(args []string, pkgs []*load.Package) (string, error) {
func runInstall(cmd *base.Command, args []string) {
BuildInit()
- InstallPackages(args)
+ InstallPackages(args, load.PackagesForBuild(args))
}
// omitTestOnly returns pkgs with test-only packages removed.
@@ -434,12 +434,12 @@ func omitTestOnly(pkgs []*load.Package) []*load.Package {
return list
}
-func InstallPackages(args []string) {
+func InstallPackages(patterns []string, pkgs []*load.Package) {
if cfg.GOBIN != "" && !filepath.IsAbs(cfg.GOBIN) {
base.Fatalf("cannot install, GOBIN must be an absolute path")
}
- pkgs := omitTestOnly(pkgsFilter(load.PackagesForBuild(args)))
+ pkgs = omitTestOnly(pkgsFilter(pkgs))
for _, p := range pkgs {
if p.Target == "" {
switch {
@@ -500,7 +500,7 @@ func InstallPackages(args []string) {
// tools above did not apply, and a is just a simple Action
// with a list of Deps, one per package named in pkgs,
// the same as in runBuild.
- a = b.buildmodeShared(ModeInstall, ModeInstall, args, pkgs, a)
+ a = b.buildmodeShared(ModeInstall, ModeInstall, patterns, pkgs, a)
}
b.Do(a)
@@ -515,7 +515,7 @@ func InstallPackages(args []string) {
// One way to view this behavior is that it is as if 'go install' first
// runs 'go build' and the moves the generated file to the install dir.
// See issue 9645.
- if len(args) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" {
+ if len(patterns) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" {
// Compute file 'go build' would have created.
// If it exists and is an executable file, remove it.
_, targ := filepath.Split(pkgs[0].ImportPath)
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 59d367edaa..0639b4d2ca 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -64,6 +64,7 @@ func init() {
help.HelpCache,
help.HelpEnvironment,
help.HelpFileType,
+ modload.HelpGoMod,
help.HelpGopath,
get.HelpGopathGet,
modfetch.HelpGoproxy,
diff --git a/src/cmd/go/testdata/script/gcflags_patterns.txt b/src/cmd/go/testdata/script/gcflags_patterns.txt
new file mode 100644
index 0000000000..fe2cf6f0fb
--- /dev/null
+++ b/src/cmd/go/testdata/script/gcflags_patterns.txt
@@ -0,0 +1,71 @@
+[!gc] skip 'using -gcflags and -ldflags'
+
+# -gcflags=-e applies to named packages, not dependencies
+go build -n -v -gcflags=-e z1 z2
+stderr 'compile.* -e .*-p z1'
+stderr 'compile.* -e .*-p z2'
+stderr 'compile.* -p y'
+! stderr 'compile.* -e .*-p [^z]'
+
+# -gcflags can specify package=flags, and can be repeated; last match wins
+go build -n -v -gcflags=-e -gcflags=z1=-N z1 z2
+stderr 'compile.* -N .*-p z1'
+! stderr 'compile.* -e .*-p z1'
+! stderr 'compile.* -N .*-p z2'
+stderr 'compile.* -e .*-p z2'
+stderr 'compile.* -p y'
+! stderr 'compile.* -e .*-p [^z]'
+! stderr 'compile.* -N .*-p [^z]'
+
+# -gcflags can have arbitrary spaces around the flags
+go build -n -v -gcflags=' z1 = -e ' z1
+stderr 'compile.* -e .*-p z1'
+
+# -ldflags for implicit test package applies to test binary
+go test -c -n -gcflags=-N -ldflags=-X=x.y=z z1
+stderr 'compile.* -N .*z_test.go'
+stderr 'link.* -X=x.y=z'
+
+# -ldflags for explicit test package applies to test binary
+go test -c -n -gcflags=z1=-N -ldflags=z1=-X=x.y=z z1
+stderr 'compile.* -N .*z_test.go'
+stderr 'link.* -X=x.y=z'
+
+# -ldflags applies to link of command
+go build -n -ldflags=-X=math.pi=3 my/cmd/prog
+stderr 'link.* -X=math.pi=3'
+
+# -ldflags applies to link of command even with strange directory name
+go build -n -ldflags=-X=math.pi=3 my/cmd/prog/
+stderr 'link.* -X=math.pi=3'
+
+# -ldflags applies to current directory
+cd my/cmd/prog
+go build -n -ldflags=-X=math.pi=3
+stderr 'link.* -X=math.pi=3'
+
+# -ldflags applies to current directory even if GOPATH is funny
+[windows] cd $WORK/GoPath/src/my/cmd/prog
+[darwin] cd $WORK/GoPath/src/my/cmd/prog
+go build -n -ldflags=-X=math.pi=3
+stderr 'link.* -X=math.pi=3'
+
+-- z1/z.go --
+package z1
+import _ "y"
+import _ "z2"
+
+-- z1/z_test.go --
+package z1_test
+import "testing"
+func Test(t *testing.T) {}
+
+-- z2/z.go --
+package z2
+
+-- y/y.go --
+package y
+
+-- my/cmd/prog/prog.go --
+package main
+func main() {}
diff --git a/src/cmd/go/testdata/script/mod_enabled.txt b/src/cmd/go/testdata/script/mod_enabled.txt
index 4901b9c5e6..8eef870b02 100644
--- a/src/cmd/go/testdata/script/mod_enabled.txt
+++ b/src/cmd/go/testdata/script/mod_enabled.txt
@@ -65,6 +65,15 @@ cd $GOPATH/foo/bar/baz
go env GOMOD
! stdout .+
+# GO111MODULE=auto should ignore and warn about /tmp/go.mod
+env GO111MODULE=auto
+cp $GOPATH/src/x/y/z/go.mod $WORK/tmp/go.mod
+mkdir $WORK/tmp/mydir
+cd $WORK/tmp/mydir
+go env GOMOD
+! stdout .+
+stderr '^go: warning: ignoring go.mod in system temp root '
+
-- $GOPATH/src/x/y/z/go.mod --
module x/y/z
-- $GOPATH/src/x/y/z/w/w.txt --
diff --git a/src/cmd/go/testdata/script/mod_fs_patterns.txt b/src/cmd/go/testdata/script/mod_fs_patterns.txt
index b5350c3eed..d7d3e0321b 100644
--- a/src/cmd/go/testdata/script/mod_fs_patterns.txt
+++ b/src/cmd/go/testdata/script/mod_fs_patterns.txt
@@ -28,6 +28,18 @@ stdout ^m/vendor$
stdout ^m/y$
! stdout ^m/y/z
+# non-existent directory should not prompt lookups
+! go build -mod=readonly example.com/nonexist
+stderr 'import lookup disabled'
+
+! go build -mod=readonly ./nonexist
+! stderr 'import lookup disabled'
+stderr '^go: no such directory ./nonexist'
+
+! go build -mod=readonly ./go.mod
+! stderr 'import lookup disabled'
+stderr '^go: ./go.mod is not a directory'
+
-- x/go.mod --
module m
diff --git a/src/cmd/go/testdata/script/mod_get_commit.txt b/src/cmd/go/testdata/script/mod_get_commit.txt
index 2608397404..589a791fd4 100644
--- a/src/cmd/go/testdata/script/mod_get_commit.txt
+++ b/src/cmd/go/testdata/script/mod_get_commit.txt
@@ -45,8 +45,8 @@ grep 'rsc.io/quote v1.5.1' go.mod
go mod edit -require rsc.io/quote@23179ee
grep 'rsc.io/quote 23179ee' go.mod
-# but go mod fix fixes them
-go mod fix
+# but other commands fix them
+go mod graph
grep 'rsc.io/quote v1.5.1' go.mod
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_list_dir.txt b/src/cmd/go/testdata/script/mod_list_dir.txt
index 29cde71fb8..800f277559 100644
--- a/src/cmd/go/testdata/script/mod_list_dir.txt
+++ b/src/cmd/go/testdata/script/mod_list_dir.txt
@@ -9,11 +9,14 @@ go list -f '{{.ImportPath}}' $GOROOT/src/math
stdout ^math$
go list -f '{{.ImportPath}}' .
stdout ^x$
+! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
+stderr '^go: no such directory.*quote@v1.5.2'
+go mod download rsc.io/quote@v1.5.2
go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
stdout '^rsc.io/quote$'
go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0
stdout '^rsc.io/sampler$'
-go get rsc.io/sampler@v1.3.1
+go get -d rsc.io/sampler@v1.3.1
go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.1
stdout '^rsc.io/sampler$'
! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0
diff --git a/src/cmd/go/testdata/script/mod_patterns.txt b/src/cmd/go/testdata/script/mod_patterns.txt
index 36d738a867..4fa436ba2d 100644
--- a/src/cmd/go/testdata/script/mod_patterns.txt
+++ b/src/cmd/go/testdata/script/mod_patterns.txt
@@ -5,43 +5,34 @@ cd m
# 'go list all' should list all of the packages used (directly or indirectly) by
# the packages in the main module, but no other packages from the standard
# library or active modules.
-go list all
-stdout example.com/m/useunicode
-stdout example.com/m/useunsafe
-[cgo] stdout example.com/m/useC
-[!cgo] ! stdout example.com/m/useC
-stdout '^unicode$'
-stdout '^unsafe$'
-! stdout index/suffixarray
-
+#
# 'go list ...' should list packages in all active modules and the standard library.
# But not cmd/* - see golang.org/issue/26924.
-go list ...
-stdout example.com/unused/useerrors
-stdout example.com/m/useunsafe
-[cgo] stdout example.com/m/useC
+#
+# 'go list example.com/m/...' should list packages in all modules that begin with 'example.com/m/'.
+#
+# 'go list ./...' should list only packages in the current module, not other active modules.
+#
+# Warnings about unmatched patterns should only be printed once.
+#
+# And the go command should be able to keep track of all this!
+go list -f '{{.ImportPath}}: {{.Match}}' all ... example.com/m/... ./... ./xyz...
+stdout 'example.com/m/useunicode: \[all \.\.\. example.com/m/... ./...\]'
+stdout 'example.com/m/useunsafe: \[all \.\.\. example.com/m/... ./...\]'
+[cgo] stdout 'example.com/m/useC: \[all \.\.\. example.com/m/... ./...\]'
[!cgo] ! stdout example.com/m/useC
-stdout '^unicode$'
-stdout '^unsafe$'
-stdout index/suffixarray
-! stdout cmd/pprof
+stdout 'example.com/unused/useerrors: \[\.\.\.\]' # but not "all"
+stdout 'example.com/m/nested/useencoding: \[\.\.\. example.com/m/...\]' # but NOT "all" or "./..."
+stdout '^unicode: \[all \.\.\.\]'
+stdout '^unsafe: \[all \.\.\.\]'
+stdout 'index/suffixarray: \[\.\.\.\]'
+! stdout cmd/pprof # golang.org/issue/26924
-# 'go list example.com/m/...' should list packages in all modules that begin with
-# "example.com/m/".
-go list example.com/m/...
-stdout example.com/m/useunicode
-stdout example.com/m/useunsafe
-! stdout example.com/[^m]
-! stdout ^[^e]
-[cgo] stdout example.com/m/useC
-[!cgo] ! stdout example.com/m/useC
+stderr -count=1 '^go: warning: "./xyz..." matched no packages$'
-# 'go list ./...' should list only packages in the current module, not other active modules.
-go list ./...
-stdout example.com/m/useunicode
-stdout example.com/m/useunsafe
-[cgo] stdout example.com/m/useC
-[!cgo] ! stdout example.com/m/useC
+env CGO_ENABLED=0
+go list -f '{{.ImportPath}}: {{.Match}}' all ... example.com/m/... ./... ./xyz...
+! stdout example.com/m/useC
-- m/go.mod --
module example.com/m
diff --git a/src/cmd/go/testdata/script/mod_run_internal.txt b/src/cmd/go/testdata/script/mod_run_internal.txt
new file mode 100644
index 0000000000..653ad282be
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_run_internal.txt
@@ -0,0 +1,46 @@
+env GO111MODULE=on
+
+go list -e -f '{{.Incomplete}}' runbad1.go
+stdout true
+! go run runbad1.go
+stderr 'use of internal package m/x/internal not allowed'
+
+go list -e -f '{{.Incomplete}}' runbad2.go
+stdout true
+! go run runbad2.go
+stderr 'use of internal package m/x/internal/y not allowed'
+
+go list -e -f '{{.Incomplete}}' runok.go
+stdout false
+go run runok.go
+
+-- go.mod --
+module m
+
+-- x/internal/internal.go --
+package internal
+
+-- x/internal/y/y.go --
+package y
+
+-- internal/internal.go --
+package internal
+
+-- internal/z/z.go --
+package z
+
+-- runbad1.go --
+package main
+import _ "m/x/internal"
+func main() {}
+
+-- runbad2.go --
+package main
+import _ "m/x/internal/y"
+func main() {}
+
+-- runok.go --
+package main
+import _ "m/internal"
+import _ "m/internal/z"
+func main() {}
diff --git a/src/cmd/go/testdata/script/mod_run_path.txt b/src/cmd/go/testdata/script/mod_run_path.txt
new file mode 100644
index 0000000000..4369ee4131
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_run_path.txt
@@ -0,0 +1,15 @@
+# Test that go run does not get confused by conflict
+# between go.mod's module path and what you'd
+# expect from GOPATH. golang.org/issue/26046.
+
+env GO111MODULE=on
+
+cd $GOPATH/src/example.com/hello
+go run main.go
+
+-- $GOPATH/src/example.com/hello/go.mod --
+module example.com/hello/v2
+
+-- $GOPATH/src/example.com/hello/main.go --
+package main
+func main() {}
diff --git a/src/cmd/go/testdata/script/mod_tidy.txt b/src/cmd/go/testdata/script/mod_tidy.txt
index 86434af7f3..449aa073a7 100644
--- a/src/cmd/go/testdata/script/mod_tidy.txt
+++ b/src/cmd/go/testdata/script/mod_tidy.txt
@@ -10,6 +10,10 @@ go list -m all
stdout '^w.1 v1.2.0'
stdout '^z.1 v1.2.0'
+# empty tidy should not crash
+cd triv
+go mod tidy
+
-- go.mod --
module m
@@ -55,3 +59,6 @@ module z
-- z/sub/sub.go --
package sub
+
+-- triv/go.mod --
+module triv
diff --git a/src/cmd/go/testdata/script/mod_vcs_missing.txt b/src/cmd/go/testdata/script/mod_vcs_missing.txt
new file mode 100644
index 0000000000..fb146b4415
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_vcs_missing.txt
@@ -0,0 +1,11 @@
+[exec:bzr] skip 'tests NOT having bzr'
+[!net] skip
+
+env GO111MODULE=on
+env GOPROXY=
+
+! go list launchpad.net/gocheck
+stderr '"bzr": executable file not found'
+
+-- go.mod --
+module m
diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go
index 90fd4ed379..a55da1d3c8 100644
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -259,6 +259,20 @@ func checkPrintfFwd(pkg *Package, w *printfWrapper, call *ast.CallExpr, kind int
}
if !call.Ellipsis.IsValid() {
+ typ, ok := pkg.types[call.Fun].Type.(*types.Signature)
+ if !ok {
+ return
+ }
+ if len(call.Args) > typ.Params().Len() {
+ // If we're passing more arguments than what the
+ // print/printf function can take, adding an ellipsis
+ // would break the program. For example:
+ //
+ // func foo(arg1 string, arg2 ...interface{} {
+ // fmt.Printf("%s %v", arg1, arg2)
+ // }
+ return
+ }
if !vcfg.VetxOnly {
desc := "printf"
if kind == kindPrint {
diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go
index 7c0cbcf05a..ecafed5fa2 100644
--- a/src/cmd/vet/testdata/print.go
+++ b/src/cmd/vet/testdata/print.go
@@ -446,6 +446,10 @@ func (*ptrStringer) BadWrapf(x int, format string, args ...interface{}) string {
return fmt.Sprintf(format, args) // ERROR "missing ... in args forwarded to printf-like function"
}
+func (*ptrStringer) WrapfFalsePositive(x int, arg1 string, arg2 ...interface{}) string {
+ return fmt.Sprintf("%s %v", arg1, arg2)
+}
+
type embeddedStringer struct {
foo string
ptrStringer
diff --git a/src/go/doc/comment.go b/src/go/doc/comment.go
index 7c4490e7c3..d068d8960c 100644
--- a/src/go/doc/comment.go
+++ b/src/go/doc/comment.go
@@ -232,7 +232,7 @@ func heading(line string) string {
}
// exclude lines with illegal characters. we allow "(),"
- if strings.ContainsAny(line, ".;:!?+*/=[]{}_^°&§~%#@<\">\\") {
+ if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") {
return ""
}
@@ -248,6 +248,18 @@ func heading(line string) string {
b = b[i+2:]
}
+ // allow "." when followed by non-space
+ for b := line;; {
+ i := strings.IndexRune(b, '.')
+ if i < 0 {
+ break
+ }
+ if i+1 >= len(b) || b[i+1] == ' ' {
+ return "" // not followed by non-space
+ }
+ b = b[i+1:]
+ }
+
return line
}
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index 3723f30e56..1de7cd81b2 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -32,8 +32,10 @@ import (
// Print as many newlines as necessary (but at least min newlines) to get to
// the current line. ws is printed before the first line break. If newSection
-// is set, the first line break is printed as formfeed. Returns true if any
-// line break was printed; returns false otherwise.
+// is set, the first line break is printed as formfeed. Returns 0 if no line
+// breaks were printed, returns 1 if there was exactly one newline printed,
+// and returns a value > 1 if there was a formfeed or more than one newline
+// printed.
//
// TODO(gri): linebreak may add too many lines if the next statement at "line"
// is preceded by comments because the computation of n assumes
@@ -43,7 +45,7 @@ import (
// linebreaks. At the moment there is no easy way to know about
// future (not yet interspersed) comments in this function.
//
-func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
+func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) {
n := nlimit(line - p.pos.Line)
if n < min {
n = min
@@ -53,11 +55,12 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin
if newSection {
p.print(formfeed)
n--
+ nbreaks = 2
}
+ nbreaks += n
for ; n > 0; n-- {
p.print(newline)
}
- printedBreak = true
}
return
}
@@ -173,7 +176,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// The first linebreak is always a formfeed since this section must not
// depend on any previous formatting.
prevBreak := -1 // index of last expression that was followed by a linebreak
- if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
+ if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) > 0 {
ws = ignore
prevBreak = 0
}
@@ -221,36 +224,19 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// If the previous line and the current line had single-
// line-expressions and the key sizes are small or the
// ratio between the current key and the geometric mean
- // does not exceed a threshold, align columns and do not use
- // formfeed.
- // If the previous line was an empty line, break the alignment.
- // (The text/tabwriter will break alignment after an empty line
- // even if we don't do anything here, but we can't see that; yet
- // we need to reset the variables used in the geomean
- // computation after an alignment break. Do it explicitly
- // instead so we're aware of the break. Was issue #26352.)
+ // if the previous key sizes does not exceed a threshold,
+ // align columns and do not use formfeed.
if prevSize > 0 && size > 0 {
const smallSize = 40
- switch {
- case prevLine+1 < line:
- useFF = true
- case count == 0, prevSize <= smallSize && size <= smallSize:
+ if count == 0 || prevSize <= smallSize && size <= smallSize {
useFF = false
- default:
+ } else {
const r = 2.5 // threshold
geomean := math.Exp(lnsum / float64(count)) // count > 0
ratio := float64(size) / geomean
useFF = r*ratio <= 1 || r <= ratio
}
}
- if useFF {
- lnsum = 0
- count = 0
- }
- if size > 0 {
- lnsum += math.Log(float64(size))
- count++
- }
needsLinebreak := 0 < prevLine && prevLine < line
if i > 0 {
@@ -266,11 +252,20 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
// Lines are broken using newlines so comments remain aligned
// unless useFF is set or there are multiple expressions on
// the same line in which case formfeed is used.
- if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
+ nbreaks := p.linebreak(line, 0, ws, useFF || prevBreak+1 < i)
+ if nbreaks > 0 {
ws = ignore
prevBreak = i
needsBlank = false // we got a line break instead
}
+ // If there was a new section or more than one new line
+ // (which means that the tabwriter will implicitly break
+ // the section), reset the geomean variables since we are
+ // starting a new group of elements with the next element.
+ if nbreaks > 1 {
+ lnsum = 0
+ count = 0
+ }
}
if needsBlank {
p.print(blank)
@@ -290,6 +285,11 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
p.expr0(x, depth)
}
+ if size > 0 {
+ lnsum += math.Log(float64(size))
+ count++
+ }
+
prevLine = line
}
@@ -347,7 +347,7 @@ func (p *printer) parameters(fields *ast.FieldList) {
p.print(token.COMMA)
}
// separator if needed (linebreak or blank)
- if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) {
+ if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 {
// break line if the opening "(" or previous parameter ended on a different line
ws = ignore
} else if i > 0 {
@@ -718,7 +718,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
if xline != yline && xline > 0 && yline > 0 {
// at least one line break, but respect an extra empty line
// in the source
- if p.linebreak(yline, 1, ws, true) {
+ if p.linebreak(yline, 1, ws, true) > 0 {
ws = ignore
printBlank = false // no blank after line break
}
diff --git a/src/go/printer/testdata/alignment.golden b/src/go/printer/testdata/alignment.golden
index 302b32e766..96086ed906 100644
--- a/src/go/printer/testdata/alignment.golden
+++ b/src/go/printer/testdata/alignment.golden
@@ -131,9 +131,42 @@ func main() {
// ----------------------------------------------------------------------------
// Examples from issue #26352.
+
var _ = map[int]string{
1: "",
12345678901234567890123456789: "",
12345678901234567890123456789012345678: "",
}
+
+func f() {
+ _ = map[int]string{
+ 1: "",
+
+ 12345678901234567: "",
+ 12345678901234567890123456789012345678901: "",
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Examples from issue #26930.
+
+var _ = S{
+ F1: []string{},
+ F2____: []string{},
+}
+
+var _ = S{
+ F1: []string{},
+ F2____: []string{},
+}
+
+var _ = S{
+ F1____: []string{},
+ F2: []string{},
+}
+
+var _ = S{
+ F1____: []string{},
+ F2: []string{},
+}
diff --git a/src/go/printer/testdata/alignment.input b/src/go/printer/testdata/alignment.input
index 83361cc7c1..323d2689e0 100644
--- a/src/go/printer/testdata/alignment.input
+++ b/src/go/printer/testdata/alignment.input
@@ -131,9 +131,49 @@ func main() {
// ----------------------------------------------------------------------------
// Examples from issue #26352.
+
var _ = map[int]string{
1: "",
12345678901234567890123456789: "",
12345678901234567890123456789012345678: "",
}
+
+func f() {
+ _ = map[int]string{
+ 1: "",
+
+ 12345678901234567: "",
+ 12345678901234567890123456789012345678901: "",
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Examples from issue #26930.
+
+var _ = S{
+ F1: []string{
+ },
+ F2____: []string{},
+}
+
+var _ = S{
+ F1: []string{
+
+
+ },
+ F2____: []string{},
+}
+
+var _ = S{
+ F1____: []string{
+ },
+ F2: []string{},
+}
+
+var _ = S{
+ F1____: []string{
+
+ },
+ F2: []string{},
+}
diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go
index 3a0b774aa2..2c74403434 100644
--- a/src/os/file_plan9.go
+++ b/src/os/file_plan9.go
@@ -478,7 +478,12 @@ func (f *File) Chown(uid, gid int) error {
}
func tempDir() string {
- return "/tmp"
+ dir := Getenv("TMPDIR")
+ if dir == "" {
+ dir = "/tmp"
+ }
+ return dir
+
}
// Chdir changes the current working directory to the file,
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index a3193b63c5..ad47d1275e 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -842,11 +842,11 @@ var (
lfenceBeforeRdtsc bool
// Set in runtime.cpuinit.
- support_erms bool
- support_popcnt bool
- support_sse2 bool
- support_sse41 bool
- arm64_support_atomics bool
+ support_erms bool
+ support_popcnt bool
+ support_sse2 bool
+ support_sse41 bool
+ arm64_support_atomics bool
goarm uint8 // set by cmd/link on arm systems
framepointer_enabled bool // set by cmd/link
diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s
index 2a2e7379ca..db74352613 100644
--- a/src/runtime/sys_darwin_amd64.s
+++ b/src/runtime/sys_darwin_amd64.s
@@ -306,7 +306,7 @@ TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
CMPQ AX, $-1
JNE ok
CALL libc_error(SB)
- MOVQ (AX), DX // errno
+ MOVLQSX (AX), DX // errno
XORL AX, AX
ok:
MOVQ AX, 32(BX)
@@ -371,7 +371,7 @@ TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0
CMPQ AX, $-1
JNE ok
CALL libc_error(SB)
- MOVQ (AX), AX // errno
+ MOVLQSX (AX), AX // errno
NEGQ AX // caller wants it as a negative error code
ok:
POPQ BP
diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s
index 4f9d0b8d58..d7ba116b84 100644
--- a/src/runtime/sys_darwin_arm64.s
+++ b/src/runtime/sys_darwin_arm64.s
@@ -70,7 +70,7 @@ TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
CMP R0, R2
BNE ok
BL libc_error(SB)
- MOVD (R0), R1
+ MOVW (R0), R1
MOVD $0, R0
ok:
MOVD R0, 32(R19) // ret 1 p
@@ -277,7 +277,7 @@ TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0
CMP R0, R2
BNE ok
BL libc_error(SB)
- MOVD (R0), R0 // errno
+ MOVW (R0), R0 // errno
NEG R0, R0 // caller wants it as a negative error code
ok:
RET
diff --git a/test/closure4.go b/test/closure4.go
new file mode 100644
index 0000000000..ec4e0a18eb
--- /dev/null
+++ b/test/closure4.go
@@ -0,0 +1,21 @@
+// run
+
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Check that calling a nil func causes a proper panic.
+
+package main
+
+func main() {
+ defer func() {
+ err := recover()
+ if err == nil {
+ panic("panic expected")
+ }
+ }()
+
+ var f func()
+ f()
+}