...

Source file src/go/doc/exports.go

Documentation: go/doc

		 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.
		16  //
		17  func filterIdentList(list []*ast.Ident) []*ast.Ident {
		18  	j := 0
		19  	for _, x := range list {
		20  		if token.IsExported(x.Name) {
		21  			list[j] = x
		22  			j++
		23  		}
		24  	}
		25  	return list[0:j]
		26  }
		27  
		28  var underscore = ast.NewIdent("_")
		29  
		30  func filterCompositeLit(lit *ast.CompositeLit, filter Filter, export bool) {
		31  	n := len(lit.Elts)
		32  	lit.Elts = filterExprList(lit.Elts, filter, export)
		33  	if len(lit.Elts) < n {
		34  		lit.Incomplete = true
		35  	}
		36  }
		37  
		38  func filterExprList(list []ast.Expr, filter Filter, export bool) []ast.Expr {
		39  	j := 0
		40  	for _, exp := range list {
		41  		switch x := exp.(type) {
		42  		case *ast.CompositeLit:
		43  			filterCompositeLit(x, filter, export)
		44  		case *ast.KeyValueExpr:
		45  			if x, ok := x.Key.(*ast.Ident); ok && !filter(x.Name) {
		46  				continue
		47  			}
		48  			if x, ok := x.Value.(*ast.CompositeLit); ok {
		49  				filterCompositeLit(x, filter, export)
		50  			}
		51  		}
		52  		list[j] = exp
		53  		j++
		54  	}
		55  	return list[0:j]
		56  }
		57  
		58  // updateIdentList replaces all unexported identifiers with underscore
		59  // and reports whether at least one exported name exists.
		60  func updateIdentList(list []*ast.Ident) (hasExported bool) {
		61  	for i, x := range list {
		62  		if token.IsExported(x.Name) {
		63  			hasExported = true
		64  		} else {
		65  			list[i] = underscore
		66  		}
		67  	}
		68  	return hasExported
		69  }
		70  
		71  // hasExportedName reports whether list contains any exported names.
		72  //
		73  func hasExportedName(list []*ast.Ident) bool {
		74  	for _, x := range list {
		75  		if x.IsExported() {
		76  			return true
		77  		}
		78  	}
		79  	return false
		80  }
		81  
		82  // removeErrorField removes anonymous fields named "error" from an interface.
		83  // This is called when "error" has been determined to be a local name,
		84  // not the predeclared type.
		85  //
		86  func removeErrorField(ityp *ast.InterfaceType) {
		87  	list := ityp.Methods.List // we know that ityp.Methods != nil
		88  	j := 0
		89  	for _, field := range list {
		90  		keepField := true
		91  		if n := len(field.Names); n == 0 {
		92  			// anonymous field
		93  			if fname, _ := baseTypeName(field.Type); fname == "error" {
		94  				keepField = false
		95  			}
		96  		}
		97  		if keepField {
		98  			list[j] = field
		99  			j++
	 100  		}
	 101  	}
	 102  	if j < len(list) {
	 103  		ityp.Incomplete = true
	 104  	}
	 105  	ityp.Methods.List = list[0:j]
	 106  }
	 107  
	 108  // filterFieldList removes unexported fields (field names) from the field list
	 109  // in place and reports whether fields were removed. Anonymous fields are
	 110  // recorded with the parent type. filterType is called with the types of
	 111  // all remaining fields.
	 112  //
	 113  func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
	 114  	if fields == nil {
	 115  		return
	 116  	}
	 117  	list := fields.List
	 118  	j := 0
	 119  	for _, field := range list {
	 120  		keepField := false
	 121  		if n := len(field.Names); n == 0 {
	 122  			// anonymous field
	 123  			fname := r.recordAnonymousField(parent, field.Type)
	 124  			if token.IsExported(fname) {
	 125  				keepField = true
	 126  			} else if ityp != nil && fname == "error" {
	 127  				// possibly the predeclared error interface; keep
	 128  				// it for now but remember this interface so that
	 129  				// it can be fixed if error is also defined locally
	 130  				keepField = true
	 131  				r.remember(ityp)
	 132  			}
	 133  		} else {
	 134  			field.Names = filterIdentList(field.Names)
	 135  			if len(field.Names) < n {
	 136  				removedFields = true
	 137  			}
	 138  			if len(field.Names) > 0 {
	 139  				keepField = true
	 140  			}
	 141  		}
	 142  		if keepField {
	 143  			r.filterType(nil, field.Type)
	 144  			list[j] = field
	 145  			j++
	 146  		}
	 147  	}
	 148  	if j < len(list) {
	 149  		removedFields = true
	 150  	}
	 151  	fields.List = list[0:j]
	 152  	return
	 153  }
	 154  
	 155  // filterParamList applies filterType to each parameter type in fields.
	 156  //
	 157  func (r *reader) filterParamList(fields *ast.FieldList) {
	 158  	if fields != nil {
	 159  		for _, f := range fields.List {
	 160  			r.filterType(nil, f.Type)
	 161  		}
	 162  	}
	 163  }
	 164  
	 165  // filterType strips any unexported struct fields or method types from typ
	 166  // in place. If fields (or methods) have been removed, the corresponding
	 167  // struct or interface type has the Incomplete field set to true.
	 168  //
	 169  func (r *reader) filterType(parent *namedType, typ ast.Expr) {
	 170  	switch t := typ.(type) {
	 171  	case *ast.Ident:
	 172  		// nothing to do
	 173  	case *ast.ParenExpr:
	 174  		r.filterType(nil, t.X)
	 175  	case *ast.ArrayType:
	 176  		r.filterType(nil, t.Elt)
	 177  	case *ast.StructType:
	 178  		if r.filterFieldList(parent, t.Fields, nil) {
	 179  			t.Incomplete = true
	 180  		}
	 181  	case *ast.FuncType:
	 182  		r.filterParamList(t.Params)
	 183  		r.filterParamList(t.Results)
	 184  	case *ast.InterfaceType:
	 185  		if r.filterFieldList(parent, t.Methods, t) {
	 186  			t.Incomplete = true
	 187  		}
	 188  	case *ast.MapType:
	 189  		r.filterType(nil, t.Key)
	 190  		r.filterType(nil, t.Value)
	 191  	case *ast.ChanType:
	 192  		r.filterType(nil, t.Value)
	 193  	}
	 194  }
	 195  
	 196  func (r *reader) filterSpec(spec ast.Spec) bool {
	 197  	switch s := spec.(type) {
	 198  	case *ast.ImportSpec:
	 199  		// always keep imports so we can collect them
	 200  		return true
	 201  	case *ast.ValueSpec:
	 202  		s.Values = filterExprList(s.Values, token.IsExported, true)
	 203  		if len(s.Values) > 0 || s.Type == nil && len(s.Values) == 0 {
	 204  			// If there are values declared on RHS, just replace the unexported
	 205  			// identifiers on the LHS with underscore, so that it matches
	 206  			// the sequence of expression on the RHS.
	 207  			//
	 208  			// Similarly, if there are no type and values, then this expression
	 209  			// must be following an iota expression, where order matters.
	 210  			if updateIdentList(s.Names) {
	 211  				r.filterType(nil, s.Type)
	 212  				return true
	 213  			}
	 214  		} else {
	 215  			s.Names = filterIdentList(s.Names)
	 216  			if len(s.Names) > 0 {
	 217  				r.filterType(nil, s.Type)
	 218  				return true
	 219  			}
	 220  		}
	 221  	case *ast.TypeSpec:
	 222  		if name := s.Name.Name; token.IsExported(name) {
	 223  			r.filterType(r.lookupType(s.Name.Name), s.Type)
	 224  			return true
	 225  		} else if name == "error" {
	 226  			// special case: remember that error is declared locally
	 227  			r.errorDecl = true
	 228  		}
	 229  	}
	 230  	return false
	 231  }
	 232  
	 233  // copyConstType returns a copy of typ with position pos.
	 234  // typ must be a valid constant type.
	 235  // In practice, only (possibly qualified) identifiers are possible.
	 236  //
	 237  func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr {
	 238  	switch typ := typ.(type) {
	 239  	case *ast.Ident:
	 240  		return &ast.Ident{Name: typ.Name, NamePos: pos}
	 241  	case *ast.SelectorExpr:
	 242  		if id, ok := typ.X.(*ast.Ident); ok {
	 243  			// presumably a qualified identifier
	 244  			return &ast.SelectorExpr{
	 245  				Sel: ast.NewIdent(typ.Sel.Name),
	 246  				X:	 &ast.Ident{Name: id.Name, NamePos: pos},
	 247  			}
	 248  		}
	 249  	}
	 250  	return nil // shouldn't happen, but be conservative and don't panic
	 251  }
	 252  
	 253  func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
	 254  	if tok == token.CONST {
	 255  		// Propagate any type information that would get lost otherwise
	 256  		// when unexported constants are filtered.
	 257  		var prevType ast.Expr
	 258  		for _, spec := range list {
	 259  			spec := spec.(*ast.ValueSpec)
	 260  			if spec.Type == nil && len(spec.Values) == 0 && prevType != nil {
	 261  				// provide current spec with an explicit type
	 262  				spec.Type = copyConstType(prevType, spec.Pos())
	 263  			}
	 264  			if hasExportedName(spec.Names) {
	 265  				// exported names are preserved so there's no need to propagate the type
	 266  				prevType = nil
	 267  			} else {
	 268  				prevType = spec.Type
	 269  			}
	 270  		}
	 271  	}
	 272  
	 273  	j := 0
	 274  	for _, s := range list {
	 275  		if r.filterSpec(s) {
	 276  			list[j] = s
	 277  			j++
	 278  		}
	 279  	}
	 280  	return list[0:j]
	 281  }
	 282  
	 283  func (r *reader) filterDecl(decl ast.Decl) bool {
	 284  	switch d := decl.(type) {
	 285  	case *ast.GenDecl:
	 286  		d.Specs = r.filterSpecList(d.Specs, d.Tok)
	 287  		return len(d.Specs) > 0
	 288  	case *ast.FuncDecl:
	 289  		// ok to filter these methods early because any
	 290  		// conflicting method will be filtered here, too -
	 291  		// thus, removing these methods early will not lead
	 292  		// to the false removal of possible conflicts
	 293  		return token.IsExported(d.Name.Name)
	 294  	}
	 295  	return false
	 296  }
	 297  
	 298  // fileExports removes unexported declarations from src in place.
	 299  //
	 300  func (r *reader) fileExports(src *ast.File) {
	 301  	j := 0
	 302  	for _, d := range src.Decls {
	 303  		if r.filterDecl(d) {
	 304  			src.Decls[j] = d
	 305  			j++
	 306  		}
	 307  	}
	 308  	src.Decls = src.Decls[0:j]
	 309  }
	 310  

View as plain text