func getTypes(fields *ast.FieldList, includeNames bool) string {
	if fields == nil {
		return ""
	}
	types := make([]string, len(fields.List))
	for i, param := range fields.List {
		if len(param.Names) > 0 {
			t := getType(param.Type, true)
			if includeNames {
				names := make([]string, len(param.Names))
				for j, n := range param.Names {
					names[j] = n.Name
				}
				t = fmt.Sprintf("%s %s", strings.Join(names, ", "), t)
			} else {
				if len(param.Names) > 1 {
					t = strings.Repeat(fmt.Sprintf("%s, ", t), len(param.Names))
					t = t[:len(t)-2]
				}
			}
			types[i] = t
		} else {
			types[i] = getType(param.Type, true)
		}
	}
	return strings.Join(types, ", ")
}
getTypes returns a comma separated list of types in fields. If includeNames is
true each type is preceded by a comma separated list of parameter names.
package main
import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"strings"
)
type tagParser struct {
	fset         *token.FileSet
	tags         []Tag     
	types        []string  
	relative     bool      
	basepath     string    
	extraSymbols FieldSet  
}
func Parse(filename string, relative bool, basepath string, extra FieldSet) ([]Tag, error) {
	p := &tagParser{
		fset:         token.NewFileSet(),
		tags:         []Tag{},
		types:        make([]string, 0),
		relative:     relative,
		basepath:     basepath,
		extraSymbols: extra,
	}
	f, err := parser.ParseFile(p.fset, filename, nil, 0)
	if err != nil {
		return nil, err
	}
	pkgName := p.parsePackage(f)
	p.parseImports(f)
	p.parseDeclarations(f, pkgName)
	return p.tags, nil
}
func (p *tagParser) parsePackage(f *ast.File) string {
	p.tags = append(p.tags, p.createTag(f.Name.Name, f.Name.Pos(), Package))
	return f.Name.Name
}
func (p *tagParser) parseImports(f *ast.File) {
	for _, im := range f.Imports {
		name := strings.Trim(im.Path.Value, "\"")
		p.tags = append(p.tags, p.createTag(name, im.Path.Pos(), Import))
	}
}
func (p *tagParser) parseDeclarations(f *ast.File, pkgName string) {
	for _, d := range f.Decls {
		if decl, ok := d.(*ast.GenDecl); ok {
			for _, s := range decl.Specs {
				switch ts := s.(type) {
				case *ast.TypeSpec:
					p.parseTypeDeclaration(ts, pkgName)
				case *ast.ValueSpec:
					p.parseValueDeclaration(ts, pkgName)
				}
			}
		}
	}
	for _, d := range f.Decls {
		if decl, ok := d.(*ast.FuncDecl); ok {
			p.parseFunction(decl, pkgName)
		}
	}
}
func (p *tagParser) parseFunction(f *ast.FuncDecl, pkgName string) {
	tag := p.createTag(f.Name.Name, f.Pos(), Function)
	tag.Fields[Access] = getAccess(tag.Name)
	tag.Fields[Signature] = fmt.Sprintf("(%s)", getTypes(f.Type.Params, true))
	tag.Fields[TypeField] = getTypes(f.Type.Results, false)
	if f.Recv != nil && len(f.Recv.List) > 0 {
		tag.Fields[ReceiverType] = getType(f.Recv.List[0].Type, false)
		tag.Type = Method
	} else if name, ok := p.belongsToReceiver(f.Type.Results); ok {
		tag.Fields[ReceiverType] = name
		tag.Type = Function
	}
	p.tags = append(p.tags, tag)
	if p.extraSymbols.Includes(ExtraTags) {
		allNames := make([]string, 0, 10)
		allNames = append(allNames, fmt.Sprintf("%s.%s", pkgName, f.Name.Name))
		if tag.Type == Method {
			allNames = append(allNames,
				fmt.Sprintf("%s.%s", tag.Fields[ReceiverType], f.Name.Name))
			allNames = append(allNames,
				fmt.Sprintf("%s.%s.%s",
					pkgName, tag.Fields[ReceiverType], f.Name.Name))
		}
		for _, n := range allNames {
			newTag := tag
			newTag.Name = n
			p.tags = append(p.tags, newTag)
		}
	}
}
func (p *tagParser) parseTypeDeclaration(ts *ast.TypeSpec, pkgName string) {
	tag := p.createTag(ts.Name.Name, ts.Pos(), Type)
	tag.Fields[Access] = getAccess(tag.Name)
	switch s := ts.Type.(type) {
	case *ast.StructType:
		tag.Fields[TypeField] = "struct"
		p.parseStructFields(tag.Name, s)
		p.types = append(p.types, tag.Name)
	case *ast.InterfaceType:
		tag.Fields[TypeField] = "interface"
		tag.Type = Interface
		p.parseInterfaceMethods(tag.Name, s)
	default:
		tag.Fields[TypeField] = getType(ts.Type, true)
	}
	p.tags = append(p.tags, tag)
	if p.extraSymbols.Includes(ExtraTags) {
		extraTag := tag
		extraTag.Name = fmt.Sprintf("%s.%s", pkgName, tag.Name)
		p.tags = append(p.tags, extraTag)
	}
}
func (p *tagParser) parseValueDeclaration(v *ast.ValueSpec, pkgName string) {
	for _, d := range v.Names {
		if d.Name == "_" {
			continue
		}
		tag := p.createTag(d.Name, d.Pos(), Variable)
		tag.Fields[Access] = getAccess(tag.Name)
		if v.Type != nil {
			tag.Fields[TypeField] = getType(v.Type, true)
		}
		switch d.Obj.Kind {
		case ast.Var:
			tag.Type = Variable
		case ast.Con:
			tag.Type = Constant
		}
		p.tags = append(p.tags, tag)
		if p.extraSymbols.Includes(ExtraTags) {
			otherTag := tag
			otherTag.Name = fmt.Sprintf("%s.%s", pkgName, tag.Name)
			p.tags = append(p.tags, otherTag)
		}
	}
}
func (p *tagParser) parseStructFields(name string, s *ast.StructType) {
	for _, f := range s.Fields.List {
		var tag Tag
		if len(f.Names) > 0 {
			for _, n := range f.Names {
				tag = p.createTag(n.Name, n.Pos(), Field)
				tag.Fields[Access] = getAccess(tag.Name)
				tag.Fields[ReceiverType] = name
				tag.Fields[TypeField] = getType(f.Type, true)
				p.tags = append(p.tags, tag)
			}
		} else {
			tag = p.createTag(getType(f.Type, true), f.Pos(), Embedded)
			tag.Fields[Access] = getAccess(tag.Name)
			tag.Fields[ReceiverType] = name
			tag.Fields[TypeField] = getType(f.Type, true)
			p.tags = append(p.tags, tag)
		}
	}
}
func (p *tagParser) parseInterfaceMethods(name string, s *ast.InterfaceType) {
	for _, f := range s.Methods.List {
		var tag Tag
		if len(f.Names) > 0 {
			tag = p.createTag(f.Names[0].Name, f.Names[0].Pos(), Method)
		} else {
			tag = p.createTag(getType(f.Type, true), f.Pos(), Embedded)
		}
		tag.Fields[Access] = getAccess(tag.Name)
		if t, ok := f.Type.(*ast.FuncType); ok {
			tag.Fields[Signature] = fmt.Sprintf("(%s)", getTypes(t.Params, true))
			tag.Fields[TypeField] = getTypes(t.Results, false)
		}
		tag.Fields[InterfaceType] = name
		p.tags = append(p.tags, tag)
	}
}
func (p *tagParser) createTag(name string, pos token.Pos, tagType TagType) Tag {
	f := p.fset.File(pos).Name()
	if p.relative {
		if abs, err := filepath.Abs(f); err != nil {
			fmt.Fprintf(os.Stderr, "could not determine absolute path: %s\n", err)
		} else if rel, err := filepath.Rel(p.basepath, abs); err != nil {
			fmt.Fprintf(os.Stderr, "could not determine relative path: %s\n", err)
		} else {
			f = rel
		}
	}
	return NewTag(name, f, p.fset.Position(pos).Line, tagType)
}
func (p *tagParser) belongsToReceiver(types *ast.FieldList) (name string, ok bool) {
	if types == nil || types.NumFields() == 0 {
		return "", false
	}
	if len(types.List[0].Names) > 1 {
		return "", false
	}
	t := getType(types.List[0].Type, false)
	for _, knownType := range p.types {
		if t == knownType {
			return knownType, true
		}
	}
	return "", false
}