// Copyright 2011 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.

// This file implements export filtering of an AST.

package doc

import (
	
	
)

// filterIdentList removes unexported names from list in place
// and returns the resulting list.
func filterIdentList( []*ast.Ident) []*ast.Ident {
	 := 0
	for ,  := range  {
		if token.IsExported(.Name) {
			[] = 
			++
		}
	}
	return [0:]
}

var underscore = ast.NewIdent("_")

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

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

// updateIdentList replaces all unexported identifiers with underscore
// and reports whether at least one exported name exists.
func updateIdentList( []*ast.Ident) ( bool) {
	for ,  := range  {
		if token.IsExported(.Name) {
			 = true
		} else {
			[] = underscore
		}
	}
	return 
}

// hasExportedName reports whether list contains any exported names.
func hasExportedName( []*ast.Ident) bool {
	for ,  := range  {
		if .IsExported() {
			return true
		}
	}
	return false
}

// removeAnonymousField removes anonymous fields named name from an interface.
func removeAnonymousField( string,  *ast.InterfaceType) {
	 := .Methods.List // we know that ityp.Methods != nil
	 := 0
	for ,  := range  {
		 := true
		if  := len(.Names);  == 0 {
			// anonymous field
			if ,  := baseTypeName(.Type);  ==  {
				 = false
			}
		}
		if  {
			[] = 
			++
		}
	}
	if  < len() {
		.Incomplete = true
	}
	.Methods.List = [0:]
}

// filterFieldList removes unexported fields (field names) from the field list
// in place and reports whether fields were removed. Anonymous fields are
// recorded with the parent type. filterType is called with the types of
// all remaining fields.
func ( *reader) ( *namedType,  *ast.FieldList,  *ast.InterfaceType) ( bool) {
	if  == nil {
		return
	}
	 := .List
	 := 0
	for ,  := range  {
		 := false
		if  := len(.Names);  == 0 {
			// anonymous field or embedded type or union element
			 := .recordAnonymousField(, .Type)
			if  != "" {
				if token.IsExported() {
					 = true
				} else if  != nil && predeclaredTypes[] {
					// possibly an embedded predeclared type; keep it for now but
					// remember this interface so that it can be fixed if name is also
					// defined locally
					 = true
					.remember(, )
				}
			} else {
				// If we're operating on an interface, assume that this is an embedded
				// type or union element.
				//
				// TODO(rfindley): consider traversing into approximation/unions
				// elements to see if they are entirely unexported.
				 =  != nil
			}
		} else {
			.Names = filterIdentList(.Names)
			if len(.Names) <  {
				 = true
			}
			if len(.Names) > 0 {
				 = true
			}
		}
		if  {
			.filterType(nil, .Type)
			[] = 
			++
		}
	}
	if  < len() {
		 = true
	}
	.List = [0:]
	return
}

// filterParamList applies filterType to each parameter type in fields.
func ( *reader) ( *ast.FieldList) {
	if  != nil {
		for ,  := range .List {
			.filterType(nil, .Type)
		}
	}
}

// filterType strips any unexported struct fields or method types from typ
// in place. If fields (or methods) have been removed, the corresponding
// struct or interface type has the Incomplete field set to true.
func ( *reader) ( *namedType,  ast.Expr) {
	switch t := .(type) {
	case *ast.Ident:
		// nothing to do
	case *ast.ParenExpr:
		.(nil, .X)
	case *ast.StarExpr: // possibly an embedded type literal
		.(nil, .X)
	case *ast.UnaryExpr:
		if .Op == token.TILDE { // approximation element
			.(nil, .X)
		}
	case *ast.BinaryExpr:
		if .Op == token.OR { // union
			.(nil, .X)
			.(nil, .Y)
		}
	case *ast.ArrayType:
		.(nil, .Elt)
	case *ast.StructType:
		if .filterFieldList(, .Fields, nil) {
			.Incomplete = true
		}
	case *ast.FuncType:
		.filterParamList(.TypeParams)
		.filterParamList(.Params)
		.filterParamList(.Results)
	case *ast.InterfaceType:
		if .filterFieldList(, .Methods, ) {
			.Incomplete = true
		}
	case *ast.MapType:
		.(nil, .Key)
		.(nil, .Value)
	case *ast.ChanType:
		.(nil, .Value)
	}
}

func ( *reader) ( ast.Spec) bool {
	switch s := .(type) {
	case *ast.ImportSpec:
		// always keep imports so we can collect them
		return true
	case *ast.ValueSpec:
		.Values = filterExprList(.Values, token.IsExported, true)
		if len(.Values) > 0 || .Type == nil && len(.Values) == 0 {
			// If there are values declared on RHS, just replace the unexported
			// identifiers on the LHS with underscore, so that it matches
			// the sequence of expression on the RHS.
			//
			// Similarly, if there are no type and values, then this expression
			// must be following an iota expression, where order matters.
			if updateIdentList(.Names) {
				.filterType(nil, .Type)
				return true
			}
		} else {
			.Names = filterIdentList(.Names)
			if len(.Names) > 0 {
				.filterType(nil, .Type)
				return true
			}
		}
	case *ast.TypeSpec:
		// Don't filter type parameters here, by analogy with function parameters
		// which are not filtered for top-level function declarations.
		if  := .Name.Name; token.IsExported() {
			.filterType(.lookupType(.Name.Name), .Type)
			return true
		} else if IsPredeclared() {
			if .shadowedPredecl == nil {
				.shadowedPredecl = make(map[string]bool)
			}
			.shadowedPredecl[] = true
		}
	}
	return false
}

// copyConstType returns a copy of typ with position pos.
// typ must be a valid constant type.
// In practice, only (possibly qualified) identifiers are possible.
func copyConstType( ast.Expr,  token.Pos) ast.Expr {
	switch typ := .(type) {
	case *ast.Ident:
		return &ast.Ident{Name: .Name, NamePos: }
	case *ast.SelectorExpr:
		if ,  := .X.(*ast.Ident);  {
			// presumably a qualified identifier
			return &ast.SelectorExpr{
				Sel: ast.NewIdent(.Sel.Name),
				X:   &ast.Ident{Name: .Name, NamePos: },
			}
		}
	}
	return nil // shouldn't happen, but be conservative and don't panic
}

func ( *reader) ( []ast.Spec,  token.Token) []ast.Spec {
	if  == token.CONST {
		// Propagate any type information that would get lost otherwise
		// when unexported constants are filtered.
		var  ast.Expr
		for ,  := range  {
			 := .(*ast.ValueSpec)
			if .Type == nil && len(.Values) == 0 &&  != nil {
				// provide current spec with an explicit type
				.Type = copyConstType(, .Pos())
			}
			if hasExportedName(.Names) {
				// exported names are preserved so there's no need to propagate the type
				 = nil
			} else {
				 = .Type
			}
		}
	}

	 := 0
	for ,  := range  {
		if .filterSpec() {
			[] = 
			++
		}
	}
	return [0:]
}

func ( *reader) ( ast.Decl) bool {
	switch d := .(type) {
	case *ast.GenDecl:
		.Specs = .filterSpecList(.Specs, .Tok)
		return len(.Specs) > 0
	case *ast.FuncDecl:
		// ok to filter these methods early because any
		// conflicting method will be filtered here, too -
		// thus, removing these methods early will not lead
		// to the false removal of possible conflicts
		return token.IsExported(.Name.Name)
	}
	return false
}

// fileExports removes unexported declarations from src in place.
func ( *reader) ( *ast.File) {
	 := 0
	for ,  := range .Decls {
		if .filterDecl() {
			.Decls[] = 
			++
		}
	}
	.Decls = .Decls[0:]
}