forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/swaggo/swag/packages.go

994 lines
18 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
package swag
import (
"fmt"
"go/ast"
goparser "go/parser"
"go/token"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"golang.org/x/tools/go/loader"
)
// PackagesDefinitions map[package import path]*PackageDefinitions.
2024-05-14 13:07:09 +00:00
type PackagesDefinitions struct {
files map[*ast.File]*AstFileInfo
packages map[string]*PackageDefinitions
2024-05-14 13:07:09 +00:00
uniqueDefinitions map[string]*TypeSpecDef
parseDependency ParseFlag
debug Debugger
2024-05-14 13:07:09 +00:00
}
// NewPackagesDefinitions create object PackagesDefinitions.
2024-05-14 13:07:09 +00:00
func NewPackagesDefinitions() *PackagesDefinitions {
2024-05-14 13:07:09 +00:00
return &PackagesDefinitions{
files: make(map[*ast.File]*AstFileInfo),
packages: make(map[string]*PackageDefinitions),
2024-05-14 13:07:09 +00:00
uniqueDefinitions: make(map[string]*TypeSpecDef),
}
2024-05-14 13:07:09 +00:00
}
// ParseFile parse a source file.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) ParseFile(packageDir, path string, src interface{}, flag ParseFlag) error {
2024-05-14 13:07:09 +00:00
// positions are relative to FileSet
2024-05-14 13:07:09 +00:00
fileSet := token.NewFileSet()
2024-05-14 13:07:09 +00:00
astFile, err := goparser.ParseFile(fileSet, path, src, goparser.ParseComments)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return fmt.Errorf("failed to parse file %s, error:%+v", path, err)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return pkgDefs.collectAstFile(fileSet, packageDir, path, astFile, flag)
2024-05-14 13:07:09 +00:00
}
// collectAstFile collect ast.file.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) collectAstFile(fileSet *token.FileSet, packageDir, path string, astFile *ast.File, flag ParseFlag) error {
2024-05-14 13:07:09 +00:00
if pkgDefs.files == nil {
2024-05-14 13:07:09 +00:00
pkgDefs.files = make(map[*ast.File]*AstFileInfo)
2024-05-14 13:07:09 +00:00
}
if pkgDefs.packages == nil {
2024-05-14 13:07:09 +00:00
pkgDefs.packages = make(map[string]*PackageDefinitions)
2024-05-14 13:07:09 +00:00
}
// return without storing the file if we lack a packageDir
2024-05-14 13:07:09 +00:00
if packageDir == "" {
2024-05-14 13:07:09 +00:00
return nil
2024-05-14 13:07:09 +00:00
}
path, err := filepath.Abs(path)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
dependency, ok := pkgDefs.packages[packageDir]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
// return without storing the file if it already exists
2024-05-14 13:07:09 +00:00
_, exists := dependency.Files[path]
2024-05-14 13:07:09 +00:00
if exists {
2024-05-14 13:07:09 +00:00
return nil
2024-05-14 13:07:09 +00:00
}
dependency.Files[path] = astFile
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
pkgDefs.packages[packageDir] = NewPackageDefinitions(astFile.Name.Name, packageDir).AddFile(path, astFile)
2024-05-14 13:07:09 +00:00
}
pkgDefs.files[astFile] = &AstFileInfo{
FileSet: fileSet,
File: astFile,
Path: path,
2024-05-14 13:07:09 +00:00
PackagePath: packageDir,
ParseFlag: flag,
2024-05-14 13:07:09 +00:00
}
return nil
2024-05-14 13:07:09 +00:00
}
// RangeFiles for range the collection of ast.File in alphabetic order.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) RangeFiles(handle func(info *AstFileInfo) error) error {
2024-05-14 13:07:09 +00:00
sortedFiles := make([]*AstFileInfo, 0, len(pkgDefs.files))
2024-05-14 13:07:09 +00:00
for _, info := range pkgDefs.files {
2024-05-14 13:07:09 +00:00
// ignore package path prefix with 'vendor' or $GOROOT,
2024-05-14 13:07:09 +00:00
// because the router info of api will not be included these files.
2024-05-14 13:07:09 +00:00
if strings.HasPrefix(info.PackagePath, "vendor") || strings.HasPrefix(info.Path, runtime.GOROOT()) {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
sortedFiles = append(sortedFiles, info)
2024-05-14 13:07:09 +00:00
}
sort.Slice(sortedFiles, func(i, j int) bool {
2024-05-14 13:07:09 +00:00
return strings.Compare(sortedFiles[i].Path, sortedFiles[j].Path) < 0
2024-05-14 13:07:09 +00:00
})
for _, info := range sortedFiles {
2024-05-14 13:07:09 +00:00
err := handle(info)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
return nil
2024-05-14 13:07:09 +00:00
}
// ParseTypes parse types
2024-05-14 13:07:09 +00:00
// @Return parsed definitions.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) ParseTypes() (map[*TypeSpecDef]*Schema, error) {
2024-05-14 13:07:09 +00:00
parsedSchemas := make(map[*TypeSpecDef]*Schema)
2024-05-14 13:07:09 +00:00
for astFile, info := range pkgDefs.files {
2024-05-14 13:07:09 +00:00
pkgDefs.parseTypesFromFile(astFile, info.PackagePath, parsedSchemas)
2024-05-14 13:07:09 +00:00
pkgDefs.parseFunctionScopedTypesFromFile(astFile, info.PackagePath, parsedSchemas)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
pkgDefs.removeAllNotUniqueTypes()
2024-05-14 13:07:09 +00:00
pkgDefs.evaluateAllConstVariables()
2024-05-14 13:07:09 +00:00
pkgDefs.collectConstEnums(parsedSchemas)
2024-05-14 13:07:09 +00:00
return parsedSchemas, nil
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) parseTypesFromFile(astFile *ast.File, packagePath string, parsedSchemas map[*TypeSpecDef]*Schema) {
2024-05-14 13:07:09 +00:00
for _, astDeclaration := range astFile.Decls {
2024-05-14 13:07:09 +00:00
generalDeclaration, ok := astDeclaration.(*ast.GenDecl)
2024-05-14 13:07:09 +00:00
if !ok {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if generalDeclaration.Tok == token.TYPE {
2024-05-14 13:07:09 +00:00
for _, astSpec := range generalDeclaration.Specs {
2024-05-14 13:07:09 +00:00
if typeSpec, ok := astSpec.(*ast.TypeSpec); ok {
2024-05-14 13:07:09 +00:00
typeSpecDef := &TypeSpecDef{
PkgPath: packagePath,
File: astFile,
2024-05-14 13:07:09 +00:00
TypeSpec: typeSpec,
}
if idt, ok := typeSpec.Type.(*ast.Ident); ok && IsGolangPrimitiveType(idt.Name) && parsedSchemas != nil {
2024-05-14 13:07:09 +00:00
parsedSchemas[typeSpecDef] = &Schema{
2024-05-14 13:07:09 +00:00
PkgPath: typeSpecDef.PkgPath,
Name: astFile.Name.Name,
Schema: PrimitiveSchema(TransToValidSchemeType(idt.Name)),
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if pkgDefs.uniqueDefinitions == nil {
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions = make(map[string]*TypeSpecDef)
2024-05-14 13:07:09 +00:00
}
fullName := typeSpecDef.TypeName()
anotherTypeDef, ok := pkgDefs.uniqueDefinitions[fullName]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
if anotherTypeDef == nil {
2024-05-14 13:07:09 +00:00
typeSpecDef.NotUnique = true
2024-05-14 13:07:09 +00:00
fullName = typeSpecDef.TypeName()
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
2024-05-14 13:07:09 +00:00
} else if typeSpecDef.PkgPath != anotherTypeDef.PkgPath {
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = nil
2024-05-14 13:07:09 +00:00
anotherTypeDef.NotUnique = true
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[anotherTypeDef.TypeName()] = anotherTypeDef
2024-05-14 13:07:09 +00:00
typeSpecDef.NotUnique = true
2024-05-14 13:07:09 +00:00
fullName = typeSpecDef.TypeName()
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
2024-05-14 13:07:09 +00:00
}
if pkgDefs.packages[typeSpecDef.PkgPath] == nil {
2024-05-14 13:07:09 +00:00
pkgDefs.packages[typeSpecDef.PkgPath] = NewPackageDefinitions(astFile.Name.Name, typeSpecDef.PkgPath).AddTypeSpec(typeSpecDef.Name(), typeSpecDef)
2024-05-14 13:07:09 +00:00
} else if _, ok = pkgDefs.packages[typeSpecDef.PkgPath].TypeDefinitions[typeSpecDef.Name()]; !ok {
2024-05-14 13:07:09 +00:00
pkgDefs.packages[typeSpecDef.PkgPath].AddTypeSpec(typeSpecDef.Name(), typeSpecDef)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else if generalDeclaration.Tok == token.CONST {
2024-05-14 13:07:09 +00:00
// collect consts
2024-05-14 13:07:09 +00:00
pkgDefs.collectConstVariables(astFile, packagePath, generalDeclaration)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *ast.File, packagePath string, parsedSchemas map[*TypeSpecDef]*Schema) {
2024-05-14 13:07:09 +00:00
for _, astDeclaration := range astFile.Decls {
2024-05-14 13:07:09 +00:00
funcDeclaration, ok := astDeclaration.(*ast.FuncDecl)
2024-05-14 13:07:09 +00:00
if ok && funcDeclaration.Body != nil {
2024-05-14 13:07:09 +00:00
for _, stmt := range funcDeclaration.Body.List {
2024-05-14 13:07:09 +00:00
if declStmt, ok := (stmt).(*ast.DeclStmt); ok {
2024-05-14 13:07:09 +00:00
if genDecl, ok := (declStmt.Decl).(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
2024-05-14 13:07:09 +00:00
for _, astSpec := range genDecl.Specs {
2024-05-14 13:07:09 +00:00
if typeSpec, ok := astSpec.(*ast.TypeSpec); ok {
2024-05-14 13:07:09 +00:00
typeSpecDef := &TypeSpecDef{
PkgPath: packagePath,
File: astFile,
TypeSpec: typeSpec,
2024-05-14 13:07:09 +00:00
ParentSpec: astDeclaration,
}
if idt, ok := typeSpec.Type.(*ast.Ident); ok && IsGolangPrimitiveType(idt.Name) && parsedSchemas != nil {
2024-05-14 13:07:09 +00:00
parsedSchemas[typeSpecDef] = &Schema{
2024-05-14 13:07:09 +00:00
PkgPath: typeSpecDef.PkgPath,
Name: astFile.Name.Name,
Schema: PrimitiveSchema(TransToValidSchemeType(idt.Name)),
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if pkgDefs.uniqueDefinitions == nil {
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions = make(map[string]*TypeSpecDef)
2024-05-14 13:07:09 +00:00
}
fullName := typeSpecDef.TypeName()
anotherTypeDef, ok := pkgDefs.uniqueDefinitions[fullName]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
if anotherTypeDef == nil {
2024-05-14 13:07:09 +00:00
typeSpecDef.NotUnique = true
2024-05-14 13:07:09 +00:00
fullName = typeSpecDef.TypeName()
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
2024-05-14 13:07:09 +00:00
} else if typeSpecDef.PkgPath != anotherTypeDef.PkgPath {
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = nil
2024-05-14 13:07:09 +00:00
anotherTypeDef.NotUnique = true
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[anotherTypeDef.TypeName()] = anotherTypeDef
2024-05-14 13:07:09 +00:00
typeSpecDef.NotUnique = true
2024-05-14 13:07:09 +00:00
fullName = typeSpecDef.TypeName()
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
2024-05-14 13:07:09 +00:00
}
if pkgDefs.packages[typeSpecDef.PkgPath] == nil {
2024-05-14 13:07:09 +00:00
pkgDefs.packages[typeSpecDef.PkgPath] = NewPackageDefinitions(astFile.Name.Name, typeSpecDef.PkgPath).AddTypeSpec(fullName, typeSpecDef)
2024-05-14 13:07:09 +00:00
} else if _, ok = pkgDefs.packages[typeSpecDef.PkgPath].TypeDefinitions[fullName]; !ok {
2024-05-14 13:07:09 +00:00
pkgDefs.packages[typeSpecDef.PkgPath].AddTypeSpec(fullName, typeSpecDef)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) collectConstVariables(astFile *ast.File, packagePath string, generalDeclaration *ast.GenDecl) {
2024-05-14 13:07:09 +00:00
pkg, ok := pkgDefs.packages[packagePath]
2024-05-14 13:07:09 +00:00
if !ok {
2024-05-14 13:07:09 +00:00
pkg = NewPackageDefinitions(astFile.Name.Name, packagePath)
2024-05-14 13:07:09 +00:00
pkgDefs.packages[packagePath] = pkg
2024-05-14 13:07:09 +00:00
}
var lastValueSpec *ast.ValueSpec
2024-05-14 13:07:09 +00:00
for _, astSpec := range generalDeclaration.Specs {
2024-05-14 13:07:09 +00:00
valueSpec, ok := astSpec.(*ast.ValueSpec)
2024-05-14 13:07:09 +00:00
if !ok {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if len(valueSpec.Names) == 1 && len(valueSpec.Values) == 1 {
2024-05-14 13:07:09 +00:00
lastValueSpec = valueSpec
2024-05-14 13:07:09 +00:00
} else if len(valueSpec.Names) == 1 && len(valueSpec.Values) == 0 && valueSpec.Type == nil && lastValueSpec != nil {
2024-05-14 13:07:09 +00:00
valueSpec.Type = lastValueSpec.Type
2024-05-14 13:07:09 +00:00
valueSpec.Values = lastValueSpec.Values
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
pkg.AddConst(astFile, valueSpec)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) evaluateAllConstVariables() {
2024-05-14 13:07:09 +00:00
for _, pkg := range pkgDefs.packages {
2024-05-14 13:07:09 +00:00
for _, constVar := range pkg.OrderedConst {
2024-05-14 13:07:09 +00:00
pkgDefs.EvaluateConstValue(pkg, constVar, nil)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// EvaluateConstValue evaluate a const variable.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) {
2024-05-14 13:07:09 +00:00
if expr, ok := cv.Value.(ast.Expr); ok {
2024-05-14 13:07:09 +00:00
defer func() {
2024-05-14 13:07:09 +00:00
if err := recover(); err != nil {
2024-05-14 13:07:09 +00:00
if fi, ok := pkgDefs.files[cv.File]; ok {
2024-05-14 13:07:09 +00:00
pos := fi.FileSet.Position(cv.Name.NamePos)
2024-05-14 13:07:09 +00:00
pkgDefs.debug.Printf("warning: failed to evaluate const %s at %s:%d:%d, %v", cv.Name.Name, fi.Path, pos.Line, pos.Column, err)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}()
2024-05-14 13:07:09 +00:00
if recursiveStack == nil {
2024-05-14 13:07:09 +00:00
recursiveStack = make(map[string]struct{})
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
fullConstName := fullTypeName(pkg.Path, cv.Name.Name)
2024-05-14 13:07:09 +00:00
if _, ok = recursiveStack[fullConstName]; ok {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
recursiveStack[fullConstName] = struct{}{}
value, evalType := pkg.evaluateConstValue(cv.File, cv.Name.Obj.Data.(int), expr, pkgDefs, recursiveStack)
2024-05-14 13:07:09 +00:00
if cv.Type == nil && evalType != nil {
2024-05-14 13:07:09 +00:00
cv.Type = evalType
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if value != nil {
2024-05-14 13:07:09 +00:00
cv.Value = value
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return value, cv.Type
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return cv.Value, cv.Type
2024-05-14 13:07:09 +00:00
}
// EvaluateConstValueByName evaluate a const variable by name.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) EvaluateConstValueByName(file *ast.File, pkgName, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr) {
2024-05-14 13:07:09 +00:00
matchedPkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports(pkgName, file)
2024-05-14 13:07:09 +00:00
for _, pkgPath := range matchedPkgPaths {
2024-05-14 13:07:09 +00:00
if pkg, ok := pkgDefs.packages[pkgPath]; ok {
2024-05-14 13:07:09 +00:00
if cv, ok := pkg.ConstTable[constVariableName]; ok {
2024-05-14 13:07:09 +00:00
return pkgDefs.EvaluateConstValue(pkg, cv, recursiveStack)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if pkgDefs.parseDependency > 0 {
2024-05-14 13:07:09 +00:00
for _, pkgPath := range externalPkgPaths {
2024-05-14 13:07:09 +00:00
if err := pkgDefs.loadExternalPackage(pkgPath); err == nil {
2024-05-14 13:07:09 +00:00
if pkg, ok := pkgDefs.packages[pkgPath]; ok {
2024-05-14 13:07:09 +00:00
if cv, ok := pkg.ConstTable[constVariableName]; ok {
2024-05-14 13:07:09 +00:00
return pkgDefs.EvaluateConstValue(pkg, cv, recursiveStack)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) collectConstEnums(parsedSchemas map[*TypeSpecDef]*Schema) {
2024-05-14 13:07:09 +00:00
for _, pkg := range pkgDefs.packages {
2024-05-14 13:07:09 +00:00
for _, constVar := range pkg.OrderedConst {
2024-05-14 13:07:09 +00:00
if constVar.Type == nil {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
ident, ok := constVar.Type.(*ast.Ident)
2024-05-14 13:07:09 +00:00
if !ok || IsGolangPrimitiveType(ident.Name) {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
typeDef, ok := pkg.TypeDefinitions[ident.Name]
2024-05-14 13:07:09 +00:00
if !ok {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
//delete it from parsed schemas, and will parse it again
2024-05-14 13:07:09 +00:00
if _, ok = parsedSchemas[typeDef]; ok {
2024-05-14 13:07:09 +00:00
delete(parsedSchemas, typeDef)
2024-05-14 13:07:09 +00:00
}
if typeDef.Enums == nil {
2024-05-14 13:07:09 +00:00
typeDef.Enums = make([]EnumValue, 0)
2024-05-14 13:07:09 +00:00
}
name := constVar.Name.Name
2024-05-14 13:07:09 +00:00
if _, ok = constVar.Value.(ast.Expr); ok {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
enumValue := EnumValue{
key: name,
2024-05-14 13:07:09 +00:00
Value: constVar.Value,
}
2024-05-14 13:07:09 +00:00
if constVar.Comment != nil && len(constVar.Comment.List) > 0 {
2024-05-14 13:07:09 +00:00
enumValue.Comment = constVar.Comment.List[0].Text
2024-05-14 13:07:09 +00:00
enumValue.Comment = strings.TrimPrefix(enumValue.Comment, "//")
2024-05-14 13:07:09 +00:00
enumValue.Comment = strings.TrimPrefix(enumValue.Comment, "/*")
2024-05-14 13:07:09 +00:00
enumValue.Comment = strings.TrimSuffix(enumValue.Comment, "*/")
2024-05-14 13:07:09 +00:00
enumValue.Comment = strings.TrimSpace(enumValue.Comment)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
typeDef.Enums = append(typeDef.Enums, enumValue)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) removeAllNotUniqueTypes() {
2024-05-14 13:07:09 +00:00
for key, ud := range pkgDefs.uniqueDefinitions {
2024-05-14 13:07:09 +00:00
if ud == nil {
2024-05-14 13:07:09 +00:00
delete(pkgDefs.uniqueDefinitions, key)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) findTypeSpec(pkgPath string, typeName string) *TypeSpecDef {
2024-05-14 13:07:09 +00:00
if pkgDefs.packages == nil {
2024-05-14 13:07:09 +00:00
return nil
2024-05-14 13:07:09 +00:00
}
pd, found := pkgDefs.packages[pkgPath]
2024-05-14 13:07:09 +00:00
if found {
2024-05-14 13:07:09 +00:00
typeSpec, ok := pd.TypeDefinitions[typeName]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
return typeSpec
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
return nil
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) loadExternalPackage(importPath string) error {
2024-05-14 13:07:09 +00:00
cwd, err := os.Getwd()
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
conf := loader.Config{
2024-05-14 13:07:09 +00:00
ParserMode: goparser.ParseComments,
Cwd: cwd,
2024-05-14 13:07:09 +00:00
}
conf.Import(importPath)
loaderProgram, err := conf.Load()
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return err
2024-05-14 13:07:09 +00:00
}
for _, info := range loaderProgram.AllPackages {
2024-05-14 13:07:09 +00:00
pkgPath := strings.TrimPrefix(info.Pkg.Path(), "vendor/")
2024-05-14 13:07:09 +00:00
for _, astFile := range info.Files {
2024-05-14 13:07:09 +00:00
pkgDefs.parseTypesFromFile(astFile, pkgPath, nil)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
return nil
2024-05-14 13:07:09 +00:00
}
// findPackagePathFromImports finds out the package path of a package via ranging imports of an ast.File
2024-05-14 13:07:09 +00:00
// @pkg the name of the target package
2024-05-14 13:07:09 +00:00
// @file current ast.File in which to search imports
2024-05-14 13:07:09 +00:00
// @return the package paths of a package of @pkg.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) findPackagePathFromImports(pkg string, file *ast.File) (matchedPkgPaths, externalPkgPaths []string) {
2024-05-14 13:07:09 +00:00
if file == nil {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
if strings.ContainsRune(pkg, '.') {
2024-05-14 13:07:09 +00:00
pkg = strings.Split(pkg, ".")[0]
2024-05-14 13:07:09 +00:00
}
matchLastPathPart := func(pkgPath string) bool {
2024-05-14 13:07:09 +00:00
paths := strings.Split(pkgPath, "/")
2024-05-14 13:07:09 +00:00
return paths[len(paths)-1] == pkg
2024-05-14 13:07:09 +00:00
}
// prior to match named package
2024-05-14 13:07:09 +00:00
for _, imp := range file.Imports {
2024-05-14 13:07:09 +00:00
path := strings.Trim(imp.Path.Value, `"`)
2024-05-14 13:07:09 +00:00
if imp.Name != nil {
2024-05-14 13:07:09 +00:00
if imp.Name.Name == pkg {
2024-05-14 13:07:09 +00:00
// if name match, break loop and return
2024-05-14 13:07:09 +00:00
_, ok := pkgDefs.packages[path]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
matchedPkgPaths = []string{path}
2024-05-14 13:07:09 +00:00
externalPkgPaths = nil
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
externalPkgPaths = []string{path}
2024-05-14 13:07:09 +00:00
matchedPkgPaths = nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
break
2024-05-14 13:07:09 +00:00
} else if imp.Name.Name == "_" && len(pkg) > 0 {
2024-05-14 13:07:09 +00:00
//for unused types
2024-05-14 13:07:09 +00:00
pd, ok := pkgDefs.packages[path]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
if pd.Name == pkg {
2024-05-14 13:07:09 +00:00
matchedPkgPaths = append(matchedPkgPaths, path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else if matchLastPathPart(path) {
2024-05-14 13:07:09 +00:00
externalPkgPaths = append(externalPkgPaths, path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else if imp.Name.Name == "." && len(pkg) == 0 {
2024-05-14 13:07:09 +00:00
_, ok := pkgDefs.packages[path]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
matchedPkgPaths = append(matchedPkgPaths, path)
2024-05-14 13:07:09 +00:00
} else if len(pkg) == 0 || matchLastPathPart(path) {
2024-05-14 13:07:09 +00:00
externalPkgPaths = append(externalPkgPaths, path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else if pkgDefs.packages != nil && len(pkg) > 0 {
2024-05-14 13:07:09 +00:00
pd, ok := pkgDefs.packages[path]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
if pd.Name == pkg {
2024-05-14 13:07:09 +00:00
matchedPkgPaths = append(matchedPkgPaths, path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else if matchLastPathPart(path) {
2024-05-14 13:07:09 +00:00
externalPkgPaths = append(externalPkgPaths, path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if len(pkg) == 0 || file.Name.Name == pkg {
2024-05-14 13:07:09 +00:00
matchedPkgPaths = append(matchedPkgPaths, pkgDefs.files[file].PackagePath)
2024-05-14 13:07:09 +00:00
}
return
2024-05-14 13:07:09 +00:00
}
func (pkgDefs *PackagesDefinitions) findTypeSpecFromPackagePaths(matchedPkgPaths, externalPkgPaths []string, name string) (typeDef *TypeSpecDef) {
2024-05-14 13:07:09 +00:00
if pkgDefs.parseDependency > 0 {
2024-05-14 13:07:09 +00:00
for _, pkgPath := range externalPkgPaths {
2024-05-14 13:07:09 +00:00
if err := pkgDefs.loadExternalPackage(pkgPath); err == nil {
2024-05-14 13:07:09 +00:00
typeDef = pkgDefs.findTypeSpec(pkgPath, name)
2024-05-14 13:07:09 +00:00
if typeDef != nil {
2024-05-14 13:07:09 +00:00
return typeDef
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
for _, pkgPath := range matchedPkgPaths {
2024-05-14 13:07:09 +00:00
typeDef = pkgDefs.findTypeSpec(pkgPath, name)
2024-05-14 13:07:09 +00:00
if typeDef != nil {
2024-05-14 13:07:09 +00:00
return typeDef
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
return typeDef
2024-05-14 13:07:09 +00:00
}
// FindTypeSpec finds out TypeSpecDef of a type by typeName
2024-05-14 13:07:09 +00:00
// @typeName the name of the target type, if it starts with a package name, find its own package path from imports on top of @file
2024-05-14 13:07:09 +00:00
// @file the ast.file in which @typeName is used
2024-05-14 13:07:09 +00:00
// @pkgPath the package path of @file.
2024-05-14 13:07:09 +00:00
func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef {
2024-05-14 13:07:09 +00:00
if IsGolangPrimitiveType(typeName) {
2024-05-14 13:07:09 +00:00
return nil
2024-05-14 13:07:09 +00:00
}
if file == nil { // for test
2024-05-14 13:07:09 +00:00
return pkgDefs.uniqueDefinitions[typeName]
2024-05-14 13:07:09 +00:00
}
parts := strings.Split(strings.Split(typeName, "[")[0], ".")
2024-05-14 13:07:09 +00:00
if len(parts) > 1 {
2024-05-14 13:07:09 +00:00
pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports(parts[0], file)
2024-05-14 13:07:09 +00:00
if len(externalPkgPaths) == 0 || pkgDefs.parseDependency == ParseNone {
2024-05-14 13:07:09 +00:00
typeDef, ok := pkgDefs.uniqueDefinitions[typeName]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
return typeDef
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
typeDef := pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, parts[1])
2024-05-14 13:07:09 +00:00
return pkgDefs.parametrizeGenericType(file, typeDef, typeName)
2024-05-14 13:07:09 +00:00
}
typeDef, ok := pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, typeName)]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
return typeDef
2024-05-14 13:07:09 +00:00
}
//in case that comment //@name renamed the type with a name without a dot
2024-05-14 13:07:09 +00:00
typeDef, ok = pkgDefs.uniqueDefinitions[typeName]
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
return typeDef
2024-05-14 13:07:09 +00:00
}
name := parts[0]
2024-05-14 13:07:09 +00:00
typeDef, ok = pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, name)]
2024-05-14 13:07:09 +00:00
if !ok {
2024-05-14 13:07:09 +00:00
pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports("", file)
2024-05-14 13:07:09 +00:00
typeDef = pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, name)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return pkgDefs.parametrizeGenericType(file, typeDef, typeName)
2024-05-14 13:07:09 +00:00
}