github.com/zach-klippenstein/go@v0.0.0-20150108044943-fcfbeb3adf58/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  // hasExportedOrBlankName reports whether list contains any exported or blank names.
    30  //
    31  func hasExportedOrBlankName(list []*ast.Ident) bool {
    32  	for _, x := range list {
    33  		if x.IsExported() || x.Name == "_" {
    34  			return true
    35  		}
    36  	}
    37  	return false
    38  }
    39  
    40  // removeErrorField removes anonymous fields named "error" from an interface.
    41  // This is called when "error" has been determined to be a local name,
    42  // not the predeclared type.
    43  //
    44  func removeErrorField(ityp *ast.InterfaceType) {
    45  	list := ityp.Methods.List // we know that ityp.Methods != nil
    46  	j := 0
    47  	for _, field := range list {
    48  		keepField := true
    49  		if n := len(field.Names); n == 0 {
    50  			// anonymous field
    51  			if fname, _ := baseTypeName(field.Type); fname == "error" {
    52  				keepField = false
    53  			}
    54  		}
    55  		if keepField {
    56  			list[j] = field
    57  			j++
    58  		}
    59  	}
    60  	if j < len(list) {
    61  		ityp.Incomplete = true
    62  	}
    63  	ityp.Methods.List = list[0:j]
    64  }
    65  
    66  // filterFieldList removes unexported fields (field names) from the field list
    67  // in place and returns true if fields were removed. Anonymous fields are
    68  // recorded with the parent type. filterType is called with the types of
    69  // all remaining fields.
    70  //
    71  func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
    72  	if fields == nil {
    73  		return
    74  	}
    75  	list := fields.List
    76  	j := 0
    77  	for _, field := range list {
    78  		keepField := false
    79  		if n := len(field.Names); n == 0 {
    80  			// anonymous field
    81  			fname := r.recordAnonymousField(parent, field.Type)
    82  			if ast.IsExported(fname) {
    83  				keepField = true
    84  			} else if ityp != nil && fname == "error" {
    85  				// possibly the predeclared error interface; keep
    86  				// it for now but remember this interface so that
    87  				// it can be fixed if error is also defined locally
    88  				keepField = true
    89  				r.remember(ityp)
    90  			}
    91  		} else {
    92  			field.Names = filterIdentList(field.Names, false)
    93  			if len(field.Names) < n {
    94  				removedFields = true
    95  			}
    96  			if len(field.Names) > 0 {
    97  				keepField = true
    98  			}
    99  		}
   100  		if keepField {
   101  			r.filterType(nil, field.Type)
   102  			list[j] = field
   103  			j++
   104  		}
   105  	}
   106  	if j < len(list) {
   107  		removedFields = true
   108  	}
   109  	fields.List = list[0:j]
   110  	return
   111  }
   112  
   113  // filterParamList applies filterType to each parameter type in fields.
   114  //
   115  func (r *reader) filterParamList(fields *ast.FieldList) {
   116  	if fields != nil {
   117  		for _, f := range fields.List {
   118  			r.filterType(nil, f.Type)
   119  		}
   120  	}
   121  }
   122  
   123  // filterType strips any unexported struct fields or method types from typ
   124  // in place. If fields (or methods) have been removed, the corresponding
   125  // struct or interface type has the Incomplete field set to true.
   126  //
   127  func (r *reader) filterType(parent *namedType, typ ast.Expr) {
   128  	switch t := typ.(type) {
   129  	case *ast.Ident:
   130  		// nothing to do
   131  	case *ast.ParenExpr:
   132  		r.filterType(nil, t.X)
   133  	case *ast.ArrayType:
   134  		r.filterType(nil, t.Elt)
   135  	case *ast.StructType:
   136  		if r.filterFieldList(parent, t.Fields, nil) {
   137  			t.Incomplete = true
   138  		}
   139  	case *ast.FuncType:
   140  		r.filterParamList(t.Params)
   141  		r.filterParamList(t.Results)
   142  	case *ast.InterfaceType:
   143  		if r.filterFieldList(parent, t.Methods, t) {
   144  			t.Incomplete = true
   145  		}
   146  	case *ast.MapType:
   147  		r.filterType(nil, t.Key)
   148  		r.filterType(nil, t.Value)
   149  	case *ast.ChanType:
   150  		r.filterType(nil, t.Value)
   151  	}
   152  }
   153  
   154  func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
   155  	switch s := spec.(type) {
   156  	case *ast.ImportSpec:
   157  		// always keep imports so we can collect them
   158  		return true
   159  	case *ast.ValueSpec:
   160  		// special case: consider blank constants as exported
   161  		// (work-around for issue 5397)
   162  		s.Names = filterIdentList(s.Names, tok == token.CONST)
   163  		if len(s.Names) > 0 {
   164  			r.filterType(nil, s.Type)
   165  			return true
   166  		}
   167  	case *ast.TypeSpec:
   168  		if name := s.Name.Name; ast.IsExported(name) {
   169  			r.filterType(r.lookupType(s.Name.Name), s.Type)
   170  			return true
   171  		} else if name == "error" {
   172  			// special case: remember that error is declared locally
   173  			r.errorDecl = true
   174  		}
   175  	}
   176  	return false
   177  }
   178  
   179  // copyConstType returns a copy of typ with position pos.
   180  // typ must be a valid constant type.
   181  // In practice, only (possibly qualified) identifiers are possible.
   182  //
   183  func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr {
   184  	switch typ := typ.(type) {
   185  	case *ast.Ident:
   186  		return &ast.Ident{Name: typ.Name, NamePos: pos}
   187  	case *ast.SelectorExpr:
   188  		if id, ok := typ.X.(*ast.Ident); ok {
   189  			// presumably a qualified identifier
   190  			return &ast.SelectorExpr{
   191  				Sel: ast.NewIdent(typ.Sel.Name),
   192  				X:   &ast.Ident{Name: id.Name, NamePos: pos},
   193  			}
   194  		}
   195  	}
   196  	return nil // shouldn't happen, but be conservative and don't panic
   197  }
   198  
   199  func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
   200  	if tok == token.CONST {
   201  		// Propagate any type information that would get lost otherwise
   202  		// when unexported constants are filtered.
   203  		var prevType ast.Expr
   204  		for _, spec := range list {
   205  			spec := spec.(*ast.ValueSpec)
   206  			if spec.Type == nil && prevType != nil {
   207  				// provide current spec with an explicit type
   208  				spec.Type = copyConstType(prevType, spec.Pos())
   209  			}
   210  			if hasExportedOrBlankName(spec.Names) {
   211  				// both exported and blank names are preserved
   212  				// so there's no need to propagate the type
   213  				prevType = nil
   214  			} else {
   215  				prevType = spec.Type
   216  			}
   217  		}
   218  	}
   219  
   220  	j := 0
   221  	for _, s := range list {
   222  		if r.filterSpec(s, tok) {
   223  			list[j] = s
   224  			j++
   225  		}
   226  	}
   227  	return list[0:j]
   228  }
   229  
   230  func (r *reader) filterDecl(decl ast.Decl) bool {
   231  	switch d := decl.(type) {
   232  	case *ast.GenDecl:
   233  		d.Specs = r.filterSpecList(d.Specs, d.Tok)
   234  		return len(d.Specs) > 0
   235  	case *ast.FuncDecl:
   236  		// ok to filter these methods early because any
   237  		// conflicting method will be filtered here, too -
   238  		// thus, removing these methods early will not lead
   239  		// to the false removal of possible conflicts
   240  		return ast.IsExported(d.Name.Name)
   241  	}
   242  	return false
   243  }
   244  
   245  // fileExports removes unexported declarations from src in place.
   246  //
   247  func (r *reader) fileExports(src *ast.File) {
   248  	j := 0
   249  	for _, d := range src.Decls {
   250  		if r.filterDecl(d) {
   251  			src.Decls[j] = d
   252  			j++
   253  		}
   254  	}
   255  	src.Decls = src.Decls[0:j]
   256  }