Beispiel #1
0
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)
	}
}
Beispiel #2
0
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
}
Beispiel #3
0
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
}
Beispiel #4
0
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
}
Beispiel #5
0
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}
}
Beispiel #6
0
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}
}
Beispiel #7
0
// 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
}
Beispiel #8
0
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
}
Beispiel #9
0
// 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
}