Commit 4da14c8a authored by k8s-merge-robot's avatar k8s-merge-robot Committed by GitHub

Merge pull request #28204 from thockin/cleanup-third_party

Automatic merge from submit-queue Cleanup third party (pt 2) Move forked-and-hacked golang code to the forked/ directory. Remove ast/build/parse code that is now in stdlib. Remove unused shell2junit
parents 8601530b 8c42c083
......@@ -18,14 +18,13 @@ package protobuf
import (
"fmt"
"go/ast"
"log"
"os"
"path/filepath"
"reflect"
"strings"
"k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/cmd/libs/go2idl/generator"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
......
......@@ -20,17 +20,17 @@ import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/printer"
"go/token"
"io/ioutil"
"os"
"reflect"
"strings"
"k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/third_party/golang/go/parser"
"k8s.io/kubernetes/third_party/golang/go/printer"
"k8s.io/kubernetes/third_party/golang/go/token"
customreflect "k8s.io/kubernetes/third_party/golang/reflect"
customreflect "k8s.io/kubernetes/third_party/forked/golang/reflect"
)
func rewriteFile(name string, header []byte, rewriteFn func(*token.FileSet, *ast.File) error) error {
......
......@@ -18,20 +18,19 @@ package parser
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
tc "go/types"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
"k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/third_party/golang/go/build"
"k8s.io/kubernetes/third_party/golang/go/parser"
"k8s.io/kubernetes/third_party/golang/go/token"
tc "k8s.io/kubernetes/third_party/golang/go/types"
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
// Builder lets you add all the go files in all the packages that you care
......
......@@ -17,7 +17,7 @@ limitations under the License.
package conversion
import (
"k8s.io/kubernetes/third_party/forked/reflect"
"k8s.io/kubernetes/third_party/forked/golang/reflect"
)
// The code for this type must be located in third_party, since it forks from
......
......@@ -27,7 +27,7 @@ import (
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
hashutil "k8s.io/kubernetes/pkg/util/hash"
"k8s.io/kubernetes/third_party/golang/expansion"
"k8s.io/kubernetes/third_party/forked/golang/expansion"
"github.com/golang/glog"
)
......
......@@ -97,7 +97,7 @@ import (
"k8s.io/kubernetes/pkg/watch"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
"k8s.io/kubernetes/third_party/golang/expansion"
"k8s.io/kubernetes/third_party/forked/golang/expansion"
)
const (
......
......@@ -32,7 +32,7 @@ import (
apierrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util/httpstream"
"k8s.io/kubernetes/third_party/golang/netutil"
"k8s.io/kubernetes/third_party/forked/golang/netutil"
)
// SpdyRoundTripper knows how to upgrade an HTTP request to one that supports
......
......@@ -23,7 +23,7 @@ import (
"reflect"
"strings"
"k8s.io/kubernetes/third_party/golang/template"
"k8s.io/kubernetes/third_party/forked/golang/template"
)
type JSONPath struct {
......
......@@ -26,7 +26,7 @@ import (
"github.com/golang/glog"
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/kubernetes/third_party/golang/netutil"
"k8s.io/kubernetes/third_party/forked/golang/netutil"
)
func DialURL(url *url.URL, transport http.RoundTripper) (net.Conn, error) {
......
......@@ -22,7 +22,7 @@ import (
"sort"
"k8s.io/kubernetes/pkg/util/json"
forkedjson "k8s.io/kubernetes/third_party/forked/json"
forkedjson "k8s.io/kubernetes/third_party/forked/golang/json"
"github.com/davecgh/go-spew/spew"
"github.com/ghodss/yaml"
......
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Vendored copy of go1.5.1's standard library's go/... packages.
Q: Why did you do this awful vendoring?
A: We need to build under go 1.3 and go 1.4 (soon to be go 1.4 and go 1.5.1). A
version of the go/types package existed for go 1.4, but it does not have the
same interface as the go 1.5 package, and @lavalamp had much better luck with
the 1.5.1 package anyway.
We will get rid of this as soon as there's a more standard way to do this, or
when we roll over to supporting go 1.5 and go 1.6.
Note that the packages here were not very happy about being transplated like
this and if you do a diff you will see the changes made to get everything to
compile.
// 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 ast
import (
"testing"
)
var comments = []struct {
list []string
text string
}{
{[]string{"//"}, ""},
{[]string{"// "}, ""},
{[]string{"//", "//", "// "}, ""},
{[]string{"// foo "}, "foo\n"},
{[]string{"//", "//", "// foo"}, "foo\n"},
{[]string{"// foo bar "}, "foo bar\n"},
{[]string{"// foo", "// bar"}, "foo\nbar\n"},
{[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
{[]string{"// foo", "/* bar */"}, "foo\n bar\n"},
{[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"},
{[]string{"/**/"}, ""},
{[]string{"/* */"}, ""},
{[]string{"/**/", "/**/", "/* */"}, ""},
{[]string{"/* Foo */"}, " Foo\n"},
{[]string{"/* Foo Bar */"}, " Foo Bar\n"},
{[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"},
{[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
{[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"},
{[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"},
{[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"},
}
func TestCommentText(t *testing.T) {
for i, c := range comments {
list := make([]*Comment, len(c.list))
for i, s := range c.list {
list[i] = &Comment{Text: s}
}
text := (&CommentGroup{list}).Text()
if text != c.text {
t.Errorf("case %d: got %q; expected %q", i, text, c.text)
}
}
}
// 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 ast
import (
"bytes"
"fmt"
"k8s.io/kubernetes/third_party/golang/go/token"
"sort"
)
type byPos []*CommentGroup
func (a byPos) Len() int { return len(a) }
func (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// sortComments sorts the list of comment groups in source order.
//
func sortComments(list []*CommentGroup) {
// TODO(gri): Does it make sense to check for sorted-ness
// first (because we know that sorted-ness is
// very likely)?
if orderedList := byPos(list); !sort.IsSorted(orderedList) {
sort.Sort(orderedList)
}
}
// A CommentMap maps an AST node to a list of comment groups
// associated with it. See NewCommentMap for a description of
// the association.
//
type CommentMap map[Node][]*CommentGroup
func (cmap CommentMap) addComment(n Node, c *CommentGroup) {
list := cmap[n]
if len(list) == 0 {
list = []*CommentGroup{c}
} else {
list = append(list, c)
}
cmap[n] = list
}
type byInterval []Node
func (a byInterval) Len() int { return len(a) }
func (a byInterval) Less(i, j int) bool {
pi, pj := a[i].Pos(), a[j].Pos()
return pi < pj || pi == pj && a[i].End() > a[j].End()
}
func (a byInterval) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// nodeList returns the list of nodes of the AST n in source order.
//
func nodeList(n Node) []Node {
var list []Node
Inspect(n, func(n Node) bool {
// don't collect comments
switch n.(type) {
case nil, *CommentGroup, *Comment:
return false
}
list = append(list, n)
return true
})
// Note: The current implementation assumes that Inspect traverses the
// AST in depth-first and thus _source_ order. If AST traversal
// does not follow source order, the sorting call below will be
// required.
// sort.Sort(byInterval(list))
return list
}
// A commentListReader helps iterating through a list of comment groups.
//
type commentListReader struct {
fset *token.FileSet
list []*CommentGroup
index int
comment *CommentGroup // comment group at current index
pos, end token.Position // source interval of comment group at current index
}
func (r *commentListReader) eol() bool {
return r.index >= len(r.list)
}
func (r *commentListReader) next() {
if !r.eol() {
r.comment = r.list[r.index]
r.pos = r.fset.Position(r.comment.Pos())
r.end = r.fset.Position(r.comment.End())
r.index++
}
}
// A nodeStack keeps track of nested nodes.
// A node lower on the stack lexically contains the nodes higher on the stack.
//
type nodeStack []Node
// push pops all nodes that appear lexically before n
// and then pushes n on the stack.
//
func (s *nodeStack) push(n Node) {
s.pop(n.Pos())
*s = append((*s), n)
}
// pop pops all nodes that appear lexically before pos
// (i.e., whose lexical extent has ended before or at pos).
// It returns the last node popped.
//
func (s *nodeStack) pop(pos token.Pos) (top Node) {
i := len(*s)
for i > 0 && (*s)[i-1].End() <= pos {
top = (*s)[i-1]
i--
}
*s = (*s)[0:i]
return top
}
// NewCommentMap creates a new comment map by associating comment groups
// of the comments list with the nodes of the AST specified by node.
//
// A comment group g is associated with a node n if:
//
// - g starts on the same line as n ends
// - g starts on the line immediately following n, and there is
// at least one empty line after g and before the next node
// - g starts before n and is not associated to the node before n
// via the previous rules
//
// NewCommentMap tries to associate a comment group to the "largest"
// node possible: For instance, if the comment is a line comment
// trailing an assignment, the comment is associated with the entire
// assignment rather than just the last operand in the assignment.
//
func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap {
if len(comments) == 0 {
return nil // no comments to map
}
cmap := make(CommentMap)
// set up comment reader r
tmp := make([]*CommentGroup, len(comments))
copy(tmp, comments) // don't change incoming comments
sortComments(tmp)
r := commentListReader{fset: fset, list: tmp} // !r.eol() because len(comments) > 0
r.next()
// create node list in lexical order
nodes := nodeList(node)
nodes = append(nodes, nil) // append sentinel
// set up iteration variables
var (
p Node // previous node
pend token.Position // end of p
pg Node // previous node group (enclosing nodes of "importance")
pgend token.Position // end of pg
stack nodeStack // stack of node groups
)
for _, q := range nodes {
var qpos token.Position
if q != nil {
qpos = fset.Position(q.Pos()) // current node position
} else {
// set fake sentinel position to infinity so that
// all comments get processed before the sentinel
const infinity = 1 << 30
qpos.Offset = infinity
qpos.Line = infinity
}
// process comments before current node
for r.end.Offset <= qpos.Offset {
// determine recent node group
if top := stack.pop(r.comment.Pos()); top != nil {
pg = top
pgend = fset.Position(pg.End())
}
// Try to associate a comment first with a node group
// (i.e., a node of "importance" such as a declaration);
// if that fails, try to associate it with the most recent
// node.
// TODO(gri) try to simplify the logic below
var assoc Node
switch {
case pg != nil &&
(pgend.Line == r.pos.Line ||
pgend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line):
// 1) comment starts on same line as previous node group ends, or
// 2) comment starts on the line immediately after the
// previous node group and there is an empty line before
// the current node
// => associate comment with previous node group
assoc = pg
case p != nil &&
(pend.Line == r.pos.Line ||
pend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line ||
q == nil):
// same rules apply as above for p rather than pg,
// but also associate with p if we are at the end (q == nil)
assoc = p
default:
// otherwise, associate comment with current node
if q == nil {
// we can only reach here if there was no p
// which would imply that there were no nodes
panic("internal error: no comments should be associated with sentinel")
}
assoc = q
}
cmap.addComment(assoc, r.comment)
if r.eol() {
return cmap
}
r.next()
}
// update previous node
p = q
pend = fset.Position(p.End())
// update previous node group if we see an "important" node
switch q.(type) {
case *File, *Field, Decl, Spec, Stmt:
stack.push(q)
}
}
return cmap
}
// Update replaces an old node in the comment map with the new node
// and returns the new node. Comments that were associated with the
// old node are associated with the new node.
//
func (cmap CommentMap) Update(old, new Node) Node {
if list := cmap[old]; len(list) > 0 {
delete(cmap, old)
cmap[new] = append(cmap[new], list...)
}
return new
}
// Filter returns a new comment map consisting of only those
// entries of cmap for which a corresponding node exists in
// the AST specified by node.
//
func (cmap CommentMap) Filter(node Node) CommentMap {
umap := make(CommentMap)
Inspect(node, func(n Node) bool {
if g := cmap[n]; len(g) > 0 {
umap[n] = g
}
return true
})
return umap
}
// Comments returns the list of comment groups in the comment map.
// The result is sorted is source order.
//
func (cmap CommentMap) Comments() []*CommentGroup {
list := make([]*CommentGroup, 0, len(cmap))
for _, e := range cmap {
list = append(list, e...)
}
sortComments(list)
return list
}
func summary(list []*CommentGroup) string {
const maxLen = 40
var buf bytes.Buffer
// collect comments text
loop:
for _, group := range list {
// Note: CommentGroup.Text() does too much work for what we
// need and would only replace this innermost loop.
// Just do it explicitly.
for _, comment := range group.List {
if buf.Len() >= maxLen {
break loop
}
buf.WriteString(comment.Text)
}
}
// truncate if too long
if buf.Len() > maxLen {
buf.Truncate(maxLen - 3)
buf.WriteString("...")
}
// replace any invisibles with blanks
bytes := buf.Bytes()
for i, b := range bytes {
switch b {
case '\t', '\n', '\r':
bytes[i] = ' '
}
}
return string(bytes)
}
func (cmap CommentMap) String() string {
var buf bytes.Buffer
fmt.Fprintln(&buf, "CommentMap {")
for node, comment := range cmap {
// print name of identifiers; print node type for other nodes
var s string
if ident, ok := node.(*Ident); ok {
s = ident.Name
} else {
s = fmt.Sprintf("%T", node)
}
fmt.Fprintf(&buf, "\t%p %20s: %s\n", node, s, summary(comment))
}
fmt.Fprintln(&buf, "}")
return buf.String()
}
// 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.
// To avoid a cyclic dependency with go/parser, this file is in a separate package.
package ast_test
import (
"bytes"
"fmt"
. "k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/third_party/golang/go/parser"
"k8s.io/kubernetes/third_party/golang/go/token"
"sort"
"testing"
)
const src = `
// the very first comment
// package p
package p /* the name is p */
// imports
import (
"bytes" // bytes
"fmt" // fmt
"k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/third_party/golang/go/parser"
)
// T
type T struct {
a, b, c int // associated with a, b, c
// associated with x, y
x, y float64 // float values
z complex128 // complex value
}
// also associated with T
// x
var x = 0 // x = 0
// also associated with x
// f1
func f1() {
/* associated with s1 */
s1()
// also associated with s1
// associated with s2
// also associated with s2
s2() // line comment for s2
}
// associated with f1
// also associated with f1
// associated with f2
// f2
func f2() {
}
func f3() {
i := 1 /* 1 */ + 2 // addition
_ = i
}
// the very last comment
`
// res maps a key of the form "line number: node type"
// to the associated comments' text.
//
var res = map[string]string{
" 5: *ast.File": "the very first comment\npackage p\n",
" 5: *ast.Ident": " the name is p\n",
" 8: *ast.GenDecl": "imports\n",
" 9: *ast.ImportSpec": "bytes\n",
"10: *ast.ImportSpec": "fmt\n",
"16: *ast.GenDecl": "T\nalso associated with T\n",
"17: *ast.Field": "associated with a, b, c\n",
"19: *ast.Field": "associated with x, y\nfloat values\n",
"20: *ast.Field": "complex value\n",
"25: *ast.GenDecl": "x\nx = 0\nalso associated with x\n",
"29: *ast.FuncDecl": "f1\nassociated with f1\nalso associated with f1\n",
"31: *ast.ExprStmt": " associated with s1\nalso associated with s1\n",
"37: *ast.ExprStmt": "associated with s2\nalso associated with s2\nline comment for s2\n",
"45: *ast.FuncDecl": "associated with f2\nf2\n",
"49: *ast.AssignStmt": "addition\n",
"49: *ast.BasicLit": " 1\n",
"50: *ast.Ident": "the very last comment\n",
}
func ctext(list []*CommentGroup) string {
var buf bytes.Buffer
for _, g := range list {
buf.WriteString(g.Text())
}
return buf.String()
}
func TestCommentMap(t *testing.T) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
cmap := NewCommentMap(fset, f, f.Comments)
// very correct association of comments
for n, list := range cmap {
key := fmt.Sprintf("%2d: %T", fset.Position(n.Pos()).Line, n)
got := ctext(list)
want := res[key]
if got != want {
t.Errorf("%s: got %q; want %q", key, got, want)
}
}
// verify that no comments got lost
if n := len(cmap.Comments()); n != len(f.Comments) {
t.Errorf("got %d comment groups in map; want %d", n, len(f.Comments))
}
// support code to update test:
// set genMap to true to generate res map
const genMap = false
if genMap {
out := make([]string, 0, len(cmap))
for n, list := range cmap {
out = append(out, fmt.Sprintf("\t\"%2d: %T\":\t%q,", fset.Position(n.Pos()).Line, n, ctext(list)))
}
sort.Strings(out)
for _, s := range out {
fmt.Println(s)
}
}
}
// TODO(gri): add tests for Filter.
// 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 ast_test
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
)
// This example demonstrates how to inspect the AST of a Go program.
func ExampleInspect() {
// src is the input for which we want to inspect the AST.
src := `
package p
const c = 1.0
var X = f(3.14)*2 + c
`
// Create the AST by parsing src.
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "src.go", src, 0)
if err != nil {
panic(err)
}
// Inspect the AST and print all identifiers and literals.
ast.Inspect(f, func(n ast.Node) bool {
var s string
switch x := n.(type) {
case *ast.BasicLit:
s = x.Value
case *ast.Ident:
s = x.Name
}
if s != "" {
fmt.Printf("%s:\t%s\n", fset.Position(n.Pos()), s)
}
return true
})
// output:
// src.go:2:9: p
// src.go:3:7: c
// src.go:3:11: 1.0
// src.go:4:5: X
// src.go:4:9: f
// src.go:4:11: 3.14
// src.go:4:17: 2
// src.go:4:21: c
}
// This example shows what an AST looks like when printed for debugging.
func ExamplePrint() {
// src is the input for which we want to print the AST.
src := `
package main
func main() {
println("Hello, World!")
}
`
// Create the AST by parsing src.
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
panic(err)
}
// Print the AST.
ast.Print(fset, f)
// output:
// 0 *ast.File {
// 1 . Package: 2:1
// 2 . Name: *ast.Ident {
// 3 . . NamePos: 2:9
// 4 . . Name: "main"
// 5 . }
// 6 . Decls: []ast.Decl (len = 1) {
// 7 . . 0: *ast.FuncDecl {
// 8 . . . Name: *ast.Ident {
// 9 . . . . NamePos: 3:6
// 10 . . . . Name: "main"
// 11 . . . . Obj: *ast.Object {
// 12 . . . . . Kind: func
// 13 . . . . . Name: "main"
// 14 . . . . . Decl: *(obj @ 7)
// 15 . . . . }
// 16 . . . }
// 17 . . . Type: *ast.FuncType {
// 18 . . . . Func: 3:1
// 19 . . . . Params: *ast.FieldList {
// 20 . . . . . Opening: 3:10
// 21 . . . . . Closing: 3:11
// 22 . . . . }
// 23 . . . }
// 24 . . . Body: *ast.BlockStmt {
// 25 . . . . Lbrace: 3:13
// 26 . . . . List: []ast.Stmt (len = 1) {
// 27 . . . . . 0: *ast.ExprStmt {
// 28 . . . . . . X: *ast.CallExpr {
// 29 . . . . . . . Fun: *ast.Ident {
// 30 . . . . . . . . NamePos: 4:2
// 31 . . . . . . . . Name: "println"
// 32 . . . . . . . }
// 33 . . . . . . . Lparen: 4:9
// 34 . . . . . . . Args: []ast.Expr (len = 1) {
// 35 . . . . . . . . 0: *ast.BasicLit {
// 36 . . . . . . . . . ValuePos: 4:10
// 37 . . . . . . . . . Kind: STRING
// 38 . . . . . . . . . Value: "\"Hello, World!\""
// 39 . . . . . . . . }
// 40 . . . . . . . }
// 41 . . . . . . . Ellipsis: -
// 42 . . . . . . . Rparen: 4:25
// 43 . . . . . . }
// 44 . . . . . }
// 45 . . . . }
// 46 . . . . Rbrace: 5:1
// 47 . . . }
// 48 . . }
// 49 . }
// 50 . Scope: *ast.Scope {
// 51 . . Objects: map[string]*ast.Object (len = 1) {
// 52 . . . "main": *(obj @ 11)
// 53 . . }
// 54 . }
// 55 . Unresolved: []*ast.Ident (len = 1) {
// 56 . . 0: *(obj @ 29)
// 57 . }
// 58 }
}
// This example illustrates how to remove a variable declaration
// in a Go program while maintaining correct comment association
// using an ast.CommentMap.
func ExampleCommentMap() {
// src is the input for which we create the AST that we
// are going to manipulate.
src := `
// This is the package comment.
package main
// This comment is associated with the hello constant.
const hello = "Hello, World!" // line comment 1
// This comment is associated with the foo variable.
var foo = hello // line comment 2
// This comment is associated with the main function.
func main() {
fmt.Println(hello) // line comment 3
}
`
// Create the AST by parsing src.
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "src.go", src, parser.ParseComments)
if err != nil {
panic(err)
}
// Create an ast.CommentMap from the ast.File's comments.
// This helps keeping the association between comments
// and AST nodes.
cmap := ast.NewCommentMap(fset, f, f.Comments)
// Remove the first variable declaration from the list of declarations.
f.Decls = removeFirstVarDecl(f.Decls)
// Use the comment map to filter comments that don't belong anymore
// (the comments associated with the variable declaration), and create
// the new comments list.
f.Comments = cmap.Filter(f).Comments()
// Print the modified AST.
var buf bytes.Buffer
if err := format.Node(&buf, fset, f); err != nil {
panic(err)
}
fmt.Printf("%s", buf.Bytes())
// output:
// // This is the package comment.
// package main
//
// // This comment is associated with the hello constant.
// const hello = "Hello, World!" // line comment 1
//
// // This comment is associated with the main function.
// func main() {
// fmt.Println(hello) // line comment 3
// }
}
func removeFirstVarDecl(list []ast.Decl) []ast.Decl {
for i, decl := range list {
if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.VAR {
copy(list[i:], list[i+1:])
return list[:len(list)-1]
}
}
panic("variable declaration not found")
}
// Copyright 2013 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.
// To avoid a cyclic dependency with go/parser, this file is in a separate package.
package ast_test
import (
"bytes"
"go/ast"
"go/format"
"go/parser"
"go/token"
"testing"
)
const input = `package p
type t1 struct{}
type t2 struct{}
func f1() {}
func f1() {}
func f2() {}
func (*t1) f1() {}
func (t1) f1() {}
func (t1) f2() {}
func (t2) f1() {}
func (t2) f2() {}
func (x *t2) f2() {}
`
// Calling ast.MergePackageFiles with ast.FilterFuncDuplicates
// keeps a duplicate entry with attached documentation in favor
// of one without, and it favors duplicate entries appearing
// later in the source over ones appearing earlier. This is why
// (*t2).f2 is kept and t2.f2 is eliminated in this test case.
//
const golden = `package p
type t1 struct{}
type t2 struct{}
func f1() {}
func f2() {}
func (t1) f1() {}
func (t1) f2() {}
func (t2) f1() {}
func (x *t2) f2() {}
`
func TestFilterDuplicates(t *testing.T) {
// parse input
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "", input, 0)
if err != nil {
t.Fatal(err)
}
// create package
files := map[string]*ast.File{"": file}
pkg, err := ast.NewPackage(fset, files, nil, nil)
if err != nil {
t.Fatal(err)
}
// filter
merged := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates)
// pretty-print
var buf bytes.Buffer
if err := format.Node(&buf, fset, merged); err != nil {
t.Fatal(err)
}
output := buf.String()
if output != golden {
t.Errorf("incorrect output:\n%s", output)
}
}
// Copyright 2011 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 ast
import (
"k8s.io/kubernetes/third_party/golang/go/token"
"sort"
"strconv"
)
// SortImports sorts runs of consecutive import lines in import blocks in f.
// It also removes duplicate imports when it is possible to do so without data loss.
func SortImports(fset *token.FileSet, f *File) {
for _, d := range f.Decls {
d, ok := d.(*GenDecl)
if !ok || d.Tok != token.IMPORT {
// Not an import declaration, so we're done.
// Imports are always first.
break
}
if !d.Lparen.IsValid() {
// Not a block: sorted by default.
continue
}
// Identify and sort runs of specs on successive lines.
i := 0
specs := d.Specs[:0]
for j, s := range d.Specs {
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
// j begins a new run. End this one.
specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
i = j
}
}
specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
d.Specs = specs
// Deduping can leave a blank line before the rparen; clean that up.
if len(d.Specs) > 0 {
lastSpec := d.Specs[len(d.Specs)-1]
lastLine := fset.Position(lastSpec.Pos()).Line
if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
fset.File(d.Rparen).MergeLine(rParenLine - 1)
}
}
}
}
func importPath(s Spec) string {
t, err := strconv.Unquote(s.(*ImportSpec).Path.Value)
if err == nil {
return t
}
return ""
}
func importName(s Spec) string {
n := s.(*ImportSpec).Name
if n == nil {
return ""
}
return n.Name
}
func importComment(s Spec) string {
c := s.(*ImportSpec).Comment
if c == nil {
return ""
}
return c.Text()
}
// collapse indicates whether prev may be removed, leaving only next.
func collapse(prev, next Spec) bool {
if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
return false
}
return prev.(*ImportSpec).Comment == nil
}
type posSpan struct {
Start token.Pos
End token.Pos
}
func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {
// Can't short-circuit here even if specs are already sorted,
// since they might yet need deduplication.
// A lone import, however, may be safely ignored.
if len(specs) <= 1 {
return specs
}
// Record positions for specs.
pos := make([]posSpan, len(specs))
for i, s := range specs {
pos[i] = posSpan{s.Pos(), s.End()}
}
// Identify comments in this range.
// Any comment from pos[0].Start to the final line counts.
lastLine := fset.Position(pos[len(pos)-1].End).Line
cstart := len(f.Comments)
cend := len(f.Comments)
for i, g := range f.Comments {
if g.Pos() < pos[0].Start {
continue
}
if i < cstart {
cstart = i
}
if fset.Position(g.End()).Line > lastLine {
cend = i
break
}
}
comments := f.Comments[cstart:cend]
// Assign each comment to the import spec preceding it.
importComment := map[*ImportSpec][]*CommentGroup{}
specIndex := 0
for _, g := range comments {
for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
specIndex++
}
s := specs[specIndex].(*ImportSpec)
importComment[s] = append(importComment[s], g)
}
// Sort the import specs by import path.
// Remove duplicates, when possible without data loss.
// Reassign the import paths to have the same position sequence.
// Reassign each comment to abut the end of its spec.
// Sort the comments by new position.
sort.Sort(byImportSpec(specs))
// Dedup. Thanks to our sorting, we can just consider
// adjacent pairs of imports.
deduped := specs[:0]
for i, s := range specs {
if i == len(specs)-1 || !collapse(s, specs[i+1]) {
deduped = append(deduped, s)
} else {
p := s.Pos()
fset.File(p).MergeLine(fset.Position(p).Line)
}
}
specs = deduped
// Fix up comment positions
for i, s := range specs {
s := s.(*ImportSpec)
if s.Name != nil {
s.Name.NamePos = pos[i].Start
}
s.Path.ValuePos = pos[i].Start
s.EndPos = pos[i].End
for _, g := range importComment[s] {
for _, c := range g.List {
c.Slash = pos[i].End
}
}
}
sort.Sort(byCommentPos(comments))
return specs
}
type byImportSpec []Spec // slice of *ImportSpec
func (x byImportSpec) Len() int { return len(x) }
func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byImportSpec) Less(i, j int) bool {
ipath := importPath(x[i])
jpath := importPath(x[j])
if ipath != jpath {
return ipath < jpath
}
iname := importName(x[i])
jname := importName(x[j])
if iname != jname {
return iname < jname
}
return importComment(x[i]) < importComment(x[j])
}
type byCommentPos []*CommentGroup
func (x byCommentPos) Len() int { return len(x) }
func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
// Copyright 2010 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.
// This file contains printing support for ASTs.
package ast
import (
"fmt"
"io"
"os"
"reflect"
"k8s.io/kubernetes/third_party/golang/go/token"
)
// A FieldFilter may be provided to Fprint to control the output.
type FieldFilter func(name string, value reflect.Value) bool
// NotNilFilter returns true for field values that are not nil;
// it returns false otherwise.
func NotNilFilter(_ string, v reflect.Value) bool {
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return !v.IsNil()
}
return true
}
// Fprint prints the (sub-)tree starting at AST node x to w.
// If fset != nil, position information is interpreted relative
// to that file set. Otherwise positions are printed as integer
// values (file set specific offsets).
//
// A non-nil FieldFilter f may be provided to control the output:
// struct fields for which f(fieldname, fieldvalue) is true are
// printed; all others are filtered from the output. Unexported
// struct fields are never printed.
//
func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
// setup printer
p := printer{
output: w,
fset: fset,
filter: f,
ptrmap: make(map[interface{}]int),
last: '\n', // force printing of line number on first line
}
// install error handler
defer func() {
if e := recover(); e != nil {
err = e.(localError).err // re-panics if it's not a localError
}
}()
// print x
if x == nil {
p.printf("nil\n")
return
}
p.print(reflect.ValueOf(x))
p.printf("\n")
return
}
// Print prints x to standard output, skipping nil fields.
// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
func Print(fset *token.FileSet, x interface{}) error {
return Fprint(os.Stdout, fset, x, NotNilFilter)
}
type printer struct {
output io.Writer
fset *token.FileSet
filter FieldFilter
ptrmap map[interface{}]int // *T -> line number
indent int // current indentation level
last byte // the last byte processed by Write
line int // current line number
}
var indent = []byte(". ")
func (p *printer) Write(data []byte) (n int, err error) {
var m int
for i, b := range data {
// invariant: data[0:n] has been written
if b == '\n' {
m, err = p.output.Write(data[n : i+1])
n += m
if err != nil {
return
}
p.line++
} else if p.last == '\n' {
_, err = fmt.Fprintf(p.output, "%6d ", p.line)
if err != nil {
return
}
for j := p.indent; j > 0; j-- {
_, err = p.output.Write(indent)
if err != nil {
return
}
}
}
p.last = b
}
if len(data) > n {
m, err = p.output.Write(data[n:])
n += m
}
return
}
// localError wraps locally caught errors so we can distinguish
// them from genuine panics which we don't want to return as errors.
type localError struct {
err error
}
// printf is a convenience wrapper that takes care of print errors.
func (p *printer) printf(format string, args ...interface{}) {
if _, err := fmt.Fprintf(p, format, args...); err != nil {
panic(localError{err})
}
}
// Implementation note: Print is written for AST nodes but could be
// used to print arbitrary data structures; such a version should
// probably be in a different package.
//
// Note: This code detects (some) cycles created via pointers but
// not cycles that are created via slices or maps containing the
// same slice or map. Code for general data structures probably
// should catch those as well.
func (p *printer) print(x reflect.Value) {
if !NotNilFilter("", x) {
p.printf("nil")
return
}
switch x.Kind() {
case reflect.Interface:
p.print(x.Elem())
case reflect.Map:
p.printf("%s (len = %d) {", x.Type(), x.Len())
if x.Len() > 0 {
p.indent++
p.printf("\n")
for _, key := range x.MapKeys() {
p.print(key)
p.printf(": ")
p.print(x.MapIndex(key))
p.printf("\n")
}
p.indent--
}
p.printf("}")
case reflect.Ptr:
p.printf("*")
// type-checked ASTs may contain cycles - use ptrmap
// to keep track of objects that have been printed
// already and print the respective line number instead
ptr := x.Interface()
if line, exists := p.ptrmap[ptr]; exists {
p.printf("(obj @ %d)", line)
} else {
p.ptrmap[ptr] = p.line
p.print(x.Elem())
}
case reflect.Array:
p.printf("%s {", x.Type())
if x.Len() > 0 {
p.indent++
p.printf("\n")
for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
p.print(x.Index(i))
p.printf("\n")
}
p.indent--
}
p.printf("}")
case reflect.Slice:
if s, ok := x.Interface().([]byte); ok {
p.printf("%#q", s)
return
}
p.printf("%s (len = %d) {", x.Type(), x.Len())
if x.Len() > 0 {
p.indent++
p.printf("\n")
for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
p.print(x.Index(i))
p.printf("\n")
}
p.indent--
}
p.printf("}")
case reflect.Struct:
t := x.Type()
p.printf("%s {", t)
p.indent++
first := true
for i, n := 0, t.NumField(); i < n; i++ {
// exclude non-exported fields because their
// values cannot be accessed via reflection
if name := t.Field(i).Name; IsExported(name) {
value := x.Field(i)
if p.filter == nil || p.filter(name, value) {
if first {
p.printf("\n")
first = false
}
p.printf("%s: ", name)
p.print(value)
p.printf("\n")
}
}
}
p.indent--
p.printf("}")
default:
v := x.Interface()
switch v := v.(type) {
case string:
// print strings in quotes
p.printf("%q", v)
return
case token.Pos:
// position values can be printed nicely if we have a file set
if p.fset != nil {
p.printf("%s", p.fset.Position(v))
return
}
}
// default
p.printf("%v", v)
}
}
// Copyright 2011 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 ast
import (
"bytes"
"strings"
"testing"
)
var tests = []struct {
x interface{} // x is printed as s
s string
}{
// basic types
{nil, "0 nil"},
{true, "0 true"},
{42, "0 42"},
{3.14, "0 3.14"},
{1 + 2.718i, "0 (1+2.718i)"},
{"foobar", "0 \"foobar\""},
// maps
{map[Expr]string{}, `0 map[ast.Expr]string (len = 0) {}`},
{map[string]int{"a": 1},
`0 map[string]int (len = 1) {
1 . "a": 1
2 }`},
// pointers
{new(int), "0 *0"},
// arrays
{[0]int{}, `0 [0]int {}`},
{[3]int{1, 2, 3},
`0 [3]int {
1 . 0: 1
2 . 1: 2
3 . 2: 3
4 }`},
{[...]int{42},
`0 [1]int {
1 . 0: 42
2 }`},
// slices
{[]int{}, `0 []int (len = 0) {}`},
{[]int{1, 2, 3},
`0 []int (len = 3) {
1 . 0: 1
2 . 1: 2
3 . 2: 3
4 }`},
// structs
{struct{}{}, `0 struct {} {}`},
{struct{ x int }{007}, `0 struct { x int } {}`},
{struct{ X, y int }{42, 991},
`0 struct { X int; y int } {
1 . X: 42
2 }`},
{struct{ X, Y int }{42, 991},
`0 struct { X int; Y int } {
1 . X: 42
2 . Y: 991
3 }`},
}
// Split s into lines, trim whitespace from all lines, and return
// the concatenated non-empty lines.
func trim(s string) string {
lines := strings.Split(s, "\n")
i := 0
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" {
lines[i] = line
i++
}
}
return strings.Join(lines[0:i], "\n")
}
func TestPrint(t *testing.T) {
var buf bytes.Buffer
for _, test := range tests {
buf.Reset()
if err := Fprint(&buf, nil, test.x, nil); err != nil {
t.Errorf("Fprint failed: %s", err)
}
if s, ts := trim(buf.String()), trim(test.s); s != ts {
t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts)
}
}
}
// Copyright 2011 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.
// This file implements NewPackage.
package ast
import (
"fmt"
"k8s.io/kubernetes/third_party/golang/go/scanner"
"k8s.io/kubernetes/third_party/golang/go/token"
"strconv"
)
type pkgBuilder struct {
fset *token.FileSet
errors scanner.ErrorList
}
func (p *pkgBuilder) error(pos token.Pos, msg string) {
p.errors.Add(p.fset.Position(pos), msg)
}
func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
p.error(pos, fmt.Sprintf(format, args...))
}
func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
alt := scope.Insert(obj)
if alt == nil && altScope != nil {
// see if there is a conflicting declaration in altScope
alt = altScope.Lookup(obj.Name)
}
if alt != nil {
prevDecl := ""
if pos := alt.Pos(); pos.IsValid() {
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
}
p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
}
}
func resolve(scope *Scope, ident *Ident) bool {
for ; scope != nil; scope = scope.Outer {
if obj := scope.Lookup(ident.Name); obj != nil {
ident.Obj = obj
return true
}
}
return false
}
// An Importer resolves import paths to package Objects.
// The imports map records the packages already imported,
// indexed by package id (canonical import path).
// An Importer must determine the canonical import path and
// check the map to see if it is already present in the imports map.
// If so, the Importer can return the map entry. Otherwise, the
// Importer should load the package data for the given path into
// a new *Object (pkg), record pkg in the imports map, and then
// return pkg.
type Importer func(imports map[string]*Object, path string) (pkg *Object, err error)
// NewPackage creates a new Package node from a set of File nodes. It resolves
// unresolved identifiers across files and updates each file's Unresolved list
// accordingly. If a non-nil importer and universe scope are provided, they are
// used to resolve identifiers not declared in any of the package files. Any
// remaining unresolved identifiers are reported as undeclared. If the files
// belong to different packages, one package name is selected and files with
// different package names are reported and then ignored.
// The result is a package node and a scanner.ErrorList if there were errors.
//
func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) {
var p pkgBuilder
p.fset = fset
// complete package scope
pkgName := ""
pkgScope := NewScope(universe)
for _, file := range files {
// package names must match
switch name := file.Name.Name; {
case pkgName == "":
pkgName = name
case name != pkgName:
p.errorf(file.Package, "package %s; expected %s", name, pkgName)
continue // ignore this file
}
// collect top-level file objects in package scope
for _, obj := range file.Scope.Objects {
p.declare(pkgScope, nil, obj)
}
}
// package global mapping of imported package ids to package objects
imports := make(map[string]*Object)
// complete file scopes with imports and resolve identifiers
for _, file := range files {
// ignore file if it belongs to a different package
// (error has already been reported)
if file.Name.Name != pkgName {
continue
}
// build file scope by processing all imports
importErrors := false
fileScope := NewScope(pkgScope)
for _, spec := range file.Imports {
if importer == nil {
importErrors = true
continue
}
path, _ := strconv.Unquote(spec.Path.Value)
pkg, err := importer(imports, path)
if err != nil {
p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
importErrors = true
continue
}
// TODO(gri) If a local package name != "." is provided,
// global identifier resolution could proceed even if the
// import failed. Consider adjusting the logic here a bit.
// local name overrides imported package name
name := pkg.Name
if spec.Name != nil {
name = spec.Name.Name
}
// add import to file scope
if name == "." {
// merge imported scope with file scope
for _, obj := range pkg.Data.(*Scope).Objects {
p.declare(fileScope, pkgScope, obj)
}
} else if name != "_" {
// declare imported package object in file scope
// (do not re-use pkg in the file scope but create
// a new object instead; the Decl field is different
// for different files)
obj := NewObj(Pkg, name)
obj.Decl = spec
obj.Data = pkg.Data
p.declare(fileScope, pkgScope, obj)
}
}
// resolve identifiers
if importErrors {
// don't use the universe scope without correct imports
// (objects in the universe may be shadowed by imports;
// with missing imports, identifiers might get resolved
// incorrectly to universe objects)
pkgScope.Outer = nil
}
i := 0
for _, ident := range file.Unresolved {
if !resolve(fileScope, ident) {
p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
file.Unresolved[i] = ident
i++
}
}
file.Unresolved = file.Unresolved[0:i]
pkgScope.Outer = universe // reset universe scope
}
p.errors.Sort()
return &Package{pkgName, pkgScope, imports, files}, p.errors.Err()
}
// Copyright 2009 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.
// This file implements scopes and the objects they contain.
package ast
import (
"bytes"
"fmt"
"k8s.io/kubernetes/third_party/golang/go/token"
)
// A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer)
// scope.
//
type Scope struct {
Outer *Scope
Objects map[string]*Object
}
// NewScope creates a new scope nested in the outer scope.
func NewScope(outer *Scope) *Scope {
const n = 4 // initial scope capacity
return &Scope{outer, make(map[string]*Object, n)}
}
// Lookup returns the object with the given name if it is
// found in scope s, otherwise it returns nil. Outer scopes
// are ignored.
//
func (s *Scope) Lookup(name string) *Object {
return s.Objects[name]
}
// Insert attempts to insert a named object obj into the scope s.
// If the scope already contains an object alt with the same name,
// Insert leaves the scope unchanged and returns alt. Otherwise
// it inserts obj and returns nil.
//
func (s *Scope) Insert(obj *Object) (alt *Object) {
if alt = s.Objects[obj.Name]; alt == nil {
s.Objects[obj.Name] = obj
}
return
}
// Debugging support
func (s *Scope) String() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "scope %p {", s)
if s != nil && len(s.Objects) > 0 {
fmt.Fprintln(&buf)
for _, obj := range s.Objects {
fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
}
}
fmt.Fprintf(&buf, "}\n")
return buf.String()
}
// ----------------------------------------------------------------------------
// Objects
// An Object describes a named language entity such as a package,
// constant, type, variable, function (incl. methods), or label.
//
// The Data fields contains object-specific data:
//
// Kind Data type Data value
// Pkg *types.Package package scope
// Con int iota for the respective declaration
// Con != nil constant value
// Typ *Scope (used as method scope during type checking - transient)
//
type Object struct {
Kind ObjKind
Name string // declared name
Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
Data interface{} // object-specific data; or nil
Type interface{} // placeholder for type information; may be nil
}
// NewObj creates a new object of a given kind and name.
func NewObj(kind ObjKind, name string) *Object {
return &Object{Kind: kind, Name: name}
}
// Pos computes the source position of the declaration of an object name.
// The result may be an invalid position if it cannot be computed
// (obj.Decl may be nil or not correct).
func (obj *Object) Pos() token.Pos {
name := obj.Name
switch d := obj.Decl.(type) {
case *Field:
for _, n := range d.Names {
if n.Name == name {
return n.Pos()
}
}
case *ImportSpec:
if d.Name != nil && d.Name.Name == name {
return d.Name.Pos()
}
return d.Path.Pos()
case *ValueSpec:
for _, n := range d.Names {
if n.Name == name {
return n.Pos()
}
}
case *TypeSpec:
if d.Name.Name == name {
return d.Name.Pos()
}
case *FuncDecl:
if d.Name.Name == name {
return d.Name.Pos()
}
case *LabeledStmt:
if d.Label.Name == name {
return d.Label.Pos()
}
case *AssignStmt:
for _, x := range d.Lhs {
if ident, isIdent := x.(*Ident); isIdent && ident.Name == name {
return ident.Pos()
}
}
case *Scope:
// predeclared object - nothing to do for now
}
return token.NoPos
}
// ObjKind describes what an object represents.
type ObjKind int
// The list of possible Object kinds.
const (
Bad ObjKind = iota // for error handling
Pkg // package
Con // constant
Typ // type
Var // variable
Fun // function or method
Lbl // label
)
var objKindStrings = [...]string{
Bad: "bad",
Pkg: "package",
Con: "const",
Typ: "type",
Var: "var",
Fun: "func",
Lbl: "label",
}
func (kind ObjKind) String() string { return objKindStrings[kind] }
// Copyright 2009 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 ast
import "fmt"
// A Visitor's Visit method is invoked for each node encountered by Walk.
// If the result visitor w is not nil, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil).
type Visitor interface {
Visit(node Node) (w Visitor)
}
// Helper functions for common node lists. They may be empty.
func walkIdentList(v Visitor, list []*Ident) {
for _, x := range list {
Walk(v, x)
}
}
func walkExprList(v Visitor, list []Expr) {
for _, x := range list {
Walk(v, x)
}
}
func walkStmtList(v Visitor, list []Stmt) {
for _, x := range list {
Walk(v, x)
}
}
func walkDeclList(v Visitor, list []Decl) {
for _, x := range list {
Walk(v, x)
}
}
// TODO(gri): Investigate if providing a closure to Walk leads to
// simpler use (and may help eliminate Inspect in turn).
// Walk traverses an AST in depth-first order: It starts by calling
// v.Visit(node); node must not be nil. If the visitor w returned by
// v.Visit(node) is not nil, Walk is invoked recursively with visitor
// w for each of the non-nil children of node, followed by a call of
// w.Visit(nil).
//
func Walk(v Visitor, node Node) {
if v = v.Visit(node); v == nil {
return
}
// walk children
// (the order of the cases matches the order
// of the corresponding node types in ast.go)
switch n := node.(type) {
// Comments and fields
case *Comment:
// nothing to do
case *CommentGroup:
for _, c := range n.List {
Walk(v, c)
}
case *Field:
if n.Doc != nil {
Walk(v, n.Doc)
}
walkIdentList(v, n.Names)
Walk(v, n.Type)
if n.Tag != nil {
Walk(v, n.Tag)
}
if n.Comment != nil {
Walk(v, n.Comment)
}
case *FieldList:
for _, f := range n.List {
Walk(v, f)
}
// Expressions
case *BadExpr, *Ident, *BasicLit:
// nothing to do
case *Ellipsis:
if n.Elt != nil {
Walk(v, n.Elt)
}
case *FuncLit:
Walk(v, n.Type)
Walk(v, n.Body)
case *CompositeLit:
if n.Type != nil {
Walk(v, n.Type)
}
walkExprList(v, n.Elts)
case *ParenExpr:
Walk(v, n.X)
case *SelectorExpr:
Walk(v, n.X)
Walk(v, n.Sel)
case *IndexExpr:
Walk(v, n.X)
Walk(v, n.Index)
case *SliceExpr:
Walk(v, n.X)
if n.Low != nil {
Walk(v, n.Low)
}
if n.High != nil {
Walk(v, n.High)
}
if n.Max != nil {
Walk(v, n.Max)
}
case *TypeAssertExpr:
Walk(v, n.X)
if n.Type != nil {
Walk(v, n.Type)
}
case *CallExpr:
Walk(v, n.Fun)
walkExprList(v, n.Args)
case *StarExpr:
Walk(v, n.X)
case *UnaryExpr:
Walk(v, n.X)
case *BinaryExpr:
Walk(v, n.X)
Walk(v, n.Y)
case *KeyValueExpr:
Walk(v, n.Key)
Walk(v, n.Value)
// Types
case *ArrayType:
if n.Len != nil {
Walk(v, n.Len)
}
Walk(v, n.Elt)
case *StructType:
Walk(v, n.Fields)
case *FuncType:
if n.Params != nil {
Walk(v, n.Params)
}
if n.Results != nil {
Walk(v, n.Results)
}
case *InterfaceType:
Walk(v, n.Methods)
case *MapType:
Walk(v, n.Key)
Walk(v, n.Value)
case *ChanType:
Walk(v, n.Value)
// Statements
case *BadStmt:
// nothing to do
case *DeclStmt:
Walk(v, n.Decl)
case *EmptyStmt:
// nothing to do
case *LabeledStmt:
Walk(v, n.Label)
Walk(v, n.Stmt)
case *ExprStmt:
Walk(v, n.X)
case *SendStmt:
Walk(v, n.Chan)
Walk(v, n.Value)
case *IncDecStmt:
Walk(v, n.X)
case *AssignStmt:
walkExprList(v, n.Lhs)
walkExprList(v, n.Rhs)
case *GoStmt:
Walk(v, n.Call)
case *DeferStmt:
Walk(v, n.Call)
case *ReturnStmt:
walkExprList(v, n.Results)
case *BranchStmt:
if n.Label != nil {
Walk(v, n.Label)
}
case *BlockStmt:
walkStmtList(v, n.List)
case *IfStmt:
if n.Init != nil {
Walk(v, n.Init)
}
Walk(v, n.Cond)
Walk(v, n.Body)
if n.Else != nil {
Walk(v, n.Else)
}
case *CaseClause:
walkExprList(v, n.List)
walkStmtList(v, n.Body)
case *SwitchStmt:
if n.Init != nil {
Walk(v, n.Init)
}
if n.Tag != nil {
Walk(v, n.Tag)
}
Walk(v, n.Body)
case *TypeSwitchStmt:
if n.Init != nil {
Walk(v, n.Init)
}
Walk(v, n.Assign)
Walk(v, n.Body)
case *CommClause:
if n.Comm != nil {
Walk(v, n.Comm)
}
walkStmtList(v, n.Body)
case *SelectStmt:
Walk(v, n.Body)
case *ForStmt:
if n.Init != nil {
Walk(v, n.Init)
}
if n.Cond != nil {
Walk(v, n.Cond)
}
if n.Post != nil {
Walk(v, n.Post)
}
Walk(v, n.Body)
case *RangeStmt:
if n.Key != nil {
Walk(v, n.Key)
}
if n.Value != nil {
Walk(v, n.Value)
}
Walk(v, n.X)
Walk(v, n.Body)
// Declarations
case *ImportSpec:
if n.Doc != nil {
Walk(v, n.Doc)
}
if n.Name != nil {
Walk(v, n.Name)
}
Walk(v, n.Path)
if n.Comment != nil {
Walk(v, n.Comment)
}
case *ValueSpec:
if n.Doc != nil {
Walk(v, n.Doc)
}
walkIdentList(v, n.Names)
if n.Type != nil {
Walk(v, n.Type)
}
walkExprList(v, n.Values)
if n.Comment != nil {
Walk(v, n.Comment)
}
case *TypeSpec:
if n.Doc != nil {
Walk(v, n.Doc)
}
Walk(v, n.Name)
Walk(v, n.Type)
if n.Comment != nil {
Walk(v, n.Comment)
}
case *BadDecl:
// nothing to do
case *GenDecl:
if n.Doc != nil {
Walk(v, n.Doc)
}
for _, s := range n.Specs {
Walk(v, s)
}
case *FuncDecl:
if n.Doc != nil {
Walk(v, n.Doc)
}
if n.Recv != nil {
Walk(v, n.Recv)
}
Walk(v, n.Name)
Walk(v, n.Type)
if n.Body != nil {
Walk(v, n.Body)
}
// Files and packages
case *File:
if n.Doc != nil {
Walk(v, n.Doc)
}
Walk(v, n.Name)
walkDeclList(v, n.Decls)
// don't walk n.Comments - they have been
// visited already through the individual
// nodes
case *Package:
for _, f := range n.Files {
Walk(v, f)
}
default:
panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
}
v.Visit(nil)
}
type inspector func(Node) bool
func (f inspector) Visit(node Node) Visitor {
if f(node) {
return f
}
return nil
}
// Inspect traverses an AST in depth-first order: It starts by calling
// f(node); node must not be nil. If f returns true, Inspect invokes f
// recursively for each of the non-nil children of node, followed by a
// call of f(nil).
//
func Inspect(node Node, f func(Node) bool) {
Walk(inspector(f), node)
}
// Copyright 2011 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 build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree. The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code. The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path. That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
// GOPATH=/home/user/gocode
//
// /home/user/gocode/
// src/
// foo/
// bar/ (go code in package bar)
// x.go
// quux/ (go code in package main)
// y.go
// bin/
// quux (installed command)
// pkg/
// linux_amd64/
// foo/
// bar.a (installed package object)
//
// Build Constraints
//
// A build constraint, also known as a build tag, is a line comment that begins
//
// // +build
//
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments. These rules mean that in Go
// files a build constraint must appear before the package clause.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
// // +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
// (linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
// // +build linux darwin
// // +build 386
//
// corresponds to the boolean formula:
//
// (linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
// - the target operating system, as spelled by runtime.GOOS
// - the target architecture, as spelled by runtime.GOARCH
// - the compiler being used, either "gc" or "gccgo"
// - "cgo", if ctxt.CgoEnabled is true
// - "go1.1", from Go version 1.1 onward
// - "go1.2", from Go version 1.2 onward
// - "go1.3", from Go version 1.3 onward
// - "go1.4", from Go version 1.4 onward
// - "go1.5", from Go version 1.5 onward
// - "go1.6", from Go version 1.6 onward
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches any of the following patterns:
// *_GOOS
// *_GOARCH
// *_GOOS_GOARCH
// (example: source_windows_amd64.go) where GOOS and GOARCH represent
// any known operating system and architecture values respectively, then
// the file is considered to have an implicit build constraint requiring
// those terms (in addition to any explicit constraints in the file).
//
// To keep a file from being considered for the build:
//
// // +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
// // +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
// // +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
// Using GOOS=android matches build tags and files as for GOOS=linux
// in addition to android tags and files.
//
package build
// 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 build
import (
"bufio"
"errors"
"io"
)
type importReader struct {
b *bufio.Reader
buf []byte
peek byte
err error
eof bool
nerr int
}
func isIdent(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= 0x80
}
var (
errSyntax = errors.New("syntax error")
errNUL = errors.New("unexpected NUL in input")
)
// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
func (r *importReader) syntaxError() {
if r.err == nil {
r.err = errSyntax
}
}
// readByte reads the next byte from the input, saves it in buf, and returns it.
// If an error occurs, readByte records the error in r.err and returns 0.
func (r *importReader) readByte() byte {
c, err := r.b.ReadByte()
if err == nil {
r.buf = append(r.buf, c)
if c == 0 {
err = errNUL
}
}
if err != nil {
if err == io.EOF {
r.eof = true
} else if r.err == nil {
r.err = err
}
c = 0
}
return c
}
// peekByte returns the next byte from the input reader but does not advance beyond it.
// If skipSpace is set, peekByte skips leading spaces and comments.
func (r *importReader) peekByte(skipSpace bool) byte {
if r.err != nil {
if r.nerr++; r.nerr > 10000 {
panic("go/build: import reader looping")
}
return 0
}
// Use r.peek as first input byte.
// Don't just return r.peek here: it might have been left by peekByte(false)
// and this might be peekByte(true).
c := r.peek
if c == 0 {
c = r.readByte()
}
for r.err == nil && !r.eof {
if skipSpace {
// For the purposes of this reader, semicolons are never necessary to
// understand the input and are treated as spaces.
switch c {
case ' ', '\f', '\t', '\r', '\n', ';':
c = r.readByte()
continue
case '/':
c = r.readByte()
if c == '/' {
for c != '\n' && r.err == nil && !r.eof {
c = r.readByte()
}
} else if c == '*' {
var c1 byte
for (c != '*' || c1 != '/') && r.err == nil {
if r.eof {
r.syntaxError()
}
c, c1 = c1, r.readByte()
}
} else {
r.syntaxError()
}
c = r.readByte()
continue
}
}
break
}
r.peek = c
return r.peek
}
// nextByte is like peekByte but advances beyond the returned byte.
func (r *importReader) nextByte(skipSpace bool) byte {
c := r.peekByte(skipSpace)
r.peek = 0
return c
}
// readKeyword reads the given keyword from the input.
// If the keyword is not present, readKeyword records a syntax error.
func (r *importReader) readKeyword(kw string) {
r.peekByte(true)
for i := 0; i < len(kw); i++ {
if r.nextByte(false) != kw[i] {
r.syntaxError()
return
}
}
if isIdent(r.peekByte(false)) {
r.syntaxError()
}
}
// readIdent reads an identifier from the input.
// If an identifier is not present, readIdent records a syntax error.
func (r *importReader) readIdent() {
c := r.peekByte(true)
if !isIdent(c) {
r.syntaxError()
return
}
for isIdent(r.peekByte(false)) {
r.peek = 0
}
}
// readString reads a quoted string literal from the input.
// If an identifier is not present, readString records a syntax error.
func (r *importReader) readString(save *[]string) {
switch r.nextByte(true) {
case '`':
start := len(r.buf) - 1
for r.err == nil {
if r.nextByte(false) == '`' {
if save != nil {
*save = append(*save, string(r.buf[start:]))
}
break
}
if r.eof {
r.syntaxError()
}
}
case '"':
start := len(r.buf) - 1
for r.err == nil {
c := r.nextByte(false)
if c == '"' {
if save != nil {
*save = append(*save, string(r.buf[start:]))
}
break
}
if r.eof || c == '\n' {
r.syntaxError()
}
if c == '\\' {
r.nextByte(false)
}
}
default:
r.syntaxError()
}
}
// readImport reads an import clause - optional identifier followed by quoted string -
// from the input.
func (r *importReader) readImport(imports *[]string) {
c := r.peekByte(true)
if c == '.' {
r.peek = 0
} else if isIdent(c) {
r.readIdent()
}
r.readString(imports)
}
// readComments is like ioutil.ReadAll, except that it only reads the leading
// block of comments in the file.
func readComments(f io.Reader) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.peekByte(true)
if r.err == nil && !r.eof {
// Didn't reach EOF, so must have found a non-space byte. Remove it.
r.buf = r.buf[:len(r.buf)-1]
}
return r.buf, r.err
}
// readImports is like ioutil.ReadAll, except that it expects a Go file as input
// and stops reading the input once the imports have completed.
func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.readKeyword("package")
r.readIdent()
for r.peekByte(true) == 'i' {
r.readKeyword("import")
if r.peekByte(true) == '(' {
r.nextByte(false)
for r.peekByte(true) != ')' && r.err == nil {
r.readImport(imports)
}
r.nextByte(false)
} else {
r.readImport(imports)
}
}
// If we stopped successfully before EOF, we read a byte that told us we were done.
// Return all but that last byte, which would cause a syntax error if we let it through.
if r.err == nil && !r.eof {
return r.buf[:len(r.buf)-1], nil
}
// If we stopped for a syntax error, consume the whole file so that
// we are sure we don't change the errors that go/parser returns.
if r.err == errSyntax && !reportSyntaxError {
r.err = nil
for r.err == nil && !r.eof {
r.readByte()
}
}
return r.buf, r.err
}
// 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 build
import (
"io"
"strings"
"testing"
)
const quote = "`"
type readTest struct {
// Test input contains ℙ where readImports should stop.
in string
err string
}
var readImportsTests = []readTest{
{
`package p`,
"",
},
{
`package p; import "x"`,
"",
},
{
`package p; import . "x"`,
"",
},
{
`package p; import "x";ℙvar x = 1`,
"",
},
{
`package p
// comment
import "x"
import _ "x"
import a "x"
/* comment */
import (
"x" /* comment */
_ "x"
a "x" // comment
` + quote + `x` + quote + `
_ /*comment*/ ` + quote + `x` + quote + `
a ` + quote + `x` + quote + `
)
import (
)
import ()
import()import()import()
import();import();import()
ℙvar x = 1
`,
"",
},
}
var readCommentsTests = []readTest{
{
`ℙpackage p`,
"",
},
{
`ℙpackage p; import "x"`,
"",
},
{
`ℙpackage p; import . "x"`,
"",
},
{
`// foo
/* bar */
/* quux */ // baz
/*/ zot */
// asdf
ℙHello, world`,
"",
},
}
func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
for i, tt := range tests {
var in, testOut string
j := strings.Index(tt.in, "ℙ")
if j < 0 {
in = tt.in
testOut = tt.in
} else {
in = tt.in[:j] + tt.in[j+len("ℙ"):]
testOut = tt.in[:j]
}
r := strings.NewReader(in)
buf, err := read(r)
if err != nil {
if tt.err == "" {
t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
continue
}
if !strings.Contains(err.Error(), tt.err) {
t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
continue
}
continue
}
if err == nil && tt.err != "" {
t.Errorf("#%d: success, expected %q", i, tt.err)
continue
}
out := string(buf)
if out != testOut {
t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
}
}
}
func TestReadImports(t *testing.T) {
testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
}
func TestReadComments(t *testing.T) {
testRead(t, readCommentsTests, readComments)
}
var readFailuresTests = []readTest{
{
`package`,
"syntax error",
},
{
"package p\n\x00\nimport `math`\n",
"unexpected NUL in input",
},
{
`package p; import`,
"syntax error",
},
{
`package p; import "`,
"syntax error",
},
{
"package p; import ` \n\n",
"syntax error",
},
{
`package p; import "x`,
"syntax error",
},
{
`package p; import _`,
"syntax error",
},
{
`package p; import _ "`,
"syntax error",
},
{
`package p; import _ "x`,
"syntax error",
},
{
`package p; import .`,
"syntax error",
},
{
`package p; import . "`,
"syntax error",
},
{
`package p; import . "x`,
"syntax error",
},
{
`package p; import (`,
"syntax error",
},
{
`package p; import ("`,
"syntax error",
},
{
`package p; import ("x`,
"syntax error",
},
{
`package p; import ("x"`,
"syntax error",
},
}
func TestReadFailures(t *testing.T) {
// Errors should be reported (true arg to readImports).
testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
}
func TestReadFailuresIgnored(t *testing.T) {
// Syntax errors should not be reported (false arg to readImports).
// Instead, entire file should be the output and no error.
// Convert tests not to return syntax errors.
tests := make([]readTest, len(readFailuresTests))
copy(tests, readFailuresTests)
for i := range tests {
tt := &tests[i]
if !strings.Contains(tt.err, "NUL") {
tt.err = ""
}
}
testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false, nil) })
}
// Copyright 2011 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 build
const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "
// Copyright 2011 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 build
import (
"runtime"
"testing"
)
var (
thisOS = runtime.GOOS
thisArch = runtime.GOARCH
otherOS = anotherOS()
otherArch = anotherArch()
)
func anotherOS() string {
if thisOS != "darwin" {
return "darwin"
}
return "linux"
}
func anotherArch() string {
if thisArch != "amd64" {
return "amd64"
}
return "386"
}
type GoodFileTest struct {
name string
result bool
}
var tests = []GoodFileTest{
{"file.go", true},
{"file.c", true},
{"file_foo.go", true},
{"file_" + thisArch + ".go", true},
{"file_" + otherArch + ".go", false},
{"file_" + thisOS + ".go", true},
{"file_" + otherOS + ".go", false},
{"file_" + thisOS + "_" + thisArch + ".go", true},
{"file_" + otherOS + "_" + thisArch + ".go", false},
{"file_" + thisOS + "_" + otherArch + ".go", false},
{"file_" + otherOS + "_" + otherArch + ".go", false},
{"file_foo_" + thisArch + ".go", true},
{"file_foo_" + otherArch + ".go", false},
{"file_" + thisOS + ".c", true},
{"file_" + otherOS + ".c", false},
}
func TestGoodOSArch(t *testing.T) {
for _, test := range tests {
if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result {
t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result)
}
}
}
// Test data - not compiled.
package main
func main() {}
// Test data - not compiled.
package test_package
func init() {}
// Test data - not compiled.
package file
func F() {}
// Test data - not compiled.
package main
import (
"./file"
)
func main() {
file.F()
}
// Copyright 2014 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.
// +build !go1.4
package constant
import (
"math"
"math/big"
)
func ratToFloat32(x *big.Rat) (float32, bool) {
// Before 1.4, there's no Rat.Float32.
// Emulate it, albeit at the cost of
// imprecision in corner cases.
x64, exact := x.Float64()
x32 := float32(x64)
if math.IsInf(float64(x32), 0) {
exact = false
}
return x32, exact
}
// Copyright 2014 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.
// +build go1.4
package constant
import "math/big"
func ratToFloat32(x *big.Rat) (float32, bool) {
return x.Float32()
}
// Copyright 2013 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 constant
import (
"k8s.io/kubernetes/third_party/golang/go/token"
"strings"
"testing"
)
// TODO(gri) expand this test framework
var opTests = []string{
// unary operations
`+ 0 = 0`,
`+ ? = ?`,
`- 1 = -1`,
`- ? = ?`,
`^ 0 = -1`,
`^ ? = ?`,
`! true = false`,
`! false = true`,
`! ? = ?`,
// etc.
// binary operations
`"" + "" = ""`,
`"foo" + "" = "foo"`,
`"" + "bar" = "bar"`,
`"foo" + "bar" = "foobar"`,
`0 + 0 = 0`,
`0 + 0.1 = 0.1`,
`0 + 0.1i = 0.1i`,
`0.1 + 0.9 = 1`,
`1e100 + 1e100 = 2e100`,
`? + 0 = ?`,
`0 + ? = ?`,
`0 - 0 = 0`,
`0 - 0.1 = -0.1`,
`0 - 0.1i = -0.1i`,
`1e100 - 1e100 = 0`,
`? - 0 = ?`,
`0 - ? = ?`,
`0 * 0 = 0`,
`1 * 0.1 = 0.1`,
`1 * 0.1i = 0.1i`,
`1i * 1i = -1`,
`? * 0 = ?`,
`0 * ? = ?`,
`0 / 0 = "division_by_zero"`,
`10 / 2 = 5`,
`5 / 3 = 5/3`,
`5i / 3i = 5/3`,
`? / 0 = ?`,
`0 / ? = ?`,
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
`10 % 3 = 1`,
`? % 0 = ?`,
`0 % ? = ?`,
`0 & 0 = 0`,
`12345 & 0 = 0`,
`0xff & 0xf = 0xf`,
`? & 0 = ?`,
`0 & ? = ?`,
`0 | 0 = 0`,
`12345 | 0 = 12345`,
`0xb | 0xa0 = 0xab`,
`? | 0 = ?`,
`0 | ? = ?`,
`0 ^ 0 = 0`,
`1 ^ -1 = -2`,
`? ^ 0 = ?`,
`0 ^ ? = ?`,
`0 &^ 0 = 0`,
`0xf &^ 1 = 0xe`,
`1 &^ 0xf = 0`,
// etc.
// shifts
`0 << 0 = 0`,
`1 << 10 = 1024`,
`0 >> 0 = 0`,
`1024 >> 10 == 1`,
`? << 0 == ?`,
`? >> 10 == ?`,
// etc.
// comparisons
`false == false = true`,
`false == true = false`,
`true == false = false`,
`true == true = true`,
`false != false = false`,
`false != true = true`,
`true != false = true`,
`true != true = false`,
`"foo" == "bar" = false`,
`"foo" != "bar" = true`,
`"foo" < "bar" = false`,
`"foo" <= "bar" = false`,
`"foo" > "bar" = true`,
`"foo" >= "bar" = true`,
`0 == 0 = true`,
`0 != 0 = false`,
`0 < 10 = true`,
`10 <= 10 = true`,
`0 > 10 = false`,
`10 >= 10 = true`,
`1/123456789 == 1/123456789 == true`,
`1/123456789 != 1/123456789 == false`,
`1/123456789 < 1/123456788 == true`,
`1/123456788 <= 1/123456789 == false`,
`0.11 > 0.11 = false`,
`0.11 >= 0.11 = true`,
`? == 0 = false`,
`? != 0 = false`,
`? < 10 = false`,
`? <= 10 = false`,
`? > 10 = false`,
`? >= 10 = false`,
`0 == ? = false`,
`0 != ? = false`,
`0 < ? = false`,
`10 <= ? = false`,
`0 > ? = false`,
`10 >= ? = false`,
// etc.
}
func TestOps(t *testing.T) {
for _, test := range opTests {
a := strings.Split(test, " ")
i := 0 // operator index
var x, x0 Value
switch len(a) {
case 4:
// unary operation
case 5:
// binary operation
x, x0 = val(a[0]), val(a[0])
i = 1
default:
t.Errorf("invalid test case: %s", test)
continue
}
op, ok := optab[a[i]]
if !ok {
panic("missing optab entry for " + a[i])
}
y, y0 := val(a[i+1]), val(a[i+1])
got := doOp(x, op, y)
want := val(a[i+3])
if !eql(got, want) {
t.Errorf("%s: got %s; want %s", test, got, want)
}
if x0 != nil && !eql(x, x0) {
t.Errorf("%s: x changed to %s", test, x)
}
if !eql(y, y0) {
t.Errorf("%s: y changed to %s", test, y)
}
}
}
func eql(x, y Value) bool {
_, ux := x.(unknownVal)
_, uy := y.(unknownVal)
if ux || uy {
return ux == uy
}
return Compare(x, token.EQL, y)
}
// ----------------------------------------------------------------------------
// Support functions
func val(lit string) Value {
if len(lit) == 0 {
return MakeUnknown()
}
switch lit {
case "?":
return MakeUnknown()
case "true":
return MakeBool(true)
case "false":
return MakeBool(false)
}
tok := token.INT
switch first, last := lit[0], lit[len(lit)-1]; {
case first == '"' || first == '`':
tok = token.STRING
lit = strings.Replace(lit, "_", " ", -1)
case first == '\'':
tok = token.CHAR
case last == 'i':
tok = token.IMAG
default:
if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") {
tok = token.FLOAT
}
}
return MakeFromLiteral(lit, tok, 0)
}
var optab = map[string]token.Token{
"!": token.NOT,
"+": token.ADD,
"-": token.SUB,
"*": token.MUL,
"/": token.QUO,
"%": token.REM,
"<<": token.SHL,
">>": token.SHR,
"&": token.AND,
"|": token.OR,
"^": token.XOR,
"&^": token.AND_NOT,
"==": token.EQL,
"!=": token.NEQ,
"<": token.LSS,
"<=": token.LEQ,
">": token.GTR,
">=": token.GEQ,
}
func panicHandler(v *Value) {
switch p := recover().(type) {
case nil:
// nothing to do
case string:
*v = MakeString(p)
case error:
*v = MakeString(p.Error())
default:
panic(p)
}
}
func doOp(x Value, op token.Token, y Value) (z Value) {
defer panicHandler(&z)
if x == nil {
return UnaryOp(op, y, 0)
}
switch op {
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
return MakeBool(Compare(x, op, y))
case token.SHL, token.SHR:
s, _ := Int64Val(y)
return Shift(x, op, uint(s))
default:
return BinaryOp(x, op, y)
}
}
// ----------------------------------------------------------------------------
// Other tests
var fracTests = []string{
"0 0 1",
"1 1 1",
"-1 -1 1",
"1.2 6 5",
"-0.991 -991 1000",
"1e100 1e100 1",
}
func TestFractions(t *testing.T) {
for _, test := range fracTests {
a := strings.Split(test, " ")
if len(a) != 3 {
t.Errorf("invalid test case: %s", test)
continue
}
x := val(a[0])
n := val(a[1])
d := val(a[2])
if got := Num(x); !eql(got, n) {
t.Errorf("%s: got num = %s; want %s", test, got, n)
}
if got := Denom(x); !eql(got, d) {
t.Errorf("%s: got denom = %s; want %s", test, got, d)
}
}
}
var bytesTests = []string{
"0",
"1",
"123456789",
"123456789012345678901234567890123456789012345678901234567890",
}
func TestBytes(t *testing.T) {
for _, test := range bytesTests {
x := val(test)
bytes := Bytes(x)
// special case 0
if Sign(x) == 0 && len(bytes) != 0 {
t.Errorf("%s: got %v; want empty byte slice", test, bytes)
}
if n := len(bytes); n > 0 && bytes[n-1] == 0 {
t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
}
if got := MakeFromBytes(bytes); !eql(got, x) {
t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
}
}
}
func TestUnknown(t *testing.T) {
u := MakeUnknown()
var values = []Value{
u,
MakeBool(false), // token.ADD ok below, operation is never considered
MakeString(""),
MakeInt64(1),
MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
MakeFloat64(1.2),
MakeImag(MakeFloat64(1.2)),
}
for _, val := range values {
x, y := val, u
for i := range [2]int{} {
if i == 1 {
x, y = y, x
}
if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
}
if got := Compare(x, token.EQL, y); got {
t.Errorf("%s == %s: got true; want false", x, y)
}
}
}
}
# Copyright 2009 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.
# Script to test heading detection heuristic
headscan: headscan.go
go build headscan.go
// Copyright 2011 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 doc
import (
"bytes"
"reflect"
"testing"
)
var headingTests = []struct {
line string
ok bool
}{
{"Section", true},
{"A typical usage", true},
{"ΔΛΞ is Greek", true},
{"Foo 42", true},
{"", false},
{"section", false},
{"A typical usage:", false},
{"This code:", false},
{"δ is Greek", false},
{"Foo §", false},
{"Fermat's Last Sentence", true},
{"Fermat's", true},
{"'sX", false},
{"Ted 'Too' Bar", false},
{"Use n+m", false},
{"Scanning:", false},
{"N:M", false},
}
func TestIsHeading(t *testing.T) {
for _, tt := range headingTests {
if h := heading(tt.line); (len(h) > 0) != tt.ok {
t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok)
}
}
}
var blocksTests = []struct {
in string
out []block
text string
}{
{
in: `Para 1.
Para 1 line 2.
Para 2.
Section
Para 3.
pre
pre1
Para 4.
pre
pre1
pre2
Para 5.
pre
pre1
pre2
Para 6.
pre
pre2
`,
out: []block{
{opPara, []string{"Para 1.\n", "Para 1 line 2.\n"}},
{opPara, []string{"Para 2.\n"}},
{opHead, []string{"Section"}},
{opPara, []string{"Para 3.\n"}},
{opPre, []string{"pre\n", "pre1\n"}},
{opPara, []string{"Para 4.\n"}},
{opPre, []string{"pre\n", "pre1\n", "\n", "pre2\n"}},
{opPara, []string{"Para 5.\n"}},
{opPre, []string{"pre\n", "\n", "\n", "pre1\n", "pre2\n"}},
{opPara, []string{"Para 6.\n"}},
{opPre, []string{"pre\n", "pre2\n"}},
},
text: `. Para 1. Para 1 line 2.
. Para 2.
. Section
. Para 3.
$ pre
$ pre1
. Para 4.
$ pre
$ pre1
$ pre2
. Para 5.
$ pre
$ pre1
$ pre2
. Para 6.
$ pre
$ pre2
`,
},
}
func TestBlocks(t *testing.T) {
for i, tt := range blocksTests {
b := blocks(tt.in)
if !reflect.DeepEqual(b, tt.out) {
t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, b, tt.out)
}
}
}
func TestToText(t *testing.T) {
var buf bytes.Buffer
for i, tt := range blocksTests {
ToText(&buf, tt.in, ". ", "$\t", 40)
if have := buf.String(); have != tt.text {
t.Errorf("#%d: mismatch\nhave: %s\nwant: %s\nhave vs want:\n%q\n%q", i, have, tt.text, have, tt.text)
}
buf.Reset()
}
}
var emphasizeTests = []struct {
in, out string
}{
{"http://www.google.com/", `<a href="http://www.google.com/">http://www.google.com/</a>`},
{"https://www.google.com/", `<a href="https://www.google.com/">https://www.google.com/</a>`},
{"http://www.google.com/path.", `<a href="http://www.google.com/path">http://www.google.com/path</a>.`},
{"http://en.wikipedia.org/wiki/Camellia_(cipher)", `<a href="http://en.wikipedia.org/wiki/Camellia_(cipher)">http://en.wikipedia.org/wiki/Camellia_(cipher)</a>`},
{"(http://www.google.com/)", `(<a href="http://www.google.com/">http://www.google.com/</a>)`},
{"http://gmail.com)", `<a href="http://gmail.com">http://gmail.com</a>)`},
{"((http://gmail.com))", `((<a href="http://gmail.com">http://gmail.com</a>))`},
{"http://gmail.com ((http://gmail.com)) ()", `<a href="http://gmail.com">http://gmail.com</a> ((<a href="http://gmail.com">http://gmail.com</a>)) ()`},
{"Foo bar http://example.com/ quux!", `Foo bar <a href="http://example.com/">http://example.com/</a> quux!`},
{"Hello http://example.com/%2f/ /world.", `Hello <a href="http://example.com/%2f/">http://example.com/%2f/</a> /world.`},
{"Lorem http: ipsum //host/path", "Lorem http: ipsum //host/path"},
{"javascript://is/not/linked", "javascript://is/not/linked"},
}
func TestEmphasize(t *testing.T) {
for i, tt := range emphasizeTests {
var buf bytes.Buffer
emphasize(&buf, tt.in, nil, true)
out := buf.String()
if out != tt.out {
t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, out, tt.out)
}
}
}
var pairedParensPrefixLenTests = []struct {
in, out string
}{
{"", ""},
{"foo", "foo"},
{"()", "()"},
{"foo()", "foo()"},
{"foo()()()", "foo()()()"},
{"foo()((()()))", "foo()((()()))"},
{"foo()((()()))bar", "foo()((()()))bar"},
{"foo)", "foo"},
{"foo))", "foo"},
{"foo)))))", "foo"},
{"(foo", ""},
{"((foo", ""},
{"(((((foo", ""},
{"(foo)", "(foo)"},
{"((((foo))))", "((((foo))))"},
{"foo()())", "foo()()"},
{"foo((()())", "foo"},
{"foo((()())) (() foo ", "foo((()())) "},
}
func TestPairedParensPrefixLen(t *testing.T) {
for i, tt := range pairedParensPrefixLenTests {
if out := tt.in[:pairedParensPrefixLen(tt.in)]; out != tt.out {
t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
}
}
}
// Copyright 2009 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 doc extracts source code documentation from a Go AST.
package doc
import (
"k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/third_party/golang/go/token"
)
// Package is the documentation for an entire package.
type Package struct {
Doc string
Name string
ImportPath string
Imports []string
Filenames []string
Notes map[string][]*Note
// Deprecated: For backward compatibility Bugs is still populated,
// but all new code should use Notes instead.
Bugs []string
// declarations
Consts []*Value
Types []*Type
Vars []*Value
Funcs []*Func
}
// Value is the documentation for a (possibly grouped) var or const declaration.
type Value struct {
Doc string
Names []string // var or const names in declaration order
Decl *ast.GenDecl
order int
}
// Type is the documentation for a type declaration.
type Type struct {
Doc string
Name string
Decl *ast.GenDecl
// associated declarations
Consts []*Value // sorted list of constants of (mostly) this type
Vars []*Value // sorted list of variables of (mostly) this type
Funcs []*Func // sorted list of functions returning this type
Methods []*Func // sorted list of methods (including embedded ones) of this type
}
// Func is the documentation for a func declaration.
type Func struct {
Doc string
Name string
Decl *ast.FuncDecl
// methods
// (for functions, these fields have the respective zero value)
Recv string // actual receiver "T" or "*T"
Orig string // original receiver "T" or "*T"
Level int // embedding level; 0 means not embedded
}
// A Note represents a marked comment starting with "MARKER(uid): note body".
// Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
// at least one character is recognized. The ":" following the uid is optional.
// Notes are collected in the Package.Notes map indexed by the notes marker.
type Note struct {
Pos, End token.Pos // position range of the comment containing the marker
UID string // uid found with the marker
Body string // note body text
}
// Mode values control the operation of New.
type Mode int
const (
// extract documentation for all package-level declarations,
// not just exported ones
AllDecls Mode = 1 << iota
// show all embedded methods, not just the ones of
// invisible (unexported) anonymous fields
AllMethods
)
// New computes the package documentation for the given package AST.
// New takes ownership of the AST pkg and may edit or overwrite it.
//
func New(pkg *ast.Package, importPath string, mode Mode) *Package {
var r reader
r.readPackage(pkg, mode)
r.computeMethodSets()
r.cleanupTypes()
return &Package{
Doc: r.doc,
Name: pkg.Name,
ImportPath: importPath,
Imports: sortedKeys(r.imports),
Filenames: r.filenames,
Notes: r.notes,
Bugs: noteBodies(r.notes["BUG"]),
Consts: sortedValues(r.values, token.CONST),
Types: sortedTypes(r.types, mode&AllMethods != 0),
Vars: sortedValues(r.values, token.VAR),
Funcs: sortedFuncs(r.funcs, true),
}
}
// 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 doc
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"text/template"
"k8s.io/kubernetes/third_party/golang/go/parser"
"k8s.io/kubernetes/third_party/golang/go/printer"
"k8s.io/kubernetes/third_party/golang/go/token"
)
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"
var templateTxt = readTemplate("template.txt")
func readTemplate(filename string) *template.Template {
t := template.New(filename)
t.Funcs(template.FuncMap{
"node": nodeFmt,
"synopsis": synopsisFmt,
"indent": indentFmt,
})
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
}
func nodeFmt(node interface{}, fset *token.FileSet) string {
var buf bytes.Buffer
printer.Fprint(&buf, fset, node)
return strings.Replace(strings.TrimSpace(buf.String()), "\n", "\n\t", -1)
}
func synopsisFmt(s string) string {
const n = 64
if len(s) > n {
// cut off excess text and go back to a word boundary
s = s[0:n]
if i := strings.LastIndexAny(s, "\t\n "); i >= 0 {
s = s[0:i]
}
s = strings.TrimSpace(s) + " ..."
}
return "// " + strings.Replace(s, "\n", " ", -1)
}
func indentFmt(indent, s string) string {
end := ""
if strings.HasSuffix(s, "\n") {
end = "\n"
s = s[:len(s)-1]
}
return indent + strings.Replace(s, "\n", "\n"+indent, -1) + end
}
func isGoFile(fi os.FileInfo) bool {
name := fi.Name()
return !fi.IsDir() &&
len(name) > 0 && name[0] != '.' && // ignore .files
filepath.Ext(name) == ".go"
}
type bundle struct {
*Package
FSet *token.FileSet
}
func test(t *testing.T, mode Mode) {
// 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, filter, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
// test packages
for _, pkg := range pkgs {
importpath := dataDir + "/" + pkg.Name
doc := New(pkg, importpath, mode)
// golden files always use / in filenames - canonicalize them
for i, filename := range doc.Filenames {
doc.Filenames[i] = filepath.ToSlash(filename)
}
// print documentation
var buf bytes.Buffer
if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil {
t.Error(err)
continue
}
got := buf.Bytes()
// update golden file if necessary
golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode))
if *update {
err := ioutil.WriteFile(golden, got, 0644)
if err != nil {
t.Error(err)
}
continue
}
// get golden file
want, err := ioutil.ReadFile(golden)
if err != nil {
t.Error(err)
continue
}
// compare
if !bytes.Equal(got, want) {
t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
}
}
}
func Test(t *testing.T) {
test(t, 0)
test(t, AllDecls)
test(t, AllMethods)
}
// Copyright 2013 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 doc_test
import (
"bytes"
"go/doc"
"go/format"
"go/parser"
"go/token"
"strings"
"testing"
)
const exampleTestFile = `
package foo_test
import (
"flag"
"fmt"
"log"
"os/exec"
)
func ExampleHello() {
fmt.Println("Hello, world!")
// Output: Hello, world!
}
func ExampleImport() {
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
func ExampleKeyValue() {
v := struct {
a string
b int
}{
a: "A",
b: 1,
}
fmt.Print(v)
// Output: a: "A", b: 1
}
func ExampleKeyValueImport() {
f := flag.Flag{
Name: "play",
}
fmt.Print(f)
// Output: Name: "play"
}
var keyValueTopDecl = struct {
a string
b int
}{
a: "B",
b: 2,
}
func ExampleKeyValueTopDecl() {
fmt.Print(keyValueTopDecl)
}
`
var exampleTestCases = []struct {
Name, Play, Output string
}{
{
Name: "Hello",
Play: exampleHelloPlay,
Output: "Hello, world!\n",
},
{
Name: "Import",
Play: exampleImportPlay,
},
{
Name: "KeyValue",
Play: exampleKeyValuePlay,
Output: "a: \"A\", b: 1\n",
},
{
Name: "KeyValueImport",
Play: exampleKeyValueImportPlay,
Output: "Name: \"play\"\n",
},
{
Name: "KeyValueTopDecl",
Play: "<nil>",
},
}
const exampleHelloPlay = `package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, world!")
}
`
const exampleImportPlay = `package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
`
const exampleKeyValuePlay = `package main
import (
"fmt"
)
func main() {
v := struct {
a string
b int
}{
a: "A",
b: 1,
}
fmt.Print(v)
}
`
const exampleKeyValueImportPlay = `package main
import (
"flag"
"fmt"
)
func main() {
f := flag.Flag{
Name: "play",
}
fmt.Print(f)
}
`
func TestExamples(t *testing.T) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
if err != nil {
t.Fatal(err)
}
for i, e := range doc.Examples(file) {
c := exampleTestCases[i]
if e.Name != c.Name {
t.Errorf("got Name == %q, want %q", e.Name, c.Name)
}
if w := c.Play; w != "" {
var g string // hah
if e.Play == nil {
g = "<nil>"
} else {
var buf bytes.Buffer
if err := format.Node(&buf, fset, e.Play); err != nil {
t.Fatal(err)
}
g = buf.String()
}
if g != w {
t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
}
}
if g, w := e.Output, c.Output; g != w {
t.Errorf("%s: got Output == %q, want %q", c.Name, g, w)
}
}
}
// Copyright 2011 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.
// This file implements export filtering of an AST.
package doc
import (
"k8s.io/kubernetes/third_party/golang/go/ast"
"k8s.io/kubernetes/third_party/golang/go/token"
)
// filterIdentList removes unexported names from list in place
// and returns the resulting list.
//
func filterIdentList(list []*ast.Ident) []*ast.Ident {
j := 0
for _, x := range list {
if ast.IsExported(x.Name) {
list[j] = x
j++
}
}
return list[0:j]
}
// hasExportedName reports whether list contains any exported names.
//
func hasExportedName(list []*ast.Ident) bool {
for _, x := range list {
if x.IsExported() {
return true
}
}
return false
}
// 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 reports whether 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, ityp *ast.InterfaceType) (removedFields bool) {
if fields == nil {
return
}
list := fields.List
j := 0
for _, field := range list {
keepField := false
if n := len(field.Names); n == 0 {
// anonymous field
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)
if len(field.Names) < n {
removedFields = true
}
if len(field.Names) > 0 {
keepField = true
}
}
if keepField {
r.filterType(nil, field.Type)
list[j] = field
j++
}
}
if j < len(list) {
removedFields = true
}
fields.List = list[0:j]
return
}
// filterParamList applies filterType to each parameter type in fields.
//
func (r *reader) filterParamList(fields *ast.FieldList) {
if fields != nil {
for _, f := range fields.List {
r.filterType(nil, f.Type)
}
}
}
// filterType strips any unexported struct fields or method types from typ
// in place. If fields (or methods) have been removed, the corresponding
// struct or interface type has the Incomplete field set to true.
//
func (r *reader) filterType(parent *namedType, typ ast.Expr) {
switch t := typ.(type) {
case *ast.Ident:
// nothing to do
case *ast.ParenExpr:
r.filterType(nil, t.X)
case *ast.ArrayType:
r.filterType(nil, t.Elt)
case *ast.StructType:
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, t) {
t.Incomplete = true
}
case *ast.MapType:
r.filterType(nil, t.Key)
r.filterType(nil, t.Value)
case *ast.ChanType:
r.filterType(nil, t.Value)
}
}
func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
switch s := spec.(type) {
case *ast.ImportSpec:
// always keep imports so we can collect them
return true
case *ast.ValueSpec:
s.Names = filterIdentList(s.Names)
if len(s.Names) > 0 {
r.filterType(nil, s.Type)
return true
}
case *ast.TypeSpec:
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
}
// copyConstType returns a copy of typ with position pos.
// typ must be a valid constant type.
// In practice, only (possibly qualified) identifiers are possible.
//
func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr {
switch typ := typ.(type) {
case *ast.Ident:
return &ast.Ident{Name: typ.Name, NamePos: pos}
case *ast.SelectorExpr:
if id, ok := typ.X.(*ast.Ident); ok {
// presumably a qualified identifier
return &ast.SelectorExpr{
Sel: ast.NewIdent(typ.Sel.Name),
X: &ast.Ident{Name: id.Name, NamePos: pos},
}
}
}
return nil // shouldn't happen, but be conservative and don't panic
}
func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
if tok == token.CONST {
// Propagate any type information that would get lost otherwise
// when unexported constants are filtered.
var prevType ast.Expr
for _, spec := range list {
spec := spec.(*ast.ValueSpec)
if spec.Type == nil && prevType != nil {
// provide current spec with an explicit type
spec.Type = copyConstType(prevType, spec.Pos())
}
if hasExportedName(spec.Names) {
// exported names are preserved so there's no need to propagate the type
prevType = nil
} else {
prevType = spec.Type
}
}
}
j := 0
for _, s := range list {
if r.filterSpec(s, tok) {
list[j] = s
j++
}
}
return list[0:j]
}
func (r *reader) filterDecl(decl ast.Decl) bool {
switch d := decl.(type) {
case *ast.GenDecl:
d.Specs = r.filterSpecList(d.Specs, d.Tok)
return len(d.Specs) > 0
case *ast.FuncDecl:
// ok to filter these methods early because any
// conflicting method will be filtered here, too -
// thus, removing these methods early will not lead
// to the false removal of possible conflicts
return ast.IsExported(d.Name.Name)
}
return false
}
// fileExports removes unexported declarations from src in place.
//
func (r *reader) fileExports(src *ast.File) {
j := 0
for _, d := range src.Decls {
if r.filterDecl(d) {
src.Decls[j] = d
j++
}
}
src.Decls = src.Decls[0:j]
}
// Copyright 2009 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 doc
import "k8s.io/kubernetes/third_party/golang/go/ast"
type Filter func(string) bool
func matchFields(fields *ast.FieldList, f Filter) bool {
if fields != nil {
for _, field := range fields.List {
for _, name := range field.Names {
if f(name.Name) {
return true
}
}
}
}
return false
}
func matchDecl(d *ast.GenDecl, f Filter) bool {
for _, d := range d.Specs {
switch v := d.(type) {
case *ast.ValueSpec:
for _, name := range v.Names {
if f(name.Name) {
return true
}
}
case *ast.TypeSpec:
if f(v.Name.Name) {
return true
}
switch t := v.Type.(type) {
case *ast.StructType:
if matchFields(t.Fields, f) {
return true
}
case *ast.InterfaceType:
if matchFields(t.Methods, f) {
return true
}
}
}
}
return false
}
func filterValues(a []*Value, f Filter) []*Value {
w := 0
for _, vd := range a {
if matchDecl(vd.Decl, f) {
a[w] = vd
w++
}
}
return a[0:w]
}
func filterFuncs(a []*Func, f Filter) []*Func {
w := 0
for _, fd := range a {
if f(fd.Name) {
a[w] = fd
w++
}
}
return a[0:w]
}
func filterTypes(a []*Type, f Filter) []*Type {
w := 0
for _, td := range a {
n := 0 // number of matches
if matchDecl(td.Decl, f) {
n = 1
} else {
// type name doesn't match, but we may have matching consts, vars, factories or methods
td.Consts = filterValues(td.Consts, f)
td.Vars = filterValues(td.Vars, f)
td.Funcs = filterFuncs(td.Funcs, f)
td.Methods = filterFuncs(td.Methods, f)
n += len(td.Consts) + len(td.Vars) + len(td.Funcs) + len(td.Methods)
}
if n > 0 {
a[w] = td
w++
}
}
return a[0:w]
}
// Filter eliminates documentation for names that don't pass through the filter f.
// TODO(gri): Recognize "Type.Method" as a name.
//
func (p *Package) Filter(f Filter) {
p.Consts = filterValues(p.Consts, f)
p.Vars = filterValues(p.Vars, f)
p.Types = filterTypes(p.Types, f)
p.Funcs = filterFuncs(p.Funcs, f)
p.Doc = "" // don't show top-level package doc
}
// Copyright 2011 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.
// +build ignore
/*
The headscan command extracts comment headings from package files;
it is used to detect false positives which may require an adjustment
to the comment formatting heuristics in comment.go.
Usage: headscan [-root root_directory]
By default, the $GOROOT/src directory is scanned.
*/
package main
import (
"bytes"
"flag"
"fmt"
"k8s.io/kubernetes/third_party/golang/go/doc"
"k8s.io/kubernetes/third_party/golang/go/parser"
"k8s.io/kubernetes/third_party/golang/go/token"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
)
var (
root = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan")
verbose = flag.Bool("v", false, "verbose mode")
)
// ToHTML in comment.go assigns a (possibly blank) ID to each heading
var html_h = regexp.MustCompile(`<h3 id="[^"]*">`)
const html_endh = "</h3>\n"
func isGoFile(fi os.FileInfo) bool {
return strings.HasSuffix(fi.Name(), ".go") &&
!strings.HasSuffix(fi.Name(), "_test.go")
}
func appendHeadings(list []string, comment string) []string {
var buf bytes.Buffer
doc.ToHTML(&buf, comment, nil)
for s := buf.String(); ; {
loc := html_h.FindStringIndex(s)
if len(loc) == 0 {
break
}
i := loc[1]
j := strings.Index(s, html_endh)
if j < 0 {
list = append(list, s[i:]) // incorrect HTML
break
}
list = append(list, s[i:j])
s = s[j+len(html_endh):]
}
return list
}
func main() {
flag.Parse()
fset := token.NewFileSet()
nheadings := 0
err := filepath.Walk(*root, func(path string, fi os.FileInfo, err error) error {
if !fi.IsDir() {
return nil
}
pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments)
if err != nil {
if *verbose {
fmt.Fprintln(os.Stderr, err)
}
return nil
}
for _, pkg := range pkgs {
d := doc.New(pkg, path, doc.Mode(0))
list := appendHeadings(nil, d.Doc)
for _, d := range d.Consts {
list = appendHeadings(list, d.Doc)
}
for _, d := range d.Types {
list = appendHeadings(list, d.Doc)
}
for _, d := range d.Vars {
list = appendHeadings(list, d.Doc)
}
for _, d := range d.Funcs {
list = appendHeadings(list, d.Doc)
}
if len(list) > 0 {
// directories may contain multiple packages;
// print path and package name
fmt.Printf("%s (package %s)\n", path, pkg.Name)
for _, h := range list {
fmt.Printf("\t%s\n", h)
}
nheadings += len(list)
}
}
return nil
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(nheadings, "headings found")
}
// 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 doc
import (
"strings"
"unicode"
)
// firstSentenceLen returns the length of the first sentence in s.
// The sentence ends after the first period followed by space and
// not preceded by exactly one uppercase letter.
//
func firstSentenceLen(s string) int {
var ppp, pp, p rune
for i, q := range s {
if q == '\n' || q == '\r' || q == '\t' {
q = ' '
}
if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
return i
}
if p == '。' || p == '.' {
return i
}
ppp, pp, p = pp, p, q
}
return len(s)
}
const (
keepNL = 1 << iota
)
// clean replaces each sequence of space, \n, \r, or \t characters
// with a single space and removes any trailing and leading spaces.
// If the keepNL flag is set, newline characters are passed through
// instead of being change to spaces.
func clean(s string, flags int) string {
var b []byte
p := byte(' ')
for i := 0; i < len(s); i++ {
q := s[i]
if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' {
q = ' '
}
if q != ' ' || p != ' ' {
b = append(b, q)
p = q
}
}
// remove trailing blank, if any
if n := len(b); n > 0 && p == ' ' {
b = b[0 : n-1]
}
return string(b)
}
// Synopsis returns a cleaned version of the first sentence in s.
// That sentence ends after the first period followed by space and
// not preceded by exactly one uppercase letter. The result string
// has no \n, \r, or \t characters and uses only single spaces between
// words. If s starts with any of the IllegalPrefixes, the result
// is the empty string.
//
func Synopsis(s string) string {
s = clean(s[0:firstSentenceLen(s)], 0)
for _, prefix := range IllegalPrefixes {
if strings.HasPrefix(strings.ToLower(s), prefix) {
return ""
}
}
return s
}
var IllegalPrefixes = []string{
"copyright",
"all rights",
"author",
}
// 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 doc
import "testing"
var tests = []struct {
txt string
fsl int
syn string
}{
{"", 0, ""},
{"foo", 3, "foo"},
{"foo.", 4, "foo."},
{"foo.bar", 7, "foo.bar"},
{" foo. ", 6, "foo."},
{" foo\t bar.\n", 12, "foo bar."},
{" foo\t bar.\n", 12, "foo bar."},
{"a b\n\nc\r\rd\t\t", 12, "a b c d"},
{"a b\n\nc\r\rd\t\t . BLA", 15, "a b c d ."},
{"Package poems by T.S.Eliot. To rhyme...", 27, "Package poems by T.S.Eliot."},
{"Package poems by T. S. Eliot. To rhyme...", 29, "Package poems by T. S. Eliot."},
{"foo implements the foo ABI. The foo ABI is...", 27, "foo implements the foo ABI."},
{"Package\nfoo. ..", 12, "Package foo."},
{"P . Q.", 3, "P ."},
{"P. Q. ", 8, "P. Q."},
{"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
{"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
{"Package こんにちは。世界", 26, "Package こんにちは。"},
{"Package 안녕.世界", 17, "Package 안녕."},
{"Package foo does bar.", 21, "Package foo does bar."},
{"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""},
{"All Rights reserved. Package foo does bar.", 20, ""},
{"All rights reserved. Package foo does bar.", 20, ""},
{"Authors: foo@bar.com. Package foo does bar.", 21, ""},
}
func TestSynopsis(t *testing.T) {
for _, e := range tests {
fsl := firstSentenceLen(e.txt)
if fsl != e.fsl {
t.Errorf("got fsl = %d; want %d for %q\n", fsl, e.fsl, e.txt)
}
syn := Synopsis(e.txt)
if syn != e.syn {
t.Errorf("got syn = %q; want %q for %q\n", syn, e.syn, e.txt)
}
}
}
// comment 0 comment 1
PACKAGE a
IMPORTPATH
testdata/a
FILENAMES
testdata/a0.go
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
bug0
bug1
BUGS
BUG(uid) bug0
BUG(uid) bug1
NOTES
NOTE(uid)
NOTE(foo) 1 of 4 - this is the first line of note 1
- note 1 continues on this 2nd line
- note 1 continues on this 3rd line
NOTE(foo) 2 of 4
NOTE(bar) 3 of 4
NOTE(bar) 4 of 4
- this is the last line of note 4
NOTE(bam) This note which contains a (parenthesized) subphrase
must appear in its entirety.
NOTE(xxx) The ':' after the marker and uid is optional.
SECBUGS
SECBUG(uid) sec hole 0
need to fix asap
TODOS
TODO(uid) todo0
TODO(uid) todo1
// comment 0 comment 1
PACKAGE a
IMPORTPATH
testdata/a
FILENAMES
testdata/a0.go
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
bug0
bug1
BUGS
BUG(uid) bug0
BUG(uid) bug1
NOTES
NOTE(uid)
NOTE(foo) 1 of 4 - this is the first line of note 1
- note 1 continues on this 2nd line
- note 1 continues on this 3rd line
NOTE(foo) 2 of 4
NOTE(bar) 3 of 4
NOTE(bar) 4 of 4
- this is the last line of note 4
NOTE(bam) This note which contains a (parenthesized) subphrase
must appear in its entirety.
NOTE(xxx) The ':' after the marker and uid is optional.
SECBUGS
SECBUG(uid) sec hole 0
need to fix asap
TODOS
TODO(uid) todo0
TODO(uid) todo1
// comment 0 comment 1
PACKAGE a
IMPORTPATH
testdata/a
FILENAMES
testdata/a0.go
testdata/a1.go
BUGS .Bugs is now deprecated, please use .Notes instead
bug0
bug1
BUGS
BUG(uid) bug0
BUG(uid) bug1
NOTES
NOTE(uid)
NOTE(foo) 1 of 4 - this is the first line of note 1
- note 1 continues on this 2nd line
- note 1 continues on this 3rd line
NOTE(foo) 2 of 4
NOTE(bar) 3 of 4
NOTE(bar) 4 of 4
- this is the last line of note 4
NOTE(bam) This note which contains a (parenthesized) subphrase
must appear in its entirety.
NOTE(xxx) The ':' after the marker and uid is optional.
SECBUGS
SECBUG(uid) sec hole 0
need to fix asap
TODOS
TODO(uid) todo0
TODO(uid) todo1
// 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.
// comment 0
package a
//BUG(uid): bug0
//TODO(uid): todo0
// A note with some spaces after it, should be ignored (watch out for
// emacs modes that remove trailing whitespace).
//NOTE(uid):
// SECBUG(uid): sec hole 0
// need to fix asap
// Multiple notes may be in the same comment group and should be
// recognized individually. Notes may start in the middle of a
// comment group as long as they start at the beginning of an
// individual comment.
//
// NOTE(foo): 1 of 4 - this is the first line of note 1
// - note 1 continues on this 2nd line
// - note 1 continues on this 3rd line
// NOTE(foo): 2 of 4
// NOTE(bar): 3 of 4
/* NOTE(bar): 4 of 4 */
// - this is the last line of note 4
//
//
// NOTE(bam): This note which contains a (parenthesized) subphrase
// must appear in its entirety.
// NOTE(xxx) The ':' after the marker and uid is optional.
// NOTE(): NO uid - should not show up.
// NOTE() NO uid - should not show up.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment