summaryrefslogtreecommitdiff
path: root/src/pkg/go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/go')
-rw-r--r--src/pkg/go/ast/ast.go76
-rw-r--r--src/pkg/go/ast/commentmap.go10
-rw-r--r--src/pkg/go/ast/filter.go2
-rw-r--r--src/pkg/go/ast/import.go98
-rw-r--r--src/pkg/go/ast/walk.go3
-rw-r--r--src/pkg/go/build/build.go285
-rw-r--r--src/pkg/go/build/build_test.go105
-rw-r--r--src/pkg/go/build/deps_test.go57
-rw-r--r--src/pkg/go/build/doc.go1
-rw-r--r--src/pkg/go/build/syslist.go2
-rw-r--r--src/pkg/go/build/syslist_test.go2
-rw-r--r--src/pkg/go/doc/comment.go11
-rw-r--r--src/pkg/go/doc/doc_test.go10
-rw-r--r--src/pkg/go/doc/example.go2
-rw-r--r--src/pkg/go/doc/example_test.go10
-rw-r--r--src/pkg/go/doc/reader.go2
-rw-r--r--src/pkg/go/doc/synopsis.go15
-rw-r--r--src/pkg/go/doc/synopsis_test.go2
-rw-r--r--src/pkg/go/doc/testdata/a.0.golden46
-rw-r--r--src/pkg/go/doc/testdata/a.1.golden46
-rw-r--r--src/pkg/go/doc/testdata/a.2.golden46
-rw-r--r--src/pkg/go/doc/testdata/bugpara.0.golden20
-rw-r--r--src/pkg/go/doc/testdata/bugpara.1.golden20
-rw-r--r--src/pkg/go/doc/testdata/bugpara.2.golden20
-rw-r--r--src/pkg/go/doc/testdata/bugpara.go5
-rw-r--r--src/pkg/go/doc/testdata/template.txt4
-rw-r--r--src/pkg/go/doc/testdata/testing.0.golden4
-rw-r--r--src/pkg/go/doc/testdata/testing.1.golden6
-rw-r--r--src/pkg/go/doc/testdata/testing.2.golden4
-rw-r--r--src/pkg/go/doc/testdata/testing.go2
-rw-r--r--src/pkg/go/format/format_test.go1
-rw-r--r--src/pkg/go/parser/interface.go15
-rw-r--r--src/pkg/go/parser/parser.go50
-rw-r--r--src/pkg/go/parser/parser_test.go18
-rw-r--r--src/pkg/go/parser/short_test.go6
-rw-r--r--src/pkg/go/printer/nodes.go32
-rw-r--r--src/pkg/go/printer/testdata/expressions.golden17
-rw-r--r--src/pkg/go/printer/testdata/expressions.input17
-rw-r--r--src/pkg/go/printer/testdata/expressions.raw17
-rw-r--r--src/pkg/go/token/position.go31
-rw-r--r--src/pkg/go/token/position_test.go8
41 files changed, 807 insertions, 321 deletions
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index bf533d1d2..6e635cd01 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -297,6 +297,8 @@ type (
Lbrack token.Pos // position of "["
Low Expr // begin of slice range; or nil
High Expr // end of slice range; or nil
+ Max Expr // maximum capacity of slice; or nil
+ Slice3 bool // true if 3-index slice (2 colons present)
Rbrack token.Pos // position of "]"
}
@@ -304,8 +306,10 @@ type (
// type assertion.
//
TypeAssertExpr struct {
- X Expr // expression
- Type Expr // asserted type; nil means type switch X.(type)
+ X Expr // expression
+ Lparen token.Pos // position of "("
+ Type Expr // asserted type; nil means type switch X.(type)
+ Rparen token.Pos // position of ")"
}
// A CallExpr node represents an expression followed by an argument list.
@@ -385,8 +389,8 @@ type (
// A FuncType node represents a function type.
FuncType struct {
- Func token.Pos // position of "func" keyword
- Params *FieldList // (incoming) parameters; or nil
+ Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
+ Params *FieldList // (incoming) parameters; non-nil
Results *FieldList // (outgoing) results; or nil
}
@@ -407,7 +411,7 @@ type (
// A ChanType node represents a channel type.
ChanType struct {
Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
- Arrow token.Pos // position of "<-" (noPos if there is no "<-")
+ Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-")
Dir ChanDir // channel direction
Value Expr // value type
}
@@ -438,10 +442,15 @@ func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
func (x *StructType) Pos() token.Pos { return x.Struct }
-func (x *FuncType) Pos() token.Pos { return x.Func }
-func (x *InterfaceType) Pos() token.Pos { return x.Interface }
-func (x *MapType) Pos() token.Pos { return x.Map }
-func (x *ChanType) Pos() token.Pos { return x.Begin }
+func (x *FuncType) Pos() token.Pos {
+ if x.Func.IsValid() || x.Params == nil { // see issue 3870
+ return x.Func
+ }
+ return x.Params.Pos() // interface method declarations have no "func" keyword
+}
+func (x *InterfaceType) Pos() token.Pos { return x.Interface }
+func (x *MapType) Pos() token.Pos { return x.Map }
+func (x *ChanType) Pos() token.Pos { return x.Begin }
func (x *BadExpr) End() token.Pos { return x.To }
func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
@@ -451,26 +460,21 @@ func (x *Ellipsis) End() token.Pos {
}
return x.Ellipsis + 3 // len("...")
}
-func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }
-func (x *FuncLit) End() token.Pos { return x.Body.End() }
-func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
-func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
-func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
-func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
-func (x *TypeAssertExpr) End() token.Pos {
- if x.Type != nil {
- return x.Type.End()
- }
- return x.X.End()
-}
-func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *StarExpr) End() token.Pos { return x.X.End() }
-func (x *UnaryExpr) End() token.Pos { return x.X.End() }
-func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
-func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
-func (x *ArrayType) End() token.Pos { return x.Elt.End() }
-func (x *StructType) End() token.Pos { return x.Fields.End() }
+func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) }
+func (x *FuncLit) End() token.Pos { return x.Body.End() }
+func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
+func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
+func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
+func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
+func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
+func (x *StarExpr) End() token.Pos { return x.X.End() }
+func (x *UnaryExpr) End() token.Pos { return x.X.End() }
+func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
+func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
+func (x *ArrayType) End() token.Pos { return x.Elt.End() }
+func (x *StructType) End() token.Pos { return x.Fields.End() }
func (x *FuncType) End() token.Pos {
if x.Results != nil {
return x.Results.End()
@@ -511,23 +515,21 @@ func (*ChanType) exprNode() {}
// ----------------------------------------------------------------------------
// Convenience functions for Idents
-var noPos token.Pos
-
// NewIdent creates a new Ident without position.
// Useful for ASTs generated by code other than the Go parser.
//
-func NewIdent(name string) *Ident { return &Ident{noPos, name, nil} }
+func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
-// IsExported returns whether name is an exported Go symbol
-// (i.e., whether it begins with an uppercase letter).
+// IsExported reports whether name is an exported Go symbol
+// (that is, whether it begins with an upper-case letter).
//
func IsExported(name string) bool {
ch, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(ch)
}
-// IsExported returns whether id is an exported Go symbol
-// (i.e., whether it begins with an uppercase letter).
+// IsExported reports whether id is an exported Go symbol
+// (that is, whether it begins with an uppercase letter).
//
func (id *Ident) IsExported() bool { return IsExported(id.Name) }
@@ -919,7 +921,7 @@ type (
Doc *CommentGroup // associated documentation; or nil
Recv *FieldList // receiver (methods); or nil (functions)
Name *Ident // function/method name
- Type *FuncType // position of Func keyword, parameters and results
+ Type *FuncType // function signature: parameters, results, and position of "func" keyword
Body *BlockStmt // function body; or nil (forward declaration)
}
)
diff --git a/src/pkg/go/ast/commentmap.go b/src/pkg/go/ast/commentmap.go
index 252d460af..1fb4867dd 100644
--- a/src/pkg/go/ast/commentmap.go
+++ b/src/pkg/go/ast/commentmap.go
@@ -129,11 +129,11 @@ func (s *nodeStack) pop(pos token.Pos) (top Node) {
//
// A comment group g is associated with a node n if:
//
-// - g starts on the same line as n ends
-// - g starts on the line immediately following n, and there is
-// at least one empty line after g and before the next node
-// - g starts before n and is not associated to the node before n
-// via the previous rules
+// - g starts on the same line as n ends
+// - g starts on the line immediately following n, and there is
+// at least one empty line after g and before the next node
+// - g starts before n and is not associated to the node before n
+// via the previous rules
//
// NewCommentMap tries to associate a comment group to the "largest"
// node possible: For instance, if the comment is a line comment
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 71c9ed776..fc3eeb4a1 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -308,7 +308,7 @@ func nameOf(f *FuncDecl) string {
// separator is an empty //-style comment that is interspersed between
// different comment groups when they are concatenated into a single group
//
-var separator = &Comment{noPos, "//"}
+var separator = &Comment{token.NoPos, "//"}
// MergePackageFiles creates a file AST by merging the ASTs of the
// files belonging to a package. The mode flags control merging behavior.
diff --git a/src/pkg/go/ast/import.go b/src/pkg/go/ast/import.go
index a68a4840f..d2770d16c 100644
--- a/src/pkg/go/ast/import.go
+++ b/src/pkg/go/ast/import.go
@@ -11,6 +11,7 @@ import (
)
// SortImports sorts runs of consecutive import lines in import blocks in f.
+// It also removes duplicate imports when it is possible to do so without data loss.
func SortImports(fset *token.FileSet, f *File) {
for _, d := range f.Decls {
d, ok := d.(*GenDecl)
@@ -27,14 +28,25 @@ func SortImports(fset *token.FileSet, f *File) {
// Identify and sort runs of specs on successive lines.
i := 0
+ specs := d.Specs[:0]
for j, s := range d.Specs {
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
// j begins a new run. End this one.
- sortSpecs(fset, f, d.Specs[i:j])
+ specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
i = j
}
}
- sortSpecs(fset, f, d.Specs[i:])
+ specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
+ d.Specs = specs
+
+ // Deduping can leave a blank line before the rparen; clean that up.
+ if len(d.Specs) > 0 {
+ lastSpec := d.Specs[len(d.Specs)-1]
+ lastLine := fset.Position(lastSpec.Pos()).Line
+ if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
+ fset.File(d.Rparen).MergeLine(rParenLine - 1)
+ }
+ }
}
}
@@ -46,22 +58,41 @@ func importPath(s Spec) string {
return ""
}
+func importName(s Spec) string {
+ n := s.(*ImportSpec).Name
+ if n == nil {
+ return ""
+ }
+ return n.Name
+}
+
+func importComment(s Spec) string {
+ c := s.(*ImportSpec).Comment
+ if c == nil {
+ return ""
+ }
+ return c.Text()
+}
+
+// collapse indicates whether prev may be removed, leaving only next.
+func collapse(prev, next Spec) bool {
+ if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
+ return false
+ }
+ return prev.(*ImportSpec).Comment == nil
+}
+
type posSpan struct {
Start token.Pos
End token.Pos
}
-func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
- // Avoid work if already sorted (also catches < 2 entries).
- sorted := true
- for i, s := range specs {
- if i > 0 && importPath(specs[i-1]) > importPath(s) {
- sorted = false
- break
- }
- }
- if sorted {
- return
+func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {
+ // Can't short-circuit here even if specs are already sorted,
+ // since they might yet need deduplication.
+ // A lone import, however, may be safely ignored.
+ if len(specs) <= 1 {
+ return specs
}
// Record positions for specs.
@@ -101,10 +132,26 @@ func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
}
// Sort the import specs by import path.
+ // Remove duplicates, when possible without data loss.
// Reassign the import paths to have the same position sequence.
// Reassign each comment to abut the end of its spec.
// Sort the comments by new position.
- sort.Sort(byImportPath(specs))
+ sort.Sort(byImportSpec(specs))
+
+ // Dedup. Thanks to our sorting, we can just consider
+ // adjacent pairs of imports.
+ deduped := specs[:0]
+ for i, s := range specs {
+ if i == len(specs)-1 || !collapse(s, specs[i+1]) {
+ deduped = append(deduped, s)
+ } else {
+ p := s.Pos()
+ fset.File(p).MergeLine(fset.Position(p).Line)
+ }
+ }
+ specs = deduped
+
+ // Fix up comment positions
for i, s := range specs {
s := s.(*ImportSpec)
if s.Name != nil {
@@ -118,14 +165,29 @@ func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
}
}
}
+
sort.Sort(byCommentPos(comments))
+
+ return specs
}
-type byImportPath []Spec // slice of *ImportSpec
+type byImportSpec []Spec // slice of *ImportSpec
-func (x byImportPath) Len() int { return len(x) }
-func (x byImportPath) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x byImportPath) Less(i, j int) bool { return importPath(x[i]) < importPath(x[j]) }
+func (x byImportSpec) Len() int { return len(x) }
+func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byImportSpec) Less(i, j int) bool {
+ ipath := importPath(x[i])
+ jpath := importPath(x[j])
+ if ipath != jpath {
+ return ipath < jpath
+ }
+ iname := importName(x[i])
+ jname := importName(x[j])
+ if iname != jname {
+ return iname < jname
+ }
+ return importComment(x[i]) < importComment(x[j])
+}
type byCommentPos []*CommentGroup
diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go
index fef2503c3..fedffb3f2 100644
--- a/src/pkg/go/ast/walk.go
+++ b/src/pkg/go/ast/walk.go
@@ -122,6 +122,9 @@ func Walk(v Visitor, node Node) {
if n.High != nil {
Walk(v, n.High)
}
+ if n.Max != nil {
+ Walk(v, n.Max)
+ }
case *TypeAssertExpr:
Walk(v, n.X)
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index cc89afb21..50d2fb4ae 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -258,21 +258,23 @@ func (ctxt *Context) SrcDirs() []string {
var Default Context = defaultContext()
var cgoEnabled = map[string]bool{
- "darwin/386": true,
- "darwin/amd64": true,
- "freebsd/386": true,
- "freebsd/amd64": true,
- "freebsd/arm": true,
- "linux/386": true,
- "linux/amd64": true,
- "linux/arm": true,
- "netbsd/386": true,
- "netbsd/amd64": true,
- "netbsd/arm": true,
- "openbsd/386": true,
- "openbsd/amd64": true,
- "windows/386": true,
- "windows/amd64": true,
+ "darwin/386": true,
+ "darwin/amd64": true,
+ "dragonfly/386": true,
+ "dragonfly/amd64": true,
+ "freebsd/386": true,
+ "freebsd/amd64": true,
+ "freebsd/arm": true,
+ "linux/386": true,
+ "linux/amd64": true,
+ "linux/arm": true,
+ "netbsd/386": true,
+ "netbsd/amd64": true,
+ "netbsd/arm": true,
+ "openbsd/386": true,
+ "openbsd/amd64": true,
+ "windows/386": true,
+ "windows/amd64": true,
}
func defaultContext() Context {
@@ -293,7 +295,7 @@ func defaultContext() Context {
// When we reach Go 1.3 the line will read
// c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3"}
// and so on.
- c.ReleaseTags = []string{"go1.1"}
+ c.ReleaseTags = []string{"go1.1", "go1.2"}
switch os.Getenv("CGO_ENABLED") {
case "1":
@@ -337,32 +339,37 @@ const (
// A Package describes the Go package found in a directory.
type Package struct {
- Dir string // directory containing package sources
- Name string // package name
- Doc string // documentation synopsis
- ImportPath string // import path of package ("" if unknown)
- Root string // root of Go tree where this package lives
- SrcRoot string // package source root directory ("" if unknown)
- PkgRoot string // package install root directory ("" if unknown)
- BinDir string // command install directory ("" if unknown)
- Goroot bool // package found in Go root
- PkgObj string // installed .a file
+ Dir string // directory containing package sources
+ Name string // package name
+ Doc string // documentation synopsis
+ ImportPath string // import path of package ("" if unknown)
+ Root string // root of Go tree where this package lives
+ SrcRoot string // package source root directory ("" if unknown)
+ PkgRoot string // package install root directory ("" if unknown)
+ BinDir string // command install directory ("" if unknown)
+ Goroot bool // package found in Go root
+ PkgObj string // installed .a file
+ AllTags []string // tags that can influence file selection in this directory
+ ConflictDir string // this directory shadows Dir in $GOPATH
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C"
IgnoredGoFiles []string // .go source files ignored for this build
CFiles []string // .c source files
- HFiles []string // .h source files
+ CXXFiles []string // .cc, .cpp and .cxx source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
SFiles []string // .s source files
- SysoFiles []string // .syso system object files to add to archive
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso system object files to add to archive
// Cgo directives
- CgoPkgConfig []string // Cgo pkg-config directives
CgoCFLAGS []string // Cgo CFLAGS directives
+ CgoCPPFLAGS []string // Cgo CPPFLAGS directives
+ CgoCXXFLAGS []string // Cgo CXXFLAGS directives
CgoLDFLAGS []string // Cgo LDFLAGS directives
+ CgoPkgConfig []string // Cgo pkg-config directives
// Dependency information
Imports []string // imports from GoFiles, CgoFiles
@@ -391,13 +398,22 @@ func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
}
// NoGoError is the error used by Import to describe a directory
-// containing no Go source files.
+// containing no buildable Go source files. (It may still contain
+// test files, files hidden by build tags, and so on.)
type NoGoError struct {
Dir string
}
func (e *NoGoError) Error() string {
- return "no Go source files in " + e.Dir
+ return "no buildable Go source files in " + e.Dir
+}
+
+func nameExt(name string) string {
+ i := strings.LastIndex(name, ".")
+ if i < 0 {
+ return ""
+ }
+ return name[i:]
}
// Import returns details about the Go package named by the import path,
@@ -429,7 +445,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
switch ctxt.Compiler {
case "gccgo":
dir, elem := pathpkg.Split(p.ImportPath)
- pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
+ pkga = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + dir + "lib" + elem + ".a"
case "gc":
suffix := ""
if ctxt.InstallSuffix != "" {
@@ -469,11 +485,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
// else first.
if ctxt.GOROOT != "" {
if dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", sub); ctxt.isDir(dir) {
+ p.ConflictDir = dir
goto Found
}
}
for _, earlyRoot := range all[:i] {
if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
+ p.ConflictDir = dir
goto Found
}
}
@@ -575,63 +593,21 @@ Found:
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
xTestImported := make(map[string][]token.Position)
+ allTags := make(map[string]bool)
fset := token.NewFileSet()
for _, d := range dirs {
if d.IsDir() {
continue
}
- name := d.Name()
- if strings.HasPrefix(name, "_") ||
- strings.HasPrefix(name, ".") {
- continue
- }
-
- i := strings.LastIndex(name, ".")
- if i < 0 {
- i = len(name)
- }
- ext := name[i:]
-
- if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
- if ext == ".go" {
- p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
- }
- continue
- }
- switch ext {
- case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
- // tentatively okay - read to make sure
- case ".syso":
- // binary objects to add to package archive
- // Likely of the form foo_windows.syso, but
- // the name was vetted above with goodOSArchFile.
- p.SysoFiles = append(p.SysoFiles, name)
- continue
- default:
- // skip
- continue
- }
+ name := d.Name()
+ ext := nameExt(name)
- filename := ctxt.joinPath(p.Dir, name)
- f, err := ctxt.openFile(filename)
+ match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags)
if err != nil {
return p, err
}
-
- var data []byte
- if strings.HasSuffix(filename, ".go") {
- data, err = readImports(f, false)
- } else {
- data, err = readComments(f)
- }
- f.Close()
- if err != nil {
- return p, fmt.Errorf("read %s: %v", filename, err)
- }
-
- // Look for +build comments to accept or reject the file.
- if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
+ if !match {
if ext == ".go" {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
@@ -643,7 +619,10 @@ Found:
case ".c":
p.CFiles = append(p.CFiles, name)
continue
- case ".h":
+ case ".cc", ".cpp", ".cxx":
+ p.CXXFiles = append(p.CXXFiles, name)
+ continue
+ case ".h", ".hh", ".hpp", ".hxx":
p.HFiles = append(p.HFiles, name)
continue
case ".s":
@@ -658,6 +637,12 @@ Found:
case ".swigcxx":
p.SwigCXXFiles = append(p.SwigCXXFiles, name)
continue
+ case ".syso":
+ // binary objects to add to package archive
+ // Likely of the form foo_windows.syso, but
+ // the name was vetted above with goodOSArchFile.
+ p.SysoFiles = append(p.SysoFiles, name)
+ continue
}
pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
@@ -730,8 +715,11 @@ Found:
}
}
if isCgo {
+ allTags["cgo"] = true
if ctxt.CgoEnabled {
p.CgoFiles = append(p.CgoFiles, name)
+ } else {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
} else if isXTest {
p.XTestGoFiles = append(p.XTestGoFiles, name)
@@ -741,10 +729,15 @@ Found:
p.GoFiles = append(p.GoFiles, name)
}
}
- if p.Name == "" {
+ if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
return p, &NoGoError{p.Dir}
}
+ for tag := range allTags {
+ p.AllTags = append(p.AllTags, tag)
+ }
+ sort.Strings(p.AllTags)
+
p.Imports, p.ImportPos = cleanImports(imported)
p.TestImports, p.TestImportPos = cleanImports(testImported)
p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
@@ -760,6 +753,79 @@ Found:
return p, pkgerr
}
+// MatchFile reports whether the file with the given name in the given directory
+// matches the context and would be included in a Package created by ImportDir
+// of that directory.
+//
+// MatchFile considers the name of the file and may use ctxt.OpenFile to
+// read some or all of the file's content.
+func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
+ match, _, _, err = ctxt.matchFile(dir, name, false, nil)
+ return
+}
+
+// matchFile determines whether the file with the given name in the given directory
+// should be included in the package being constructed.
+// It returns the data read from the file.
+// If returnImports is true and name denotes a Go program, matchFile reads
+// until the end of the imports (and returns that data) even though it only
+// considers text until the first non-comment.
+// If allTags is non-nil, matchFile records any encountered build tag
+// by setting allTags[tag] = true.
+func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool) (match bool, data []byte, filename string, err error) {
+ if strings.HasPrefix(name, "_") ||
+ strings.HasPrefix(name, ".") {
+ return
+ }
+
+ i := strings.LastIndex(name, ".")
+ if i < 0 {
+ i = len(name)
+ }
+ ext := name[i:]
+
+ if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
+ return
+ }
+
+ switch ext {
+ case ".go", ".c", ".cc", ".cxx", ".cpp", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx":
+ // tentatively okay - read to make sure
+ case ".syso":
+ // binary, no reading
+ match = true
+ return
+ default:
+ // skip
+ return
+ }
+
+ filename = ctxt.joinPath(dir, name)
+ f, err := ctxt.openFile(filename)
+ if err != nil {
+ return
+ }
+
+ if strings.HasSuffix(filename, ".go") {
+ data, err = readImports(f, false)
+ } else {
+ data, err = readComments(f)
+ }
+ f.Close()
+ if err != nil {
+ err = fmt.Errorf("read %s: %v", filename, err)
+ return
+ }
+
+ // Look for +build comments to accept or reject the file.
+ if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles {
+ return
+ }
+
+ match = true
+ return
+}
+
func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
all := make([]string, 0, len(m))
for path := range m {
@@ -794,7 +860,7 @@ var slashslash = []byte("//")
//
// marks the file as applicable only on Windows and Linux.
//
-func (ctxt *Context) shouldBuild(content []byte) bool {
+func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) bool {
// Pass 1. Identify leading run of // comments and blank lines,
// which must be followed by a blank line.
end := 0
@@ -819,6 +885,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
// Pass 2. Process each line in the run.
p = content
+ allok := true
for len(p) > 0 {
line := p
if i := bytes.IndexByte(line, '\n'); i >= 0 {
@@ -835,24 +902,24 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
if f[0] == "+build" {
ok := false
for _, tok := range f[1:] {
- if ctxt.match(tok) {
+ if ctxt.match(tok, allTags) {
ok = true
- break
}
}
if !ok {
- return false // this one doesn't match
+ allok = false
}
}
}
}
}
- return true // everything matches
+
+ return allok
}
// saveCgo saves the information from the #cgo lines in the import "C" comment.
-// These lines set CFLAGS and LDFLAGS and pkg-config directives that affect
-// the way cgo's C code is built.
+// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
+// that affect the way cgo's C code is built.
//
// TODO(rsc): This duplicates code in cgo.
// Once the dust settles, remove this code from cgo.
@@ -887,7 +954,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
if len(cond) > 0 {
ok := false
for _, c := range cond {
- if ctxt.match(c) {
+ if ctxt.match(c, nil) {
ok = true
break
}
@@ -902,7 +969,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
}
for _, arg := range args {
- if !safeName(arg) {
+ if !safeCgoName(arg) {
return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
}
}
@@ -910,6 +977,10 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
switch verb {
case "CFLAGS":
di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
+ case "CPPFLAGS":
+ di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
+ case "CXXFLAGS":
+ di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
case "LDFLAGS":
di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
case "pkg-config":
@@ -921,9 +992,12 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return nil
}
-var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:")
+// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
+// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
+// See golang.org/issue/6038.
+var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$")
-func safeName(s string) bool {
+func safeCgoName(s string) bool {
if s == "" {
return false
}
@@ -1008,19 +1082,28 @@ func splitQuoted(s string) (r []string, err error) {
// !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags)
// a comma-separated list of any of these
//
-func (ctxt *Context) match(name string) bool {
+func (ctxt *Context) match(name string, allTags map[string]bool) bool {
if name == "" {
+ if allTags != nil {
+ allTags[name] = true
+ }
return false
}
if i := strings.Index(name, ","); i >= 0 {
// comma-separated list
- return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
+ ok1 := ctxt.match(name[:i], allTags)
+ ok2 := ctxt.match(name[i+1:], allTags)
+ return ok1 && ok2
}
if strings.HasPrefix(name, "!!") { // bad syntax, reject always
return false
}
if strings.HasPrefix(name, "!") { // negation
- return len(name) > 1 && !ctxt.match(name[1:])
+ return len(name) > 1 && !ctxt.match(name[1:], allTags)
+ }
+
+ if allTags != nil {
+ allTags[name] = true
}
// Tags must be letters, digits, underscores or dots.
@@ -1065,7 +1148,7 @@ func (ctxt *Context) match(name string) bool {
// name_$(GOARCH)_test.*
// name_$(GOOS)_$(GOARCH)_test.*
//
-func (ctxt *Context) goodOSArchFile(name string) bool {
+func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
if dot := strings.Index(name, "."); dot != -1 {
name = name[:dot]
}
@@ -1075,12 +1158,22 @@ func (ctxt *Context) goodOSArchFile(name string) bool {
}
n := len(l)
if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-2]] = true
+ allTags[l[n-1]] = true
+ }
return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
}
if n >= 1 && knownOS[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-1]] = true
+ }
return l[n-1] == ctxt.GOOS
}
if n >= 1 && knownArch[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-1]] = true
+ }
return l[n-1] == ctxt.GOARCH
}
return true
diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go
index d8cf98840..fca8d4bdb 100644
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -5,38 +5,49 @@
package build
import (
+ "io"
"os"
"path/filepath"
+ "reflect"
"runtime"
+ "strings"
"testing"
)
func TestMatch(t *testing.T) {
ctxt := Default
what := "default"
- match := func(tag string) {
- if !ctxt.match(tag) {
+ match := func(tag string, want map[string]bool) {
+ m := make(map[string]bool)
+ if !ctxt.match(tag, m) {
t.Errorf("%s context should match %s, does not", what, tag)
}
+ if !reflect.DeepEqual(m, want) {
+ t.Errorf("%s tags = %v, want %v", tag, m, want)
+ }
}
- nomatch := func(tag string) {
- if ctxt.match(tag) {
+ nomatch := func(tag string, want map[string]bool) {
+ m := make(map[string]bool)
+ if ctxt.match(tag, m) {
t.Errorf("%s context should NOT match %s, does", what, tag)
}
+ if !reflect.DeepEqual(m, want) {
+ t.Errorf("%s tags = %v, want %v", tag, m, want)
+ }
}
- match(runtime.GOOS + "," + runtime.GOARCH)
- match(runtime.GOOS + "," + runtime.GOARCH + ",!foo")
- nomatch(runtime.GOOS + "," + runtime.GOARCH + ",foo")
+ match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
+ match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
+ nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
what = "modified"
ctxt.BuildTags = []string{"foo"}
- match(runtime.GOOS + "," + runtime.GOARCH)
- match(runtime.GOOS + "," + runtime.GOARCH + ",foo")
- nomatch(runtime.GOOS + "," + runtime.GOARCH + ",!foo")
- match(runtime.GOOS + "," + runtime.GOARCH + ",!bar")
- nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar")
- nomatch("!")
+ match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true})
+ match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
+ nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true})
+ match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
+ nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true})
+ nomatch("!", map[string]bool{})
}
func TestDotSlashImport(t *testing.T) {
@@ -92,28 +103,84 @@ func TestLocalDirectory(t *testing.T) {
func TestShouldBuild(t *testing.T) {
const file1 = "// +build tag1\n\n" +
"package main\n"
+ want1 := map[string]bool{"tag1": true}
const file2 = "// +build cgo\n\n" +
"// This package implements parsing of tags like\n" +
"// +build tag1\n" +
"package build"
+ want2 := map[string]bool{"cgo": true}
const file3 = "// Copyright The Go Authors.\n\n" +
"package build\n\n" +
"// shouldBuild checks tags given by lines of the form\n" +
"// +build tag\n" +
"func shouldBuild(content []byte)\n"
+ want3 := map[string]bool{}
ctx := &Context{BuildTags: []string{"tag1"}}
- if !ctx.shouldBuild([]byte(file1)) {
- t.Errorf("should not build file1, expected the contrary")
+ m := map[string]bool{}
+ if !ctx.shouldBuild([]byte(file1), m) {
+ t.Errorf("shouldBuild(file1) = false, want true")
+ }
+ if !reflect.DeepEqual(m, want1) {
+ t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1)
+ }
+
+ m = map[string]bool{}
+ if ctx.shouldBuild([]byte(file2), m) {
+ t.Errorf("shouldBuild(file2) = true, want fakse")
}
- if ctx.shouldBuild([]byte(file2)) {
- t.Errorf("should build file2, expected the contrary")
+ if !reflect.DeepEqual(m, want2) {
+ t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2)
}
+ m = map[string]bool{}
ctx = &Context{BuildTags: nil}
- if !ctx.shouldBuild([]byte(file3)) {
- t.Errorf("should not build file3, expected the contrary")
+ if !ctx.shouldBuild([]byte(file3), m) {
+ t.Errorf("shouldBuild(file3) = false, want true")
+ }
+ if !reflect.DeepEqual(m, want3) {
+ t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3)
+ }
+}
+
+type readNopCloser struct {
+ io.Reader
+}
+
+func (r readNopCloser) Close() error {
+ return nil
+}
+
+var matchFileTests = []struct {
+ name string
+ data string
+ match bool
+}{
+ {"foo_arm.go", "", true},
+ {"foo1_arm.go", "// +build linux\n\npackage main\n", false},
+ {"foo_darwin.go", "", false},
+ {"foo.go", "", true},
+ {"foo1.go", "// +build linux\n\npackage main\n", false},
+ {"foo.badsuffix", "", false},
+}
+
+func TestMatchFile(t *testing.T) {
+ for _, tt := range matchFileTests {
+ ctxt := Context{GOARCH: "arm", GOOS: "plan9"}
+ ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) {
+ if path != "x+"+tt.name {
+ t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name)
+ }
+ return &readNopCloser{strings.NewReader(tt.data)}, nil
+ }
+ ctxt.JoinPath = func(elem ...string) string {
+ return strings.Join(elem, "+")
+ }
+ match, err := ctxt.MatchFile("x", tt.name)
+ if match != tt.match || err != nil {
+ t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match)
+ }
}
}
diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go
index 71b1bcf06..dd162c7db 100644
--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -82,24 +82,27 @@ var pkgDeps = map[string][]string{
// L3 adds reflection and some basic utility packages
// and interface definitions, but nothing that makes
// system calls.
- "crypto": {"L2", "hash"}, // interfaces
- "crypto/cipher": {"L2"}, // interfaces
- "encoding/base32": {"L2"},
- "encoding/base64": {"L2"},
- "encoding/binary": {"L2", "reflect"},
- "hash": {"L2"}, // interfaces
- "hash/adler32": {"L2", "hash"},
- "hash/crc32": {"L2", "hash"},
- "hash/crc64": {"L2", "hash"},
- "hash/fnv": {"L2", "hash"},
- "image": {"L2", "image/color"}, // interfaces
- "image/color": {"L2"}, // interfaces
- "reflect": {"L2"},
+ "crypto": {"L2", "hash"}, // interfaces
+ "crypto/cipher": {"L2", "crypto/subtle"}, // interfaces
+ "crypto/subtle": {},
+ "encoding/base32": {"L2"},
+ "encoding/base64": {"L2"},
+ "encoding/binary": {"L2", "reflect"},
+ "hash": {"L2"}, // interfaces
+ "hash/adler32": {"L2", "hash"},
+ "hash/crc32": {"L2", "hash"},
+ "hash/crc64": {"L2", "hash"},
+ "hash/fnv": {"L2", "hash"},
+ "image": {"L2", "image/color"}, // interfaces
+ "image/color": {"L2"}, // interfaces
+ "image/color/palette": {"L2", "image/color"},
+ "reflect": {"L2"},
"L3": {
"L2",
"crypto",
"crypto/cipher",
+ "crypto/subtle",
"encoding/base32",
"encoding/base64",
"encoding/binary",
@@ -110,6 +113,7 @@ var pkgDeps = map[string][]string{
"hash/fnv",
"image",
"image/color",
+ "image/color/palette",
"reflect",
},
@@ -183,26 +187,27 @@ var pkgDeps = map[string][]string{
"compress/gzip": {"L4", "compress/flate"},
"compress/lzw": {"L4"},
"compress/zlib": {"L4", "compress/flate"},
- "database/sql": {"L4", "database/sql/driver"},
+ "database/sql": {"L4", "container/list", "database/sql/driver"},
"database/sql/driver": {"L4", "time"},
"debug/dwarf": {"L4"},
"debug/elf": {"L4", "OS", "debug/dwarf"},
"debug/gosym": {"L4"},
"debug/macho": {"L4", "OS", "debug/dwarf"},
"debug/pe": {"L4", "OS", "debug/dwarf"},
+ "encoding": {"L4"},
"encoding/ascii85": {"L4"},
"encoding/asn1": {"L4", "math/big"},
"encoding/csv": {"L4"},
- "encoding/gob": {"L4", "OS"},
+ "encoding/gob": {"L4", "OS", "encoding"},
"encoding/hex": {"L4"},
- "encoding/json": {"L4"},
+ "encoding/json": {"L4", "encoding"},
"encoding/pem": {"L4"},
- "encoding/xml": {"L4"},
+ "encoding/xml": {"L4", "encoding"},
"flag": {"L4", "OS"},
"go/build": {"L4", "OS", "GOPARSER"},
"html": {"L4"},
"image/draw": {"L4"},
- "image/gif": {"L4", "compress/lzw"},
+ "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
"image/jpeg": {"L4"},
"image/png": {"L4", "compress/zlib"},
"index/suffixarray": {"L4", "regexp"},
@@ -228,7 +233,8 @@ var pkgDeps = map[string][]string{
// that shows up in programs that use cgo.
"C": {},
- "os/user": {"L4", "CGO", "syscall"},
+ // Plan 9 alone needs io/ioutil and os.
+ "os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"},
// Basic networking.
// Because net must be used by any package that wants to
@@ -248,15 +254,10 @@ var pkgDeps = map[string][]string{
"net/mail": {"L4", "NET", "OS"},
"net/textproto": {"L4", "OS", "net"},
- // Support libraries for crypto that aren't L2.
- "CRYPTO-SUPPORT": {
- "crypto/subtle",
- },
-
// Core crypto.
"crypto/aes": {"L3"},
"crypto/des": {"L3"},
- "crypto/hmac": {"L3", "CRYPTO-SUPPORT"},
+ "crypto/hmac": {"L3"},
"crypto/md5": {"L3"},
"crypto/rc4": {"L3"},
"crypto/sha1": {"L3"},
@@ -264,7 +265,6 @@ var pkgDeps = map[string][]string{
"crypto/sha512": {"L3"},
"CRYPTO": {
- "CRYPTO-SUPPORT",
"crypto/aes",
"crypto/des",
"crypto/hmac",
@@ -359,7 +359,7 @@ func allowed(pkg string) map[string]bool {
}
var bools = []bool{false, true}
-var geese = []string{"darwin", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"}
+var geese = []string{"darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"}
var goarches = []string{"386", "amd64", "arm"}
type osPkg struct {
@@ -392,6 +392,9 @@ func TestDependencies(t *testing.T) {
if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
continue
}
+ if !ctxt.CgoEnabled && pkg == "runtime/cgo" {
+ continue
+ }
// Some of the combinations we try might not
// be reasonable (like arm,plan9,cgo), so ignore
// errors for the auto-generated combinations.
diff --git a/src/pkg/go/build/doc.go b/src/pkg/go/build/doc.go
index b5fc071d6..b2f04ea45 100644
--- a/src/pkg/go/build/doc.go
+++ b/src/pkg/go/build/doc.go
@@ -94,6 +94,7 @@
// - the compiler being used, either "gc" or "gccgo"
// - "cgo", if ctxt.CgoEnabled is true
// - "go1.1", from Go version 1.1 onward
+// - "go1.2", from Go version 1.2 onward
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
diff --git a/src/pkg/go/build/syslist.go b/src/pkg/go/build/syslist.go
index ea21f3c74..e1fbf6330 100644
--- a/src/pkg/go/build/syslist.go
+++ b/src/pkg/go/build/syslist.go
@@ -4,5 +4,5 @@
package build
-const goosList = "darwin freebsd linux netbsd openbsd plan9 windows "
+const goosList = "darwin dragonfly freebsd linux netbsd openbsd plan9 windows "
const goarchList = "386 amd64 arm "
diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go
index 9157faf8c..3be2928f5 100644
--- a/src/pkg/go/build/syslist_test.go
+++ b/src/pkg/go/build/syslist_test.go
@@ -55,7 +55,7 @@ var tests = []GoodFileTest{
func TestGoodOSArch(t *testing.T) {
for _, test := range tests {
- if Default.goodOSArchFile(test.name) != test.result {
+ if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result {
t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
}
}
diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go
index c4b7e6ae6..5c8c43e0c 100644
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -239,9 +239,14 @@ func anchorID(line string) string {
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
-// Turn each run of multiple \n into </p><p>.
-// Turn each run of indented lines into a <pre> block without indent.
-// Enclose headings with header tags.
+// Each span of unindented non-blank lines is converted into
+// a single paragraph. There is one exception to the rule: a span that
+// consists of a single line, is followed by another paragraph span,
+// begins with a capital letter, and contains no punctuation
+// is formatted as a heading.
+//
+// A span of indented lines is converted into a <pre> block,
+// with the common indent prefix removed.
//
// URLs in the comment text are converted into links; if the URL also appears
// in the words map, the link is taken from the map (if the corresponding map
diff --git a/src/pkg/go/doc/doc_test.go b/src/pkg/go/doc/doc_test.go
index 8043038b4..ad8ba5378 100644
--- a/src/pkg/go/doc/doc_test.go
+++ b/src/pkg/go/doc/doc_test.go
@@ -32,6 +32,7 @@ func readTemplate(filename string) *template.Template {
t.Funcs(template.FuncMap{
"node": nodeFmt,
"synopsis": synopsisFmt,
+ "indent": indentFmt,
})
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
}
@@ -55,6 +56,15 @@ func synopsisFmt(s string) string {
return "// " + strings.Replace(s, "\n", " ", -1)
}
+func indentFmt(indent, s string) string {
+ end := ""
+ if strings.HasSuffix(s, "\n") {
+ end = "\n"
+ s = s[:len(s)-1]
+ }
+ return indent + strings.Replace(s, "\n", "\n"+indent, -1) + end
+}
+
func isGoFile(fi os.FileInfo) bool {
name := fi.Name()
return !fi.IsDir() &&
diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go
index 2761083c7..2358ed389 100644
--- a/src/pkg/go/doc/example.go
+++ b/src/pkg/go/doc/example.go
@@ -265,7 +265,7 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
// Synthesize main function.
funcDecl := &ast.FuncDecl{
Name: ast.NewIdent("main"),
- Type: &ast.FuncType{},
+ Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil
Body: body,
}
diff --git a/src/pkg/go/doc/example_test.go b/src/pkg/go/doc/example_test.go
index e0477e3f6..e154ea8bf 100644
--- a/src/pkg/go/doc/example_test.go
+++ b/src/pkg/go/doc/example_test.go
@@ -159,8 +159,8 @@ func main() {
`
func TestExamples(t *testing.T) {
- fs := token.NewFileSet()
- file, err := parser.ParseFile(fs, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
if err != nil {
t.Fatal(err)
}
@@ -174,11 +174,11 @@ func TestExamples(t *testing.T) {
if e.Play == nil {
g = "<nil>"
} else {
- b := new(bytes.Buffer)
- if err := format.Node(b, fs, e.Play); err != nil {
+ var buf bytes.Buffer
+ if err := format.Node(&buf, fset, e.Play); err != nil {
t.Fatal(err)
}
- g = b.String()
+ g = buf.String()
}
if g != w {
t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go
index 4fa6fd9d5..ed82c47cd 100644
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -414,7 +414,7 @@ func (r *reader) readNote(list []*ast.Comment) {
// We remove any formatting so that we don't
// get spurious line breaks/indentation when
// showing the TODO body.
- body := clean(text[m[1]:])
+ body := clean(text[m[1]:], keepNL)
if body != "" {
marker := text[m[2]:m[3]]
r.notes[marker] = append(r.notes[marker], &Note{
diff --git a/src/pkg/go/doc/synopsis.go b/src/pkg/go/doc/synopsis.go
index 2d1817439..c90080b7c 100644
--- a/src/pkg/go/doc/synopsis.go
+++ b/src/pkg/go/doc/synopsis.go
@@ -22,19 +22,28 @@ func firstSentenceLen(s string) int {
if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
return i
}
+ if p == '。' || p == '.' {
+ return i
+ }
ppp, pp, p = pp, p, q
}
return len(s)
}
+const (
+ keepNL = 1 << iota
+)
+
// clean replaces each sequence of space, \n, \r, or \t characters
// with a single space and removes any trailing and leading spaces.
-func clean(s string) string {
+// If the keepNL flag is set, newline characters are passed through
+// instead of being change to spaces.
+func clean(s string, flags int) string {
var b []byte
p := byte(' ')
for i := 0; i < len(s); i++ {
q := s[i]
- if q == '\n' || q == '\r' || q == '\t' {
+ if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' {
q = ' '
}
if q != ' ' || p != ' ' {
@@ -57,7 +66,7 @@ func clean(s string) string {
// is the empty string.
//
func Synopsis(s string) string {
- s = clean(s[0:firstSentenceLen(s)])
+ s = clean(s[0:firstSentenceLen(s)], 0)
for _, prefix := range IllegalPrefixes {
if strings.HasPrefix(strings.ToLower(s), prefix) {
return ""
diff --git a/src/pkg/go/doc/synopsis_test.go b/src/pkg/go/doc/synopsis_test.go
index fd7081a07..59b253cb8 100644
--- a/src/pkg/go/doc/synopsis_test.go
+++ b/src/pkg/go/doc/synopsis_test.go
@@ -28,6 +28,8 @@ var tests = []struct {
{"P. Q. ", 8, "P. Q."},
{"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
{"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
+ {"Package こんにちは。世界", 26, "Package こんにちは。"},
+ {"Package 안녕.世界", 17, "Package 안녕."},
{"Package foo does bar.", 21, "Package foo does bar."},
{"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""},
{"All Rights reserved. Package foo does bar.", 20, ""},
diff --git a/src/pkg/go/doc/testdata/a.0.golden b/src/pkg/go/doc/testdata/a.0.golden
index cd98f4e0e..7e680b80b 100644
--- a/src/pkg/go/doc/testdata/a.0.golden
+++ b/src/pkg/go/doc/testdata/a.0.golden
@@ -9,24 +9,44 @@ FILENAMES
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
- // bug0
- // bug1
+ bug0
+
+ bug1
+
BUGS
- // bug0 (uid: uid)
- // bug1 (uid: uid)
+BUG(uid) bug0
+
+BUG(uid) bug1
+
NOTES
- // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo)
- // 2 of 4 (uid: foo)
- // 3 of 4 (uid: bar)
- // 4 of 4 - this is the last line of note 4 (uid: bar)
- // This note which contains a (parenthesized) subphrase must ... (uid: bam)
- // The ':' after the marker and uid is optional. (uid: xxx)
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
SECBUGS
- // sec hole 0 need to fix asap (uid: uid)
+SECBUG(uid) sec hole 0
+ need to fix asap
+
TODOS
- // todo0 (uid: uid)
- // todo1 (uid: uid)
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/pkg/go/doc/testdata/a.1.golden b/src/pkg/go/doc/testdata/a.1.golden
index cd98f4e0e..7e680b80b 100644
--- a/src/pkg/go/doc/testdata/a.1.golden
+++ b/src/pkg/go/doc/testdata/a.1.golden
@@ -9,24 +9,44 @@ FILENAMES
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
- // bug0
- // bug1
+ bug0
+
+ bug1
+
BUGS
- // bug0 (uid: uid)
- // bug1 (uid: uid)
+BUG(uid) bug0
+
+BUG(uid) bug1
+
NOTES
- // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo)
- // 2 of 4 (uid: foo)
- // 3 of 4 (uid: bar)
- // 4 of 4 - this is the last line of note 4 (uid: bar)
- // This note which contains a (parenthesized) subphrase must ... (uid: bam)
- // The ':' after the marker and uid is optional. (uid: xxx)
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
SECBUGS
- // sec hole 0 need to fix asap (uid: uid)
+SECBUG(uid) sec hole 0
+ need to fix asap
+
TODOS
- // todo0 (uid: uid)
- // todo1 (uid: uid)
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/pkg/go/doc/testdata/a.2.golden b/src/pkg/go/doc/testdata/a.2.golden
index cd98f4e0e..7e680b80b 100644
--- a/src/pkg/go/doc/testdata/a.2.golden
+++ b/src/pkg/go/doc/testdata/a.2.golden
@@ -9,24 +9,44 @@ FILENAMES
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
- // bug0
- // bug1
+ bug0
+
+ bug1
+
BUGS
- // bug0 (uid: uid)
- // bug1 (uid: uid)
+BUG(uid) bug0
+
+BUG(uid) bug1
+
NOTES
- // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo)
- // 2 of 4 (uid: foo)
- // 3 of 4 (uid: bar)
- // 4 of 4 - this is the last line of note 4 (uid: bar)
- // This note which contains a (parenthesized) subphrase must ... (uid: bam)
- // The ':' after the marker and uid is optional. (uid: xxx)
+NOTE(uid)
+
+NOTE(foo) 1 of 4 - this is the first line of note 1
+ - note 1 continues on this 2nd line
+ - note 1 continues on this 3rd line
+
+NOTE(foo) 2 of 4
+
+NOTE(bar) 3 of 4
+
+NOTE(bar) 4 of 4
+ - this is the last line of note 4
+
+NOTE(bam) This note which contains a (parenthesized) subphrase
+ must appear in its entirety.
+
+NOTE(xxx) The ':' after the marker and uid is optional.
+
SECBUGS
- // sec hole 0 need to fix asap (uid: uid)
+SECBUG(uid) sec hole 0
+ need to fix asap
+
TODOS
- // todo0 (uid: uid)
- // todo1 (uid: uid)
+TODO(uid) todo0
+
+TODO(uid) todo1
+
diff --git a/src/pkg/go/doc/testdata/bugpara.0.golden b/src/pkg/go/doc/testdata/bugpara.0.golden
new file mode 100644
index 000000000..580485950
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.0.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/pkg/go/doc/testdata/bugpara.1.golden b/src/pkg/go/doc/testdata/bugpara.1.golden
new file mode 100644
index 000000000..580485950
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.1.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/pkg/go/doc/testdata/bugpara.2.golden b/src/pkg/go/doc/testdata/bugpara.2.golden
new file mode 100644
index 000000000..580485950
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.2.golden
@@ -0,0 +1,20 @@
+//
+PACKAGE bugpara
+
+IMPORTPATH
+ testdata/bugpara
+
+FILENAMES
+ testdata/bugpara.go
+
+BUGS .Bugs is now deprecated, please use .Notes instead
+ Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
+
+BUGS
+BUG(rsc) Sometimes bugs have multiple paragraphs.
+
+ Like this one.
+
diff --git a/src/pkg/go/doc/testdata/bugpara.go b/src/pkg/go/doc/testdata/bugpara.go
new file mode 100644
index 000000000..f5345a797
--- /dev/null
+++ b/src/pkg/go/doc/testdata/bugpara.go
@@ -0,0 +1,5 @@
+package bugpara
+
+// BUG(rsc): Sometimes bugs have multiple paragraphs.
+//
+// Like this one.
diff --git a/src/pkg/go/doc/testdata/template.txt b/src/pkg/go/doc/testdata/template.txt
index 26482f7c2..1b0738261 100644
--- a/src/pkg/go/doc/testdata/template.txt
+++ b/src/pkg/go/doc/testdata/template.txt
@@ -61,8 +61,8 @@ TYPES
*/}}{{with .Bugs}}
BUGS .Bugs is now deprecated, please use .Notes instead
-{{range .}} {{synopsis .}}
+{{range .}}{{indent "\t" .}}
{{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}}
{{$marker}}S
-{{range $content}} {{synopsis .Body}} (uid: {{.UID}})
+{{range $content}}{{$marker}}({{.UID}}){{indent "\t" .Body}}
{{end}}{{end}}{{end}} \ No newline at end of file
diff --git a/src/pkg/go/doc/testdata/testing.0.golden b/src/pkg/go/doc/testdata/testing.0.golden
index 15a903986..f8348f1ac 100644
--- a/src/pkg/go/doc/testdata/testing.0.golden
+++ b/src/pkg/go/doc/testdata/testing.0.golden
@@ -57,7 +57,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *B) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -136,7 +136,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *T) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
diff --git a/src/pkg/go/doc/testdata/testing.1.golden b/src/pkg/go/doc/testdata/testing.1.golden
index ffdb5c3b5..282bb1015 100644
--- a/src/pkg/go/doc/testdata/testing.1.golden
+++ b/src/pkg/go/doc/testdata/testing.1.golden
@@ -130,7 +130,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *B) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -232,7 +232,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *T) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -278,7 +278,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *common) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *common) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
diff --git a/src/pkg/go/doc/testdata/testing.2.golden b/src/pkg/go/doc/testdata/testing.2.golden
index 15a903986..f8348f1ac 100644
--- a/src/pkg/go/doc/testdata/testing.2.golden
+++ b/src/pkg/go/doc/testdata/testing.2.golden
@@ -57,7 +57,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *B) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *B) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
@@ -136,7 +136,7 @@ TYPES
// FailNow marks the function as having failed and stops its ...
func (c *T) FailNow()
- // Failed returns whether the function has failed.
+ // Failed reports whether the function has failed.
func (c *T) Failed() bool
// Fatal is equivalent to Log() followed by FailNow().
diff --git a/src/pkg/go/doc/testdata/testing.go b/src/pkg/go/doc/testdata/testing.go
index c2499ad77..93ed494c3 100644
--- a/src/pkg/go/doc/testdata/testing.go
+++ b/src/pkg/go/doc/testdata/testing.go
@@ -130,7 +130,7 @@ type T struct {
// Fail marks the function as having failed but continues execution.
func (c *common) Fail() { c.failed = true }
-// Failed returns whether the function has failed.
+// Failed reports whether the function has failed.
func (c *common) Failed() bool { return c.failed }
// FailNow marks the function as having failed and stops its execution.
diff --git a/src/pkg/go/format/format_test.go b/src/pkg/go/format/format_test.go
index 7d7940bb5..93f099247 100644
--- a/src/pkg/go/format/format_test.go
+++ b/src/pkg/go/format/format_test.go
@@ -90,7 +90,6 @@ var tests = []string{
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings
// erroneous programs
- "ERRORvar x",
"ERROR1 + 2 +",
"ERRORx := 0",
}
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 149257ca6..0f83ca931 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -15,6 +15,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
)
// If src != nil, readSource converts src to a []byte if possible;
@@ -115,11 +116,13 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
return
}
-// ParseDir calls ParseFile for the files in the directory specified by path and
-// returns a map of package name -> package AST with all the packages found. If
-// filter != nil, only the files with os.FileInfo entries passing through the filter
-// are considered. The mode bits are passed to ParseFile unchanged. Position
-// information is recorded in the file set fset.
+// ParseDir calls ParseFile for all files with names ending in ".go" in the
+// directory specified by path and returns a map of package name -> package
+// AST with all the packages found.
+//
+// If filter != nil, only the files with os.FileInfo entries passing through
+// the filter (and ending in ".go") are considered. The mode bits are passed
+// to ParseFile unchanged. Position information is recorded in fset.
//
// If the directory couldn't be read, a nil map and the respective error are
// returned. If a parse error occurred, a non-nil but incomplete map and the
@@ -139,7 +142,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
pkgs = make(map[string]*ast.Package)
for _, d := range list {
- if filter == nil || filter(d) {
+ if strings.HasSuffix(d.Name(), ".go") && (filter == nil || filter(d)) {
filename := filepath.Join(path, d.Name())
if src, err := ParseFile(fset, filename, nil, mode); err == nil {
name := src.Name.Name
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index f4a690a6f..c4523318f 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -64,7 +64,7 @@ type parser struct {
}
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
- p.file = fset.AddFile(filename, fset.Base(), len(src))
+ p.file = fset.AddFile(filename, -1, len(src))
var m scanner.Mode
if mode&ParseComments != 0 {
m = scanner.ScanComments
@@ -1150,7 +1150,7 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
defer un(trace(p, "TypeAssertion"))
}
- p.expect(token.LPAREN)
+ lparen := p.expect(token.LPAREN)
var typ ast.Expr
if p.tok == token.TYPE {
// type switch: typ == nil
@@ -1158,9 +1158,9 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
} else {
typ = p.parseType()
}
- p.expect(token.RPAREN)
+ rparen := p.expect(token.RPAREN)
- return &ast.TypeAssertExpr{X: x, Type: typ}
+ return &ast.TypeAssertExpr{X: x, Type: typ, Lparen: lparen, Rparen: rparen}
}
func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
@@ -1170,25 +1170,27 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
lbrack := p.expect(token.LBRACK)
p.exprLev++
- var low, high ast.Expr
- isSlice := false
+ var index [3]ast.Expr // change the 3 to 2 to disable slice expressions w/ cap
if p.tok != token.COLON {
- low = p.parseRhs()
+ index[0] = p.parseRhs()
}
- if p.tok == token.COLON {
- isSlice = true
+ ncolons := 0
+ for p.tok == token.COLON && ncolons < len(index)-1 {
p.next()
- if p.tok != token.RBRACK {
- high = p.parseRhs()
+ ncolons++
+ if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
+ index[ncolons] = p.parseRhs()
}
}
p.exprLev--
rbrack := p.expect(token.RBRACK)
- if isSlice {
- return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: low, High: high, Rbrack: rbrack}
+ if ncolons > 0 {
+ // slice expression
+ return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: ncolons == 2, Rbrack: rbrack}
}
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: low, Rbrack: rbrack}
+
+ return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
@@ -1406,7 +1408,7 @@ L:
}
switch p.tok {
case token.IDENT:
- x = p.parseSelector(p.checkExpr(x))
+ x = p.parseSelector(p.checkExprOrType(x))
case token.LPAREN:
x = p.parseTypeAssertion(p.checkExpr(x))
default:
@@ -2145,12 +2147,13 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as
ident = p.parseIdent()
}
- var path *ast.BasicLit
+ pos := p.pos
+ var path string
if p.tok == token.STRING {
- if !isValidImport(p.lit) {
- p.error(p.pos, "invalid import path: "+p.lit)
+ path = p.lit
+ if !isValidImport(path) {
+ p.error(pos, "invalid import path: "+path)
}
- path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
} else {
p.expect(token.STRING) // use expect() error handling
@@ -2161,7 +2164,7 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as
spec := &ast.ImportSpec{
Doc: doc,
Name: ident,
- Path: path,
+ Path: &ast.BasicLit{ValuePos: pos, Kind: token.STRING, Value: path},
Comment: p.lineComment,
}
p.imports = append(p.imports, spec)
@@ -2177,8 +2180,9 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
idents := p.parseIdentList()
typ := p.tryType()
var values []ast.Expr
- if p.tok == token.ASSIGN || keyword == token.CONST && (typ != nil || iota == 0) || keyword == token.VAR && typ == nil {
- p.expect(token.ASSIGN)
+ // always permit optional initialization for more tolerant parsing
+ if p.tok == token.ASSIGN {
+ p.next()
values = p.parseRhsList()
}
p.expectSemi() // call before accessing p.linecomment
@@ -2381,7 +2385,7 @@ func (p *parser) parseFile() *ast.File {
// Go spec: The package clause is not a declaration;
// the package name does not appear in any scope.
ident := p.parseIdent()
- if ident.Name == "_" {
+ if ident.Name == "_" && p.mode&DeclarationErrors != 0 {
p.error(p.pos, "invalid package name _")
}
p.expectSemi()
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 48813d106..0a34b7e50 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -34,13 +34,12 @@ func TestParse(t *testing.T) {
func nameFilter(filename string) bool {
switch filename {
- case "parser.go":
- case "interface.go":
- case "parser_test.go":
- default:
- return false
+ case "parser.go", "interface.go", "parser_test.go":
+ return true
+ case "parser.go.orig":
+ return true // permit but should be ignored by ParseDir
}
- return true
+ return false
}
func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
@@ -51,14 +50,17 @@ func TestParseDir(t *testing.T) {
if err != nil {
t.Fatalf("ParseDir(%s): %v", path, err)
}
- if len(pkgs) != 1 {
- t.Errorf("incorrect number of packages: %d", len(pkgs))
+ if n := len(pkgs); n != 1 {
+ t.Errorf("got %d packages; want 1", n)
}
pkg := pkgs["parser"]
if pkg == nil {
t.Errorf(`package "parser" not found`)
return
}
+ if n := len(pkg.Files); n != 3 {
+ t.Errorf("got %d package files; want 3", n)
+ }
for filename := range pkg.Files {
if !nameFilter(filename) {
t.Errorf("unexpected package file: %s", filename)
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index 62277c0d2..0ef0c560c 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -33,6 +33,8 @@ var valids = []string{
`package p; func f() { if ; true {} };`,
`package p; func f() { switch ; {} };`,
`package p; func f() { for _ = range "foo" + "bar" {} };`,
+ `package p; func f() { var s []int; g(s[:], s[i:], s[:j], s[i:j], s[i:j:k], s[:j:k]) };`,
+ `package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`,
}
func TestValid(t *testing.T) {
@@ -46,7 +48,6 @@ var invalids = []string{
`package p; func f() { if { /* ERROR "expected operand" */ } };`,
`package p; func f() { if ; { /* ERROR "expected operand" */ } };`,
`package p; func f() { if f(); { /* ERROR "expected operand" */ } };`,
- `package p; const c; /* ERROR "expected '='" */`,
`package p; func f() { if _ /* ERROR "expected condition" */ = range x; true {} };`,
`package p; func f() { switch _ /* ERROR "expected condition" */ = range x; true {} };`,
`package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`,
@@ -74,6 +75,9 @@ var invalids = []string{
`package p; func f() { if x := g(); x = /* ERROR "expected '=='" */ 0 {}};`,
`package p; func f() { _ = x = /* ERROR "expected '=='" */ 0 {}};`,
`package p; func f() { _ = 1 == func()int { var x bool; x = x = /* ERROR "expected '=='" */ true; return x }() };`,
+ `package p; func f() { var s []int; _ = s[] /* ERROR "expected operand" */ };`,
+ `package p; func f() { var s []int; _ = s[::: /* ERROR "expected ']'" */ ] };`,
+ `package p; func f() { var s []int; _ = s[i:j:k: /* ERROR "expected ']'" */ l] };`,
}
func TestInvalid(t *testing.T) {
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 7cd068e22..583c6c370 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -754,13 +754,13 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.TypeAssertExpr:
p.expr1(x.X, token.HighestPrec, depth)
- p.print(token.PERIOD, token.LPAREN)
+ p.print(token.PERIOD, x.Lparen, token.LPAREN)
if x.Type != nil {
p.expr(x.Type)
} else {
p.print(token.TYPE)
}
- p.print(token.RPAREN)
+ p.print(x.Rparen, token.RPAREN)
case *ast.IndexExpr:
// TODO(gri): should treat[] like parentheses and undo one level of depth
@@ -773,17 +773,25 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
// TODO(gri): should treat[] like parentheses and undo one level of depth
p.expr1(x.X, token.HighestPrec, 1)
p.print(x.Lbrack, token.LBRACK)
- if x.Low != nil {
- p.expr0(x.Low, depth+1)
- }
- // blanks around ":" if both sides exist and either side is a binary expression
- if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) {
- p.print(blank, token.COLON, blank)
- } else {
- p.print(token.COLON)
+ indices := []ast.Expr{x.Low, x.High}
+ if x.Max != nil {
+ indices = append(indices, x.Max)
}
- if x.High != nil {
- p.expr0(x.High, depth+1)
+ for i, y := range indices {
+ if i > 0 {
+ // blanks around ":" if both sides exist and either side is a binary expression
+ // TODO(gri) once we have committed a variant of a[i:j:k] we may want to fine-
+ // tune the formatting here
+ x := indices[i-1]
+ if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) {
+ p.print(blank, token.COLON, blank)
+ } else {
+ p.print(token.COLON)
+ }
+ }
+ if y != nil {
+ p.expr0(y, depth+1)
+ }
}
p.print(x.Rbrack, token.RBRACK)
diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden
index 4291c557c..fbe8275b3 100644
--- a/src/pkg/go/printer/testdata/expressions.golden
+++ b/src/pkg/go/printer/testdata/expressions.golden
@@ -114,6 +114,23 @@ func _() {
x < y || z > 42
}
+// slice expressions with cap
+func _() {
+ _ = x[a:b:c]
+ _ = x[a:b : c+d]
+ _ = x[a : b+d : c]
+ _ = x[a : b+d : c+d]
+ _ = x[a+d : b:c]
+ _ = x[a+d : b : c+d]
+ _ = x[a+d : b+d : c]
+ _ = x[a+d : b+d : c+d]
+
+ _ = x[:b:c]
+ _ = x[:b : c+d]
+ _ = x[:b+d : c]
+ _ = x[:b+d : c+d]
+}
+
func _() {
_ = a + b
_ = a + b + c
diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input
index 1ec12a050..f4d20fa0f 100644
--- a/src/pkg/go/printer/testdata/expressions.input
+++ b/src/pkg/go/printer/testdata/expressions.input
@@ -116,6 +116,23 @@ func _() {
}
+// slice expressions with cap
+func _() {
+ _ = x[a:b:c]
+ _ = x[a:b:c+d]
+ _ = x[a:b+d:c]
+ _ = x[a:b+d:c+d]
+ _ = x[a+d:b:c]
+ _ = x[a+d:b:c+d]
+ _ = x[a+d:b+d:c]
+ _ = x[a+d:b+d:c+d]
+
+ _ = x[:b:c]
+ _ = x[:b:c+d]
+ _ = x[:b+d:c]
+ _ = x[:b+d:c+d]
+}
+
func _() {
_ = a+b
_ = a+b+c
diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw
index 062900e07..97bc81dad 100644
--- a/src/pkg/go/printer/testdata/expressions.raw
+++ b/src/pkg/go/printer/testdata/expressions.raw
@@ -114,6 +114,23 @@ func _() {
x < y || z > 42
}
+// slice expressions with cap
+func _() {
+ _ = x[a:b:c]
+ _ = x[a:b : c+d]
+ _ = x[a : b+d : c]
+ _ = x[a : b+d : c+d]
+ _ = x[a+d : b:c]
+ _ = x[a+d : b : c+d]
+ _ = x[a+d : b+d : c]
+ _ = x[a+d : b+d : c+d]
+
+ _ = x[:b:c]
+ _ = x[:b : c+d]
+ _ = x[:b+d : c]
+ _ = x[:b+d : c+d]
+}
+
func _() {
_ = a + b
_ = a + b + c
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go
index f5d999561..e6f0ae6a6 100644
--- a/src/pkg/go/token/position.go
+++ b/src/pkg/go/token/position.go
@@ -97,7 +97,7 @@ type File struct {
size int // file size as provided to AddFile
// lines and infos are protected by set.mutex
- lines []int
+ lines []int // lines contains the offset of the first character for each line (the first entry is always 0)
infos []lineInfo
}
@@ -136,6 +136,29 @@ func (f *File) AddLine(offset int) {
f.set.mutex.Unlock()
}
+// MergeLine merges a line with the following line. It is akin to replacing
+// the newline character at the end of the line with a space (to not change the
+// remaining offsets). To obtain the line number, consult e.g. Position.Line.
+// MergeLine will panic if given an invalid line number.
+//
+func (f *File) MergeLine(line int) {
+ if line <= 0 {
+ panic("illegal line number (line numbering starts at 1)")
+ }
+ f.set.mutex.Lock()
+ defer f.set.mutex.Unlock()
+ if line >= len(f.lines) {
+ panic("illegal line number")
+ }
+ // To merge the line numbered <line> with the line numbered <line+1>,
+ // we need to remove the entry in lines corresponding to the line
+ // numbered <line+1>. The entry in lines corresponding to the line
+ // numbered <line+1> is located at index <line>, since indices in lines
+ // are 0-based and line numbers are 1-based.
+ copy(f.lines[line:], f.lines[line+1:])
+ f.lines = f.lines[:len(f.lines)-1]
+}
+
// SetLines sets the line offsets for a file and returns true if successful.
// The line offsets are the offsets of the first character of each line;
// for instance for the content "ab\nc\n" the line offsets are {0, 3}.
@@ -314,7 +337,8 @@ func (s *FileSet) Base() int {
// AddFile adds a new file with a given filename, base offset, and file size
// to the file set s and returns the file. Multiple files may have the same
// name. The base offset must not be smaller than the FileSet's Base(), and
-// size must not be negative.
+// size must not be negative. As a special case, if a negative base is provided,
+// the current value of the FileSet's Base() is used instead.
//
// Adding the file will set the file set's Base() value to base + size + 1
// as the minimum base value for the next file. The following relationship
@@ -329,6 +353,9 @@ func (s *FileSet) Base() int {
func (s *FileSet) AddFile(filename string, base, size int) *File {
s.mutex.Lock()
defer s.mutex.Unlock()
+ if base < 0 {
+ base = s.base
+ }
if base < s.base || size < 0 {
panic("illegal base or size")
}
diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go
index 1d36c2226..ef6cfd93c 100644
--- a/src/pkg/go/token/position_test.go
+++ b/src/pkg/go/token/position_test.go
@@ -167,7 +167,13 @@ func TestLineInfo(t *testing.T) {
func TestFiles(t *testing.T) {
fset := NewFileSet()
for i, test := range tests {
- fset.AddFile(test.filename, fset.Base(), test.size)
+ base := fset.Base()
+ if i%2 == 1 {
+ // Setting a negative base is equivalent to
+ // fset.Base(), so test some of each.
+ base = -1
+ }
+ fset.AddFile(test.filename, base, test.size)
j := 0
fset.Iterate(func(f *File) bool {
if f.Name() != tests[j].filename {