summaryrefslogtreecommitdiff
path: root/src/pkg/go
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-09-13 12:00:31 +0200
committerOndřej Surý <ondrej@sury.org>2011-09-13 12:00:31 +0200
commit04f99b387021a8ce32a8795360cba9beaf986a81 (patch)
treef806c632c5dec5bb83190946d6d8ff8bd33c0e57 /src/pkg/go
parentd9514677ddaa705852cbba5034cb6d284261b53a (diff)
downloadgolang-04f99b387021a8ce32a8795360cba9beaf986a81.tar.gz
Imported Upstream version 2011.09.07upstream-weekly/2011.09.07
Diffstat (limited to 'src/pkg/go')
-rw-r--r--src/pkg/go/ast/filter.go301
-rw-r--r--src/pkg/go/build/build_test.go66
-rw-r--r--src/pkg/go/build/dir.go149
-rw-r--r--src/pkg/go/build/pkgtest/pkgtest.go6
-rw-r--r--src/pkg/go/build/pkgtest/sqrt_386_test.go1
-rw-r--r--src/pkg/go/build/pkgtest/sqrt_amd64_test.go1
-rw-r--r--src/pkg/go/build/pkgtest/sqrt_arm_test.go1
-rw-r--r--src/pkg/go/build/pkgtest/sqrt_test.go5
-rw-r--r--src/pkg/go/build/pkgtest/xsqrt_test.go5
-rw-r--r--src/pkg/go/build/syslist_test.go2
-rw-r--r--src/pkg/go/parser/parser.go88
-rw-r--r--src/pkg/go/parser/parser_test.go8
-rw-r--r--src/pkg/go/printer/nodes.go16
-rw-r--r--src/pkg/go/printer/testdata/declarations.golden111
-rw-r--r--src/pkg/go/printer/testdata/declarations.input76
15 files changed, 532 insertions, 304 deletions
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 26733430d..1bd8990f8 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -9,10 +9,45 @@ import "go/token"
// ----------------------------------------------------------------------------
// Export filtering
-func identListExports(list []*Ident) []*Ident {
+// exportFilter is a special filter function to extract exported nodes.
+func exportFilter(name string) bool {
+ return IsExported(name)
+}
+
+// FileExports trims the AST for a Go source file in place such that
+// only exported nodes remain: all top-level identifiers which are not exported
+// and their associated information (such as type, initial value, or function
+// body) are removed. Non-exported fields and methods of exported types are
+// stripped, and the function bodies of exported functions are set to nil.
+// The File.Comments list is not changed.
+//
+// FileExports returns true if there are exported declarationa;
+// it returns false otherwise.
+//
+func FileExports(src *File) bool {
+ return FilterFile(src, exportFilter)
+}
+
+// PackageExports trims the AST for a Go package in place such that
+// only exported nodes remain. The pkg.Files list is not changed, so that
+// file names and top-level package comments don't get lost.
+//
+// PackageExports returns true if there are exported declarations;
+// it returns false otherwise.
+//
+func PackageExports(pkg *Package) bool {
+ return FilterPackage(pkg, exportFilter)
+}
+
+// ----------------------------------------------------------------------------
+// General filtering
+
+type Filter func(string) bool
+
+func filterIdentList(list []*Ident, f Filter) []*Ident {
j := 0
for _, x := range list {
- if x.IsExported() {
+ if f(x.Name) {
list[j] = x
j++
}
@@ -38,33 +73,30 @@ func fieldName(x Expr) *Ident {
return nil
}
-func fieldListExports(fields *FieldList) (removedFields bool) {
+func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
if fields == nil {
- return
+ return false
}
list := fields.List
j := 0
for _, f := range list {
- exported := false
+ keepField := false
if len(f.Names) == 0 {
// anonymous field
- // (Note that a non-exported anonymous field
- // may still refer to a type with exported
- // fields, so this is not absolutely correct.
- // However, this cannot be done w/o complete
- // type information.)
name := fieldName(f.Type)
- exported = name != nil && name.IsExported()
+ keepField = name != nil && filter(name.Name)
} else {
n := len(f.Names)
- f.Names = identListExports(f.Names)
+ f.Names = filterIdentList(f.Names, filter)
if len(f.Names) < n {
removedFields = true
}
- exported = len(f.Names) > 0
+ keepField = len(f.Names) > 0
}
- if exported {
- typeExports(f.Type)
+ if keepField {
+ if filter == exportFilter {
+ filterType(f.Type, filter)
+ }
list[j] = f
j++
}
@@ -76,185 +108,75 @@ func fieldListExports(fields *FieldList) (removedFields bool) {
return
}
-func paramListExports(fields *FieldList) {
+func filterParamList(fields *FieldList, filter Filter) bool {
if fields == nil {
- return
+ return false
}
+ var b bool
for _, f := range fields.List {
- typeExports(f.Type)
+ if filterType(f.Type, filter) {
+ b = true
+ }
}
+ return b
}
-func typeExports(typ Expr) {
+func filterType(typ Expr, f Filter) bool {
switch t := typ.(type) {
+ case *Ident:
+ return f(t.Name)
+ case *ParenExpr:
+ return filterType(t.X, f)
case *ArrayType:
- typeExports(t.Elt)
+ return filterType(t.Elt, f)
case *StructType:
- if fieldListExports(t.Fields) {
+ if filterFieldList(t.Fields, f) {
t.Incomplete = true
}
+ return len(t.Fields.List) > 0
case *FuncType:
- paramListExports(t.Params)
- paramListExports(t.Results)
+ b1 := filterParamList(t.Params, f)
+ b2 := filterParamList(t.Results, f)
+ return b1 || b2
case *InterfaceType:
- if fieldListExports(t.Methods) {
+ if filterFieldList(t.Methods, f) {
t.Incomplete = true
}
+ return len(t.Methods.List) > 0
case *MapType:
- typeExports(t.Key)
- typeExports(t.Value)
+ b1 := filterType(t.Key, f)
+ b2 := filterType(t.Value, f)
+ return b1 || b2
case *ChanType:
- typeExports(t.Value)
- }
-}
-
-func specExports(spec Spec) bool {
- switch s := spec.(type) {
- case *ValueSpec:
- s.Names = identListExports(s.Names)
- if len(s.Names) > 0 {
- typeExports(s.Type)
- return true
- }
- case *TypeSpec:
- if s.Name.IsExported() {
- typeExports(s.Type)
- return true
- }
+ return filterType(t.Value, f)
}
return false
}
-func specListExports(list []Spec) []Spec {
- j := 0
- for _, s := range list {
- if specExports(s) {
- list[j] = s
- j++
- }
- }
- return list[0:j]
-}
-
-func declExports(decl Decl) bool {
- switch d := decl.(type) {
- case *GenDecl:
- d.Specs = specListExports(d.Specs)
- return len(d.Specs) > 0
- case *FuncDecl:
- d.Body = nil // strip body
- return d.Name.IsExported()
- }
- return false
-}
-
-// FileExports trims the AST for a Go source file in place such that only
-// exported nodes remain: all top-level identifiers which are not exported
-// and their associated information (such as type, initial value, or function
-// body) are removed. Non-exported fields and methods of exported types are
-// stripped, and the function bodies of exported functions are set to nil.
-// The File.comments list is not changed.
-//
-// FileExports returns true if there is an exported declaration; it returns
-// false otherwise.
-//
-func FileExports(src *File) bool {
- j := 0
- for _, d := range src.Decls {
- if declExports(d) {
- src.Decls[j] = d
- j++
- }
- }
- src.Decls = src.Decls[0:j]
- return j > 0
-}
-
-// PackageExports trims the AST for a Go package in place such that only
-// exported nodes remain. The pkg.Files list is not changed, so that file
-// names and top-level package comments don't get lost.
-//
-// PackageExports returns true if there is an exported declaration; it
-// returns false otherwise.
-//
-func PackageExports(pkg *Package) bool {
- hasExports := false
- for _, f := range pkg.Files {
- if FileExports(f) {
- hasExports = true
- }
- }
- return hasExports
-}
-
-// ----------------------------------------------------------------------------
-// General filtering
-
-type Filter func(string) bool
-
-func filterIdentList(list []*Ident, f Filter) []*Ident {
- j := 0
- for _, x := range list {
- if f(x.Name) {
- list[j] = x
- j++
- }
- }
- return list[0:j]
-}
-
-func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
- if fields == nil {
- return false
- }
- list := fields.List
- j := 0
- for _, f := range list {
- keepField := false
- if len(f.Names) == 0 {
- // anonymous field
- name := fieldName(f.Type)
- keepField = name != nil && filter(name.Name)
- } else {
- n := len(f.Names)
- f.Names = filterIdentList(f.Names, filter)
- if len(f.Names) < n {
- removedFields = true
- }
- keepField = len(f.Names) > 0
- }
- if keepField {
- list[j] = f
- j++
- }
- }
- if j < len(list) {
- removedFields = true
- }
- fields.List = list[0:j]
- return
-}
-
func filterSpec(spec Spec, f Filter) bool {
switch s := spec.(type) {
case *ValueSpec:
s.Names = filterIdentList(s.Names, f)
- return len(s.Names) > 0
+ if len(s.Names) > 0 {
+ if f == exportFilter {
+ filterType(s.Type, f)
+ }
+ return true
+ }
case *TypeSpec:
if f(s.Name.Name) {
+ if f == exportFilter {
+ filterType(s.Type, f)
+ }
return true
}
- switch t := s.Type.(type) {
- case *StructType:
- if filterFieldList(t.Fields, f) {
- t.Incomplete = true
- }
- return len(t.Fields.List) > 0
- case *InterfaceType:
- if filterFieldList(t.Methods, f) {
- t.Incomplete = true
- }
- return len(t.Methods.List) > 0
+ if f != exportFilter {
+ // For general filtering (not just exports),
+ // filter type even if name is not filtered
+ // out.
+ // If the type contains filtered elements,
+ // keep the declaration.
+ return filterType(s.Type, f)
}
}
return false
@@ -284,6 +206,9 @@ func FilterDecl(decl Decl, f Filter) bool {
d.Specs = filterSpecList(d.Specs, f)
return len(d.Specs) > 0
case *FuncDecl:
+ if f == exportFilter {
+ d.Body = nil // strip body
+ }
return f(d.Name.Name)
}
return false
@@ -293,8 +218,8 @@ func FilterDecl(decl Decl, f Filter) bool {
// names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
-// the declaration is removed from the AST.
-// The File.comments list is not changed.
+// the declaration is removed from the AST. The File.Comments list
+// is not changed.
//
// FilterFile returns true if there are any top-level declarations
// left after filtering; it returns false otherwise.
@@ -311,13 +236,13 @@ func FilterFile(src *File, f Filter) bool {
return j > 0
}
-// FilterPackage trims the AST for a Go package in place by removing all
-// names from top-level declarations (including struct field and
+// FilterPackage trims the AST for a Go package in place by removing
+// all names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
-// the declaration is removed from the AST.
-// The pkg.Files list is not changed, so that file names and top-level
-// package comments don't get lost.
+// the declaration is removed from the AST. The pkg.Files list is not
+// changed, so that file names and top-level package comments don't get
+// lost.
//
// FilterPackage returns true if there are any top-level declarations
// left after filtering; it returns false otherwise.
@@ -344,6 +269,8 @@ const (
// If set, comments that are not associated with a specific
// AST node (as Doc or Comment) are excluded.
FilterUnassociatedComments
+ // If set, duplicate import declarations are excluded.
+ FilterImportDuplicates
)
// separator is an empty //-style comment that is interspersed between
@@ -459,6 +386,31 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
}
}
+ // Collect import specs from all package files.
+ var imports []*ImportSpec
+ if mode&FilterImportDuplicates != 0 {
+ seen := make(map[string]bool)
+ for _, f := range pkg.Files {
+ for _, imp := range f.Imports {
+ if path := imp.Path.Value; !seen[path] {
+ // TODO: consider handling cases where:
+ // - 2 imports exist with the same import path but
+ // have different local names (one should probably
+ // keep both of them)
+ // - 2 imports exist but only one has a comment
+ // - 2 imports exist and they both have (possibly
+ // different) comments
+ imports = append(imports, imp)
+ seen[path] = true
+ }
+ }
+ }
+ } else {
+ for _, f := range pkg.Files {
+ imports = append(imports, f.Imports...)
+ }
+ }
+
// Collect comments from all package files.
var comments []*CommentGroup
if mode&FilterUnassociatedComments == 0 {
@@ -469,7 +421,6 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
}
}
- // TODO(gri) need to compute pkgScope and unresolved identifiers!
- // TODO(gri) need to compute imports!
- return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}
+ // TODO(gri) need to compute unresolved identifiers!
+ return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
}
diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go
index e59d87672..592ebbd9e 100644
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -7,44 +7,85 @@ package build
import (
"exec"
"path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
"testing"
)
-var buildPkgs = []string{
- "go/build/pkgtest",
- "go/build/cmdtest",
- "go/build/cgotest",
+func sortstr(x []string) []string {
+ sort.Strings(x)
+ return x
+}
+
+var buildPkgs = []struct {
+ dir string
+ info *DirInfo
+}{
+ {
+ "go/build/pkgtest",
+ &DirInfo{
+ GoFiles: []string{"pkgtest.go"},
+ SFiles: []string{"sqrt_" + runtime.GOARCH + ".s"},
+ PkgName: "pkgtest",
+ Imports: []string{"os"},
+ TestImports: []string{"fmt", "pkgtest"},
+ TestGoFiles: sortstr([]string{"sqrt_test.go", "sqrt_" + runtime.GOARCH + "_test.go"}),
+ XTestGoFiles: []string{"xsqrt_test.go"},
+ },
+ },
+ {
+ "go/build/cmdtest",
+ &DirInfo{
+ GoFiles: []string{"main.go"},
+ PkgName: "main",
+ Imports: []string{"go/build/pkgtest"},
+ },
+ },
+ {
+ "go/build/cgotest",
+ &DirInfo{
+ CgoFiles: []string{"cgotest.go"},
+ CFiles: []string{"cgotest.c"},
+ Imports: []string{"C", "unsafe"},
+ PkgName: "cgotest",
+ },
+ },
}
const cmdtestOutput = "3"
func TestBuild(t *testing.T) {
- for _, pkg := range buildPkgs {
+ for _, tt := range buildPkgs {
tree := Path[0] // Goroot
- dir := filepath.Join(tree.SrcDir(), pkg)
+ dir := filepath.Join(tree.SrcDir(), tt.dir)
info, err := ScanDir(dir, true)
if err != nil {
- t.Error("ScanDir:", err)
+ t.Errorf("ScanDir(%#q): %v", tt.dir, err)
+ continue
+ }
+ if !reflect.DeepEqual(info, tt.info) {
+ t.Errorf("ScanDir(%#q) = %#v, want %#v\n", tt.dir, info, tt.info)
continue
}
- s, err := Build(tree, pkg, info)
+ s, err := Build(tree, tt.dir, info)
if err != nil {
- t.Error("Build:", err)
+ t.Errorf("Build(%#q): %v", tt.dir, err)
continue
}
if err := s.Run(); err != nil {
- t.Error("Run:", err)
+ t.Errorf("Run(%#q): %v", tt.dir, err)
continue
}
- if pkg == "go/build/cmdtest" {
+ if tt.dir == "go/build/cmdtest" {
bin := s.Output[0]
b, err := exec.Command(bin).CombinedOutput()
if err != nil {
- t.Errorf("exec: %s: %v", bin, err)
+ t.Errorf("exec %s: %v", bin, err)
continue
}
if string(b) != cmdtestOutput {
@@ -52,6 +93,7 @@ func TestBuild(t *testing.T) {
}
}
+ // Deferred because cmdtest depends on pkgtest.
defer func(s *Script) {
if err := s.Nuke(); err != nil {
t.Errorf("nuking: %v", err)
diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go
index e0000b534..fa4d9e913 100644
--- a/src/pkg/go/build/dir.go
+++ b/src/pkg/go/build/dir.go
@@ -7,27 +7,60 @@ package build
import (
"go/parser"
"go/token"
+ "io/ioutil"
"log"
"os"
"path/filepath"
+ "sort"
"strconv"
"strings"
"runtime"
)
+// A Context specifies the supporting context for a build.
+type Context struct {
+ GOARCH string // target architecture
+ GOOS string // target operating system
+ // TODO(rsc,adg): GOPATH
+}
+
+// The DefaultContext is the default Context for builds.
+// It uses the GOARCH and GOOS environment variables
+// if set, or else the compiled code's GOARCH and GOOS.
+var DefaultContext = Context{
+ envOr("GOARCH", runtime.GOARCH),
+ envOr("GOOS", runtime.GOOS),
+}
+
+func envOr(name, def string) string {
+ s := os.Getenv(name)
+ if s == "" {
+ return def
+ }
+ return s
+}
+
type DirInfo struct {
- GoFiles []string // .go files in dir (excluding CgoFiles)
- CgoFiles []string // .go files that import "C"
- CFiles []string // .c files in dir
- SFiles []string // .s files in dir
- Imports []string // All packages imported by goFiles
- PkgName string // Name of package in dir
+ GoFiles []string // .go files in dir (excluding CgoFiles)
+ CgoFiles []string // .go files that import "C"
+ CFiles []string // .c files in dir
+ SFiles []string // .s files in dir
+ Imports []string // All packages imported by GoFiles
+ TestImports []string // All packages imported by (X)TestGoFiles
+ PkgName string // Name of package in dir
+ TestGoFiles []string // _test.go files in package
+ XTestGoFiles []string // _test.go files outside package
}
func (d *DirInfo) IsCommand() bool {
return d.PkgName == "main"
}
+// ScanDir calls DefaultContext.ScanDir.
+func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
+ return DefaultContext.ScanDir(dir, allowMain)
+}
+
// ScanDir returns a structure with details about the Go content found
// in the given directory. The file lists exclude:
//
@@ -36,36 +69,29 @@ func (d *DirInfo) IsCommand() bool {
// - files ending in _test.go
// - files starting with _ or .
//
-// Only files that satisfy the goodOSArch function are included.
-func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
- f, err := os.Open(dir)
- if err != nil {
- return nil, err
- }
- dirs, err := f.Readdir(-1)
- f.Close()
+func (ctxt *Context) ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
+ dirs, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
var di DirInfo
imported := make(map[string]bool)
+ testImported := make(map[string]bool)
fset := token.NewFileSet()
- for i := range dirs {
- d := &dirs[i]
+ for _, d := range dirs {
if strings.HasPrefix(d.Name, "_") ||
strings.HasPrefix(d.Name, ".") {
continue
}
- if !goodOSArch(d.Name) {
+ if !ctxt.goodOSArch(d.Name) {
continue
}
+ isTest := false
switch filepath.Ext(d.Name) {
case ".go":
- if strings.HasSuffix(d.Name, "_test.go") {
- continue
- }
+ isTest = strings.HasSuffix(d.Name, "_test.go")
case ".c":
di.CFiles = append(di.CFiles, d.Name)
continue
@@ -81,21 +107,24 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
if err != nil {
return nil, err
}
- s := string(pf.Name.Name)
- if s == "main" && !allowMain {
+ pkg := string(pf.Name.Name)
+ if pkg == "main" && !allowMain {
continue
}
- if s == "documentation" {
+ if pkg == "documentation" {
continue
}
+ if isTest && strings.HasSuffix(pkg, "_test") {
+ pkg = pkg[:len(pkg)-len("_test")]
+ }
if di.PkgName == "" {
- di.PkgName = s
- } else if di.PkgName != s {
+ di.PkgName = pkg
+ } else if di.PkgName != pkg {
// Only if all files in the directory are in package main
// do we return PkgName=="main".
// A mix of main and another package reverts
// to the original (allowMain=false) behaviour.
- if s == "main" || di.PkgName == "main" {
+ if pkg == "main" || di.PkgName == "main" {
return ScanDir(dir, false)
}
return nil, os.NewError("multiple package names in " + dir)
@@ -107,13 +136,26 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
if err != nil {
log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
}
- imported[path] = true
+ if isTest {
+ testImported[path] = true
+ } else {
+ imported[path] = true
+ }
if path == "C" {
+ if isTest {
+ return nil, os.NewError("use of cgo in test " + filename)
+ }
isCgo = true
}
}
if isCgo {
di.CgoFiles = append(di.CgoFiles, d.Name)
+ } else if isTest {
+ if pkg == string(pf.Name.Name) {
+ di.TestGoFiles = append(di.TestGoFiles, d.Name)
+ } else {
+ di.XTestGoFiles = append(di.XTestGoFiles, d.Name)
+ }
} else {
di.GoFiles = append(di.GoFiles, d.Name)
}
@@ -124,49 +166,58 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) {
di.Imports[i] = p
i++
}
+ di.TestImports = make([]string, len(testImported))
+ i = 0
+ for p := range testImported {
+ di.TestImports[i] = p
+ i++
+ }
+ // File name lists are sorted because ioutil.ReadDir sorts.
+ sort.Strings(di.Imports)
+ sort.Strings(di.TestImports)
return &di, nil
}
-// goodOSArch returns false if the filename contains a $GOOS or $GOARCH
+// goodOSArch returns false if the name contains a $GOOS or $GOARCH
// suffix which does not match the current system.
-// The recognized filename formats are:
+// The recognized name formats are:
//
// name_$(GOOS).*
// name_$(GOARCH).*
// name_$(GOOS)_$(GOARCH).*
+// name_$(GOOS)_test.*
+// name_$(GOARCH)_test.*
+// name_$(GOOS)_$(GOARCH)_test.*
//
-func goodOSArch(filename string) bool {
- if dot := strings.Index(filename, "."); dot != -1 {
- filename = filename[:dot]
+func (ctxt *Context) goodOSArch(name string) bool {
+ if dot := strings.Index(name, "."); dot != -1 {
+ name = name[:dot]
+ }
+ l := strings.Split(name, "_")
+ if n := len(l); n > 0 && l[n-1] == "test" {
+ l = l[:n-1]
}
- l := strings.Split(filename, "_")
n := len(l)
- if n == 0 {
- return true
+ if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+ return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
}
- if good, known := goodOS[l[n-1]]; known {
- return good
+ if n >= 1 && knownOS[l[n-1]] {
+ return l[n-1] == ctxt.GOOS
}
- if good, known := goodArch[l[n-1]]; known {
- if !good || n < 2 {
- return false
- }
- good, known = goodOS[l[n-2]]
- return good || !known
+ if n >= 1 && knownArch[l[n-1]] {
+ return l[n-1] == ctxt.GOARCH
}
return true
}
-var goodOS = make(map[string]bool)
-var goodArch = make(map[string]bool)
+var knownOS = make(map[string]bool)
+var knownArch = make(map[string]bool)
func init() {
- goodOS = make(map[string]bool)
- goodArch = make(map[string]bool)
for _, v := range strings.Fields(goosList) {
- goodOS[v] = v == runtime.GOOS
+ knownOS[v] = true
}
for _, v := range strings.Fields(goarchList) {
- goodArch[v] = v == runtime.GOARCH
+ knownArch[v] = true
}
}
diff --git a/src/pkg/go/build/pkgtest/pkgtest.go b/src/pkg/go/build/pkgtest/pkgtest.go
index 9322f5ebd..03ebb9893 100644
--- a/src/pkg/go/build/pkgtest/pkgtest.go
+++ b/src/pkg/go/build/pkgtest/pkgtest.go
@@ -4,6 +4,10 @@
package pkgtest
-func Foo() {}
+import "os"
+
+func Foo() os.Error {
+ return nil
+}
func Sqrt(x float64) float64
diff --git a/src/pkg/go/build/pkgtest/sqrt_386_test.go b/src/pkg/go/build/pkgtest/sqrt_386_test.go
new file mode 100644
index 000000000..26b483fa0
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_386_test.go
@@ -0,0 +1 @@
+package pkgtest
diff --git a/src/pkg/go/build/pkgtest/sqrt_amd64_test.go b/src/pkg/go/build/pkgtest/sqrt_amd64_test.go
new file mode 100644
index 000000000..26b483fa0
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_amd64_test.go
@@ -0,0 +1 @@
+package pkgtest
diff --git a/src/pkg/go/build/pkgtest/sqrt_arm_test.go b/src/pkg/go/build/pkgtest/sqrt_arm_test.go
new file mode 100644
index 000000000..26b483fa0
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_arm_test.go
@@ -0,0 +1 @@
+package pkgtest
diff --git a/src/pkg/go/build/pkgtest/sqrt_test.go b/src/pkg/go/build/pkgtest/sqrt_test.go
new file mode 100644
index 000000000..95fb62552
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/sqrt_test.go
@@ -0,0 +1,5 @@
+package pkgtest
+
+import "fmt"
+
+var _ = fmt.Printf
diff --git a/src/pkg/go/build/pkgtest/xsqrt_test.go b/src/pkg/go/build/pkgtest/xsqrt_test.go
new file mode 100644
index 000000000..77e903d96
--- /dev/null
+++ b/src/pkg/go/build/pkgtest/xsqrt_test.go
@@ -0,0 +1,5 @@
+package pkgtest_test
+
+import "pkgtest"
+
+var _ = pkgtest.Foo
diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go
index eb0e5dcb6..2e8b4c865 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 goodOSArch(test.name) != test.result {
+ if DefaultContext.goodOSArch(test.name) != test.result {
t.Fatalf("goodOSArch(%q) != %v", test.name, test.result)
}
}
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 9c14d1667..be82b2f80 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -587,7 +587,6 @@ func (p *parser) parseStructType() *ast.StructType {
}
rbrace := p.expect(token.RBRACE)
- // TODO(gri): store struct scope in AST
return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
}
@@ -611,9 +610,6 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
p.error(pos, "'...' parameter is missing type")
typ = &ast.BadExpr{pos, p.pos}
}
- if p.tok != token.RPAREN {
- p.error(pos, "can use '...' with last parameter type only")
- }
return &ast.Ellipsis{pos, typ}
}
return p.tryIdentOrType(false)
@@ -636,21 +632,21 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
}
// a list of identifiers looks like a list of type names
- for {
- // parseVarType accepts any type (including parenthesized ones)
- // even though the syntax does not permit them here: we
- // accept them all for more robust parsing and complain
- // afterwards
- list = append(list, p.parseVarType(isParam))
+ //
+ // parse/tryVarType accepts any type (including parenthesized
+ // ones) even though the syntax does not permit them here: we
+ // accept them all for more robust parsing and complain later
+ for typ := p.parseVarType(isParam); typ != nil; {
+ list = append(list, typ)
if p.tok != token.COMMA {
break
}
p.next()
+ typ = p.tryVarType(isParam) // maybe nil as in: func f(int,) {}
}
// if we had a list of identifiers, it must be followed by a type
- typ = p.tryVarType(isParam)
- if typ != nil {
+ if typ = p.tryVarType(isParam); typ != nil {
p.resolve(typ)
}
@@ -800,7 +796,6 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
}
rbrace := p.expect(token.RBRACE)
- // TODO(gri): store interface scope in AST
return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
}
@@ -1436,14 +1431,14 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
case token.ARROW:
// send statement
arrow := p.pos
- p.next() // consume "<-"
+ p.next()
y := p.parseRhs()
return &ast.SendStmt{x[0], arrow, y}, false
case token.INC, token.DEC:
// increment or decrement
s := &ast.IncDecStmt{x[0], p.pos, p.tok}
- p.next() // consume "++" or "--"
+ p.next()
return s, false
}
@@ -1591,7 +1586,7 @@ func (p *parser) parseTypeList() (list []ast.Expr) {
return
}
-func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
+func (p *parser) parseCaseClause(typeSwitch bool) *ast.CaseClause {
if p.trace {
defer un(trace(p, "CaseClause"))
}
@@ -1600,10 +1595,10 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
var list []ast.Expr
if p.tok == token.CASE {
p.next()
- if exprSwitch {
- list = p.parseRhsList()
- } else {
+ if typeSwitch {
list = p.parseTypeList()
+ } else {
+ list = p.parseRhsList()
}
} else {
p.expect(token.DEFAULT)
@@ -1617,15 +1612,19 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
return &ast.CaseClause{pos, list, colon, body}
}
-func isExprSwitch(s ast.Stmt) bool {
- if s == nil {
- return true
- }
- if e, ok := s.(*ast.ExprStmt); ok {
- if a, ok := e.X.(*ast.TypeAssertExpr); ok {
- return a.Type != nil // regular type assertion
- }
- return true
+func isTypeSwitchAssert(x ast.Expr) bool {
+ a, ok := x.(*ast.TypeAssertExpr)
+ return ok && a.Type == nil
+}
+
+func isTypeSwitchGuard(s ast.Stmt) bool {
+ switch t := s.(type) {
+ case *ast.ExprStmt:
+ // x.(nil)
+ return isTypeSwitchAssert(t.X)
+ case *ast.AssignStmt:
+ // v := x.(nil)
+ return len(t.Lhs) == 1 && t.Tok == token.DEFINE && len(t.Rhs) == 1 && isTypeSwitchAssert(t.Rhs[0])
}
return false
}
@@ -1651,28 +1650,41 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
s1 = s2
s2 = nil
if p.tok != token.LBRACE {
+ // A TypeSwitchGuard may declare a variable in addition
+ // to the variable declared in the initial SimpleStmt.
+ // Introduce extra scope to avoid redeclaration errors:
+ //
+ // switch t := 0; t := x.(T) { ... }
+ //
+ // (this code is not valid Go because the first t will
+ // cannot be accessed and thus is never used, the extra
+ // scope is needed for the correct error message).
+ //
+ // If we don't have a type switch, s2 must be an expression.
+ // Having the extra nested but empty scope won't affect it.
+ p.openScope()
+ defer p.closeScope()
s2, _ = p.parseSimpleStmt(basic)
}
}
p.exprLev = prevLev
}
- exprSwitch := isExprSwitch(s2)
+ typeSwitch := isTypeSwitchGuard(s2)
lbrace := p.expect(token.LBRACE)
var list []ast.Stmt
for p.tok == token.CASE || p.tok == token.DEFAULT {
- list = append(list, p.parseCaseClause(exprSwitch))
+ list = append(list, p.parseCaseClause(typeSwitch))
}
rbrace := p.expect(token.RBRACE)
p.expectSemi()
body := &ast.BlockStmt{lbrace, list, rbrace}
- if exprSwitch {
- return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
+ if typeSwitch {
+ return &ast.TypeSwitchStmt{pos, s1, s2, body}
}
- // type switch
- // TODO(gri): do all the checks!
- return &ast.TypeSwitchStmt{pos, s1, s2, body}
+
+ return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
}
func (p *parser) parseCommClause() *ast.CommClause {
@@ -2001,14 +2013,12 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
defer un(trace(p, "Receiver"))
}
- pos := p.pos
par := p.parseParameters(scope, false)
// must have exactly one receiver
if par.NumFields() != 1 {
- p.errorExpected(pos, "exactly one receiver")
- // TODO determine a better range for BadExpr below
- par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{pos, pos}}}
+ p.errorExpected(par.Opening, "exactly one receiver")
+ par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{par.Opening, par.Closing + 1}}}
return par
}
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 39a78e515..9705dcff2 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -26,6 +26,9 @@ var illegalInputs = []interface{}{
`package p; func f() { for _ = range x ; ; {} };`,
`package p; func f() { for ; ; _ = range x {} };`,
`package p; func f() { for ; _ = range x ; {} };`,
+ `package p; func f() { switch t = t.(type) {} };`,
+ `package p; func f() { switch t, t = t.(type) {} };`,
+ `package p; func f() { switch t = t.(type), t {} };`,
`package p; var a = [1]int; /* illegal expression */`,
`package p; var a = [...]int; /* illegal expression */`,
`package p; var a = struct{} /* illegal expression */`,
@@ -61,6 +64,9 @@ var validPrograms = []interface{}{
`package p; func f(...T);`,
`package p; func f(float, ...int);`,
`package p; func f(x int, a ...int) { f(0, a...); f(1, a...,) };`,
+ `package p; func f(int,) {};`,
+ `package p; func f(...int,) {};`,
+ `package p; func f(x ...int,) {};`,
`package p; type T []int; var a []bool; func f() { if a[T{42}[0]] {} };`,
`package p; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`,
`package p; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`,
@@ -74,7 +80,7 @@ var validPrograms = []interface{}{
func TestParseValidPrograms(t *testing.T) {
for _, src := range validPrograms {
- _, err := ParseFile(fset, "", src, 0)
+ _, err := ParseFile(fset, "", src, SpuriousErrors)
if err != nil {
t.Errorf("ParseFile(%q): %v", src, err)
}
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 9cd975ec1..364530634 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -269,6 +269,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
p.print(fields.Opening, token.LPAREN)
if len(fields.List) > 0 {
+ ws := indent
var prevLine, line int
for i, par := range fields.List {
if i > 0 {
@@ -278,19 +279,30 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
} else {
line = p.fset.Position(par.Type.Pos()).Line
}
- if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ignore, true) {
+ if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ws, true) {
+ ws = ignore
*multiLine = true
} else {
p.print(blank)
}
}
if len(par.Names) > 0 {
- p.identList(par.Names, false, multiLine)
+ // Very subtle: If we indented before (ws == ignore), identList
+ // won't indent again. If we didn't (ws == indent), identList will
+ // indent if the identList spans multiple lines, and it will outdent
+ // again at the end (and still ws == indent). Thus, a subsequent indent
+ // by a linebreak call after a type, or in the next multi-line identList
+ // will do the right thing.
+ p.identList(par.Names, ws == indent, multiLine)
p.print(blank)
}
p.expr(par.Type, multiLine)
prevLine = p.fset.Position(par.Type.Pos()).Line
}
+ if ws == ignore {
+ // unindent if we indented
+ p.print(unindent)
+ }
}
p.print(fields.Closing, token.RPAREN)
}
diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden
index 970533e8c..bfa2568c2 100644
--- a/src/pkg/go/printer/testdata/declarations.golden
+++ b/src/pkg/go/printer/testdata/declarations.golden
@@ -692,56 +692,119 @@ func _(x ...chan int)
// these parameter lists must remain multi-line since they are multi-line in the source
func _(bool,
-int) {
+ int) {
}
func _(x bool,
-y int) {
+ y int) {
}
func _(x,
-y bool) {
+ y bool) {
}
func _(bool, // comment
-int) {
+ int) {
}
func _(x bool, // comment
-y int) {
+ y int) {
}
func _(x, // comment
-y bool) {
+ y bool) {
}
func _(bool, // comment
-// comment
-int) {
+ // comment
+ int) {
}
func _(x bool, // comment
-// comment
-y int) {
+ // comment
+ y int) {
}
func _(x, // comment
-// comment
-y bool) {
+ // comment
+ y bool) {
}
func _(bool,
-// comment
-int) {
+ // comment
+ int) {
}
func _(x bool,
-// comment
-y int) {
+ // comment
+ y int) {
}
func _(x,
-// comment
-y bool) {
+ // comment
+ y bool) {
}
func _(x, // comment
-y, // comment
-z bool) {
+ y, // comment
+ z bool) {
}
func _(x, // comment
-y, // comment
-z bool) {
+ y, // comment
+ z bool) {
}
func _(x int, // comment
-y float, // comment
-z bool) {
+ y float, // comment
+ z bool) {
+}
+
+// properly indent multi-line signatures
+func ManageStatus(in <-chan *Status, req <-chan Request,
+ stat chan<- *TargetInfo,
+ TargetHistorySize int) {
+}
+
+func MultiLineSignature0(a, b, c int) {
+}
+
+func MultiLineSignature1(a, b, c int,
+ u, v, w float) {
+}
+
+func MultiLineSignature2(a, b,
+ c int) {
+}
+
+func MultiLineSignature3(a, b,
+ c int, u, v,
+ w float,
+ x ...int) {
+}
+
+func MultiLineSignature4(a, b, c int,
+ u, v,
+ w float,
+ x ...int) {
+}
+
+func MultiLineSignature5(a, b, c int,
+ u, v, w float,
+ p, q,
+ r string,
+ x ...int) {
+}
+
+// make sure it also works for methods in interfaces
+type _ interface {
+ MultiLineSignature0(a, b, c int)
+
+ MultiLineSignature1(a, b, c int,
+ u, v, w float)
+
+ MultiLineSignature2(a, b,
+ c int)
+
+ MultiLineSignature3(a, b,
+ c int, u, v,
+ w float,
+ x ...int)
+
+ MultiLineSignature4(a, b, c int,
+ u, v,
+ w float,
+ x ...int)
+
+ MultiLineSignature5(a, b, c int,
+ u, v, w float,
+ p, q,
+ r string,
+ x ...int)
}
diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input
index c6134096b..1d69c57b5 100644
--- a/src/pkg/go/printer/testdata/declarations.input
+++ b/src/pkg/go/printer/testdata/declarations.input
@@ -755,3 +755,79 @@ func _(x int, // comment
y float, // comment
z bool) {
}
+
+
+// properly indent multi-line signatures
+func ManageStatus(in <-chan *Status, req <-chan Request,
+stat chan<- *TargetInfo,
+TargetHistorySize int) {
+}
+
+func MultiLineSignature0(
+a, b, c int,
+) {}
+
+func MultiLineSignature1(
+a, b, c int,
+u, v, w float,
+) {}
+
+func MultiLineSignature2(
+a, b,
+c int,
+) {}
+
+func MultiLineSignature3(
+a, b,
+c int, u, v,
+w float,
+ x ...int) {}
+
+func MultiLineSignature4(
+a, b, c int,
+u, v,
+w float,
+ x ...int) {}
+
+func MultiLineSignature5(
+a, b, c int,
+u, v, w float,
+p, q,
+r string,
+ x ...int) {}
+
+// make sure it also works for methods in interfaces
+type _ interface {
+MultiLineSignature0(
+a, b, c int,
+)
+
+MultiLineSignature1(
+a, b, c int,
+u, v, w float,
+)
+
+MultiLineSignature2(
+a, b,
+c int,
+)
+
+MultiLineSignature3(
+a, b,
+c int, u, v,
+w float,
+ x ...int)
+
+MultiLineSignature4(
+a, b, c int,
+u, v,
+w float,
+ x ...int)
+
+MultiLineSignature5(
+a, b, c int,
+u, v, w float,
+p, q,
+r string,
+ x ...int)
+}