Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements: search imports sequencely, till find the type. #1374

Merged
merged 4 commits into from Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion generics_test.go
Expand Up @@ -106,11 +106,12 @@ func TestParseGenericsNames(t *testing.T) {
func TestParseGenericsPackageAlias(t *testing.T) {
t.Parallel()

searchDir := "testdata/generics_package_alias"
searchDir := "testdata/generics_package_alias/internal"
expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json"))
assert.NoError(t, err)

p := New()
p.ParseDependency = true
err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth)
assert.NoError(t, err)
b, err := json.MarshalIndent(p.swagger, "", " ")
Expand Down
142 changes: 65 additions & 77 deletions packages.go
Expand Up @@ -287,76 +287,93 @@ func (pkgDefs *PackagesDefinitions) loadExternalPackage(importPath string) error
// findPackagePathFromImports finds out the package path of a package via ranging imports of an ast.File
// @pkg the name of the target package
// @file current ast.File in which to search imports
// @fuzzy search for the package path that the last part matches the @pkg if true
// @return the package path of a package of @pkg.
func (pkgDefs *PackagesDefinitions) findPackagePathFromImports(pkg string, file *ast.File, fuzzy bool) string {
// @return the package paths of a package of @pkg.
func (pkgDefs *PackagesDefinitions) findPackagePathFromImports(pkg string, file *ast.File) (matchedPkgPaths, externalPkgPaths []string) {
if file == nil {
return ""
return
}

if strings.ContainsRune(pkg, '.') {
pkg = strings.Split(pkg, ".")[0]
}

hasAnonymousPkg := false

matchLastPathPart := func(pkgPath string) bool {
paths := strings.Split(pkgPath, "/")

return paths[len(paths)-1] == pkg
}

// prior to match named package
for _, imp := range file.Imports {
path := strings.Trim(imp.Path.Value, `"`)
if imp.Name != nil {
if imp.Name.Name == pkg {
return strings.Trim(imp.Path.Value, `"`)
// if name match, break loop and return
_, ok := pkgDefs.packages[path]
if ok {
matchedPkgPaths = []string{path}
externalPkgPaths = nil
} else {
externalPkgPaths = []string{path}
matchedPkgPaths = nil
}
break
} else if imp.Name.Name == "_" && len(pkg) > 0 {
//for unused types
pd, ok := pkgDefs.packages[path]
if ok {
if pd.Name == pkg {
matchedPkgPaths = append(matchedPkgPaths, path)
}
} else if matchLastPathPart(path) {
externalPkgPaths = append(externalPkgPaths, path)
}
} else if imp.Name.Name == "." && len(pkg) == 0 {
_, ok := pkgDefs.packages[path]
if ok {
matchedPkgPaths = append(matchedPkgPaths, path)
} else if len(pkg) == 0 || matchLastPathPart(path) {
externalPkgPaths = append(externalPkgPaths, path)
}
}

if imp.Name.Name == "_" {
hasAnonymousPkg = true
} else if pkgDefs.packages != nil && len(pkg) > 0 {
pd, ok := pkgDefs.packages[path]
if ok {
if pd.Name == pkg {
matchedPkgPaths = append(matchedPkgPaths, path)
}
} else if matchLastPathPart(path) {
externalPkgPaths = append(externalPkgPaths, path)
}

continue
}
}

if pkgDefs.packages != nil {
path := strings.Trim(imp.Path.Value, `"`)
if fuzzy {
if matchLastPathPart(path) {
return path
}
if len(pkg) == 0 || file.Name.Name == pkg {
matchedPkgPaths = append(matchedPkgPaths, pkgDefs.files[file].PackagePath)
}

continue
}
return
}

pd, ok := pkgDefs.packages[path]
if ok && pd.Name == pkg {
return path
}
func (pkgDefs *PackagesDefinitions) findTypeSpecFromPackagePaths(matchedPkgPaths, externalPkgPaths []string, name string, parseDependency bool) (typeDef *TypeSpecDef) {
for _, pkgPath := range matchedPkgPaths {
typeDef = pkgDefs.findTypeSpec(pkgPath, name)
if typeDef != nil {
return typeDef
}
}

// match unnamed package
if hasAnonymousPkg && pkgDefs.packages != nil {
for _, imp := range file.Imports {
if imp.Name == nil {
continue
}
if imp.Name.Name == "_" {
path := strings.Trim(imp.Path.Value, `"`)
if fuzzy {
if matchLastPathPart(path) {
return path
}
} else if pd, ok := pkgDefs.packages[path]; ok && pd.Name == pkg {
return path
if parseDependency {
for _, pkgPath := range externalPkgPaths {
if err := pkgDefs.loadExternalPackage(pkgPath); err == nil {
typeDef = pkgDefs.findTypeSpec(pkgPath, name)
if typeDef != nil {
return typeDef
}
}
}
}

return ""
return typeDef
}

// FindTypeSpec finds out TypeSpecDef of a type by typeName
Expand All @@ -379,23 +396,8 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File
return typeDef
}

pkgPath := pkgDefs.findPackagePathFromImports(parts[0], file, false)
if len(pkgPath) == 0 {
// check if the current package
if parts[0] == file.Name.Name {
pkgPath = pkgDefs.files[file].PackagePath
} else if parseDependency {
// take it as an external package, needs to be loaded
if pkgPath = pkgDefs.findPackagePathFromImports(parts[0], file, true); len(pkgPath) > 0 {
if err := pkgDefs.loadExternalPackage(pkgPath); err != nil {
return nil
}
}
}
}

typeDef = pkgDefs.findTypeSpec(pkgPath, parts[1])

pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports(parts[0], file)
typeDef = pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, parts[1], parseDependency)
return pkgDefs.parametrizeGenericType(file, typeDef, typeName, parseDependency)
}

Expand All @@ -410,25 +412,11 @@ func (pkgDefs *PackagesDefinitions) FindTypeSpec(typeName string, file *ast.File
return typeDef
}

typeDef = func() *TypeSpecDef {
name := parts[0]
typeDef, ok := pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, name)]
if ok {
return typeDef
}
typeDef = pkgDefs.findTypeSpec(pkgDefs.files[file].PackagePath, name)
if typeDef != nil {
return typeDef
}
for _, imp := range file.Imports {
if imp.Name != nil && imp.Name.Name == "." {
typeDef = pkgDefs.findTypeSpec(strings.Trim(imp.Path.Value, `"`), name)
if typeDef != nil {
break
}
}
}
return typeDef
}()
name := parts[0]
typeDef, ok = pkgDefs.uniqueDefinitions[fullTypeName(file.Name.Name, name)]
if !ok {
pkgPaths, externalPkgPaths := pkgDefs.findPackagePathFromImports("", file)
typeDef = pkgDefs.findTypeSpecFromPackagePaths(pkgPaths, externalPkgPaths, name, parseDependency)
}
return pkgDefs.parametrizeGenericType(file, typeDef, typeName, parseDependency)
}