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 (
	
	
)
---------------------------------------------------------------------------- Export filtering
exportFilter is a special filter function to extract exported nodes.
func ( string) bool {
	return IsExported()
}
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 reports whether there are exported declarations.
func ( *File) bool {
	return filterFile(, 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 reports whether there are exported declarations; it returns false otherwise.
---------------------------------------------------------------------------- General filtering

type Filter func(string) bool

func ( []*Ident,  Filter) []*Ident {
	 := 0
	for ,  := range  {
		if (.Name) {
			[] = 
			++
		}
	}
	return [0:]
}
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 ( Expr) *Ident {
	switch t := .(type) {
	case *Ident:
		return 
	case *SelectorExpr:
		if ,  := .X.(*Ident);  {
			return .Sel
		}
	case *StarExpr:
		return (.X)
	}
	return nil
}

func ( *FieldList,  Filter,  bool) ( bool) {
	if  == nil {
		return false
	}
	 := .List
	 := 0
	for ,  := range  {
		 := false
anonymous field
			 := fieldName(.Type)
			 =  != nil && (.Name)
		} else {
			 := len(.Names)
			.Names = filterIdentList(.Names, )
			if len(.Names) <  {
				 = true
			}
			 = len(.Names) > 0
		}
		if  {
			if  {
				filterType(.Type, , )
			}
			[] = 
			++
		}
	}
	if  < len() {
		 = true
	}
	.List = [0:]
	return
}

func ( *CompositeLit,  Filter,  bool) {
	 := len(.Elts)
	.Elts = filterExprList(.Elts, , )
	if len(.Elts) <  {
		.Incomplete = true
	}
}

func ( []Expr,  Filter,  bool) []Expr {
	 := 0
	for ,  := range  {
		switch x := .(type) {
		case *CompositeLit:
			filterCompositeLit(, , )
		case *KeyValueExpr:
			if ,  := .Key.(*Ident);  && !(.Name) {
				continue
			}
			if ,  := .Value.(*CompositeLit);  {
				filterCompositeLit(, , )
			}
		}
		[] = 
		++
	}
	return [0:]
}

func ( *FieldList,  Filter,  bool) bool {
	if  == nil {
		return false
	}
	var  bool
	for ,  := range .List {
		if filterType(.Type, , ) {
			 = true
		}
	}
	return 
}

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

func ( Spec,  Filter,  bool) bool {
	switch s := .(type) {
	case *ValueSpec:
		.Names = filterIdentList(.Names, )
		.Values = filterExprList(.Values, , )
		if len(.Names) > 0 {
			if  {
				filterType(.Type, , )
			}
			return true
		}
	case *TypeSpec:
		if (.Name.Name) {
			if  {
				filterType(.Type, , )
			}
			return true
		}
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(.Type, , )
		}
	}
	return false
}

func ( []Spec,  Filter,  bool) []Spec {
	 := 0
	for ,  := range  {
		if filterSpec(, , ) {
			[] = 
			++
		}
	}
	return [0:]
}
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 reports whether there are any declared names left after filtering.
func ( Decl,  Filter) bool {
	return filterDecl(, , false)
}

func ( Decl,  Filter,  bool) bool {
	switch d := .(type) {
	case *GenDecl:
		.Specs = filterSpecList(.Specs, , )
		return len(.Specs) > 0
	case *FuncDecl:
		return (.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. Import declarations are always removed. The File.Comments list is not changed. FilterFile reports whether there are any top-level declarations left after filtering.
func ( *File,  Filter) bool {
	return filterFile(, , false)
}

func ( *File,  Filter,  bool) bool {
	 := 0
	for ,  := range .Decls {
		if filterDecl(, , ) {
			.Decls[] = 
			++
		}
	}
	.Decls = .Decls[0:]
	return  > 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 reports whether there are any top-level declarations left after filtering.
func ( *Package,  Filter) bool {
	return filterPackage(, , false)
}

func ( *Package,  Filter,  bool) bool {
	 := false
	for ,  := range .Files {
		if filterFile(, , ) {
			 = true
		}
	}
	return 
}
---------------------------------------------------------------------------- Merging of package files
The MergeMode flags control the behavior of MergePackageFiles.
If set, duplicate function declarations are excluded.
If set, comments that are not associated with a specific AST node (as Doc or Comment) are excluded.
If set, duplicate import declarations are excluded.
nameOf returns the function (foo) or method name (foo.bar) for the given function declaration. If the AST is incorrect for the receiver, it assumes a function instead.
func ( *FuncDecl) string {
looks like a correct receiver declaration
dereference pointer receiver types
		if ,  := .(*StarExpr);  != nil {
			 = .X
the receiver type must be a type name
		if ,  := .(*Ident);  != nil {
			return .Name + "." + .Name.Name
otherwise assume a function instead
	}
	return .Name.Name
}
separator is an empty //-style comment that is interspersed between different comment groups when they are concatenated into a single group
var separator = &Comment{token.NoPos, "//"}
MergePackageFiles creates a file AST by merging the ASTs of the files belonging to a package. The mode flags control merging behavior.
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.
	 := 0
	 := 0
	 := 0
	 := make([]string, len(.Files))
	 := 0
	for ,  := range .Files {
		[] = 
		++
		if .Doc != nil {
			 += len(.Doc.List) + 1 // +1 for separator
		}
		 += len(.Comments)
		 += len(.Decls)
	}
	sort.Strings()
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  *CommentGroup
	var  token.Pos
	if  > 0 {
		 := make([]*Comment, -1) // -1: no separator before first group
		 := 0
		for ,  := range  {
			 := .Files[]
			if .Doc != nil {
not the first group - add separator
					[] = separator
					++
				}
				for ,  := range .Doc.List {
					[] = 
					++
				}
Keep the maximum package clause position as position for the package clause of the merged files.
					 = .Package
				}
			}
		}
		 = &CommentGroup{}
	}
Collect declarations from all package files.
	var  []Decl
	if  > 0 {
		 = make([]Decl, )
		 := make(map[string]int) // map of func name -> decls index
		 := 0                        // current index
		 := 0                        // number of filtered entries
		for ,  := range  {
			 := .Files[]
			for ,  := range .Decls {
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 ,  := .(*FuncDecl);  {
						 := nameOf()
function declared already
existing declaration has no documentation; ignore the existing declaration
								[] = nil
ignore the new declaration
								 = nil
							}
							++ // filtered an entry
						} else {
							[] = 
						}
					}
				}
				[] = 
				++
			}
		}
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  > 0 {
			 = 0
			for ,  := range  {
				if  != nil {
					[] = 
					++
				}
			}
			 = [0:]
		}
	}
Collect import specs from all package files.
	var  []*ImportSpec
	if &FilterImportDuplicates != 0 {
		 := make(map[string]bool)
		for ,  := range  {
			 := .Files[]
			for ,  := range .Imports {
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
					 = append(, )
					[] = true
				}
			}
		}
Iterate over filenames for deterministic order.
		for ,  := range  {
			 := .Files[]
			 = append(, .Imports...)
		}
	}
Collect comments from all package files.
	var  []*CommentGroup
	if &FilterUnassociatedComments == 0 {
		 = make([]*CommentGroup, )
		 := 0
		for ,  := range  {
			 := .Files[]
			 += copy([:], .Comments)
		}
	}
TODO(gri) need to compute unresolved identifiers!
	return &File{, , NewIdent(.Name), , .Scope, , nil, }