1
0
mirror of https://github.com/golang/go synced 2024-11-26 04:07: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:
Russ Cox 2022-11-29 13:30:50 -05:00
parent f07fafacef
commit bd8ec78b08
16 changed files with 157 additions and 15 deletions

6
api/next/56986.txt Normal file
View 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

View File

@ -230,6 +230,9 @@ type PackageInternal struct {
TestmainGo *[]byte // content for _testmain.go
Embed map[string][]string // //go:embed comment mapping
OrigImportPath string // original import path before adding '_test' suffix
Directives []build.Directive
TestDirectives []build.Directive
XTestDirectives []build.Directive
Asmflags []string // -asmflags 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.XTestEmbedPatterns = pp.XTestEmbedPatterns
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.

View File

@ -376,13 +376,14 @@ var dummyPkg build.Package
// fileInfo records information learned about a file included in a build.
type fileInfo struct {
name string // full name including dir
header []byte
fset *token.FileSet
parsed *ast.File
parseErr error
imports []fileImport
embeds []fileEmbed
name string // full name including dir
header []byte
fset *token.FileSet
parsed *ast.File
parseErr error
imports []fileImport
embeds []fileEmbed
directives []build.Directive
// Additional fields added to go/build's fileinfo for the purposes of the modindex package.
binaryOnly bool

View File

@ -13,6 +13,7 @@ import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"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",
// we have to look for //go:embed comments
// in the remainder of the file.

View File

@ -580,6 +580,7 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
var fileList *[]string
var importMap, embedMap map[string][]token.Position
var directives *[]build.Directive
switch {
case isCgo:
allTags["cgo"] = true
@ -587,6 +588,7 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
fileList = &p.CgoFiles
importMap = importPos
embedMap = embedPos
directives = &p.Directives
} else {
// Ignore Imports and Embeds from cgo files if cgo is disabled.
fileList = &p.IgnoredGoFiles
@ -595,14 +597,17 @@ func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *b
fileList = &p.XTestGoFiles
importMap = xTestImportPos
embedMap = xTestEmbedPos
directives = &p.XTestDirectives
case isTest:
fileList = &p.TestGoFiles
importMap = testImportPos
embedMap = testEmbedPos
directives = &p.TestDirectives
default:
fileList = &p.GoFiles
importMap = importPos
embedMap = embedPos
directives = &p.Directives
}
*fileList = append(*fileList, name)
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)
}
}
if directives != nil {
*directives = append(*directives, tf.directives()...)
}
}
p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos)
@ -902,6 +910,13 @@ func (sf *sourceFile) embedsOffset() int {
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 {
sf.onceReadImports.Do(func() {
importsOffset := sf.importsOffset()
@ -927,6 +942,17 @@ func (sf *sourceFile) embeds() []embed {
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 {
return unsafe.String(unsafe.SliceData(b), len(b))
}

View File

@ -11,6 +11,7 @@ import (
"encoding/json"
"errors"
"fmt"
"go/build"
"go/doc"
"go/scanner"
"go/token"
@ -159,6 +160,7 @@ type rawFile struct {
plusBuildConstraints []string
imports []rawImport
embeds []embed
directives []build.Directive
}
type rawImport struct {
@ -231,6 +233,7 @@ func importRaw(modroot, reldir string) *rawPackage {
goBuildConstraint: info.goBuildConstraint,
plusBuildConstraints: info.plusBuildConstraints,
binaryOnly: info.binaryOnly,
directives: info.directives,
}
if info.parsed != nil {
rf.pkgName = info.parsed.Name.Name

View File

@ -11,7 +11,7 @@ import (
"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 may modify the packages slice.
@ -84,6 +84,12 @@ func encodeFile(e *encoder, f *rawFile) {
e.String(embed.pattern)
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 {

View File

@ -455,6 +455,11 @@ type Package struct {
TestGoFiles []string // _test.go files in 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
Imports []string // import paths from GoFiles, CgoFiles
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
}
// 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
// command to be installed (not just a library).
// Packages named "main" are treated as commands.
@ -969,6 +980,7 @@ Found:
var fileList *[]string
var importMap, embedMap map[string][]token.Position
var directives *[]Directive
switch {
case isCgo:
allTags["cgo"] = true
@ -976,6 +988,7 @@ Found:
fileList = &p.CgoFiles
importMap = importPos
embedMap = embedPos
directives = &p.Directives
} else {
// Ignore imports and embeds from cgo files if cgo is disabled.
fileList = &p.IgnoredGoFiles
@ -984,14 +997,17 @@ Found:
fileList = &p.XTestGoFiles
importMap = xTestImportPos
embedMap = xTestEmbedPos
directives = &p.XTestDirectives
case isTest:
fileList = &p.TestGoFiles
importMap = testImportPos
embedMap = testEmbedPos
directives = &p.TestDirectives
default:
fileList = &p.GoFiles
importMap = importPos
embedMap = embedPos
directives = &p.Directives
}
*fileList = append(*fileList, name)
if importMap != nil {
@ -1004,6 +1020,9 @@ Found:
embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos)
}
}
if directives != nil {
*directives = append(*directives, info.directives...)
}
}
for tag := range allTags {
@ -1383,13 +1402,14 @@ var dummyPkg Package
// fileInfo records information learned about a file included in a build.
type fileInfo struct {
name string // full name including dir
header []byte
fset *token.FileSet
parsed *ast.File
parseErr error
imports []fileImport
embeds []fileEmbed
name string // full name including dir
header []byte
fset *token.FileSet
parsed *ast.File
parseErr error
imports []fileImport
embeds []fileEmbed
directives []Directive
}
type fileImport struct {

View File

@ -5,6 +5,7 @@
package build
import (
"fmt"
"internal/testenv"
"io"
"os"
@ -801,3 +802,27 @@ func TestAllTagsNonSourceFile(t *testing.T) {
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"}]`)
}

View File

@ -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",
// we have to look for //go:embed comments
// in the remainder of the file.

3
src/go/build/testdata/directives/a.go vendored Normal file
View File

@ -0,0 +1,3 @@
//go:main1
package p

View File

@ -0,0 +1,3 @@
//go:test1
package p

View File

@ -0,0 +1,5 @@
//go:test2
package p
//go:ignored

View File

@ -0,0 +1,5 @@
//go:xtest1
package p_test
//go:ignored

View File

@ -0,0 +1,4 @@
//go:xtest2
//go:xtest3
package p_test

View File

@ -0,0 +1,4 @@
//go:plant
//axiom:plant
package p