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