URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [go/] [doc/] [reader.go] - Rev 747
Compare with Previous | Blame | View Log
// 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 docimport ("go/ast""go/token""regexp""sort""strconv")// ----------------------------------------------------------------------------// function/method sets//// Internally, we treat functions like methods and collect them in method sets.// methodSet describes a set of methods. Entries where Decl == nil are conflict// entries (more then one method with the same name at the same embedding level).//type methodSet map[string]*Func// recvString returns a string representation of recv of the// form "T", "*T", or "BADRECV" (if not a proper receiver type).//func recvString(recv ast.Expr) string {switch t := recv.(type) {case *ast.Ident:return t.Namecase *ast.StarExpr:return "*" + recvString(t.X)}return "BADRECV"}// set creates the corresponding Func for f and adds it to mset.// If there are multiple f's with the same name, set keeps the first// one with documentation; conflicts are ignored.//func (mset methodSet) set(f *ast.FuncDecl) {name := f.Name.Nameif g := mset[name]; g != nil && g.Doc != "" {// A function with the same name has already been registered;// since it has documentation, assume f is simply another// implementation and ignore it. This does not happen if the// caller is using go/build.ScanDir to determine the list of// files implementing a package.return}// function doesn't exist or has no documentation; use frecv := ""if f.Recv != nil {var typ ast.Expr// be careful in case of incorrect ASTsif list := f.Recv.List; len(list) == 1 {typ = list[0].Type}recv = recvString(typ)}mset[name] = &Func{Doc: f.Doc.Text(),Name: name,Decl: f,Recv: recv,Orig: recv,}f.Doc = nil // doc consumed - remove from AST}// add adds method m to the method set; m is ignored if the method set// already contains a method with the same name at the same or a higher// level then m.//func (mset methodSet) add(m *Func) {old := mset[m.Name]if old == nil || m.Level < old.Level {mset[m.Name] = mreturn}if old != nil && m.Level == old.Level {// conflict - mark it using a method with nil Declmset[m.Name] = &Func{Name: m.Name,Level: m.Level,}}}// ----------------------------------------------------------------------------// Named types// baseTypeName returns the name of the base type of x (or "")// and whether the type is imported or not.//func baseTypeName(x ast.Expr) (name string, imported bool) {switch t := x.(type) {case *ast.Ident:return t.Name, falsecase *ast.SelectorExpr:if _, ok := t.X.(*ast.Ident); ok {// only possible for qualified type names;// assume type is importedreturn t.Sel.Name, true}case *ast.StarExpr:return baseTypeName(t.X)}return}// A namedType represents a named unqualified (package local, or possibly// predeclared) type. The namedType for a type name is always found via// reader.lookupType.//type namedType struct {doc string // doc comment for typename string // type namedecl *ast.GenDecl // nil if declaration hasn't been seen yetisEmbedded bool // true if this type is embeddedisStruct bool // true if this type is a structembedded map[*namedType]bool // true if the embedded type is a pointer// associated declarationsvalues []*Value // consts and varsfuncs methodSetmethods methodSet}// ----------------------------------------------------------------------------// AST reader// reader accumulates documentation for a single package.// It modifies the AST: Comments (declaration documentation)// that have been collected by the reader are set to nil// in the respective AST nodes so that they are not printed// twice (once when printing the documentation and once when// printing the corresponding AST node).//type reader struct {mode Mode// package propertiesdoc string // package documentation, if anyfilenames []stringbugs []string// declarationsimports map[string]intvalues []*Value // consts and varstypes map[string]*namedTypefuncs methodSet}func (r *reader) isVisible(name string) bool {return r.mode&AllDecls != 0 || ast.IsExported(name)}// lookupType returns the base type with the given name.// If the base type has not been encountered yet, a new// type with the given name but no associated declaration// is added to the type map.//func (r *reader) lookupType(name string) *namedType {if name == "" || name == "_" {return nil // no type docs for anonymous types}if typ, found := r.types[name]; found {return typ}// type not found - add one without declarationtyp := &namedType{name: name,embedded: make(map[*namedType]bool),funcs: make(methodSet),methods: make(methodSet),}r.types[name] = typreturn typ}// recordAnonymousField registers fieldType as the type of an// anonymous field in the parent type. If the field is imported// (qualified name) or the parent is nil, the field is ignored.// The function returns the field name.//func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {fname, imp := baseTypeName(fieldType)if parent == nil || imp {return}if ftype := r.lookupType(fname); ftype != nil {ftype.isEmbedded = true_, ptr := fieldType.(*ast.StarExpr)parent.embedded[ftype] = ptr}return}func (r *reader) readDoc(comment *ast.CommentGroup) {// By convention there should be only one package comment// but collect all of them if there are more then one.text := comment.Text()if r.doc == "" {r.doc = textreturn}r.doc += "\n" + text}func specNames(specs []ast.Spec) []string {names := make([]string, 0, len(specs)) // reasonable estimatefor _, s := range specs {// s guaranteed to be an *ast.ValueSpec by readValuefor _, ident := range s.(*ast.ValueSpec).Names {names = append(names, ident.Name)}}return names}// readValue processes a const or var declaration.//func (r *reader) readValue(decl *ast.GenDecl) {// determine if decl should be associated with a type// Heuristic: For each typed entry, determine the type name, if any.// If there is exactly one type name that is sufficiently// frequent, associate the decl with the respective type.domName := ""domFreq := 0prev := ""n := 0for _, spec := range decl.Specs {s, ok := spec.(*ast.ValueSpec)if !ok {continue // should not happen, but be conservative}name := ""switch {case s.Type != nil:// a type is present; determine its nameif n, imp := baseTypeName(s.Type); !imp {name = n}case decl.Tok == token.CONST:// no type is present but we have a constant declaration;// use the previous type name (w/o more type information// we cannot handle the case of unnamed variables with// initializer expressions except for some trivial cases)name = prev}if name != "" {// entry has a named typeif domName != "" && domName != name {// more than one type name - do not associate// with any typedomName = ""break}domName = namedomFreq++}prev = namen++}// nothing to do w/o a legal declarationif n == 0 {return}// determine values list with which to associate the Value for this declvalues := &r.valuesconst threshold = 0.75if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {// typed entries are sufficiently frequentif typ := r.lookupType(domName); typ != nil {values = &typ.values // associate with that type}}*values = append(*values, &Value{Doc: decl.Doc.Text(),Names: specNames(decl.Specs),Decl: decl,order: len(*values),})decl.Doc = nil // doc consumed - remove from AST}// fields returns a struct's fields or an interface's methods.//func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {var fields *ast.FieldListswitch t := typ.(type) {case *ast.StructType:fields = t.FieldsisStruct = truecase *ast.InterfaceType:fields = t.Methods}if fields != nil {list = fields.List}return}// readType processes a type declaration.//func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {typ := r.lookupType(spec.Name.Name)if typ == nil {return // no name or blank name - ignore the type}// A type should be added at most once, so info.decl// should be nil - if it is not, simply overwrite it.typ.decl = decl// compute documentationdoc := spec.Docspec.Doc = nil // doc consumed - remove from ASTif doc == nil {// no doc associated with the spec, use the declaration doc, if anydoc = decl.Doc}decl.Doc = nil // doc consumed - remove from ASTtyp.doc = doc.Text()// record anonymous fields (they may contribute methods)// (some fields may have been recorded already when filtering// exports, but that's ok)var list []*ast.Fieldlist, typ.isStruct = fields(spec.Type)for _, field := range list {if len(field.Names) == 0 {r.recordAnonymousField(typ, field.Type)}}}// readFunc processes a func or method declaration.//func (r *reader) readFunc(fun *ast.FuncDecl) {// strip function bodyfun.Body = nil// associate methods with the receiver type, if anyif fun.Recv != nil {// methodrecvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)if imp {// should not happen (incorrect AST);// don't show this methodreturn}if typ := r.lookupType(recvTypeName); typ != nil {typ.methods.set(fun)}// otherwise ignore the method// TODO(gri): There may be exported methods of non-exported types// that can be called because of exported values (consts, vars, or// function results) of that type. Could determine if that is the// case and then show those methods in an appropriate section.return}// associate factory functions with the first visible result type, if anyif fun.Type.Results.NumFields() >= 1 {res := fun.Type.Results.List[0]if len(res.Names) <= 1 {// exactly one (named or anonymous) result associated// with the first type in result signature (there may// be more than one result)if n, imp := baseTypeName(res.Type); !imp && r.isVisible(n) {if typ := r.lookupType(n); typ != nil {// associate function with typtyp.funcs.set(fun)return}}}}// just an ordinary functionr.funcs.set(fun)}var (bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char)// readFile adds the AST for a source file to the reader.//func (r *reader) readFile(src *ast.File) {// add package documentationif src.Doc != nil {r.readDoc(src.Doc)src.Doc = nil // doc consumed - remove from AST}// add all declarationsfor _, decl := range src.Decls {switch d := decl.(type) {case *ast.GenDecl:switch d.Tok {case token.IMPORT:// imports are handled individuallyfor _, spec := range d.Specs {if s, ok := spec.(*ast.ImportSpec); ok {if import_, err := strconv.Unquote(s.Path.Value); err == nil {r.imports[import_] = 1}}}case token.CONST, token.VAR:// constants and variables are always handled as a groupr.readValue(d)case token.TYPE:// types are handled individuallyfor _, spec := range d.Specs {if s, ok := spec.(*ast.TypeSpec); ok {// use an individual (possibly fake) declaration// for each type; this also ensures that each type// gets to (re-)use the declaration documentation// if there's none associated with the spec itselffake := &ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos,[]ast.Spec{s}, token.NoPos,}r.readType(fake, s)}}}case *ast.FuncDecl:r.readFunc(d)}}// collect BUG(...) commentsfor _, c := range src.Comments {text := c.List[0].Textif m := bug_markers.FindStringIndex(text); m != nil {// found a BUG comment; maybe emptyif btxt := text[m[1]:]; bug_content.MatchString(btxt) {// non-empty BUG comment; collect comment without BUG prefixlist := append([]*ast.Comment(nil), c.List...) // make a copylist[0].Text = text[m[1]:]r.bugs = append(r.bugs, (&ast.CommentGroup{list}).Text())}}}src.Comments = nil // consumed unassociated comments - remove from AST}func (r *reader) readPackage(pkg *ast.Package, mode Mode) {// initialize readerr.filenames = make([]string, len(pkg.Files))r.imports = make(map[string]int)r.mode = moder.types = make(map[string]*namedType)r.funcs = make(methodSet)// sort package files before reading them so that the// result result does not depend on map iteration orderi := 0for filename := range pkg.Files {r.filenames[i] = filenamei++}sort.Strings(r.filenames)// process files in sorted orderfor _, filename := range r.filenames {f := pkg.Files[filename]if mode&AllDecls == 0 {r.fileExports(f)}r.readFile(f)}}// ----------------------------------------------------------------------------// Typesvar predeclaredTypes = map[string]bool{"bool": true,"byte": true,"complex64": true,"complex128": true,"error": true,"float32": true,"float64": true,"int": true,"int8": true,"int16": true,"int32": true,"int64": true,"rune": true,"string": true,"uint": true,"uint8": true,"uint16": true,"uint32": true,"uint64": true,"uintptr": true,}func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {return f // shouldn't happen, but be safe}// copy existing receiver field and set new typenewField := *f.Decl.Recv.List[0]_, origRecvIsPtr := newField.Type.(*ast.StarExpr)var typ ast.Expr = ast.NewIdent(recvTypeName)if !embeddedIsPtr && origRecvIsPtr {typ = &ast.StarExpr{token.NoPos, typ}}newField.Type = typ// copy existing receiver field list and set new receiver fieldnewFieldList := *f.Decl.RecvnewFieldList.List = []*ast.Field{&newField}// copy existing function declaration and set new receiver field listnewFuncDecl := *f.DeclnewFuncDecl.Recv = &newFieldList// copy existing function documentation and set new declarationnewF := *fnewF.Decl = &newFuncDeclnewF.Recv = recvString(typ)// the Orig field never changesnewF.Level = levelreturn &newF}// collectEmbeddedMethods collects the embedded methods of typ in mset.//func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int) {for embedded, isPtr := range typ.embedded {// Once an embedded type is embedded as a pointer type// all embedded types in those types are treated like// pointer types for the purpose of the receiver type// computation; i.e., embeddedIsPtr is sticky for this// embedding hierarchy.thisEmbeddedIsPtr := embeddedIsPtr || isPtrfor _, m := range embedded.methods {// only top-level methods are embeddedif m.Level == 0 {mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))}}r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1)}}// computeMethodSets determines the actual method sets for each type encountered.//func (r *reader) computeMethodSets() {for _, t := range r.types {// collect embedded methods for tif t.isStruct {// structr.collectEmbeddedMethods(t.methods, t, t.name, false, 1)} else {// interface// TODO(gri) fix this}}}// cleanupTypes removes the association of functions and methods with// types that have no declaration. Instead, these functions and methods// are shown at the package level. It also removes types with missing// declarations or which are not visible.//func (r *reader) cleanupTypes() {for _, t := range r.types {visible := r.isVisible(t.name)if t.decl == nil && (predeclaredTypes[t.name] || t.isEmbedded && visible) {// t.name is a predeclared type (and was not redeclared in this package),// or it was embedded somewhere but its declaration is missing (because// the AST is incomplete): move any associated values, funcs, and methods// back to the top-level so that they are not lost.// 1) move valuesr.values = append(r.values, t.values...)// 2) move factory functionsfor name, f := range t.funcs {// in a correct AST, package-level function names// are all different - no need to check for conflictsr.funcs[name] = f}// 3) move methodsfor name, m := range t.methods {// don't overwrite functions with the same name - drop themif _, found := r.funcs[name]; !found {r.funcs[name] = m}}}// remove types w/o declaration or which are not visibleif t.decl == nil || !visible {delete(r.types, t.name)}}}// ----------------------------------------------------------------------------// Sortingtype data struct {n intswap func(i, j int)less func(i, j int) bool}func (d *data) Len() int { return d.n }func (d *data) Swap(i, j int) { d.swap(i, j) }func (d *data) Less(i, j int) bool { return d.less(i, j) }// sortBy is a helper function for sortingfunc sortBy(less func(i, j int) bool, swap func(i, j int), n int) {sort.Sort(&data{n, swap, less})}func sortedKeys(m map[string]int) []string {list := make([]string, len(m))i := 0for key := range m {list[i] = keyi++}sort.Strings(list)return list}// sortingName returns the name to use when sorting d into place.//func sortingName(d *ast.GenDecl) string {if len(d.Specs) == 1 {if s, ok := d.Specs[0].(*ast.ValueSpec); ok {return s.Names[0].Name}}return ""}func sortedValues(m []*Value, tok token.Token) []*Value {list := make([]*Value, len(m)) // big enough in any casei := 0for _, val := range m {if val.Decl.Tok == tok {list[i] = vali++}}list = list[0:i]sortBy(func(i, j int) bool {if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {return ni < nj}return list[i].order < list[j].order},func(i, j int) { list[i], list[j] = list[j], list[i] },len(list),)return list}func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {list := make([]*Type, len(m))i := 0for _, t := range m {list[i] = &Type{Doc: t.doc,Name: t.name,Decl: t.decl,Consts: sortedValues(t.values, token.CONST),Vars: sortedValues(t.values, token.VAR),Funcs: sortedFuncs(t.funcs, true),Methods: sortedFuncs(t.methods, allMethods),}i++}sortBy(func(i, j int) bool { return list[i].Name < list[j].Name },func(i, j int) { list[i], list[j] = list[j], list[i] },len(list),)return list}func removeStar(s string) string {if len(s) > 0 && s[0] == '*' {return s[1:]}return s}func sortedFuncs(m methodSet, allMethods bool) []*Func {list := make([]*Func, len(m))i := 0for _, m := range m {// determine which methods to includeswitch {case m.Decl == nil:// exclude conflict entrycase allMethods, m.Level == 0, !ast.IsExported(removeStar(m.Orig)):// forced inclusion, method not embedded, or method// embedded but original receiver type not exportedlist[i] = mi++}}list = list[0:i]sortBy(func(i, j int) bool { return list[i].Name < list[j].Name },func(i, j int) { list[i], list[j] = list[j], list[i] },len(list),)return list}
