github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/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 }