func (p *Package) processFile(file *ast.File) { if file.Doc != nil { // package documentation, overwrite p.Doc = doc.CommentText(file.Doc) } for _, d := range file.Decls { p.processDecl(d) } }
func newType(decl *ast.GenDecl, spec *ast.TypeSpec) *Type { if !ast.IsExported(spec.Name.Name) { return nil } t := new(Type) if spec.Doc != nil { t.Doc = doc.CommentText(spec.Doc) spec.Doc = nil } else { // if spec has no docs, try pulling comment from decl t.Doc = doc.CommentText(decl.Doc) } cleanUnexportedFields(spec.Type) t.Name = spec.Name.Name t.Decl = spec return t }
func newFunc(decl *ast.FuncDecl) *Func { if !ast.IsExported(decl.Name.Name) { return nil } f := new(Func) f.Doc = doc.CommentText(decl.Doc) decl.Doc = nil f.Name = decl.Name.Name if decl.Recv != nil { f.Recv = recvAsString(decl.Recv.List[0].Type) } f.Decl = decl decl.Body = nil // remove body return f }
func newValue(decl *ast.GenDecl) *Value { v := new(Value) v.Doc = doc.CommentText(decl.Doc) decl.Doc = nil // count names and figure out type n := 0 for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) for _, name := range vspec.Names { if ast.IsExported(name.Name) { n++ } } if v.Type == "" { t := typeAsString(vspec.Type) if t != "" && ast.IsExported(t) { v.Type = t } } } if n == 0 { return nil } // collect names v.Names = make([]string, n) i := 0 for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) for _, name := range vspec.Names { if !ast.IsExported(name.Name) { continue } v.Names[i] = name.Name i++ } } v.Decl = decl return v }
func newDirTree(path, name string, depth, maxDepth int) *Directory { if depth >= maxDepth { // return a dummy directory so that the parent directory // doesn't get discarded just because we reached the max // directory depth return &Directory{depth, path, name, "", nil} } list, _ := ioutil.ReadDir(path) // ignore errors // determine number of subdirectories and package files ndirs := 0 nfiles := 0 text := "" for _, d := range list { switch { case isPkgDir(d): ndirs++ case isPkgFile(d): nfiles++ if text == "" { // no package documentation yet; take the first found file, err := parser.ParseFile(pathutil.Join(path, d.Name), nil, nil, parser.ParseComments|parser.PackageClauseOnly) if err == nil && // Also accept fakePkgName, so we get synopses for commmands. // Note: This may lead to incorrect results if there is a // (left-over) "documentation" package somewhere in a package // directory of different name, but this is very unlikely and // against current conventions. (file.Name.Name() == name || file.Name.Name() == fakePkgName) && file.Doc != nil { // found documentation; extract a synopsys text = firstSentence(doc.CommentText(file.Doc)) } } } } // create subdirectory tree var dirs []*Directory if ndirs > 0 { dirs = make([]*Directory, ndirs) i := 0 for _, d := range list { if isPkgDir(d) { dd := newDirTree(pathutil.Join(path, d.Name), d.Name, depth+1, maxDepth) if dd != nil { dirs[i] = dd i++ } } } dirs = dirs[0:i] } // if there are no package files and no subdirectories // (with package files), ignore the directory if nfiles == 0 && len(dirs) == 0 { return nil } return &Directory{depth, path, name, text, dirs} }
func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory { if b.pathFilter != nil && !b.pathFilter(path) { return nil } if depth >= b.maxDepth { // return a dummy directory so that the parent directory // doesn't get discarded just because we reached the max // directory depth return &Directory{depth, path, name, "", nil} } list, err := ioutil.ReadDir(path) if err != nil { // newDirTree is called with a path that should be a package // directory; errors here should not happen, but if they do, // we want to know about them log.Printf("ioutil.ReadDir(%s): %s", path, err) } // determine number of subdirectories and if there are package files ndirs := 0 hasPkgFiles := false var synopses [4]string // prioritized package documentation (0 == highest priority) for _, d := range list { switch { case isPkgDir(d): ndirs++ case isPkgFile(d): // looks like a package file, but may just be a file ending in ".go"; // don't just count it yet (otherwise we may end up with hasPkgFiles even // though the directory doesn't contain any real package files - was bug) if synopses[0] == "" { // no "optimal" package synopsis yet; continue to collect synopses file, err := parser.ParseFile(fset, filepath.Join(path, d.Name), nil, parser.ParseComments|parser.PackageClauseOnly) if err == nil { hasPkgFiles = true if file.Doc != nil { // prioritize documentation i := -1 switch file.Name.Name { case name: i = 0 // normal case: directory name matches package name case fakePkgName: i = 1 // synopses for commands case "main": i = 2 // directory contains a main package default: i = 3 // none of the above } if 0 <= i && i < len(synopses) && synopses[i] == "" { synopses[i] = firstSentence(doc.CommentText(file.Doc)) } } } } } } // create subdirectory tree var dirs []*Directory if ndirs > 0 { dirs = make([]*Directory, ndirs) i := 0 for _, d := range list { if isPkgDir(d) { dd := b.newDirTree(fset, filepath.Join(path, d.Name), d.Name, depth+1) if dd != nil { dirs[i] = dd i++ } } } dirs = dirs[0:i] } // if there are no package files and no subdirectories // containing package files, ignore the directory if !hasPkgFiles && len(dirs) == 0 { return nil } // select the highest-priority synopsis for the directory entry, if any synopsis := "" for _, synopsis = range synopses { if synopsis != "" { break } } return &Directory{depth, path, name, synopsis, dirs} }
// saveCgo saves the information from the #cgo lines in the import "C" comment. // These lines set CFLAGS and LDFLAGS and pkg-config directives that affect // the way cgo's C code is built. // // TODO(rsc): This duplicates code in cgo. // Once the dust settles, remove this code from cgo. func (ctxt *Context) saveCgo(filename string, di *DirInfo, cg *ast.CommentGroup) os.Error { text := doc.CommentText(cg) for _, line := range strings.Split(text, "\n") { orig := line // Line is // #cgo [GOOS/GOARCH...] LDFLAGS: stuff // line = strings.TrimSpace(line) if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { continue } // Split at colon. line = strings.TrimSpace(line[4:]) i := strings.Index(line, ":") if i < 0 { return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) } line, argstr := line[:i], line[i+1:] // Parse GOOS/GOARCH stuff. f := strings.Fields(line) if len(f) < 1 { return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) } cond, verb := f[:len(f)-1], f[len(f)-1] if len(cond) > 0 { ok := false for _, c := range cond { if ctxt.matchOSArch(c) { ok = true break } } if !ok { continue } } args, err := splitQuoted(argstr) if err != nil { return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) } for _, arg := range args { if !safeName(arg) { return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) } } switch verb { case "CFLAGS": di.CgoCFLAGS = append(di.CgoCFLAGS, args...) case "LDFLAGS": di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) case "pkg-config": di.CgoPkgConfig = append(di.CgoPkgConfig, args...) default: return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) } } return nil }
func openProg(name string) *Prog { p := new(Prog) var err os.Error p.AST, err = parser.ParsePkgFile("", name, parser.ParseComments) if err != nil { if list, ok := err.(scanner.ErrorList); ok { // If err is a scanner.ErrorList, its String will print just // the first error and then (+n more errors). // Instead, turn it into a new Error that will return // details for all the errors. for _, e := range list { fmt.Fprintln(os.Stderr, e) } os.Exit(2) } fatal("parsing %s: %s", name, err) } p.Package = p.AST.Name.Value // Find the import "C" line and get any extra C preamble. // Delete the import "C" line along the way. sawC := false w := 0 for _, decl := range p.AST.Decls { d, ok := decl.(*ast.GenDecl) if !ok { p.AST.Decls[w] = decl w++ continue } ws := 0 for _, spec := range d.Specs { s, ok := spec.(*ast.ImportSpec) if !ok || len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { d.Specs[ws] = spec ws++ continue } sawC = true if s.Name != nil { error(s.Path[0].Pos(), `cannot rename import "C"`) } if s.Doc != nil { p.Preamble += doc.CommentText(s.Doc) + "\n" } else if len(d.Specs) == 1 && d.Doc != nil { p.Preamble += doc.CommentText(d.Doc) + "\n" } } if ws == 0 { continue } d.Specs = d.Specs[0:ws] p.AST.Decls[w] = d w++ } p.AST.Decls = p.AST.Decls[0:w] if !sawC { error(noPos, `cannot find import "C"`) } // Accumulate pointers to uses of C.x. p.Crefs = make([]*Cref, 0, 8) walk(p.AST, p, "prog") return p }
// ReadGo populates f with information learned from reading the // Go source file with the given file name. It gathers the C preamble // attached to the import "C" comment, a list of references to C.xxx, // a list of exported functions, and the actual AST, to be rewritten and // printed. func (f *File) ReadGo(name string) { // Two different parses: once with comments, once without. // The printer is not good enough at printing comments in the // right place when we start editing the AST behind its back, // so we use ast1 to look for the doc comments on import "C" // and on exported functions, and we use ast2 for translating // and reprinting. ast1 := parse(name, parser.ParseComments) ast2 := parse(name, 0) f.Package = ast1.Name.Name f.Name = make(map[string]*Name) // In ast1, find the import "C" line and get any extra C preamble. sawC := false for _, decl := range ast1.Decls { d, ok := decl.(*ast.GenDecl) if !ok { continue } for _, spec := range d.Specs { s, ok := spec.(*ast.ImportSpec) if !ok || string(s.Path.Value) != `"C"` { continue } sawC = true if s.Name != nil { error(s.Path.Pos(), `cannot rename import "C"`) } cg := s.Doc if cg == nil && len(d.Specs) == 1 { cg = d.Doc } if cg != nil { f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) f.Preamble += doc.CommentText(cg) + "\n" } } } if !sawC { error(token.NoPos, `cannot find import "C"`) } // In ast2, strip the import "C" line. w := 0 for _, decl := range ast2.Decls { d, ok := decl.(*ast.GenDecl) if !ok { ast2.Decls[w] = decl w++ continue } ws := 0 for _, spec := range d.Specs { s, ok := spec.(*ast.ImportSpec) if !ok || string(s.Path.Value) != `"C"` { d.Specs[ws] = spec ws++ } } if ws == 0 { continue } d.Specs = d.Specs[0:ws] ast2.Decls[w] = d w++ } ast2.Decls = ast2.Decls[0:w] // Accumulate pointers to uses of C.x. if f.Ref == nil { f.Ref = make([]*Ref, 0, 8) } f.walk(ast2, "prog", (*File).saveRef) // Accumulate exported functions. // The comments are only on ast1 but we need to // save the function bodies from ast2. // The first walk fills in ExpFunc, and the // second walk changes the entries to // refer to ast2 instead. f.walk(ast1, "prog", (*File).saveExport) f.walk(ast2, "prog", (*File).saveExport2) f.AST = ast2 }