summaryrefslogtreecommitdiff
path: root/libgo/go/go
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-02 16:38:43 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-02 16:38:43 +0000
commit2d2d80b8bd963f59534897b3d51ef8bd546cb4bc (patch)
treeefa0c55763b34cbc633bc494c2743d1b5d9aaff3 /libgo/go/go
parent2ad2700dbf70b2e49575f3f2307839a45cf2f71c (diff)
downloadgcc-2d2d80b8bd963f59534897b3d51ef8bd546cb4bc.tar.gz
libgo: Update to weekly.2012-02-14 release.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@184798 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/go')
-rw-r--r--libgo/go/go/ast/resolve.go9
-rw-r--r--libgo/go/go/build/dir.go28
-rw-r--r--libgo/go/go/build/path.go3
-rw-r--r--libgo/go/go/doc/doc_test.go20
-rw-r--r--libgo/go/go/doc/example.go32
-rw-r--r--libgo/go/go/doc/exports.go47
-rw-r--r--libgo/go/go/doc/headscan.go2
-rw-r--r--libgo/go/go/doc/reader.go42
-rw-r--r--libgo/go/go/doc/testdata/b.0.golden28
-rw-r--r--libgo/go/go/doc/testdata/b.1.golden36
-rw-r--r--libgo/go/go/doc/testdata/b.2.golden28
-rw-r--r--libgo/go/go/doc/testdata/b.go28
-rw-r--r--libgo/go/go/doc/testdata/e.0.golden67
-rw-r--r--libgo/go/go/doc/testdata/e.1.golden72
-rw-r--r--libgo/go/go/doc/testdata/e.2.golden85
-rw-r--r--libgo/go/go/doc/testdata/e.go70
-rw-r--r--libgo/go/go/doc/testdata/error1.0.golden30
-rw-r--r--libgo/go/go/doc/testdata/error1.1.golden32
-rw-r--r--libgo/go/go/doc/testdata/error1.2.golden30
-rw-r--r--libgo/go/go/doc/testdata/error1.go24
-rw-r--r--libgo/go/go/doc/testdata/error2.0.golden27
-rw-r--r--libgo/go/go/doc/testdata/error2.1.golden37
-rw-r--r--libgo/go/go/doc/testdata/error2.2.golden27
-rw-r--r--libgo/go/go/doc/testdata/error2.go29
-rw-r--r--libgo/go/go/doc/testdata/f.go2
-rw-r--r--libgo/go/go/parser/interface.go14
-rw-r--r--libgo/go/go/parser/parser.go36
-rw-r--r--libgo/go/go/printer/performance_test.go2
-rw-r--r--libgo/go/go/printer/printer.go282
-rw-r--r--libgo/go/go/printer/printer_test.go137
-rw-r--r--libgo/go/go/printer/testdata/comments.golden41
-rw-r--r--libgo/go/go/printer/testdata/comments.input41
-rw-r--r--libgo/go/go/scanner/errors.go135
-rw-r--r--libgo/go/go/scanner/scanner.go11
-rw-r--r--libgo/go/go/scanner/scanner_test.go47
35 files changed, 1266 insertions, 315 deletions
diff --git a/libgo/go/go/ast/resolve.go b/libgo/go/go/ast/resolve.go
index c7c8e7c101e..908e61c5da0 100644
--- a/libgo/go/go/ast/resolve.go
+++ b/libgo/go/go/ast/resolve.go
@@ -14,12 +14,12 @@ import (
)
type pkgBuilder struct {
- scanner.ErrorVector
- fset *token.FileSet
+ fset *token.FileSet
+ errors scanner.ErrorList
}
func (p *pkgBuilder) error(pos token.Pos, msg string) {
- p.Error(p.fset.Position(pos), msg)
+ p.errors.Add(p.fset.Position(pos), msg)
}
func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
@@ -169,5 +169,6 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer,
pkgScope.Outer = universe // reset universe scope
}
- return &Package{pkgName, pkgScope, imports, files}, p.GetError(scanner.Sorted)
+ p.errors.Sort()
+ return &Package{pkgName, pkgScope, imports, files}, p.errors.Err()
}
diff --git a/libgo/go/go/build/dir.go b/libgo/go/go/build/dir.go
index 0917e736aa4..6b30f76265b 100644
--- a/libgo/go/go/build/dir.go
+++ b/libgo/go/go/build/dir.go
@@ -25,10 +25,11 @@ import (
// A Context specifies the supporting context for a build.
type Context struct {
- GOARCH string // target architecture
- GOOS string // target operating system
- CgoEnabled bool // whether cgo can be used
- BuildTags []string // additional tags to recognize in +build lines
+ GOARCH string // target architecture
+ GOOS string // target operating system
+ CgoEnabled bool // whether cgo can be used
+ BuildTags []string // additional tags to recognize in +build lines
+ UseAllFiles bool // use files regardless of +build lines, file names
// By default, ScanDir uses the operating system's
// file system calls to read directories and files.
@@ -225,6 +226,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
var Sfiles []string // files with ".S" (capital S)
var di DirInfo
+ var firstFile string
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
fset := token.NewFileSet()
@@ -237,7 +239,7 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
strings.HasPrefix(name, ".") {
continue
}
- if !ctxt.goodOSArchFile(name) {
+ if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
continue
}
@@ -250,12 +252,13 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
continue
}
- // Look for +build comments to accept or reject the file.
filename, data, err := ctxt.readFile(dir, name)
if err != nil {
return nil, err
}
- if !ctxt.shouldBuild(data) {
+
+ // Look for +build comments to accept or reject the file.
+ if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
continue
}
@@ -281,9 +284,6 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
}
pkg := string(pf.Name.Name)
- if pkg == "main" && di.Package != "" && di.Package != "main" {
- continue
- }
if pkg == "documentation" {
continue
}
@@ -293,15 +293,11 @@ func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
pkg = pkg[:len(pkg)-len("_test")]
}
- if pkg != di.Package && di.Package == "main" {
- // Found non-main package but was recording
- // information about package main. Reset.
- di = DirInfo{}
- }
if di.Package == "" {
di.Package = pkg
+ firstFile = name
} else if pkg != di.Package {
- return nil, fmt.Errorf("%s: found packages %s and %s", dir, pkg, di.Package)
+ return nil, fmt.Errorf("%s: found packages %s (%s) and %s (%s)", dir, di.Package, firstFile, pkg, name)
}
if pf.Doc != nil {
if di.PackageComment != nil {
diff --git a/libgo/go/go/build/path.go b/libgo/go/go/build/path.go
index 7e931faff19..e160ac3b280 100644
--- a/libgo/go/go/build/path.go
+++ b/libgo/go/go/build/path.go
@@ -12,6 +12,9 @@ import (
"runtime"
)
+// ToolDir is the directory containing build tools.
+var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
+
// Path is a validated list of Trees derived from $GOROOT and $GOPATH at init.
var Path []*Tree
diff --git a/libgo/go/go/doc/doc_test.go b/libgo/go/go/doc/doc_test.go
index 9ffe72032c2..f957ede4abf 100644
--- a/libgo/go/go/doc/doc_test.go
+++ b/libgo/go/go/doc/doc_test.go
@@ -14,12 +14,14 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "regexp"
"strings"
"testing"
"text/template"
)
var update = flag.Bool("update", false, "update golden (.out) files")
+var files = flag.String("files", "", "consider only Go test files matching this regular expression")
const dataDir = "testdata"
@@ -66,14 +68,26 @@ type bundle struct {
}
func test(t *testing.T, mode Mode) {
- // get all packages
+ // determine file filter
+ filter := isGoFile
+ if *files != "" {
+ rx, err := regexp.Compile(*files)
+ if err != nil {
+ t.Fatal(err)
+ }
+ filter = func(fi os.FileInfo) bool {
+ return isGoFile(fi) && rx.MatchString(fi.Name())
+ }
+ }
+
+ // get packages
fset := token.NewFileSet()
- pkgs, err := parser.ParseDir(fset, dataDir, isGoFile, parser.ParseComments)
+ pkgs, err := parser.ParseDir(fset, dataDir, filter, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
- // test all packages
+ // test packages
for _, pkg := range pkgs {
importpath := dataDir + "/" + pkg.Name
doc := New(pkg, importpath, mode)
diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go
index d5b58d26643..1c23b0d95c3 100644
--- a/libgo/go/go/doc/example.go
+++ b/libgo/go/go/doc/example.go
@@ -9,6 +9,7 @@ package doc
import (
"go/ast"
"go/printer"
+ "go/token"
"strings"
"unicode"
"unicode/utf8"
@@ -21,28 +22,47 @@ type Example struct {
}
func Examples(pkg *ast.Package) []*Example {
- var examples []*Example
- for _, src := range pkg.Files {
- for _, decl := range src.Decls {
+ var list []*Example
+ for _, file := range pkg.Files {
+ hasTests := false // file contains tests or benchmarks
+ numDecl := 0 // number of non-import declarations in the file
+ var flist []*Example
+ for _, decl := range file.Decls {
+ if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT {
+ numDecl++
+ continue
+ }
f, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
+ numDecl++
name := f.Name.Name
+ if isTest(name, "Test") || isTest(name, "Benchmark") {
+ hasTests = true
+ continue
+ }
if !isTest(name, "Example") {
continue
}
- examples = append(examples, &Example{
+ flist = append(flist, &Example{
Name: name[len("Example"):],
Body: &printer.CommentedNode{
Node: f.Body,
- Comments: src.Comments,
+ Comments: file.Comments,
},
Output: f.Doc.Text(),
})
}
+ if !hasTests && numDecl > 1 && len(flist) == 1 {
+ // If this file only has one example function, some
+ // other top-level declarations, and no tests or
+ // benchmarks, use the whole file as the example.
+ flist[0].Body.Node = file
+ }
+ list = append(list, flist...)
}
- return examples
+ return list
}
// isTest tells whether name looks like a test, example, or benchmark.
diff --git a/libgo/go/go/doc/exports.go b/libgo/go/go/doc/exports.go
index 68dd3841bed..146be5d8707 100644
--- a/libgo/go/go/doc/exports.go
+++ b/libgo/go/go/doc/exports.go
@@ -22,12 +22,38 @@ func filterIdentList(list []*ast.Ident) []*ast.Ident {
return list[0:j]
}
+// removeErrorField removes anonymous fields named "error" from an interface.
+// This is called when "error" has been determined to be a local name,
+// not the predeclared type.
+//
+func removeErrorField(ityp *ast.InterfaceType) {
+ list := ityp.Methods.List // we know that ityp.Methods != nil
+ j := 0
+ for _, field := range list {
+ keepField := true
+ if n := len(field.Names); n == 0 {
+ // anonymous field
+ if fname, _ := baseTypeName(field.Type); fname == "error" {
+ keepField = false
+ }
+ }
+ if keepField {
+ list[j] = field
+ j++
+ }
+ }
+ if j < len(list) {
+ ityp.Incomplete = true
+ }
+ ityp.Methods.List = list[0:j]
+}
+
// filterFieldList removes unexported fields (field names) from the field list
// in place and returns true if fields were removed. Anonymous fields are
// recorded with the parent type. filterType is called with the types of
// all remaining fields.
//
-func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList) (removedFields bool) {
+func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
if fields == nil {
return
}
@@ -37,9 +63,15 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList) (remo
keepField := false
if n := len(field.Names); n == 0 {
// anonymous field
- name := r.recordAnonymousField(parent, field.Type)
- if ast.IsExported(name) {
+ fname := r.recordAnonymousField(parent, field.Type)
+ if ast.IsExported(fname) {
+ keepField = true
+ } else if ityp != nil && fname == "error" {
+ // possibly the predeclared error interface; keep
+ // it for now but remember this interface so that
+ // it can be fixed if error is also defined locally
keepField = true
+ r.remember(ityp)
}
} else {
field.Names = filterIdentList(field.Names)
@@ -86,14 +118,14 @@ func (r *reader) filterType(parent *namedType, typ ast.Expr) {
case *ast.ArrayType:
r.filterType(nil, t.Elt)
case *ast.StructType:
- if r.filterFieldList(parent, t.Fields) {
+ if r.filterFieldList(parent, t.Fields, nil) {
t.Incomplete = true
}
case *ast.FuncType:
r.filterParamList(t.Params)
r.filterParamList(t.Results)
case *ast.InterfaceType:
- if r.filterFieldList(parent, t.Methods) {
+ if r.filterFieldList(parent, t.Methods, t) {
t.Incomplete = true
}
case *ast.MapType:
@@ -116,9 +148,12 @@ func (r *reader) filterSpec(spec ast.Spec) bool {
return true
}
case *ast.TypeSpec:
- if ast.IsExported(s.Name.Name) {
+ if name := s.Name.Name; ast.IsExported(name) {
r.filterType(r.lookupType(s.Name.Name), s.Type)
return true
+ } else if name == "error" {
+ // special case: remember that error is declared locally
+ r.errorDecl = true
}
}
return false
diff --git a/libgo/go/go/doc/headscan.go b/libgo/go/go/doc/headscan.go
index 37486b126fd..f5593476382 100644
--- a/libgo/go/go/doc/headscan.go
+++ b/libgo/go/go/doc/headscan.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build ignore
+
/*
The headscan command extracts comment headings from package files;
it is used to detect false positives which may require an adjustment
diff --git a/libgo/go/go/doc/reader.go b/libgo/go/go/doc/reader.go
index dcf49f68fd3..bdfb294adb0 100644
--- a/libgo/go/go/doc/reader.go
+++ b/libgo/go/go/doc/reader.go
@@ -17,7 +17,7 @@ import (
//
// Internally, we treat functions like methods and collect them in method sets.
-// methodSet describes a set of methods. Entries where Decl == nil are conflict
+// A methodSet describes a set of methods. Entries where Decl == nil are conflict
// entries (more then one method with the same name at the same embedding level).
//
type methodSet map[string]*Func
@@ -110,6 +110,9 @@ func baseTypeName(x ast.Expr) (name string, imported bool) {
return
}
+// An embeddedSet describes a set of embedded types.
+type embeddedSet map[*namedType]bool
+
// A namedType represents a named unqualified (package local, or possibly
// predeclared) type. The namedType for a type name is always found via
// reader.lookupType.
@@ -119,9 +122,9 @@ type namedType struct {
name string // type name
decl *ast.GenDecl // nil if declaration hasn't been seen yet
- isEmbedded bool // true if this type is embedded
- isStruct bool // true if this type is a struct
- embedded map[*namedType]bool // true if the embedded type is a pointer
+ isEmbedded bool // true if this type is embedded
+ isStruct bool // true if this type is a struct
+ embedded embeddedSet // true if the embedded type is a pointer
// associated declarations
values []*Value // consts and vars
@@ -152,6 +155,10 @@ type reader struct {
values []*Value // consts and vars
types map[string]*namedType
funcs methodSet
+
+ // support for package-local error type declarations
+ errorDecl bool // if set, type "error" was declared locally
+ fixlist []*ast.InterfaceType // list of interfaces containing anonymous field "error"
}
func (r *reader) isVisible(name string) bool {
@@ -173,7 +180,7 @@ func (r *reader) lookupType(name string) *namedType {
// type not found - add one without declaration
typ := &namedType{
name: name,
- embedded: make(map[*namedType]bool),
+ embedded: make(embeddedSet),
funcs: make(methodSet),
methods: make(methodSet),
}
@@ -210,6 +217,10 @@ func (r *reader) readDoc(comment *ast.CommentGroup) {
r.doc += "\n" + text
}
+func (r *reader) remember(typ *ast.InterfaceType) {
+ r.fixlist = append(r.fixlist, typ)
+}
+
func specNames(specs []ast.Spec) []string {
names := make([]string, 0, len(specs)) // reasonable estimate
for _, s := range specs {
@@ -274,7 +285,7 @@ func (r *reader) readValue(decl *ast.GenDecl) {
// determine values list with which to associate the Value for this decl
values := &r.values
const threshold = 0.75
- if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
+ if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {
// typed entries are sufficiently frequent
if typ := r.lookupType(domName); typ != nil {
values = &typ.values // associate with that type
@@ -315,7 +326,7 @@ func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
return // no name or blank name - ignore the type
}
- // A type should be added at most once, so info.decl
+ // A type should be added at most once, so typ.decl
// should be nil - if it is not, simply overwrite it.
typ.decl = decl
@@ -543,7 +554,8 @@ func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int)
// collectEmbeddedMethods collects the embedded methods of typ in mset.
//
-func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int) {
+func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {
+ visited[typ] = true
for embedded, isPtr := range typ.embedded {
// Once an embedded type is embedded as a pointer type
// all embedded types in those types are treated like
@@ -557,8 +569,11 @@ func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvType
mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
}
}
- r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1)
+ if !visited[embedded] {
+ r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)
+ }
}
+ delete(visited, typ)
}
// computeMethodSets determines the actual method sets for each type encountered.
@@ -568,12 +583,19 @@ func (r *reader) computeMethodSets() {
// collect embedded methods for t
if t.isStruct {
// struct
- r.collectEmbeddedMethods(t.methods, t, t.name, false, 1)
+ r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))
} else {
// interface
// TODO(gri) fix this
}
}
+
+ // if error was declared locally, don't treat it as exported field anymore
+ if r.errorDecl {
+ for _, ityp := range r.fixlist {
+ removeErrorField(ityp)
+ }
+ }
}
// cleanupTypes removes the association of functions and methods with
diff --git a/libgo/go/go/doc/testdata/b.0.golden b/libgo/go/go/doc/testdata/b.0.golden
index 7c33300616d..9d93392eaa5 100644
--- a/libgo/go/go/doc/testdata/b.0.golden
+++ b/libgo/go/go/doc/testdata/b.0.golden
@@ -12,18 +12,46 @@ FILENAMES
CONSTANTS
//
+ const (
+ C1 notExported = iota
+ C2
+
+ C4
+ C5
+ )
+
+ //
+ const C notExported = 0
+
+ //
const Pi = 3.14 // Pi
VARIABLES
//
+ var (
+ U1, U2, U4, U5 notExported
+
+ U7 notExported = 7
+ )
+
+ //
var MaxInt int // MaxInt
+ //
+ var V notExported
+
+ //
+ var V1, V2, V4, V5 notExported
+
FUNCTIONS
//
func F(x int) int
+ //
+ func F1() notExported
+
// Always under the package functions list.
func NotAFactory() int
diff --git a/libgo/go/go/doc/testdata/b.1.golden b/libgo/go/go/doc/testdata/b.1.golden
index f30380516bd..66c47b5c2a7 100644
--- a/libgo/go/go/doc/testdata/b.1.golden
+++ b/libgo/go/go/doc/testdata/b.1.golden
@@ -38,8 +38,42 @@ TYPES
//
func (x *T) M()
+ //
+ type notExported int
+
+ //
+ const (
+ C1 notExported = iota
+ C2
+ c3
+ C4
+ C5
+ )
+
+ //
+ const C notExported = 0
+
+ //
+ var (
+ U1, U2, u3, U4, U5 notExported
+ u6 notExported
+ U7 notExported = 7
+ )
+
+ //
+ var V notExported
+
+ //
+ var V1, V2, v3, V4, V5 notExported
+
+ //
+ func F1() notExported
+
+ //
+ func f2() notExported
+
// Should only appear if AllDecls is set.
- type uint struct{}
+ type uint struct{} // overrides a predeclared type uint
// Associated with uint type if AllDecls is set.
func UintFactory() uint
diff --git a/libgo/go/go/doc/testdata/b.2.golden b/libgo/go/go/doc/testdata/b.2.golden
index 7c33300616d..9d93392eaa5 100644
--- a/libgo/go/go/doc/testdata/b.2.golden
+++ b/libgo/go/go/doc/testdata/b.2.golden
@@ -12,18 +12,46 @@ FILENAMES
CONSTANTS
//
+ const (
+ C1 notExported = iota
+ C2
+
+ C4
+ C5
+ )
+
+ //
+ const C notExported = 0
+
+ //
const Pi = 3.14 // Pi
VARIABLES
//
+ var (
+ U1, U2, U4, U5 notExported
+
+ U7 notExported = 7
+ )
+
+ //
var MaxInt int // MaxInt
+ //
+ var V notExported
+
+ //
+ var V1, V2, V4, V5 notExported
+
FUNCTIONS
//
func F(x int) int
+ //
+ func F1() notExported
+
// Always under the package functions list.
func NotAFactory() int
diff --git a/libgo/go/go/doc/testdata/b.go b/libgo/go/go/doc/testdata/b.go
index 28660f9be7c..e50663b3dfa 100644
--- a/libgo/go/go/doc/testdata/b.go
+++ b/libgo/go/go/doc/testdata/b.go
@@ -6,6 +6,7 @@ package b
import "a"
+// ----------------------------------------------------------------------------
// Basic declarations
const Pi = 3.14 // Pi
@@ -28,3 +29,30 @@ func uintFactory() uint {}
// Should only appear if AllDecls is set.
type uint struct{} // overrides a predeclared type uint
+
+// ----------------------------------------------------------------------------
+// Exported declarations associated with non-exported types must always be shown.
+
+type notExported int
+
+const C notExported = 0
+
+const (
+ C1 notExported = iota
+ C2
+ c3
+ C4
+ C5
+)
+
+var V notExported
+var V1, V2, v3, V4, V5 notExported
+
+var (
+ U1, U2, u3, U4, U5 notExported
+ u6 notExported
+ U7 notExported = 7
+)
+
+func F1() notExported {}
+func f2() notExported {}
diff --git a/libgo/go/go/doc/testdata/e.0.golden b/libgo/go/go/doc/testdata/e.0.golden
index 096a50ff41f..6987e5867cf 100644
--- a/libgo/go/go/doc/testdata/e.0.golden
+++ b/libgo/go/go/doc/testdata/e.0.golden
@@ -40,3 +40,70 @@ TYPES
T4
}
+ //
+ type U1 struct {
+ *U1
+ }
+
+ // U1.M should appear as method of U1.
+ func (*U1) M()
+
+ //
+ type U2 struct {
+ *U3
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (*U2) M()
+
+ //
+ type U3 struct {
+ *U2
+ }
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (*U3) N()
+
+ //
+ type U4 struct {
+ // contains filtered or unexported fields
+ }
+
+ // U4.M should appear as method of U4.
+ func (*U4) M()
+
+ //
+ type V1 struct {
+ *V2
+ *V5
+ }
+
+ //
+ type V2 struct {
+ *V3
+ }
+
+ //
+ type V3 struct {
+ *V4
+ }
+
+ //
+ type V4 struct {
+ *V5
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (*V4) M()
+
+ //
+ type V5 struct {
+ *V6
+ }
+
+ //
+ type V6 struct{}
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (*V6) M()
+
diff --git a/libgo/go/go/doc/testdata/e.1.golden b/libgo/go/go/doc/testdata/e.1.golden
index 28be74a1fd6..cbe22e0bf63 100644
--- a/libgo/go/go/doc/testdata/e.1.golden
+++ b/libgo/go/go/doc/testdata/e.1.golden
@@ -43,6 +43,73 @@ TYPES
}
//
+ type U1 struct {
+ *U1
+ }
+
+ // U1.M should appear as method of U1.
+ func (*U1) M()
+
+ //
+ type U2 struct {
+ *U3
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (*U2) M()
+
+ //
+ type U3 struct {
+ *U2
+ }
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (*U3) N()
+
+ //
+ type U4 struct {
+ *u5
+ }
+
+ // U4.M should appear as method of U4.
+ func (*U4) M()
+
+ //
+ type V1 struct {
+ *V2
+ *V5
+ }
+
+ //
+ type V2 struct {
+ *V3
+ }
+
+ //
+ type V3 struct {
+ *V4
+ }
+
+ //
+ type V4 struct {
+ *V5
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (*V4) M()
+
+ //
+ type V5 struct {
+ *V6
+ }
+
+ //
+ type V6 struct{}
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (*V6) M()
+
+ //
type t1 struct{}
// t1.M should not appear as method in a Tx type.
@@ -70,3 +137,8 @@ TYPES
// t2.M should not appear as method in a Tx type.
func (t2e) M()
+ //
+ type u5 struct {
+ *U4
+ }
+
diff --git a/libgo/go/go/doc/testdata/e.2.golden b/libgo/go/go/doc/testdata/e.2.golden
index f9a2b816774..e7b05e80faf 100644
--- a/libgo/go/go/doc/testdata/e.2.golden
+++ b/libgo/go/go/doc/testdata/e.2.golden
@@ -43,3 +43,88 @@ TYPES
// T4.M should appear as method of T5 only if AllMethods is set.
func (*T5) M()
+ //
+ type U1 struct {
+ *U1
+ }
+
+ // U1.M should appear as method of U1.
+ func (*U1) M()
+
+ //
+ type U2 struct {
+ *U3
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (*U2) M()
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (U2) N()
+
+ //
+ type U3 struct {
+ *U2
+ }
+
+ // U2.M should appear as method of U2 and as method of U3 only if ...
+ func (U3) M()
+
+ // U3.N should appear as method of U3 and as method of U2 only if ...
+ func (*U3) N()
+
+ //
+ type U4 struct {
+ // contains filtered or unexported fields
+ }
+
+ // U4.M should appear as method of U4.
+ func (*U4) M()
+
+ //
+ type V1 struct {
+ *V2
+ *V5
+ }
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (V1) M()
+
+ //
+ type V2 struct {
+ *V3
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (V2) M()
+
+ //
+ type V3 struct {
+ *V4
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (V3) M()
+
+ //
+ type V4 struct {
+ *V5
+ }
+
+ // V4.M should appear as method of V2 and V3 if AllMethods is set.
+ func (*V4) M()
+
+ //
+ type V5 struct {
+ *V6
+ }
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (V5) M()
+
+ //
+ type V6 struct{}
+
+ // V6.M should appear as method of V1 and V5 if AllMethods is set.
+ func (*V6) M()
+
diff --git a/libgo/go/go/doc/testdata/e.go b/libgo/go/go/doc/testdata/e.go
index 526a91f4f00..19dd138cf40 100644
--- a/libgo/go/go/doc/testdata/e.go
+++ b/libgo/go/go/doc/testdata/e.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -77,3 +77,71 @@ func (*T4) M() {}
type T5 struct {
T4
}
+
+// ----------------------------------------------------------------------------
+// Recursive type declarations must not lead to endless recursion.
+
+type U1 struct {
+ *U1
+}
+
+// U1.M should appear as method of U1.
+func (*U1) M() {}
+
+type U2 struct {
+ *U3
+}
+
+// U2.M should appear as method of U2 and as method of U3 only if AllMethods is set.
+func (*U2) M() {}
+
+type U3 struct {
+ *U2
+}
+
+// U3.N should appear as method of U3 and as method of U2 only if AllMethods is set.
+func (*U3) N() {}
+
+type U4 struct {
+ *u5
+}
+
+// U4.M should appear as method of U4.
+func (*U4) M() {}
+
+type u5 struct {
+ *U4
+}
+
+// ----------------------------------------------------------------------------
+// A higher-level embedded type (and its methods) wins over the same type (and
+// its methods) embedded at a lower level.
+
+type V1 struct {
+ *V2
+ *V5
+}
+
+type V2 struct {
+ *V3
+}
+
+type V3 struct {
+ *V4
+}
+
+type V4 struct {
+ *V5
+}
+
+type V5 struct {
+ *V6
+}
+
+type V6 struct{}
+
+// V4.M should appear as method of V2 and V3 if AllMethods is set.
+func (*V4) M() {}
+
+// V6.M should appear as method of V1 and V5 if AllMethods is set.
+func (*V6) M() {}
diff --git a/libgo/go/go/doc/testdata/error1.0.golden b/libgo/go/go/doc/testdata/error1.0.golden
new file mode 100644
index 00000000000..6c6fe5d49bd
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error1.0.golden
@@ -0,0 +1,30 @@
+//
+PACKAGE error1
+
+IMPORTPATH
+ testdata/error1
+
+FILENAMES
+ testdata/error1.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+ }
+
diff --git a/libgo/go/go/doc/testdata/error1.1.golden b/libgo/go/go/doc/testdata/error1.1.golden
new file mode 100644
index 00000000000..a8dc2e71dc3
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error1.1.golden
@@ -0,0 +1,32 @@
+//
+PACKAGE error1
+
+IMPORTPATH
+ testdata/error1
+
+FILENAMES
+ testdata/error1.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+ }
+
+ //
+ type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+ }
+
diff --git a/libgo/go/go/doc/testdata/error1.2.golden b/libgo/go/go/doc/testdata/error1.2.golden
new file mode 100644
index 00000000000..6c6fe5d49bd
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error1.2.golden
@@ -0,0 +1,30 @@
+//
+PACKAGE error1
+
+IMPORTPATH
+ testdata/error1
+
+FILENAMES
+ testdata/error1.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+ }
+
diff --git a/libgo/go/go/doc/testdata/error1.go b/libgo/go/go/doc/testdata/error1.go
new file mode 100644
index 00000000000..3c777a78005
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error1.go
@@ -0,0 +1,24 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package error1
+
+type I0 interface {
+ // When embedded, the predeclared error interface
+ // must remain visible in interface types.
+ error
+}
+
+type T0 struct {
+ ExportedField interface {
+ // error should be visible
+ error
+ }
+}
+
+type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+}
diff --git a/libgo/go/go/doc/testdata/error2.0.golden b/libgo/go/go/doc/testdata/error2.0.golden
new file mode 100644
index 00000000000..dedfe412a0f
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error2.0.golden
@@ -0,0 +1,27 @@
+//
+PACKAGE error2
+
+IMPORTPATH
+ testdata/error2
+
+FILENAMES
+ testdata/error2.go
+
+TYPES
+ //
+ type I0 interface {
+ // contains filtered or unexported methods
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // contains filtered or unexported methods
+ }
+ }
+
diff --git a/libgo/go/go/doc/testdata/error2.1.golden b/libgo/go/go/doc/testdata/error2.1.golden
new file mode 100644
index 00000000000..776bd1b3e40
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error2.1.golden
@@ -0,0 +1,37 @@
+//
+PACKAGE error2
+
+IMPORTPATH
+ testdata/error2
+
+FILENAMES
+ testdata/error2.go
+
+TYPES
+ //
+ type I0 interface {
+ // When embedded, the the locally declared error interface
+ // is only visible if all declarations are shown.
+ error
+ }
+
+ //
+ type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // error should not be visible
+ error
+ }
+ }
+
+ // This error declaration shadows the predeclared error type.
+ type error interface {
+ Error() string
+ }
+
diff --git a/libgo/go/go/doc/testdata/error2.2.golden b/libgo/go/go/doc/testdata/error2.2.golden
new file mode 100644
index 00000000000..dedfe412a0f
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error2.2.golden
@@ -0,0 +1,27 @@
+//
+PACKAGE error2
+
+IMPORTPATH
+ testdata/error2
+
+FILENAMES
+ testdata/error2.go
+
+TYPES
+ //
+ type I0 interface {
+ // contains filtered or unexported methods
+ }
+
+ //
+ type S0 struct {
+ // contains filtered or unexported fields
+ }
+
+ //
+ type T0 struct {
+ ExportedField interface {
+ // contains filtered or unexported methods
+ }
+ }
+
diff --git a/libgo/go/go/doc/testdata/error2.go b/libgo/go/go/doc/testdata/error2.go
new file mode 100644
index 00000000000..6cc36feef3e
--- /dev/null
+++ b/libgo/go/go/doc/testdata/error2.go
@@ -0,0 +1,29 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package error2
+
+type I0 interface {
+ // When embedded, the the locally declared error interface
+ // is only visible if all declarations are shown.
+ error
+}
+
+type T0 struct {
+ ExportedField interface {
+ // error should not be visible
+ error
+ }
+}
+
+type S0 struct {
+ // In struct types, an embedded error must only be visible
+ // if AllDecls is set.
+ error
+}
+
+// This error declaration shadows the predeclared error type.
+type error interface {
+ Error() string
+}
diff --git a/libgo/go/go/doc/testdata/f.go b/libgo/go/go/doc/testdata/f.go
index a3051e1fb3b..7e9add90784 100644
--- a/libgo/go/go/doc/testdata/f.go
+++ b/libgo/go/go/doc/testdata/f.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go
index f1b4ce34d1a..5c203a7846e 100644
--- a/libgo/go/go/parser/interface.go
+++ b/libgo/go/go/parser/interface.go
@@ -80,13 +80,25 @@ const (
// are returned via a scanner.ErrorList which is sorted by file position.
//
func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (*ast.File, error) {
+ // get source
text, err := readSource(filename, src)
if err != nil {
return nil, err
}
+
+ // parse source
var p parser
p.init(fset, filename, text, mode)
- return p.parseFile(), p.errors()
+ f := p.parseFile()
+
+ // sort errors
+ if p.mode&SpuriousErrors == 0 {
+ p.errors.RemoveMultiples()
+ } else {
+ p.errors.Sort()
+ }
+
+ return f, p.errors.Err()
}
// ParseDir calls ParseFile for the files in the directory specified by path and
diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go
index 6bee8de9f65..6a0b61eb48c 100644
--- a/libgo/go/go/parser/parser.go
+++ b/libgo/go/go/parser/parser.go
@@ -18,8 +18,8 @@ import (
// The parser structure holds the parser's internal state.
type parser struct {
- file *token.File
- scanner.ErrorVector
+ file *token.File
+ errors scanner.ErrorList
scanner scanner.Scanner
// Tracing/debugging
@@ -58,7 +58,8 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod
if mode&ParseComments != 0 {
m = scanner.ScanComments
}
- p.scanner.Init(p.file, src, p, m)
+ eh := func(pos token.Position, msg string) { p.errors.Add(pos, msg) }
+ p.scanner.Init(p.file, src, eh, m)
p.mode = mode
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
@@ -74,14 +75,6 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod
p.openLabelScope()
}
-func (p *parser) errors() error {
- m := scanner.Sorted
- if p.mode&SpuriousErrors == 0 {
- m = scanner.NoMultiples
- }
- return p.GetError(m)
-}
-
// ----------------------------------------------------------------------------
// Scoping support
@@ -334,7 +327,7 @@ func (p *parser) next() {
}
func (p *parser) error(pos token.Pos, msg string) {
- p.Error(p.file.Position(pos), msg)
+ p.errors.Add(p.file.Position(pos), msg)
}
func (p *parser) errorExpected(pos token.Pos, msg string) {
@@ -342,7 +335,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
if pos == p.pos {
// the error happened at the current position;
// make the error message more specific
- if p.tok == token.SEMICOLON && p.lit[0] == '\n' {
+ if p.tok == token.SEMICOLON && p.lit == "\n" {
msg += ", found newline"
} else {
msg += ", found '" + p.tok.String() + "'"
@@ -363,6 +356,17 @@ func (p *parser) expect(tok token.Token) token.Pos {
return pos
}
+// expectClosing is like expect but provides a better error message
+// for the common case of a missing comma before a newline.
+//
+func (p *parser) expectClosing(tok token.Token, construct string) token.Pos {
+ if p.tok != tok && p.tok == token.SEMICOLON && p.lit == "\n" {
+ p.error(p.pos, "missing ',' before newline in "+construct)
+ p.next()
+ }
+ return p.expect(tok)
+}
+
func (p *parser) expectSemi() {
if p.tok != token.RPAREN && p.tok != token.RBRACE {
p.expect(token.SEMICOLON)
@@ -1063,7 +1067,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
p.next()
}
p.exprLev--
- rparen := p.expect(token.RPAREN)
+ rparen := p.expectClosing(token.RPAREN, "argument list")
return &ast.CallExpr{fun, lparen, list, ellipsis, rparen}
}
@@ -1118,7 +1122,7 @@ func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
elts = p.parseElementList()
}
p.exprLev--
- rbrace := p.expect(token.RBRACE)
+ rbrace := p.expectClosing(token.RBRACE, "composite literal")
return &ast.CompositeLit{typ, lbrace, elts, rbrace}
}
@@ -2123,7 +2127,7 @@ func (p *parser) parseFile() *ast.File {
// Don't bother parsing the rest if we had errors already.
// Likely not a Go source file at all.
- if p.ErrorCount() == 0 && p.mode&PackageClauseOnly == 0 {
+ if p.errors.Len() == 0 && p.mode&PackageClauseOnly == 0 {
// import decls
for p.tok == token.IMPORT {
decls = append(decls, p.parseGenDecl(token.IMPORT, parseImportSpec))
diff --git a/libgo/go/go/printer/performance_test.go b/libgo/go/go/printer/performance_test.go
index dbd942292b5..0c6a4e71f13 100644
--- a/libgo/go/go/printer/performance_test.go
+++ b/libgo/go/go/printer/performance_test.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// This file implements a simple printer performance benchmark:
-// gotest -bench=BenchmarkPrint
+// go test -bench=BenchmarkPrint
package printer
diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go
index fe99e675eb0..c9949205e8a 100644
--- a/libgo/go/go/printer/printer.go
+++ b/libgo/go/go/printer/printer.go
@@ -6,7 +6,6 @@
package printer
import (
- "bytes"
"fmt"
"go/ast"
"go/token"
@@ -51,22 +50,22 @@ type printer struct {
fset *token.FileSet
// Current state
- output bytes.Buffer // raw printer result
+ output []byte // raw printer result
indent int // current indentation
mode pmode // current printer mode
impliedSemi bool // if set, a linebreak implies a semicolon
lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
wsbuf []whiteSpace // delayed white space
- // The (possibly estimated) position in the generated output;
- // in AST space (i.e., pos is set whenever a token position is
- // known accurately, and updated dependending on what has been
- // written).
- pos token.Position
-
- // The value of pos immediately after the last item has been
- // written using writeItem.
- last token.Position
+ // Positions
+ // The out position differs from the pos position when the result
+ // formatting differs from the source formatting (in the amount of
+ // white space). If there's a difference and SourcePos is set in
+ // ConfigMode, //line comments are used in the output to restore
+ // original source positions for a reader.
+ pos token.Position // current position in AST (source) space
+ out token.Position // current position in output space
+ last token.Position // value of pos after calling writeString
// The list of all source comments, in order of appearance.
comments []*ast.CommentGroup // may be nil
@@ -89,6 +88,8 @@ type printer struct {
func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
p.Config = *cfg
p.fset = fset
+ p.pos = token.Position{Line: 1, Column: 1}
+ p.out = token.Position{Line: 1, Column: 1}
p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
p.nodeSizes = nodeSizes
p.cachedPos = -1
@@ -151,41 +152,57 @@ func (p *printer) lineFor(pos token.Pos) int {
return p.cachedLine
}
-// writeByte writes ch to p.output and updates p.pos.
-func (p *printer) writeByte(ch byte) {
- p.output.WriteByte(ch)
- p.pos.Offset++
- p.pos.Column++
-
- if ch == '\n' || ch == '\f' {
- // write indentation
- // use "hard" htabs - indentation columns
- // must not be discarded by the tabwriter
- const htabs = "\t\t\t\t\t\t\t\t"
- j := p.indent
- for j > len(htabs) {
- p.output.WriteString(htabs)
- j -= len(htabs)
- }
- p.output.WriteString(htabs[0:j])
+// atLineBegin emits a //line comment if necessary and prints indentation.
+func (p *printer) atLineBegin(pos token.Position) {
+ // write a //line comment if necessary
+ if p.Config.Mode&SourcePos != 0 && pos.IsValid() && (p.out.Line != pos.Line || p.out.Filename != pos.Filename) {
+ p.output = append(p.output, tabwriter.Escape) // protect '\n' in //line from tabwriter interpretation
+ p.output = append(p.output, fmt.Sprintf("//line %s:%d\n", pos.Filename, pos.Line)...)
+ p.output = append(p.output, tabwriter.Escape)
+ // p.out must match the //line comment
+ p.out.Filename = pos.Filename
+ p.out.Line = pos.Line
+ }
- // update p.pos
- p.pos.Line++
- p.pos.Offset += p.indent
- p.pos.Column = 1 + p.indent
+ // write indentation
+ // use "hard" htabs - indentation columns
+ // must not be discarded by the tabwriter
+ for i := 0; i < p.indent; i++ {
+ p.output = append(p.output, '\t')
}
+
+ // update positions
+ i := p.indent
+ p.pos.Offset += i
+ p.pos.Column += i
+ p.out.Column += i
}
-// writeByteN writes ch n times to p.output and updates p.pos.
-func (p *printer) writeByteN(ch byte, n int) {
- for n > 0 {
- p.writeByte(ch)
- n--
+// writeByte writes ch n times to p.output and updates p.pos.
+func (p *printer) writeByte(ch byte, n int) {
+ if p.out.Column == 1 {
+ p.atLineBegin(p.pos)
+ }
+
+ for i := 0; i < n; i++ {
+ p.output = append(p.output, ch)
+ }
+
+ // update positions
+ p.pos.Offset += n
+ if ch == '\n' || ch == '\f' {
+ p.pos.Line += n
+ p.out.Line += n
+ p.pos.Column = 1
+ p.out.Column = 1
+ return
}
+ p.pos.Column += n
+ p.out.Column += n
}
-// writeString writes the string s to p.output and updates p.pos.
-// If isLit is set, s is escaped w/ tabwriter.Escape characters
+// writeString writes the string s to p.output and updates p.pos, p.out,
+// and p.last. If isLit is set, s is escaped w/ tabwriter.Escape characters
// to protect s from being interpreted by the tabwriter.
//
// Note: writeString is only used to write Go tokens, literals, and
@@ -195,64 +212,69 @@ func (p *printer) writeByteN(ch byte, n int) {
// avoids processing extra escape characters and reduces run time of the
// printer benchmark by up to 10%.
//
-func (p *printer) writeString(s string, isLit bool) {
+func (p *printer) writeString(pos token.Position, s string, isLit bool) {
+ if p.out.Column == 1 {
+ p.atLineBegin(pos)
+ }
+
+ if pos.IsValid() {
+ // update p.pos (if pos is invalid, continue with existing p.pos)
+ // Note: Must do this after handling line beginnings because
+ // atLineBegin updates p.pos if there's indentation, but p.pos
+ // is the position of s.
+ p.pos = pos
+ // reset state if the file changed
+ // (used when printing merged ASTs of different files
+ // e.g., the result of ast.MergePackageFiles)
+ if p.last.IsValid() && p.last.Filename != pos.Filename {
+ p.indent = 0
+ p.mode = 0
+ p.wsbuf = p.wsbuf[0:0]
+ }
+ }
+
if isLit {
// Protect s such that is passes through the tabwriter
// unchanged. Note that valid Go programs cannot contain
// tabwriter.Escape bytes since they do not appear in legal
// UTF-8 sequences.
- p.output.WriteByte(tabwriter.Escape)
+ p.output = append(p.output, tabwriter.Escape)
}
- p.output.WriteString(s)
+ if debug {
+ p.output = append(p.output, fmt.Sprintf("/*%s*/", pos)...) // do not update p.pos!
+ }
+ p.output = append(p.output, s...)
- // update p.pos
+ // update positions
nlines := 0
- column := p.pos.Column + len(s)
+ var li int // index of last newline; valid if nlines > 0
for i := 0; i < len(s); i++ {
+ // Go tokens cannot contain '\f' - no need to look for it
if s[i] == '\n' {
nlines++
- column = len(s) - i
+ li = i
}
}
p.pos.Offset += len(s)
- p.pos.Line += nlines
- p.pos.Column = column
+ if nlines > 0 {
+ p.pos.Line += nlines
+ p.out.Line += nlines
+ c := len(s) - li
+ p.pos.Column = c
+ p.out.Column = c
+ } else {
+ p.pos.Column += len(s)
+ p.out.Column += len(s)
+ }
if isLit {
- p.output.WriteByte(tabwriter.Escape)
+ p.output = append(p.output, tabwriter.Escape)
}
-}
-// writeItem writes data at position pos. data is the text corresponding to
-// a single lexical token, but may also be comment text. pos is the actual
-// (or at least very accurately estimated) position of the data in the original
-// source text. writeItem updates p.last to the position immediately following
-// the data.
-//
-func (p *printer) writeItem(pos token.Position, data string, isLit bool) {
- if pos.IsValid() {
- // continue with previous position if we don't have a valid pos
- if p.last.IsValid() && p.last.Filename != pos.Filename {
- // the file has changed - reset state
- // (used when printing merged ASTs of different files
- // e.g., the result of ast.MergePackageFiles)
- p.indent = 0
- p.mode = 0
- p.wsbuf = p.wsbuf[0:0]
- }
- p.pos = pos
- }
- if debug {
- // do not update p.pos - use write0
- fmt.Fprintf(&p.output, "/*%s*/", pos)
- }
- p.writeString(data, isLit)
p.last = p.pos
}
-const linePrefix = "//line "
-
// writeCommentPrefix writes the whitespace before a comment.
// If there is any pending whitespace, it consumes as much of
// it as is likely to help position the comment nicely.
@@ -262,14 +284,14 @@ const linePrefix = "//line "
// next item is a keyword.
//
func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) {
- if p.output.Len() == 0 {
+ if len(p.output) == 0 {
// the comment is the first item to be printed - don't write any whitespace
return
}
if pos.IsValid() && pos.Filename != p.last.Filename {
// comment in a different file - separate with newlines
- p.writeByteN('\f', maxNewlines)
+ p.writeByte('\f', maxNewlines)
return
}
@@ -309,7 +331,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
// with a blank instead of a tab
sep = ' '
}
- p.writeByte(sep)
+ p.writeByte(sep, 1)
}
} else {
@@ -373,23 +395,17 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
}
if n > 0 {
- // turn off indent if we're about to print a line directive
- indent := p.indent
- if strings.HasPrefix(comment.Text, linePrefix) {
- p.indent = 0
- }
// use formfeeds to break columns before a comment;
// this is analogous to using formfeeds to separate
// individual lines of /*-style comments
- p.writeByteN('\f', nlimit(n))
- p.indent = indent // restore indent
+ p.writeByte('\f', nlimit(n))
}
}
}
// Split comment text into lines
// (using strings.Split(text, "\n") is significantly slower for
-// this specific purpose, as measured with: gotest -bench=Print)
+// this specific purpose, as measured with: go test -bench=Print)
func split(text string) []string {
// count lines (comment text never ends in a newline)
n := 1
@@ -564,30 +580,33 @@ func stripCommonPrefix(lines []string) {
func (p *printer) writeComment(comment *ast.Comment) {
text := comment.Text
+ pos := p.posFor(comment.Pos())
- if strings.HasPrefix(text, linePrefix) {
- pos := strings.TrimSpace(text[len(linePrefix):])
- i := strings.LastIndex(pos, ":")
- if i >= 0 {
- // The line directive we are about to print changed
- // the Filename and Line number used by go/token
- // as it was reading the input originally.
- // In order to match the original input, we have to
- // update our own idea of the file and line number
- // accordingly, after printing the directive.
- file := pos[:i]
- line, _ := strconv.Atoi(pos[i+1:])
- defer func() {
- p.pos.Filename = file
- p.pos.Line = line
- p.pos.Column = 1
- }()
+ const linePrefix = "//line "
+ if strings.HasPrefix(text, linePrefix) && (!pos.IsValid() || pos.Column == 1) {
+ // possibly a line directive
+ ldir := strings.TrimSpace(text[len(linePrefix):])
+ if i := strings.LastIndex(ldir, ":"); i >= 0 {
+ if line, err := strconv.Atoi(ldir[i+1:]); err == nil && line > 0 {
+ // The line directive we are about to print changed
+ // the Filename and Line number used for subsequent
+ // tokens. We have to update our AST-space position
+ // accordingly and suspend indentation temporarily.
+ indent := p.indent
+ p.indent = 0
+ defer func() {
+ p.pos.Filename = ldir[:i]
+ p.pos.Line = line
+ p.pos.Column = 1
+ p.indent = indent
+ }()
+ }
}
}
// shortcut common case of //-style comments
if text[1] == '/' {
- p.writeItem(p.posFor(comment.Pos()), text, true)
+ p.writeString(pos, text, true)
return
}
@@ -598,14 +617,13 @@ func (p *printer) writeComment(comment *ast.Comment) {
// write comment lines, separated by formfeed,
// without a line break after the last line
- pos := p.posFor(comment.Pos())
for i, line := range lines {
if i > 0 {
- p.writeByte('\f')
+ p.writeByte('\f', 1)
pos = p.pos
}
if len(line) > 0 {
- p.writeItem(pos, line, true)
+ p.writeString(pos, line, true)
}
}
}
@@ -643,7 +661,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, dropped
// make sure we have a line break
if needsLinebreak {
- p.writeByte('\n')
+ p.writeByte('\n', 1)
wroteNewline = true
}
@@ -671,7 +689,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line {
// the last comment is a /*-style comment and the next item
// follows on the same line: separate with an extra blank
- p.writeByte(' ')
+ p.writeByte(' ', 1)
}
// ensure that there is a line break after a //-style comment,
// before a closing '}' unless explicitly disabled, or at eof
@@ -722,7 +740,7 @@ func (p *printer) writeWhitespace(n int) {
}
fallthrough
default:
- p.writeByte(byte(ch))
+ p.writeByte(byte(ch), 1)
}
}
@@ -886,12 +904,12 @@ func (p *printer) print(args ...interface{}) {
if droppedFF {
ch = '\f' // use formfeed since we dropped one before
}
- p.writeByteN(ch, n)
+ p.writeByte(ch, n)
impliedSemi = false
}
}
- p.writeItem(next, data, isLit)
+ p.writeString(next, data, isLit)
p.impliedSemi = impliedSemi
}
}
@@ -1027,7 +1045,7 @@ unsupported:
type trimmer struct {
output io.Writer
state int
- space bytes.Buffer
+ space []byte
}
// trimmer is implemented as a state machine.
@@ -1038,6 +1056,11 @@ const (
inText // inside text
)
+func (p *trimmer) resetSpace() {
+ p.state = inSpace
+ p.space = p.space[0:0]
+}
+
// Design note: It is tempting to eliminate extra blanks occurring in
// whitespace in this function as it could simplify some
// of the blanks logic in the node printing functions.
@@ -1062,36 +1085,33 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
case inSpace:
switch b {
case '\t', ' ':
- p.space.WriteByte(b) // WriteByte returns no errors
+ p.space = append(p.space, b)
case '\n', '\f':
- p.space.Reset() // discard trailing space
+ p.resetSpace() // discard trailing space
_, err = p.output.Write(aNewline)
case tabwriter.Escape:
- _, err = p.output.Write(p.space.Bytes())
+ _, err = p.output.Write(p.space)
p.state = inEscape
m = n + 1 // +1: skip tabwriter.Escape
default:
- _, err = p.output.Write(p.space.Bytes())
+ _, err = p.output.Write(p.space)
p.state = inText
m = n
}
case inEscape:
if b == tabwriter.Escape {
_, err = p.output.Write(data[m:n])
- p.state = inSpace
- p.space.Reset()
+ p.resetSpace()
}
case inText:
switch b {
case '\t', ' ':
_, err = p.output.Write(data[m:n])
- p.state = inSpace
- p.space.Reset()
- p.space.WriteByte(b) // WriteByte returns no errors
+ p.resetSpace()
+ p.space = append(p.space, b)
case '\n', '\f':
_, err = p.output.Write(data[m:n])
- p.state = inSpace
- p.space.Reset()
+ p.resetSpace()
_, err = p.output.Write(aNewline)
case tabwriter.Escape:
_, err = p.output.Write(data[m:n])
@@ -1110,8 +1130,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
switch p.state {
case inEscape, inText:
_, err = p.output.Write(data[m:n])
- p.state = inSpace
- p.space.Reset()
+ p.resetSpace()
}
return
@@ -1120,16 +1139,19 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
// ----------------------------------------------------------------------------
// Public interface
-// General printing is controlled with these Config.Mode flags.
+// A Mode value is a set of flags (or 0). They coontrol printing.
+type Mode uint
+
const (
- RawFormat uint = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
+ RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
TabIndent // use tabs for indentation independent of UseSpaces
UseSpaces // use spaces instead of tabs for alignment
+ SourcePos // emit //line comments to preserve original source positions
)
// A Config node controls the output of Fprint.
type Config struct {
- Mode uint // default: 0
+ Mode Mode // default: 0
Tabwidth int // default: 8
}
@@ -1170,7 +1192,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{
}
// write printer result via tabwriter/trimmer to output
- if _, err = output.Write(p.output.Bytes()); err != nil {
+ if _, err = output.Write(p.output); err != nil {
return
}
diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go
index 9adf48cda61..fa133cd35f0 100644
--- a/libgo/go/go/printer/printer_test.go
+++ b/libgo/go/go/printer/printer_test.go
@@ -67,6 +67,13 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) {
}
res := buf.Bytes()
+ // formatted source must be valid
+ if _, err := parser.ParseFile(fset, "", res, 0); err != nil {
+ t.Error(err)
+ t.Logf("\n%s", res)
+ return
+ }
+
// update golden files if necessary
if *update {
if err := ioutil.WriteFile(golden, res, 0644); err != nil {
@@ -133,7 +140,7 @@ type entry struct {
mode checkMode
}
-// Use gotest -update to create/update the respective golden files.
+// Use go test -update to create/update the respective golden files.
var data = []entry{
{"empty.input", "empty.golden", 0},
{"comments.input", "comments.golden", 0},
@@ -223,7 +230,8 @@ func TestBadNodes(t *testing.T) {
}
}
-// Print and parse f with
+// testComment verifies that f can be parsed again after printing it
+// with its first comment set to comment at any possible source offset.
func testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) {
f.Comments[0].List[0] = comment
var buf bytes.Buffer
@@ -280,3 +288,128 @@ func fibo(n int) {
testComment(t, f, len(src), &ast.Comment{pos, "/*-style \n comment */"})
testComment(t, f, len(src), &ast.Comment{pos, "/*-style comment \n\n\n */"})
}
+
+type visitor chan *ast.Ident
+
+func (v visitor) Visit(n ast.Node) (w ast.Visitor) {
+ if ident, ok := n.(*ast.Ident); ok {
+ v <- ident
+ }
+ return v
+}
+
+// idents is an iterator that returns all idents in f via the result channel.
+func idents(f *ast.File) <-chan *ast.Ident {
+ v := make(visitor)
+ go func() {
+ ast.Walk(v, f)
+ close(v)
+ }()
+ return v
+}
+
+// identCount returns the number of identifiers found in f.
+func identCount(f *ast.File) int {
+ n := 0
+ for _ = range idents(f) {
+ n++
+ }
+ return n
+}
+
+// Verify that the SourcePos mode emits correct //line comments
+// by testing that position information for matching identifiers
+// is maintained.
+func TestSourcePos(t *testing.T) {
+ const src = `
+package p
+import ( "go/printer"; "math" )
+const pi = 3.14; var x = 0
+type t struct{ x, y, z int; u, v, w float32 }
+func (t *t) foo(a, b, c int) int {
+ return a*t.x + b*t.y +
+ // two extra lines here
+ // ...
+ c*t.z
+}
+`
+
+ // parse original
+ f1, err := parser.ParseFile(fset, "src", src, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // pretty-print original
+ var buf bytes.Buffer
+ err = (&Config{Mode: UseSpaces | SourcePos, Tabwidth: 8}).Fprint(&buf, fset, f1)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // parse pretty printed original
+ // (//line comments must be interpreted even w/o parser.ParseComments set)
+ f2, err := parser.ParseFile(fset, "", buf.Bytes(), 0)
+ if err != nil {
+ t.Fatalf("%s\n%s", err, buf.Bytes())
+ }
+
+ // At this point the position information of identifiers in f2 should
+ // match the position information of corresponding identifiers in f1.
+
+ // number of identifiers must be > 0 (test should run) and must match
+ n1 := identCount(f1)
+ n2 := identCount(f2)
+ if n1 == 0 {
+ t.Fatal("got no idents")
+ }
+ if n2 != n1 {
+ t.Errorf("got %d idents; want %d", n2, n1)
+ }
+
+ // verify that all identifiers have correct line information
+ i2range := idents(f2)
+ for i1 := range idents(f1) {
+ i2 := <-i2range
+
+ if i2.Name != i1.Name {
+ t.Errorf("got ident %s; want %s", i2.Name, i1.Name)
+ }
+
+ l1 := fset.Position(i1.Pos()).Line
+ l2 := fset.Position(i2.Pos()).Line
+ if l2 != l1 {
+ t.Errorf("got line %d; want %d for %s", l2, l1, i1.Name)
+ }
+ }
+
+ if t.Failed() {
+ t.Logf("\n%s", buf.Bytes())
+ }
+}
+
+// TextX is a skeleton test that can be filled in for debugging one-off cases.
+// Do not remove.
+func TestX(t *testing.T) {
+ const src = `
+package p
+func _() {}
+`
+ // parse original
+ f, err := parser.ParseFile(fset, "src", src, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // pretty-print original
+ var buf bytes.Buffer
+ if err = (&Config{Mode: UseSpaces, Tabwidth: 8}).Fprint(&buf, fset, f); err != nil {
+ t.Fatal(err)
+ }
+
+ // parse pretty printed original
+ if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil {
+ t.Fatalf("%s\n%s", err, buf.Bytes())
+ }
+
+}
diff --git a/libgo/go/go/printer/testdata/comments.golden b/libgo/go/go/printer/testdata/comments.golden
index d2ad9e3a2ff..e5826eecefe 100644
--- a/libgo/go/go/printer/testdata/comments.golden
+++ b/libgo/go/go/printer/testdata/comments.golden
@@ -404,7 +404,7 @@ func _() {
*/
}
-// Some interesting interspersed comments
+// Some interesting interspersed comments.
func _( /* this */ x /* is */ /* an */ int) {
}
@@ -428,6 +428,26 @@ func _() {
_ = []int{0, 1 /* don't introduce a newline after this comment - was issue 1365 */ }
}
+// Test cases from issue 1542:
+// Comments must not be placed before commas and cause invalid programs.
+func _() {
+ var a = []int{1, 2 /*jasldf*/}
+ _ = a
+}
+
+func _() {
+ var a = []int{1, 2}/*jasldf
+ */
+
+ _ = a
+}
+
+func _() {
+ var a = []int{1, 2}// jasldf
+
+ _ = a
+}
+
// Comments immediately adjacent to punctuation (for which the go/printer
// may only have estimated position information) must remain after the punctuation.
func _() {
@@ -459,6 +479,25 @@ func _() {
}
}
+// Print line directives correctly.
+
+// The following is a legal line directive.
+//line foo:1
+func _() {
+ _ = 0
+ // The following is a legal line directive. It must not be indented:
+//line foo:2
+ _ = 1
+
+ // The following is not a legal line directive (it doesn't start in column 1):
+ //line foo:2
+ _ = 2
+
+ // The following is not a legal line directive (negative line number):
+ //line foo:-3
+ _ = 3
+}
+
// Line comments with tabs
func _() {
var finput *bufio.Reader // input file
diff --git a/libgo/go/go/printer/testdata/comments.input b/libgo/go/go/printer/testdata/comments.input
index 222e0a713d4..55f6b61f21f 100644
--- a/libgo/go/go/printer/testdata/comments.input
+++ b/libgo/go/go/printer/testdata/comments.input
@@ -410,7 +410,7 @@ func _() {
}
-// Some interesting interspersed comments
+// Some interesting interspersed comments.
func _(/* this */x/* is *//* an */ int) {
}
@@ -432,6 +432,26 @@ func _() {
_ = []int{0, 1 /* don't introduce a newline after this comment - was issue 1365 */}
}
+// Test cases from issue 1542:
+// Comments must not be placed before commas and cause invalid programs.
+func _() {
+ var a = []int{1, 2, /*jasldf*/
+ }
+ _ = a
+}
+
+func _() {
+ var a = []int{1, 2, /*jasldf
+ */
+ }
+ _ = a
+}
+
+func _() {
+ var a = []int{1, 2, // jasldf
+ }
+ _ = a
+}
// Comments immediately adjacent to punctuation (for which the go/printer
// may only have estimated position information) must remain after the punctuation.
@@ -467,6 +487,25 @@ func _() {
}
+// Print line directives correctly.
+
+// The following is a legal line directive.
+//line foo:1
+func _() {
+ _ = 0
+// The following is a legal line directive. It must not be indented:
+//line foo:2
+ _ = 1
+
+// The following is not a legal line directive (it doesn't start in column 1):
+ //line foo:2
+ _ = 2
+
+// The following is not a legal line directive (negative line number):
+//line foo:-3
+ _ = 3
+}
+
// Line comments with tabs
func _() {
var finput *bufio.Reader // input file
diff --git a/libgo/go/go/scanner/errors.go b/libgo/go/go/scanner/errors.go
index cd9620b878b..8a75a96508e 100644
--- a/libgo/go/go/scanner/errors.go
+++ b/libgo/go/go/scanner/errors.go
@@ -11,44 +11,18 @@ import (
"sort"
)
-// An implementation of an ErrorHandler may be provided to the Scanner.
-// If a syntax error is encountered and a handler was installed, Error
-// is called with a position and an error message. The position points
-// to the beginning of the offending token.
-//
-type ErrorHandler interface {
- Error(pos token.Position, msg string)
-}
-
-// ErrorVector implements the ErrorHandler interface. It maintains a list
-// of errors which can be retrieved with GetErrorList and GetError. The
-// zero value for an ErrorVector is an empty ErrorVector ready to use.
-//
-// A common usage pattern is to embed an ErrorVector alongside a
-// scanner in a data structure that uses the scanner. By passing a
-// reference to an ErrorVector to the scanner's Init call, default
-// error handling is obtained.
-//
-type ErrorVector struct {
- errors []*Error
-}
-
-// Reset resets an ErrorVector to no errors.
-func (h *ErrorVector) Reset() { h.errors = h.errors[:0] }
-
-// ErrorCount returns the number of errors collected.
-func (h *ErrorVector) ErrorCount() int { return len(h.errors) }
-
-// Within ErrorVector, an error is represented by an Error node. The
-// position Pos, if valid, points to the beginning of the offending
-// token, and the error condition is described by Msg.
+// In an ErrorList, an error is represented by an *Error.
+// The position Pos, if valid, points to the beginning of
+// the offending token, and the error condition is described
+// by Msg.
//
type Error struct {
Pos token.Position
Msg string
}
-func (e *Error) Error() string {
+// Error implements the error interface.
+func (e Error) Error() string {
if e.Pos.Filename != "" || e.Pos.IsValid() {
// don't print "<unknown position>"
// TODO(gri) reconsider the semantics of Position.IsValid
@@ -57,9 +31,19 @@ func (e *Error) Error() string {
return e.Msg
}
-// An ErrorList is a (possibly sorted) list of Errors.
+// ErrorList is a list of *Errors.
+// The zero value for an ErrorList is an empty ErrorList ready to use.
+//
type ErrorList []*Error
+// Add adds an Error with given position and error message to an ErrorList.
+func (p *ErrorList) Add(pos token.Position, msg string) {
+ *p = append(*p, &Error{pos, msg})
+}
+
+// Reset resets an ErrorList to no errors.
+func (p *ErrorList) Reset() { *p = (*p)[0:0] }
+
// ErrorList implements the sort Interface.
func (p ErrorList) Len() int { return len(p) }
func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
@@ -84,72 +68,47 @@ func (p ErrorList) Less(i, j int) bool {
return false
}
+// Sort sorts an ErrorList. *Error entries are sorted by position,
+// other errors are sorted by error message, and before any *Error
+// entry.
+//
+func (p ErrorList) Sort() {
+ sort.Sort(p)
+}
+
+// RemoveMultiples sorts an ErrorList and removes all but the first error per line.
+func (p *ErrorList) RemoveMultiples() {
+ sort.Sort(p)
+ var last token.Position // initial last.Line is != any legal error line
+ i := 0
+ for _, e := range *p {
+ if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
+ last = e.Pos
+ (*p)[i] = e
+ i++
+ }
+ }
+ (*p) = (*p)[0:i]
+}
+
+// An ErrorList implements the error interface.
func (p ErrorList) Error() string {
switch len(p) {
case 0:
- return "unspecified error"
+ return "no errors"
case 1:
return p[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
}
-// These constants control the construction of the ErrorList
-// returned by GetErrors.
-//
-const (
- Raw = iota // leave error list unchanged
- Sorted // sort error list by file, line, and column number
- NoMultiples // sort error list and leave only the first error per line
-)
-
-// GetErrorList returns the list of errors collected by an ErrorVector.
-// The construction of the ErrorList returned is controlled by the mode
-// parameter. If there are no errors, the result is nil.
-//
-func (h *ErrorVector) GetErrorList(mode int) ErrorList {
- if len(h.errors) == 0 {
- return nil
- }
-
- list := make(ErrorList, len(h.errors))
- copy(list, h.errors)
-
- if mode >= Sorted {
- sort.Sort(list)
- }
-
- if mode >= NoMultiples {
- var last token.Position // initial last.Line is != any legal error line
- i := 0
- for _, e := range list {
- if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
- last = e.Pos
- list[i] = e
- i++
- }
- }
- list = list[0:i]
- }
-
- return list
-}
-
-// GetError is like GetErrorList, but it returns an error instead
-// so that a nil result can be assigned to an error variable and
-// remains nil.
-//
-func (h *ErrorVector) GetError(mode int) error {
- if len(h.errors) == 0 {
+// Err returns an error equivalent to this error list.
+// If the list is empty, Err returns nil.
+func (p ErrorList) Err() error {
+ if len(p) == 0 {
return nil
}
-
- return h.GetErrorList(mode)
-}
-
-// ErrorVector implements the ErrorHandler interface.
-func (h *ErrorVector) Error(pos token.Position, msg string) {
- h.errors = append(h.errors, &Error{pos, msg})
+ return p
}
// PrintError is a utility function that prints a list of errors to w,
diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go
index 0aabfe34c41..458e1f9f37a 100644
--- a/libgo/go/go/scanner/scanner.go
+++ b/libgo/go/go/scanner/scanner.go
@@ -30,6 +30,13 @@ import (
"unicode/utf8"
)
+// An ErrorHandler may be provided to Scanner.Init. If a syntax error is
+// encountered and a handler was installed, the handler is called with a
+// position and an error message. The position points to the beginning of
+// the offending token.
+//
+type ErrorHandler func(pos token.Position, msg string)
+
// A Scanner holds the scanner's internal state while processing
// a given text. It can be allocated as part of another data
// structure but must be initialized via Init before use.
@@ -103,7 +110,7 @@ const (
// line information which is already present is ignored. Init causes a
// panic if the file size does not match the src size.
//
-// Calls to Scan will use the error handler err if they encounter a
+// Calls to Scan will invoke the error handler err if they encounter a
// syntax error and err is not nil. Also, for each error encountered,
// the Scanner field ErrorCount is incremented by one. The mode parameter
// determines how comments are handled.
@@ -134,7 +141,7 @@ func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode
func (s *Scanner) error(offs int, msg string) {
if s.err != nil {
- s.err.Error(s.file.Position(s.file.Pos(offs)), msg)
+ s.err(s.file.Position(s.file.Pos(offs)), msg)
}
s.ErrorCount++
}
diff --git a/libgo/go/go/scanner/scanner_test.go b/libgo/go/go/scanner/scanner_test.go
index e7f7cd1c1e9..06223e23bd8 100644
--- a/libgo/go/go/scanner/scanner_test.go
+++ b/libgo/go/go/scanner/scanner_test.go
@@ -186,14 +186,6 @@ var source = func() []byte {
return src
}()
-type testErrorHandler struct {
- t *testing.T
-}
-
-func (h *testErrorHandler) Error(pos token.Position, msg string) {
- h.t.Errorf("Error() called (msg = %s)", msg)
-}
-
func newlineCount(s string) int {
n := 0
for i := 0; i < len(s); i++ {
@@ -226,9 +218,14 @@ func TestScan(t *testing.T) {
src_linecount := newlineCount(string(source))
whitespace_linecount := newlineCount(whitespace)
+ // error handler
+ eh := func(_ token.Position, msg string) {
+ t.Errorf("error handler called (msg = %s)", msg)
+ }
+
// verify scan
var s Scanner
- s.Init(fset.AddFile("", fset.Base(), len(source)), source, &testErrorHandler{t}, ScanComments|dontInsertSemis)
+ s.Init(fset.AddFile("", fset.Base(), len(source)), source, eh, ScanComments|dontInsertSemis)
index := 0
// epos is the expected position
epos := token.Position{
@@ -569,36 +566,37 @@ func TestStdErrorHander(t *testing.T) {
"//line File1:1\n" +
"@ @ @" // original file, line 1 again
- v := new(ErrorVector)
+ var list ErrorList
+ eh := func(pos token.Position, msg string) { list.Add(pos, msg) }
+
var s Scanner
- s.Init(fset.AddFile("File1", fset.Base(), len(src)), []byte(src), v, dontInsertSemis)
+ s.Init(fset.AddFile("File1", fset.Base(), len(src)), []byte(src), eh, dontInsertSemis)
for {
if _, tok, _ := s.Scan(); tok == token.EOF {
break
}
}
- list := v.GetErrorList(Raw)
+ if len(list) != s.ErrorCount {
+ t.Errorf("found %d errors, expected %d", len(list), s.ErrorCount)
+ }
+
if len(list) != 9 {
t.Errorf("found %d raw errors, expected 9", len(list))
PrintError(os.Stderr, list)
}
- list = v.GetErrorList(Sorted)
+ list.Sort()
if len(list) != 9 {
t.Errorf("found %d sorted errors, expected 9", len(list))
PrintError(os.Stderr, list)
}
- list = v.GetErrorList(NoMultiples)
+ list.RemoveMultiples()
if len(list) != 4 {
t.Errorf("found %d one-per-line errors, expected 4", len(list))
PrintError(os.Stderr, list)
}
-
- if v.ErrorCount() != s.ErrorCount {
- t.Errorf("found %d errors, expected %d", v.ErrorCount(), s.ErrorCount)
- }
}
type errorCollector struct {
@@ -607,16 +605,15 @@ type errorCollector struct {
pos token.Position // last error position encountered
}
-func (h *errorCollector) Error(pos token.Position, msg string) {
- h.cnt++
- h.msg = msg
- h.pos = pos
-}
-
func checkError(t *testing.T, src string, tok token.Token, pos int, err string) {
var s Scanner
var h errorCollector
- s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), &h, ScanComments|dontInsertSemis)
+ eh := func(pos token.Position, msg string) {
+ h.cnt++
+ h.msg = msg
+ h.pos = pos
+ }
+ s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), eh, ScanComments|dontInsertSemis)
_, tok0, _ := s.Scan()
_, tok1, _ := s.Scan()
if tok0 != tok {