github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/go/doc/exports.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file implements export filtering of an AST.
     6  
     7  package doc
     8  
     9  import (
    10  	"go/ast"
    11  	"go/token"
    12  )
    13  
    14  // filterIdentList removes unexported names from list in place
    15  // and returns the resulting list. If blankOk is set, blank
    16  // identifiers are considered exported names.
    17  //
    18  func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident {
    19  	j := 0
    20  	for _, x := range list {
    21  		if ast.IsExported(x.Name) || (blankOk && x.Name == "_") {
    22  			list[j] = x
    23  			j++
    24  		}
    25  	}
    26  	return list[0:j]
    27  }
    28  
    29  // removeErrorField removes anonymous fields named "error" from an interface.
    30  // This is called when "error" has been determined to be a local name,
    31  // not the predeclared type.
    32  //
    33  func removeErrorField(ityp *ast.InterfaceType) {
    34  	list := ityp.Methods.List // we know that ityp.Methods != nil
    35  	j := 0
    36  	for _, field := range list {
    37  		keepField := true
    38  		if n := len(field.Names); n == 0 {
    39  			// anonymous field
    40  			if fname, _ := baseTypeName(field.Type); fname == "error" {
    41  				keepField = false
    42  			}
    43  		}
    44  		if keepField {
    45  			list[j] = field
    46  			j++
    47  		}
    48  	}
    49  	if j < len(list) {
    50  		ityp.Incomplete = true
    51  	}
    52  	ityp.Methods.List = list[0:j]
    53  }
    54  
    55  // filterFieldList removes unexported fields (field names) from the field list
    56  // in place and returns true if fields were removed. Anonymous fields are
    57  // recorded with the parent type. filterType is called with the types of
    58  // all remaining fields.
    59  //
    60  func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
    61  	if fields == nil {
    62  		return
    63  	}
    64  	list := fields.List
    65  	j := 0
    66  	for _, field := range list {
    67  		keepField := false
    68  		if n := len(field.Names); n == 0 {
    69  			// anonymous field
    70  			fname := r.recordAnonymousField(parent, field.Type)
    71  			if ast.IsExported(fname) {
    72  				keepField = true
    73  			} else if ityp != nil && fname == "error" {
    74  				// possibly the predeclared error interface; keep
    75  				// it for now but remember this interface so that
    76  				// it can be fixed if error is also defined locally
    77  				keepField = true
    78  				r.remember(ityp)
    79  			}
    80  		} else {
    81  			field.Names = filterIdentList(field.Names, false)
    82  			if len(field.Names) < n {
    83  				removedFields = true
    84  			}
    85  			if len(field.Names) > 0 {
    86  				keepField = true
    87  			}
    88  		}
    89  		if keepField {
    90  			r.filterType(nil, field.Type)
    91  			list[j] = field
    92  			j++
    93  		}
    94  	}
    95  	if j < len(list) {
    96  		removedFields = true
    97  	}
    98  	fields.List = list[0:j]
    99  	return
   100  }
   101  
   102  // filterParamList applies filterType to each parameter type in fields.
   103  //
   104  func (r *reader) filterParamList(fields *ast.FieldList) {
   105  	if fields != nil {
   106  		for _, f := range fields.List {
   107  			r.filterType(nil, f.Type)
   108  		}
   109  	}
   110  }
   111  
   112  // filterType strips any unexported struct fields or method types from typ
   113  // in place. If fields (or methods) have been removed, the corresponding
   114  // struct or interface type has the Incomplete field set to true.
   115  //
   116  func (r *reader) filterType(parent *namedType, typ ast.Expr) {
   117  	switch t := typ.(type) {
   118  	case *ast.Ident:
   119  		// nothing to do
   120  	case *ast.ParenExpr:
   121  		r.filterType(nil, t.X)
   122  	case *ast.ArrayType:
   123  		r.filterType(nil, t.Elt)
   124  	case *ast.StructType:
   125  		if r.filterFieldList(parent, t.Fields, nil) {
   126  			t.Incomplete = true
   127  		}
   128  	case *ast.FuncType:
   129  		r.filterParamList(t.Params)
   130  		r.filterParamList(t.Results)
   131  	case *ast.InterfaceType:
   132  		if r.filterFieldList(parent, t.Methods, t) {
   133  			t.Incomplete = true
   134  		}
   135  	case *ast.MapType:
   136  		r.filterType(nil, t.Key)
   137  		r.filterType(nil, t.Value)
   138  	case *ast.ChanType:
   139  		r.filterType(nil, t.Value)
   140  	}
   141  }
   142  
   143  func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
   144  	switch s := spec.(type) {
   145  	case *ast.ImportSpec:
   146  		// always keep imports so we can collect them
   147  		return true
   148  	case *ast.ValueSpec:
   149  		// special case: consider blank constants as exported
   150  		// (work-around for issue 5397)
   151  		s.Names = filterIdentList(s.Names, tok == token.CONST)
   152  		if len(s.Names) > 0 {
   153  			r.filterType(nil, s.Type)
   154  			return true
   155  		}
   156  	case *ast.TypeSpec:
   157  		if name := s.Name.Name; ast.IsExported(name) {
   158  			r.filterType(r.lookupType(s.Name.Name), s.Type)
   159  			return true
   160  		} else if name == "error" {
   161  			// special case: remember that error is declared locally
   162  			r.errorDecl = true
   163  		}
   164  	}
   165  	return false
   166  }
   167  
   168  func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
   169  	j := 0
   170  	for _, s := range list {
   171  		if r.filterSpec(s, tok) {
   172  			list[j] = s
   173  			j++
   174  		}
   175  	}
   176  	return list[0:j]
   177  }
   178  
   179  func (r *reader) filterDecl(decl ast.Decl) bool {
   180  	switch d := decl.(type) {
   181  	case *ast.GenDecl:
   182  		d.Specs = r.filterSpecList(d.Specs, d.Tok)
   183  		return len(d.Specs) > 0
   184  	case *ast.FuncDecl:
   185  		// ok to filter these methods early because any
   186  		// conflicting method will be filtered here, too -
   187  		// thus, removing these methods early will not lead
   188  		// to the false removal of possible conflicts
   189  		return ast.IsExported(d.Name.Name)
   190  	}
   191  	return false
   192  }
   193  
   194  // fileExports removes unexported declarations from src in place.
   195  //
   196  func (r *reader) fileExports(src *ast.File) {
   197  	j := 0
   198  	for _, d := range src.Decls {
   199  		if r.filterDecl(d) {
   200  			src.Decls[j] = d
   201  			j++
   202  		}
   203  	}
   204  	src.Decls = src.Decls[0:j]
   205  }