github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/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 // removeErrorField removes anonymous fields named "error" from an interface. 30 // This is called when "error" has been determined to be a local name, 31 // not the predeclared type. 32 // 33 func removeErrorField(ityp *ast.InterfaceType) { 34 list := ityp.Methods.List // we know that ityp.Methods != nil 35 j := 0 36 for _, field := range list { 37 keepField := true 38 if n := len(field.Names); n == 0 { 39 // anonymous field 40 if fname, _ := baseTypeName(field.Type); fname == "error" { 41 keepField = false 42 } 43 } 44 if keepField { 45 list[j] = field 46 j++ 47 } 48 } 49 if j < len(list) { 50 ityp.Incomplete = true 51 } 52 ityp.Methods.List = list[0:j] 53 } 54 55 // filterFieldList removes unexported fields (field names) from the field list 56 // in place and returns true if fields were removed. Anonymous fields are 57 // recorded with the parent type. filterType is called with the types of 58 // all remaining fields. 59 // 60 func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) { 61 if fields == nil { 62 return 63 } 64 list := fields.List 65 j := 0 66 for _, field := range list { 67 keepField := false 68 if n := len(field.Names); n == 0 { 69 // anonymous field 70 fname := r.recordAnonymousField(parent, field.Type) 71 if ast.IsExported(fname) { 72 keepField = true 73 } else if ityp != nil && fname == "error" { 74 // possibly the predeclared error interface; keep 75 // it for now but remember this interface so that 76 // it can be fixed if error is also defined locally 77 keepField = true 78 r.remember(ityp) 79 } 80 } else { 81 field.Names = filterIdentList(field.Names, false) 82 if len(field.Names) < n { 83 removedFields = true 84 } 85 if len(field.Names) > 0 { 86 keepField = true 87 } 88 } 89 if keepField { 90 r.filterType(nil, field.Type) 91 list[j] = field 92 j++ 93 } 94 } 95 if j < len(list) { 96 removedFields = true 97 } 98 fields.List = list[0:j] 99 return 100 } 101 102 // filterParamList applies filterType to each parameter type in fields. 103 // 104 func (r *reader) filterParamList(fields *ast.FieldList) { 105 if fields != nil { 106 for _, f := range fields.List { 107 r.filterType(nil, f.Type) 108 } 109 } 110 } 111 112 // filterType strips any unexported struct fields or method types from typ 113 // in place. If fields (or methods) have been removed, the corresponding 114 // struct or interface type has the Incomplete field set to true. 115 // 116 func (r *reader) filterType(parent *namedType, typ ast.Expr) { 117 switch t := typ.(type) { 118 case *ast.Ident: 119 // nothing to do 120 case *ast.ParenExpr: 121 r.filterType(nil, t.X) 122 case *ast.ArrayType: 123 r.filterType(nil, t.Elt) 124 case *ast.StructType: 125 if r.filterFieldList(parent, t.Fields, nil) { 126 t.Incomplete = true 127 } 128 case *ast.FuncType: 129 r.filterParamList(t.Params) 130 r.filterParamList(t.Results) 131 case *ast.InterfaceType: 132 if r.filterFieldList(parent, t.Methods, t) { 133 t.Incomplete = true 134 } 135 case *ast.MapType: 136 r.filterType(nil, t.Key) 137 r.filterType(nil, t.Value) 138 case *ast.ChanType: 139 r.filterType(nil, t.Value) 140 } 141 } 142 143 func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool { 144 switch s := spec.(type) { 145 case *ast.ImportSpec: 146 // always keep imports so we can collect them 147 return true 148 case *ast.ValueSpec: 149 // special case: consider blank constants as exported 150 // (work-around for issue 5397) 151 s.Names = filterIdentList(s.Names, tok == token.CONST) 152 if len(s.Names) > 0 { 153 r.filterType(nil, s.Type) 154 return true 155 } 156 case *ast.TypeSpec: 157 if name := s.Name.Name; ast.IsExported(name) { 158 r.filterType(r.lookupType(s.Name.Name), s.Type) 159 return true 160 } else if name == "error" { 161 // special case: remember that error is declared locally 162 r.errorDecl = true 163 } 164 } 165 return false 166 } 167 168 func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec { 169 j := 0 170 for _, s := range list { 171 if r.filterSpec(s, tok) { 172 list[j] = s 173 j++ 174 } 175 } 176 return list[0:j] 177 } 178 179 func (r *reader) filterDecl(decl ast.Decl) bool { 180 switch d := decl.(type) { 181 case *ast.GenDecl: 182 d.Specs = r.filterSpecList(d.Specs, d.Tok) 183 return len(d.Specs) > 0 184 case *ast.FuncDecl: 185 // ok to filter these methods early because any 186 // conflicting method will be filtered here, too - 187 // thus, removing these methods early will not lead 188 // to the false removal of possible conflicts 189 return ast.IsExported(d.Name.Name) 190 } 191 return false 192 } 193 194 // fileExports removes unexported declarations from src in place. 195 // 196 func (r *reader) fileExports(src *ast.File) { 197 j := 0 198 for _, d := range src.Decls { 199 if r.filterDecl(d) { 200 src.Decls[j] = d 201 j++ 202 } 203 } 204 src.Decls = src.Decls[0:j] 205 }