98 lines
2.5 KiB
Go
98 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Import analysis types
|
|
type ImportInfo struct {
|
|
Package string `json:"package"`
|
|
File string `json:"file"`
|
|
Imports []ImportDetail `json:"imports"`
|
|
UnusedImports []string `json:"unused_imports,omitempty"`
|
|
}
|
|
|
|
type ImportDetail struct {
|
|
Path string `json:"path"`
|
|
Alias string `json:"alias,omitempty"`
|
|
Used []string `json:"used_symbols,omitempty"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
func findImports(dir string) ([]ImportInfo, error) {
|
|
var imports []ImportInfo
|
|
|
|
err := walkGoFiles(dir, func(path string, src []byte, file *ast.File, fset *token.FileSet) error {
|
|
info := ImportInfo{
|
|
Package: file.Name.Name,
|
|
File: path,
|
|
Imports: []ImportDetail{},
|
|
}
|
|
|
|
// Collect all imports
|
|
importMap := make(map[string]*ImportDetail)
|
|
for _, imp := range file.Imports {
|
|
importPath := strings.Trim(imp.Path.Value, `"`)
|
|
pos := fset.Position(imp.Pos())
|
|
detail := &ImportDetail{
|
|
Path: importPath,
|
|
Position: newPosition(pos),
|
|
}
|
|
if imp.Name != nil {
|
|
detail.Alias = imp.Name.Name
|
|
}
|
|
importMap[importPath] = detail
|
|
info.Imports = append(info.Imports, *detail)
|
|
}
|
|
|
|
// Track which imports are used
|
|
usedImports := make(map[string]map[string]bool)
|
|
ast.Inspect(file, func(n ast.Node) bool {
|
|
switch x := n.(type) {
|
|
case *ast.SelectorExpr:
|
|
if ident, ok := x.X.(*ast.Ident); ok {
|
|
pkgName := ident.Name
|
|
symbol := x.Sel.Name
|
|
|
|
// Find matching import
|
|
for importPath, detail := range importMap {
|
|
importName := filepath.Base(importPath)
|
|
if detail.Alias != "" && detail.Alias == pkgName {
|
|
if usedImports[importPath] == nil {
|
|
usedImports[importPath] = make(map[string]bool)
|
|
}
|
|
usedImports[importPath][symbol] = true
|
|
} else if importName == pkgName {
|
|
if usedImports[importPath] == nil {
|
|
usedImports[importPath] = make(map[string]bool)
|
|
}
|
|
usedImports[importPath][symbol] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
|
|
// Update import details with used symbols
|
|
for i, imp := range info.Imports {
|
|
if used, ok := usedImports[imp.Path]; ok {
|
|
for symbol := range used {
|
|
info.Imports[i].Used = append(info.Imports[i].Used, symbol)
|
|
}
|
|
} else if !strings.HasSuffix(imp.Path, "_test") && imp.Alias != "_" {
|
|
info.UnusedImports = append(info.UnusedImports, imp.Path)
|
|
}
|
|
}
|
|
|
|
if len(info.Imports) > 0 {
|
|
imports = append(imports, info)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return imports, err
|
|
} |