diff --git a/go/ast/scope.go b/go/ast/scope.go index 8e32912..681e853 100644 --- a/go/ast/scope.go +++ b/go/ast/scope.go @@ -83,6 +83,7 @@ type Object struct { Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil Data interface{} // object-specific data; or nil Type interface{} // place holder for type information; may be nil + Next *Object // corresponding platform dependent implement; or nil } // NewObj creates a new object of a given kind and name. diff --git a/go/parser/parser.go b/go/parser/parser.go index a69f185..bfda935 100644 --- a/go/parser/parser.go +++ b/go/parser/parser.go @@ -16,6 +16,7 @@ import ( "fmt" "regexp" "strconv" + "strings" ) // The mode parameter to the Parse* functions is a set of flags (or 0). @@ -216,6 +217,11 @@ func (p *parser) declare1(decl ast.Node, scope *ast.Scope, kind ast.ObjKind, ide } func (p *parser) redeclared(ident *ast.Ident, prev *ast.Object, reason string) { + if f := p.fset.File(ident.Pos()); f.IsPlatformDependent() { + ident.Obj.Next = prev.Next + prev.Next = ident.Obj + return + } if p.mode&DeclarationErrors == 0 { return } @@ -2215,6 +2221,9 @@ func (p *parser) parseFile() *ast.File { } initialScope := p.topScope + // get platform dependent info from file name or build tag + platformDependent(p.file, p.comments) + // package clause doc := p.leadComment pos := p.expect(token.PACKAGE) @@ -2248,3 +2257,65 @@ func (p *parser) parseFile() *ast.File { return &ast.File{doc, pos, ident, decls, p.fileScope, nil, nil, p.comments} } + +var ( + goosSuffixes = []string{ + "_dragonfly.go", + "_netbsd.go", + "_openbsd.go", + "_solaris.go", + "_freebsd.go", + "_nacl.go", + "_android.go", + "_plan9.go", + "_darwin.go", + "_linux.go", + "_windows.go", + } + goarchSuffixes = []string{ + "_amd64.go", + "_amd64p32.go", + "_arm.go", + "_ppc64.go", + "_ppc64le.go", + "_386.go", + } + buildPrefix = "// +build" +) + +// platformDependent sets file is platform dependent or not +func platformDependent(f *token.File, cgs []*ast.CommentGroup) { + if f == nil { + return + } + + //TODO: if file already has platform dependent info, return + + // get from file name + fn := f.Name() + for _, os := range goosSuffixes { + if strings.HasSuffix(fn, os) { + f.PlatformDependent() + return + } + } + for _, arch := range goarchSuffixes { + if strings.HasSuffix(fn, arch) { + f.PlatformDependent() + return + } + } + + // get from build constraint + if cgs == nil { + return + } + for _, cg := range cgs { + for _, c := range cg.List { + if strings.HasPrefix(c.Text, buildPrefix) { + f.PlatformDependent() + return + } + } + } +} diff --git a/go/token/position.go b/go/token/position.go index fbe1763..464599a 100644 --- a/go/token/position.go +++ b/go/token/position.go @@ -166,6 +166,8 @@ type File struct { // lines and infos are protected by set.mutex lines []int infos []lineInfo + + platformDep bool // platformDep indicate whether this file is platform dependent } // Name returns the file name of file f as registered with AddFile. @@ -314,6 +316,16 @@ func (f *File) info(offset int) (filename string, line, column int) { return } +// IsPlatformDependent returns whether this file platform dependent +func (f *File) IsPlatformDependent() bool { + return f.platformDep +} + +// PlatformDependent sets this file is platform dependent (default is false) +func (f *File) PlatformDependent() { + f.platformDep = true +} + // A FileSet represents a set of source files. // Methods of file sets are synchronized; multiple goroutines // may invoke them concurrently. @@ -366,7 +378,7 @@ func (s *FileSet) AddFile(filename string, base, size int) *File { panic("illegal base or size") } // base >= s.base && size >= 0 - f := &File{s, filename, base, size, []int{0}, nil} + f := &File{s, filename, base, size, []int{0}, nil, false} base += size + 1 // +1 because EOF also has a position if base < 0 { panic("token.Pos offset overflow (> 2G of source code in file set)") diff --git a/godef.go b/godef.go index a9198a3..2f98868 100644 --- a/godef.go +++ b/godef.go @@ -28,6 +28,7 @@ var aflag = flag.Bool("a", false, "print public type and member information") var Aflag = flag.Bool("A", false, "print all type and members information") var fflag = flag.String("f", "", "Go source filename") var acmeFlag = flag.Bool("acme", false, "use current acme window") +var pflag = flag.Bool("p", false, "print all platform-dependent definitions") func fail(s string, a ...interface{}) { fmt.Fprint(os.Stderr, "godef: "+fmt.Sprintf(s, a...)+"\n") @@ -86,7 +87,12 @@ func main() { src = b } pkgScope := ast.NewScope(parser.Universe) - f, err := parser.ParseFile(types.FileSet, filename, src, 0, pkgScope) + var mode uint + if *pflag { + // parse comments to get build constraint + mode |= parser.ParseComments + } + f, err := parser.ParseFile(types.FileSet, filename, src, mode, pkgScope) if f == nil { fail("cannot parse %s: %v", filename, err) } @@ -218,8 +224,15 @@ func (o orderedObjects) Swap(i, j int) { o[i], o[j] = o[j], o[i] } func done(obj *ast.Object, typ types.Type) { defer os.Exit(0) - pos := types.FileSet.Position(types.DeclPos(obj)) - fmt.Printf("%v\n", pos) + if *pflag { + for o := obj; o != nil; o = o.Next { + pos := types.FileSet.Position(types.DeclPos(o)) + fmt.Printf("%v\n", pos) + } + } else { + pos := types.FileSet.Position(types.DeclPos(obj)) + fmt.Printf("%v\n", pos) + } if typ.Kind == ast.Bad || !*tflag { return } @@ -310,6 +323,12 @@ func parseLocalPackage(filename string, src *ast.File, pkgScope *ast.Scope) (*as return nil, errNoPkgFiles } + var mode uint + if *pflag { + // parse comments to get build constraint + mode |= parser.ParseComments + } + for _, pf := range list { file := filepath.Join(d, pf) if !strings.HasSuffix(pf, ".go") || @@ -317,7 +336,7 @@ func parseLocalPackage(filename string, src *ast.File, pkgScope *ast.Scope) (*as pkgName(file) != pkg.Name { continue } - src, err := parser.ParseFile(types.FileSet, file, nil, 0, pkg.Scope) + src, err := parser.ParseFile(types.FileSet, file, nil, mode, pkg.Scope) if err == nil { pkg.Files[file] = src }