mirror of
https://github.com/golang/go
synced 2024-11-26 08:17:59 -07:00
cmd/go, go/build: parse directives in file headers
For #56986, go/build needs to report up to cmd/go about //go:debug lines found in the source code. Rather than make a special case for //go:debug, this change gathers all top-level directives above the package line and includes them in the result. The go command's module index must match go/build, so this CL contains the code to update the index as well. A future CL will use the //go:debug lines to prepare the default GODEBUG settings, as well as rejecting such lines in non-main packages. Change-Id: I66ab8dc72f9cd65c503b10b744367caca233f8a6 Reviewed-on: https://go-review.googlesource.com/c/go/+/453603 Reviewed-by: Bryan Mills <bcmills@google.com> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
f07fafacef
commit
bd8ec78b08
6
api/next/56986.txt
Normal file
6
api/next/56986.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pkg go/build, type Directive struct #56986
|
||||||
|
pkg go/build, type Directive struct, Pos token.Position #56986
|
||||||
|
pkg go/build, type Directive struct, Text string #56986
|
||||||
|
pkg go/build, type Package struct, Directives []Directive #56986
|
||||||
|
pkg go/build, type Package struct, TestDirectives []Directive #56986
|
||||||
|
pkg go/build, type Package struct, XTestDirectives []Directive #56986
|
@ -230,6 +230,9 @@ type PackageInternal struct {
|
|||||||
TestmainGo *[]byte // content for _testmain.go
|
TestmainGo *[]byte // content for _testmain.go
|
||||||
Embed map[string][]string // //go:embed comment mapping
|
Embed map[string][]string // //go:embed comment mapping
|
||||||
OrigImportPath string // original import path before adding '_test' suffix
|
OrigImportPath string // original import path before adding '_test' suffix
|
||||||
|
Directives []build.Directive
|
||||||
|
TestDirectives []build.Directive
|
||||||
|
XTestDirectives []build.Directive
|
||||||
|
|
||||||
Asmflags []string // -asmflags for this package
|
Asmflags []string // -asmflags for this package
|
||||||
Gcflags []string // -gcflags for this package
|
Gcflags []string // -gcflags for this package
|
||||||
@ -435,6 +438,9 @@ func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) {
|
|||||||
p.TestEmbedPatterns = pp.TestEmbedPatterns
|
p.TestEmbedPatterns = pp.TestEmbedPatterns
|
||||||
p.XTestEmbedPatterns = pp.XTestEmbedPatterns
|
p.XTestEmbedPatterns = pp.XTestEmbedPatterns
|
||||||
p.Internal.OrigImportPath = pp.ImportPath
|
p.Internal.OrigImportPath = pp.ImportPath
|
||||||
|
p.Internal.Directives = pp.Directives
|
||||||
|
p.Internal.TestDirectives = pp.TestDirectives
|
||||||
|
p.Internal.XTestDirectives = pp.XTestDirectives
|
||||||
}
|
}
|
||||||
|
|
||||||
// A PackageError describes an error loading information about a package.
|
// A PackageError describes an error loading information about a package.
|
||||||
|
@ -376,13 +376,14 @@ var dummyPkg build.Package
|
|||||||
|
|
||||||
// fileInfo records information learned about a file included in a build.
|
// fileInfo records information learned about a file included in a build.
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
name string // full name including dir
|
name string // full name including dir
|
||||||
header []byte
|
header []byte
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
parsed *ast.File
|
parsed *ast.File
|
||||||
parseErr error
|
parseErr error
|
||||||
imports []fileImport
|
imports []fileImport
|
||||||
embeds []fileEmbed
|
embeds []fileEmbed
|
||||||
|
directives []build.Directive
|
||||||
|
|
||||||
// Additional fields added to go/build's fileinfo for the purposes of the modindex package.
|
// Additional fields added to go/build's fileinfo for the purposes of the modindex package.
|
||||||
binaryOnly bool
|
binaryOnly bool
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
@ -474,6 +475,18 @@ func readGoInfo(f io.Reader, info *fileInfo) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract directives.
|
||||||
|
for _, group := range info.parsed.Comments {
|
||||||
|
if group.Pos() >= info.parsed.Package {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, c := range group.List {
|
||||||
|
if strings.HasPrefix(c.Text, "//go:") {
|
||||||
|
info.directives = append(info.directives, build.Directive{Text: c.Text, Pos: info.fset.Position(c.Slash)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the file imports "embed",
|
// If the file imports "embed",
|
||||||
// we have to look for //go:embed comments
|
// we have to look for //go:embed comments
|
||||||
// in the remainder of the file.
|
// in the remainder of the file.
|
||||||
|
@ -580,6 +580,7 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
|
|||||||
|
|
||||||
var fileList *[]string
|
var fileList *[]string
|
||||||
var importMap, embedMap map[string][]token.Position
|
var importMap, embedMap map[string][]token.Position
|
||||||
|
var directives *[]build.Directive
|
||||||
switch {
|
switch {
|
||||||
case isCgo:
|
case isCgo:
|
||||||
allTags["cgo"] = true
|
allTags["cgo"] = true
|
||||||
@ -587,6 +588,7 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
|
|||||||
fileList = &p.CgoFiles
|
fileList = &p.CgoFiles
|
||||||
importMap = importPos
|
importMap = importPos
|
||||||
embedMap = embedPos
|
embedMap = embedPos
|
||||||
|
directives = &p.Directives
|
||||||
} else {
|
} else {
|
||||||
// Ignore Imports and Embeds from cgo files if cgo is disabled.
|
// Ignore Imports and Embeds from cgo files if cgo is disabled.
|
||||||
fileList = &p.IgnoredGoFiles
|
fileList = &p.IgnoredGoFiles
|
||||||
@ -595,14 +597,17 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
|
|||||||
fileList = &p.XTestGoFiles
|
fileList = &p.XTestGoFiles
|
||||||
importMap = xTestImportPos
|
importMap = xTestImportPos
|
||||||
embedMap = xTestEmbedPos
|
embedMap = xTestEmbedPos
|
||||||
|
directives = &p.XTestDirectives
|
||||||
case isTest:
|
case isTest:
|
||||||
fileList = &p.TestGoFiles
|
fileList = &p.TestGoFiles
|
||||||
importMap = testImportPos
|
importMap = testImportPos
|
||||||
embedMap = testEmbedPos
|
embedMap = testEmbedPos
|
||||||
|
directives = &p.TestDirectives
|
||||||
default:
|
default:
|
||||||
fileList = &p.GoFiles
|
fileList = &p.GoFiles
|
||||||
importMap = importPos
|
importMap = importPos
|
||||||
embedMap = embedPos
|
embedMap = embedPos
|
||||||
|
directives = &p.Directives
|
||||||
}
|
}
|
||||||
*fileList = append(*fileList, name)
|
*fileList = append(*fileList, name)
|
||||||
if importMap != nil {
|
if importMap != nil {
|
||||||
@ -615,6 +620,9 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
|
|||||||
embedMap[e.pattern] = append(embedMap[e.pattern], e.position)
|
embedMap[e.pattern] = append(embedMap[e.pattern], e.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if directives != nil {
|
||||||
|
*directives = append(*directives, tf.directives()...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
|
p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
|
||||||
@ -902,6 +910,13 @@ func (sf *sourceFile) embedsOffset() int {
|
|||||||
return pos + 4 + n*(4*5)
|
return pos + 4 + n*(4*5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sf *sourceFile) directivesOffset() int {
|
||||||
|
pos := sf.embedsOffset()
|
||||||
|
n := sf.d.intAt(pos)
|
||||||
|
// each import is 5 uint32s (string + tokpos)
|
||||||
|
return pos + 4 + n*(4*5)
|
||||||
|
}
|
||||||
|
|
||||||
func (sf *sourceFile) imports() []rawImport {
|
func (sf *sourceFile) imports() []rawImport {
|
||||||
sf.onceReadImports.Do(func() {
|
sf.onceReadImports.Do(func() {
|
||||||
importsOffset := sf.importsOffset()
|
importsOffset := sf.importsOffset()
|
||||||
@ -927,6 +942,17 @@ func (sf *sourceFile) embeds() []embed {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sf *sourceFile) directives() []build.Directive {
|
||||||
|
directivesOffset := sf.directivesOffset()
|
||||||
|
r := sf.d.readAt(directivesOffset)
|
||||||
|
numDirectives := r.int()
|
||||||
|
ret := make([]build.Directive, numDirectives)
|
||||||
|
for i := range ret {
|
||||||
|
ret[i] = build.Directive{Text: r.string(), Pos: r.tokpos()}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func asString(b []byte) string {
|
func asString(b []byte) string {
|
||||||
return unsafe.String(unsafe.SliceData(b), len(b))
|
return unsafe.String(unsafe.SliceData(b), len(b))
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/build"
|
||||||
"go/doc"
|
"go/doc"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
@ -159,6 +160,7 @@ type rawFile struct {
|
|||||||
plusBuildConstraints []string
|
plusBuildConstraints []string
|
||||||
imports []rawImport
|
imports []rawImport
|
||||||
embeds []embed
|
embeds []embed
|
||||||
|
directives []build.Directive
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawImport struct {
|
type rawImport struct {
|
||||||
@ -231,6 +233,7 @@ func importRaw(modroot, reldir string) *rawPackage {
|
|||||||
goBuildConstraint: info.goBuildConstraint,
|
goBuildConstraint: info.goBuildConstraint,
|
||||||
plusBuildConstraints: info.plusBuildConstraints,
|
plusBuildConstraints: info.plusBuildConstraints,
|
||||||
binaryOnly: info.binaryOnly,
|
binaryOnly: info.binaryOnly,
|
||||||
|
directives: info.directives,
|
||||||
}
|
}
|
||||||
if info.parsed != nil {
|
if info.parsed != nil {
|
||||||
rf.pkgName = info.parsed.Name.Name
|
rf.pkgName = info.parsed.Name.Name
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
const indexVersion = "go index v1" // 11 bytes (plus \n), to align uint32s in index
|
const indexVersion = "go index v2" // 11 bytes (plus \n), to align uint32s in index
|
||||||
|
|
||||||
// encodeModuleBytes produces the encoded representation of the module index.
|
// encodeModuleBytes produces the encoded representation of the module index.
|
||||||
// encodeModuleBytes may modify the packages slice.
|
// encodeModuleBytes may modify the packages slice.
|
||||||
@ -84,6 +84,12 @@ func encodeFile(e *encoder, f *rawFile) {
|
|||||||
e.String(embed.pattern)
|
e.String(embed.pattern)
|
||||||
e.Position(embed.position)
|
e.Position(embed.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.Int(len(f.directives))
|
||||||
|
for _, d := range f.directives {
|
||||||
|
e.String(d.Text)
|
||||||
|
e.Position(d.Pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEncoder() *encoder {
|
func newEncoder() *encoder {
|
||||||
|
@ -455,6 +455,11 @@ type Package struct {
|
|||||||
TestGoFiles []string // _test.go files in package
|
TestGoFiles []string // _test.go files in package
|
||||||
XTestGoFiles []string // _test.go files outside package
|
XTestGoFiles []string // _test.go files outside package
|
||||||
|
|
||||||
|
// Go directive comments (//go:zzz...) found in source files.
|
||||||
|
Directives []Directive
|
||||||
|
TestDirectives []Directive
|
||||||
|
XTestDirectives []Directive
|
||||||
|
|
||||||
// Dependency information
|
// Dependency information
|
||||||
Imports []string // import paths from GoFiles, CgoFiles
|
Imports []string // import paths from GoFiles, CgoFiles
|
||||||
ImportPos map[string][]token.Position // line information for Imports
|
ImportPos map[string][]token.Position // line information for Imports
|
||||||
@ -476,6 +481,12 @@ type Package struct {
|
|||||||
XTestEmbedPatternPos map[string][]token.Position // line information for XTestEmbedPatternPos
|
XTestEmbedPatternPos map[string][]token.Position // line information for XTestEmbedPatternPos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Directive is a Go directive comment (//go:zzz...) found in a source file.
|
||||||
|
type Directive struct {
|
||||||
|
Text string // full line comment including leading slashes
|
||||||
|
Pos token.Position // position of comment
|
||||||
|
}
|
||||||
|
|
||||||
// IsCommand reports whether the package is considered a
|
// IsCommand reports whether the package is considered a
|
||||||
// command to be installed (not just a library).
|
// command to be installed (not just a library).
|
||||||
// Packages named "main" are treated as commands.
|
// Packages named "main" are treated as commands.
|
||||||
@ -969,6 +980,7 @@ Found:
|
|||||||
|
|
||||||
var fileList *[]string
|
var fileList *[]string
|
||||||
var importMap, embedMap map[string][]token.Position
|
var importMap, embedMap map[string][]token.Position
|
||||||
|
var directives *[]Directive
|
||||||
switch {
|
switch {
|
||||||
case isCgo:
|
case isCgo:
|
||||||
allTags["cgo"] = true
|
allTags["cgo"] = true
|
||||||
@ -976,6 +988,7 @@ Found:
|
|||||||
fileList = &p.CgoFiles
|
fileList = &p.CgoFiles
|
||||||
importMap = importPos
|
importMap = importPos
|
||||||
embedMap = embedPos
|
embedMap = embedPos
|
||||||
|
directives = &p.Directives
|
||||||
} else {
|
} else {
|
||||||
// Ignore imports and embeds from cgo files if cgo is disabled.
|
// Ignore imports and embeds from cgo files if cgo is disabled.
|
||||||
fileList = &p.IgnoredGoFiles
|
fileList = &p.IgnoredGoFiles
|
||||||
@ -984,14 +997,17 @@ Found:
|
|||||||
fileList = &p.XTestGoFiles
|
fileList = &p.XTestGoFiles
|
||||||
importMap = xTestImportPos
|
importMap = xTestImportPos
|
||||||
embedMap = xTestEmbedPos
|
embedMap = xTestEmbedPos
|
||||||
|
directives = &p.XTestDirectives
|
||||||
case isTest:
|
case isTest:
|
||||||
fileList = &p.TestGoFiles
|
fileList = &p.TestGoFiles
|
||||||
importMap = testImportPos
|
importMap = testImportPos
|
||||||
embedMap = testEmbedPos
|
embedMap = testEmbedPos
|
||||||
|
directives = &p.TestDirectives
|
||||||
default:
|
default:
|
||||||
fileList = &p.GoFiles
|
fileList = &p.GoFiles
|
||||||
importMap = importPos
|
importMap = importPos
|
||||||
embedMap = embedPos
|
embedMap = embedPos
|
||||||
|
directives = &p.Directives
|
||||||
}
|
}
|
||||||
*fileList = append(*fileList, name)
|
*fileList = append(*fileList, name)
|
||||||
if importMap != nil {
|
if importMap != nil {
|
||||||
@ -1004,6 +1020,9 @@ Found:
|
|||||||
embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
|
embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if directives != nil {
|
||||||
|
*directives = append(*directives, info.directives...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for tag := range allTags {
|
for tag := range allTags {
|
||||||
@ -1383,13 +1402,14 @@ var dummyPkg Package
|
|||||||
|
|
||||||
// fileInfo records information learned about a file included in a build.
|
// fileInfo records information learned about a file included in a build.
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
name string // full name including dir
|
name string // full name including dir
|
||||||
header []byte
|
header []byte
|
||||||
fset *token.FileSet
|
fset *token.FileSet
|
||||||
parsed *ast.File
|
parsed *ast.File
|
||||||
parseErr error
|
parseErr error
|
||||||
imports []fileImport
|
imports []fileImport
|
||||||
embeds []fileEmbed
|
embeds []fileEmbed
|
||||||
|
directives []Directive
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileImport struct {
|
type fileImport struct {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -801,3 +802,27 @@ func TestAllTagsNonSourceFile(t *testing.T) {
|
|||||||
t.Errorf("AllTags = %v, want empty", p.AllTags)
|
t.Errorf("AllTags = %v, want empty", p.AllTags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDirectives(t *testing.T) {
|
||||||
|
p, err := ImportDir("testdata/directives", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not import testdata: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
check := func(name string, list []Directive, want string) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
want = strings.ReplaceAll(want, "testdata/directives/", `testdata\\directives\\`)
|
||||||
|
}
|
||||||
|
t.Helper()
|
||||||
|
s := fmt.Sprintf("%q", list)
|
||||||
|
if s != want {
|
||||||
|
t.Errorf("%s = %s, want %s", name, s, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check("Directives", p.Directives,
|
||||||
|
`[{"//go:main1" "testdata/directives/a.go:1:1"} {"//go:plant" "testdata/directives/eve.go:1:1"}]`)
|
||||||
|
check("TestDirectives", p.TestDirectives,
|
||||||
|
`[{"//go:test1" "testdata/directives/a_test.go:1:1"} {"//go:test2" "testdata/directives/b_test.go:1:1"}]`)
|
||||||
|
check("XTestDirectives", p.XTestDirectives,
|
||||||
|
`[{"//go:xtest1" "testdata/directives/c_test.go:1:1"} {"//go:xtest2" "testdata/directives/d_test.go:1:1"} {"//go:xtest3" "testdata/directives/d_test.go:2:1"}]`)
|
||||||
|
}
|
||||||
|
@ -471,6 +471,18 @@ func readGoInfo(f io.Reader, info *fileInfo) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract directives.
|
||||||
|
for _, group := range info.parsed.Comments {
|
||||||
|
if group.Pos() >= info.parsed.Package {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, c := range group.List {
|
||||||
|
if strings.HasPrefix(c.Text, "//go:") {
|
||||||
|
info.directives = append(info.directives, Directive{c.Text, info.fset.Position(c.Slash)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the file imports "embed",
|
// If the file imports "embed",
|
||||||
// we have to look for //go:embed comments
|
// we have to look for //go:embed comments
|
||||||
// in the remainder of the file.
|
// in the remainder of the file.
|
||||||
|
3
src/go/build/testdata/directives/a.go
vendored
Normal file
3
src/go/build/testdata/directives/a.go
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//go:main1
|
||||||
|
|
||||||
|
package p
|
3
src/go/build/testdata/directives/a_test.go
vendored
Normal file
3
src/go/build/testdata/directives/a_test.go
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//go:test1
|
||||||
|
|
||||||
|
package p
|
5
src/go/build/testdata/directives/b_test.go
vendored
Normal file
5
src/go/build/testdata/directives/b_test.go
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//go:test2
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
//go:ignored
|
5
src/go/build/testdata/directives/c_test.go
vendored
Normal file
5
src/go/build/testdata/directives/c_test.go
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//go:xtest1
|
||||||
|
|
||||||
|
package p_test
|
||||||
|
|
||||||
|
//go:ignored
|
4
src/go/build/testdata/directives/d_test.go
vendored
Normal file
4
src/go/build/testdata/directives/d_test.go
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//go:xtest2
|
||||||
|
//go:xtest3
|
||||||
|
|
||||||
|
package p_test
|
4
src/go/build/testdata/directives/eve.go
vendored
Normal file
4
src/go/build/testdata/directives/eve.go
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//go:plant
|
||||||
|
//axiom:plant
|
||||||
|
|
||||||
|
package p
|
Loading…
Reference in New Issue
Block a user