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