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.
5 // This file implements export filtering of an AST.
14 // filterIdentList removes unexported names from list in place
15 // and returns the resulting list.
17 func filterIdentList(list
[]*ast
.Ident
) []*ast
.Ident
{
19 for _
, x
:= range list
{
20 if ast
.IsExported(x
.Name
) {
28 // hasExportedName reports whether list contains any exported names.
30 func hasExportedName(list
[]*ast
.Ident
) bool {
31 for _
, x
:= range list
{
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.
43 func removeErrorField(ityp
*ast
.InterfaceType
) {
44 list
:= ityp
.Methods
.List
// we know that ityp.Methods != nil
46 for _
, field
:= range list
{
48 if n
:= len(field
.Names
); n
== 0 {
50 if fname
, _
:= baseTypeName(field
.Type
); fname
== "error" {
60 ityp
.Incomplete
= true
62 ityp
.Methods
.List
= list
[0:j
]
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.
70 func (r
*reader
) filterFieldList(parent
*namedType
, fields
*ast
.FieldList
, ityp
*ast
.InterfaceType
) (removedFields
bool) {
76 for _
, field
:= range list
{
78 if n
:= len(field
.Names
); n
== 0 {
80 fname
:= r
.recordAnonymousField(parent
, field
.Type
)
81 if ast
.IsExported(fname
) {
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
91 field
.Names
= filterIdentList(field
.Names
)
92 if len(field
.Names
) < n
{
95 if len(field
.Names
) > 0 {
100 r
.filterType(nil, field
.Type
)
108 fields
.List
= list
[0:j
]
112 // filterParamList applies filterType to each parameter type in fields.
114 func (r
*reader
) filterParamList(fields
*ast
.FieldList
) {
116 for _
, f
:= range fields
.List
{
117 r
.filterType(nil, f
.Type
)
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.
126 func (r
*reader
) filterType(parent
*namedType
, typ ast
.Expr
) {
127 switch t
:= typ
.(type) {
131 r
.filterType(nil, t
.X
)
133 r
.filterType(nil, t
.Elt
)
134 case *ast
.StructType
:
135 if r
.filterFieldList(parent
, t
.Fields
, nil) {
139 r
.filterParamList(t
.Params
)
140 r
.filterParamList(t
.Results
)
141 case *ast
.InterfaceType
:
142 if r
.filterFieldList(parent
, t
.Methods
, t
) {
146 r
.filterType(nil, t
.Key
)
147 r
.filterType(nil, t
.Value
)
149 r
.filterType(nil, t
.Value
)
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
159 s
.Names
= filterIdentList(s
.Names
)
160 if len(s
.Names
) > 0 {
161 r
.filterType(nil, s
.Type
)
165 if name
:= s
.Name
.Name
; ast
.IsExported(name
) {
166 r
.filterType(r
.lookupType(s
.Name
.Name
), s
.Type
)
168 } else if name
== "error" {
169 // special case: remember that error is declared locally
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.
180 func copyConstType(typ ast
.Expr
, pos token
.Pos
) ast
.Expr
{
181 switch typ
:= typ
.(type) {
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
},
193 return nil // shouldn't happen, but be conservative and don't panic
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 && len(spec
.Values
) == 0 && prevType
!= nil {
204 // provide current spec with an explicit type
205 spec
.Type
= copyConstType(prevType
, spec
.Pos())
207 if hasExportedName(spec
.Names
) {
208 // exported names are preserved so there's no need to propagate the type
217 for _
, s
:= range list
{
226 func (r
*reader
) filterDecl(decl ast
.Decl
) bool {
227 switch d
:= decl
.(type) {
229 d
.Specs
= r
.filterSpecList(d
.Specs
, d
.Tok
)
230 return len(d
.Specs
) > 0
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
)
241 // fileExports removes unexported declarations from src in place.
243 func (r
*reader
) fileExports(src
*ast
.File
) {
245 for _
, d
:= range src
.Decls
{
251 src
.Decls
= src
.Decls
[0:j
]