OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [go/] [ast/] [filter.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 ast

import (
        "go/token"
        "sort"
)

// ----------------------------------------------------------------------------
// Export filtering

// exportFilter is a special filter function to extract exported nodes.
func exportFilter(name string) bool {
        return IsExported(name)
}

// FileExports trims the AST for a Go source file in place such that
// only exported nodes remain: all top-level identifiers which are not exported
// and their associated information (such as type, initial value, or function
// body) are removed. Non-exported fields and methods of exported types are
// stripped. The File.Comments list is not changed.
//
// FileExports returns true if there are exported declarations;
// it returns false otherwise.
//
func FileExports(src *File) bool {
        return filterFile(src, exportFilter, true)
}

// PackageExports trims the AST for a Go package in place such that
// only exported nodes remain. The pkg.Files list is not changed, so that
// file names and top-level package comments don't get lost.
//
// PackageExports returns true if there are exported declarations;
// it returns false otherwise.
//
func PackageExports(pkg *Package) bool {
        return filterPackage(pkg, exportFilter, true)
}

// ----------------------------------------------------------------------------
// General filtering

type Filter func(string) bool

func filterIdentList(list []*Ident, f Filter) []*Ident {
        j := 0
        for _, x := range list {
                if f(x.Name) {
                        list[j] = x
                        j++
                }
        }
        return list[0:j]
}

// fieldName assumes that x is the type of an anonymous field and
// returns the corresponding field name. If x is not an acceptable
// anonymous field, the result is nil.
//
func fieldName(x Expr) *Ident {
        switch t := x.(type) {
        case *Ident:
                return t
        case *SelectorExpr:
                if _, ok := t.X.(*Ident); ok {
                        return t.Sel
                }
        case *StarExpr:
                return fieldName(t.X)
        }
        return nil
}

func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) {
        if fields == nil {
                return false
        }
        list := fields.List
        j := 0
        for _, f := range list {
                keepField := false
                if len(f.Names) == 0 {
                        // anonymous field
                        name := fieldName(f.Type)
                        keepField = name != nil && filter(name.Name)
                } else {
                        n := len(f.Names)
                        f.Names = filterIdentList(f.Names, filter)
                        if len(f.Names) < n {
                                removedFields = true
                        }
                        keepField = len(f.Names) > 0
                }
                if keepField {
                        if export {
                                filterType(f.Type, filter, export)
                        }
                        list[j] = f
                        j++
                }
        }
        if j < len(list) {
                removedFields = true
        }
        fields.List = list[0:j]
        return
}

func filterParamList(fields *FieldList, filter Filter, export bool) bool {
        if fields == nil {
                return false
        }
        var b bool
        for _, f := range fields.List {
                if filterType(f.Type, filter, export) {
                        b = true
                }
        }
        return b
}

func filterType(typ Expr, f Filter, export bool) bool {
        switch t := typ.(type) {
        case *Ident:
                return f(t.Name)
        case *ParenExpr:
                return filterType(t.X, f, export)
        case *ArrayType:
                return filterType(t.Elt, f, export)
        case *StructType:
                if filterFieldList(t.Fields, f, export) {
                        t.Incomplete = true
                }
                return len(t.Fields.List) > 0
        case *FuncType:
                b1 := filterParamList(t.Params, f, export)
                b2 := filterParamList(t.Results, f, export)
                return b1 || b2
        case *InterfaceType:
                if filterFieldList(t.Methods, f, export) {
                        t.Incomplete = true
                }
                return len(t.Methods.List) > 0
        case *MapType:
                b1 := filterType(t.Key, f, export)
                b2 := filterType(t.Value, f, export)
                return b1 || b2
        case *ChanType:
                return filterType(t.Value, f, export)
        }
        return false
}

func filterSpec(spec Spec, f Filter, export bool) bool {
        switch s := spec.(type) {
        case *ValueSpec:
                s.Names = filterIdentList(s.Names, f)
                if len(s.Names) > 0 {
                        if export {
                                filterType(s.Type, f, export)
                        }
                        return true
                }
        case *TypeSpec:
                if f(s.Name.Name) {
                        if export {
                                filterType(s.Type, f, export)
                        }
                        return true
                }
                if !export {
                        // For general filtering (not just exports),
                        // filter type even if name is not filtered
                        // out.
                        // If the type contains filtered elements,
                        // keep the declaration.
                        return filterType(s.Type, f, export)
                }
        }
        return false
}

func filterSpecList(list []Spec, f Filter, export bool) []Spec {
        j := 0
        for _, s := range list {
                if filterSpec(s, f, export) {
                        list[j] = s
                        j++
                }
        }
        return list[0:j]
}

// FilterDecl trims the AST for a Go declaration in place by removing
// all names (including struct field and interface method names, but
// not from parameter lists) that don't pass through the filter f.
//
// FilterDecl returns true if there are any declared names left after
// filtering; it returns false otherwise.
//
func FilterDecl(decl Decl, f Filter) bool {
        return filterDecl(decl, f, false)
}

func filterDecl(decl Decl, f Filter, export bool) bool {
        switch d := decl.(type) {
        case *GenDecl:
                d.Specs = filterSpecList(d.Specs, f, export)
                return len(d.Specs) > 0
        case *FuncDecl:
                return f(d.Name.Name)
        }
        return false
}

// FilterFile trims the AST for a Go file in place by removing all
// names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
// the declaration is removed from the AST. The File.Comments list
// is not changed.
//
// FilterFile returns true if there are any top-level declarations
// left after filtering; it returns false otherwise.
//
func FilterFile(src *File, f Filter) bool {
        return filterFile(src, f, false)
}

func filterFile(src *File, f Filter, export bool) bool {
        j := 0
        for _, d := range src.Decls {
                if filterDecl(d, f, export) {
                        src.Decls[j] = d
                        j++
                }
        }
        src.Decls = src.Decls[0:j]
        return j > 0
}

// FilterPackage trims the AST for a Go package in place by removing
// all names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
// the declaration is removed from the AST. The pkg.Files list is not
// changed, so that file names and top-level package comments don't get
// lost.
//
// FilterPackage returns true if there are any top-level declarations
// left after filtering; it returns false otherwise.
//
func FilterPackage(pkg *Package, f Filter) bool {
        return filterPackage(pkg, f, false)
}

func filterPackage(pkg *Package, f Filter, export bool) bool {
        hasDecls := false
        for _, src := range pkg.Files {
                if filterFile(src, f, export) {
                        hasDecls = true
                }
        }
        return hasDecls
}

// ----------------------------------------------------------------------------
// Merging of package files

// The MergeMode flags control the behavior of MergePackageFiles.
type MergeMode uint

const (
        // If set, duplicate function declarations are excluded.
        FilterFuncDuplicates MergeMode = 1 << iota
        // If set, comments that are not associated with a specific
        // AST node (as Doc or Comment) are excluded.
        FilterUnassociatedComments
        // If set, duplicate import declarations are excluded.
        FilterImportDuplicates
)

// separator is an empty //-style comment that is interspersed between
// different comment groups when they are concatenated into a single group
//
var separator = &Comment{noPos, "//"}

// MergePackageFiles creates a file AST by merging the ASTs of the
// files belonging to a package. The mode flags control merging behavior.
//
func MergePackageFiles(pkg *Package, mode MergeMode) *File {
        // Count the number of package docs, comments and declarations across
        // all package files. Also, compute sorted list of filenames, so that
        // subsequent iterations can always iterate in the same order.
        ndocs := 0
        ncomments := 0
        ndecls := 0
        filenames := make([]string, len(pkg.Files))
        i := 0
        for filename, f := range pkg.Files {
                filenames[i] = filename
                i++
                if f.Doc != nil {
                        ndocs += len(f.Doc.List) + 1 // +1 for separator
                }
                ncomments += len(f.Comments)
                ndecls += len(f.Decls)
        }
        sort.Strings(filenames)

        // Collect package comments from all package files into a single
        // CommentGroup - the collected package documentation. In general
        // there should be only one file with a package comment; but it's
        // better to collect extra comments than drop them on the floor.
        var doc *CommentGroup
        var pos token.Pos
        if ndocs > 0 {
                list := make([]*Comment, ndocs-1) // -1: no separator before first group
                i := 0
                for _, filename := range filenames {
                        f := pkg.Files[filename]
                        if f.Doc != nil {
                                if i > 0 {
                                        // not the first group - add separator
                                        list[i] = separator
                                        i++
                                }
                                for _, c := range f.Doc.List {
                                        list[i] = c
                                        i++
                                }
                                if f.Package > pos {
                                        // Keep the maximum package clause position as
                                        // position for the package clause of the merged
                                        // files.
                                        pos = f.Package
                                }
                        }
                }
                doc = &CommentGroup{list}
        }

        // Collect declarations from all package files.
        var decls []Decl
        if ndecls > 0 {
                decls = make([]Decl, ndecls)
                funcs := make(map[string]int) // map of global function name -> decls index
                i := 0                        // current index
                n := 0                        // number of filtered entries
                for _, filename := range filenames {
                        f := pkg.Files[filename]
                        for _, d := range f.Decls {
                                if mode&FilterFuncDuplicates != 0 {
                                        // A language entity may be declared multiple
                                        // times in different package files; only at
                                        // build time declarations must be unique.
                                        // For now, exclude multiple declarations of
                                        // functions - keep the one with documentation.
                                        //
                                        // TODO(gri): Expand this filtering to other
                                        //            entities (const, type, vars) if
                                        //            multiple declarations are common.
                                        if f, isFun := d.(*FuncDecl); isFun {
                                                name := f.Name.Name
                                                if j, exists := funcs[name]; exists {
                                                        // function declared already
                                                        if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
                                                                // existing declaration has no documentation;
                                                                // ignore the existing declaration
                                                                decls[j] = nil
                                                        } else {
                                                                // ignore the new declaration
                                                                d = nil
                                                        }
                                                        n++ // filtered an entry
                                                } else {
                                                        funcs[name] = i
                                                }
                                        }
                                }
                                decls[i] = d
                                i++
                        }
                }

                // Eliminate nil entries from the decls list if entries were
                // filtered. We do this using a 2nd pass in order to not disturb
                // the original declaration order in the source (otherwise, this
                // would also invalidate the monotonically increasing position
                // info within a single file).
                if n > 0 {
                        i = 0
                        for _, d := range decls {
                                if d != nil {
                                        decls[i] = d
                                        i++
                                }
                        }
                        decls = decls[0:i]
                }
        }

        // Collect import specs from all package files.
        var imports []*ImportSpec
        if mode&FilterImportDuplicates != 0 {
                seen := make(map[string]bool)
                for _, filename := range filenames {
                        f := pkg.Files[filename]
                        for _, imp := range f.Imports {
                                if path := imp.Path.Value; !seen[path] {
                                        // TODO: consider handling cases where:
                                        // - 2 imports exist with the same import path but
                                        //   have different local names (one should probably 
                                        //   keep both of them)
                                        // - 2 imports exist but only one has a comment
                                        // - 2 imports exist and they both have (possibly
                                        //   different) comments
                                        imports = append(imports, imp)
                                        seen[path] = true
                                }
                        }
                }
        } else {
                for _, f := range pkg.Files {
                        imports = append(imports, f.Imports...)
                }
        }

        // Collect comments from all package files.
        var comments []*CommentGroup
        if mode&FilterUnassociatedComments == 0 {
                comments = make([]*CommentGroup, ncomments)
                i := 0
                for _, f := range pkg.Files {
                        i += copy(comments[i:], f.Comments)
                }
        }

        // TODO(gri) need to compute unresolved identifiers!
        return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.