package doc
import (
"go/ast"
"go/token"
)
func filterIdentList(list []*ast .Ident ) []*ast .Ident {
j := 0
for _ , x := range list {
if token .IsExported (x .Name ) {
list [j ] = x
j ++
}
}
return list [0 :j ]
}
var underscore = ast .NewIdent ("_" )
func filterCompositeLit(lit *ast .CompositeLit , filter Filter , export bool ) {
n := len (lit .Elts )
lit .Elts = filterExprList (lit .Elts , filter , export )
if len (lit .Elts ) < n {
lit .Incomplete = true
}
}
func filterExprList(list []ast .Expr , filter Filter , export bool ) []ast .Expr {
j := 0
for _ , exp := range list {
switch x := exp .(type ) {
case *ast .CompositeLit :
filterCompositeLit (x , filter , export )
case *ast .KeyValueExpr :
if x , ok := x .Key .(*ast .Ident ); ok && !filter (x .Name ) {
continue
}
if x , ok := x .Value .(*ast .CompositeLit ); ok {
filterCompositeLit (x , filter , export )
}
}
list [j ] = exp
j ++
}
return list [0 :j ]
}
func updateIdentList(list []*ast .Ident ) (hasExported bool ) {
for i , x := range list {
if token .IsExported (x .Name ) {
hasExported = true
} else {
list [i ] = underscore
}
}
return hasExported
}
func hasExportedName(list []*ast .Ident ) bool {
for _ , x := range list {
if x .IsExported () {
return true
}
}
return false
}
func removeAnonymousField(name string , ityp *ast .InterfaceType ) {
list := ityp .Methods .List
j := 0
for _ , field := range list {
keepField := true
if n := len (field .Names ); n == 0 {
if fname , _ := baseTypeName (field .Type ); fname == name {
keepField = false
}
}
if keepField {
list [j ] = field
j ++
}
}
if j < len (list ) {
ityp .Incomplete = true
}
ityp .Methods .List = list [0 :j ]
}
func (r *reader ) filterFieldList (parent *namedType , fields *ast .FieldList , ityp *ast .InterfaceType ) (removedFields bool ) {
if fields == nil {
return
}
list := fields .List
j := 0
for _ , field := range list {
keepField := false
if n := len (field .Names ); n == 0 {
fname := r .recordAnonymousField (parent , field .Type )
if fname != "" {
if token .IsExported (fname ) {
keepField = true
} else if ityp != nil && predeclaredTypes [fname ] {
keepField = true
r .remember (fname , ityp )
}
} else {
keepField = ityp != nil
}
} else {
field .Names = filterIdentList (field .Names )
if len (field .Names ) < n {
removedFields = true
}
if len (field .Names ) > 0 {
keepField = true
}
}
if keepField {
r .filterType (nil , field .Type )
list [j ] = field
j ++
}
}
if j < len (list ) {
removedFields = true
}
fields .List = list [0 :j ]
return
}
func (r *reader ) filterParamList (fields *ast .FieldList ) {
if fields != nil {
for _ , f := range fields .List {
r .filterType (nil , f .Type )
}
}
}
func (r *reader ) filterType (parent *namedType , typ ast .Expr ) {
switch t := typ .(type ) {
case *ast .Ident :
case *ast .ParenExpr :
r .filterType (nil , t .X )
case *ast .StarExpr :
r .filterType (nil , t .X )
case *ast .UnaryExpr :
if t .Op == token .TILDE {
r .filterType (nil , t .X )
}
case *ast .BinaryExpr :
if t .Op == token .OR {
r .filterType (nil , t .X )
r .filterType (nil , t .Y )
}
case *ast .ArrayType :
r .filterType (nil , t .Elt )
case *ast .StructType :
if r .filterFieldList (parent , t .Fields , nil ) {
t .Incomplete = true
}
case *ast .FuncType :
r .filterParamList (t .TypeParams )
r .filterParamList (t .Params )
r .filterParamList (t .Results )
case *ast .InterfaceType :
if r .filterFieldList (parent , t .Methods , t ) {
t .Incomplete = true
}
case *ast .MapType :
r .filterType (nil , t .Key )
r .filterType (nil , t .Value )
case *ast .ChanType :
r .filterType (nil , t .Value )
}
}
func (r *reader ) filterSpec (spec ast .Spec ) bool {
switch s := spec .(type ) {
case *ast .ImportSpec :
return true
case *ast .ValueSpec :
s .Values = filterExprList (s .Values , token .IsExported , true )
if len (s .Values ) > 0 || s .Type == nil && len (s .Values ) == 0 {
if updateIdentList (s .Names ) {
r .filterType (nil , s .Type )
return true
}
} else {
s .Names = filterIdentList (s .Names )
if len (s .Names ) > 0 {
r .filterType (nil , s .Type )
return true
}
}
case *ast .TypeSpec :
if name := s .Name .Name ; token .IsExported (name ) {
r .filterType (r .lookupType (s .Name .Name ), s .Type )
return true
} else if IsPredeclared (name ) {
if r .shadowedPredecl == nil {
r .shadowedPredecl = make (map [string ]bool )
}
r .shadowedPredecl [name ] = true
}
}
return false
}
func copyConstType(typ ast .Expr , pos token .Pos ) ast .Expr {
switch typ := typ .(type ) {
case *ast .Ident :
return &ast .Ident {Name : typ .Name , NamePos : pos }
case *ast .SelectorExpr :
if id , ok := typ .X .(*ast .Ident ); ok {
return &ast .SelectorExpr {
Sel : ast .NewIdent (typ .Sel .Name ),
X : &ast .Ident {Name : id .Name , NamePos : pos },
}
}
}
return nil
}
func (r *reader ) filterSpecList (list []ast .Spec , tok token .Token ) []ast .Spec {
if tok == token .CONST {
var prevType ast .Expr
for _ , spec := range list {
spec := spec .(*ast .ValueSpec )
if spec .Type == nil && len (spec .Values ) == 0 && prevType != nil {
spec .Type = copyConstType (prevType , spec .Pos ())
}
if hasExportedName (spec .Names ) {
prevType = nil
} else {
prevType = spec .Type
}
}
}
j := 0
for _ , s := range list {
if r .filterSpec (s ) {
list [j ] = s
j ++
}
}
return list [0 :j ]
}
func (r *reader ) filterDecl (decl ast .Decl ) bool {
switch d := decl .(type ) {
case *ast .GenDecl :
d .Specs = r .filterSpecList (d .Specs , d .Tok )
return len (d .Specs ) > 0
case *ast .FuncDecl :
return token .IsExported (d .Name .Name )
}
return false
}
func (r *reader ) fileExports (src *ast .File ) {
j := 0
for _ , d := range src .Decls {
if r .filterDecl (d ) {
src .Decls [j ] = d
j ++
}
}
src .Decls = src .Decls [0 :j ]
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .