mirror of
https://github.com/Dvorinka/Devour.git
synced 2026-06-03 20:13:03 +00:00
update
This commit is contained in:
@@ -58,7 +58,10 @@ func (d *SingleUseDetector) Detect(ctx context.Context, path string, config *qua
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
key := obj.Pkg().Path() + "." + obj.Name()
|
||||
key, ok := functionKey(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
callCounts[key]++
|
||||
case *types.TypeName:
|
||||
if obj.Pkg() != nil {
|
||||
@@ -75,17 +78,18 @@ func (d *SingleUseDetector) Detect(ctx context.Context, path string, config *qua
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
if obj.Pkg() != nil {
|
||||
key := obj.Pkg().Path() + "." + obj.Name()
|
||||
pos := pkg.Fset.Position(obj.Pos())
|
||||
funcDefs[key] = FuncDef{
|
||||
Name: obj.Name(),
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
Exported: obj.Exported(),
|
||||
Signature: obj.Type().String(),
|
||||
}
|
||||
key, ok := functionKey(obj)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
pos := pkg.Fset.Position(obj.Pos())
|
||||
funcDefs[key] = FuncDef{
|
||||
Name: obj.Name(),
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
Exported: obj.Exported(),
|
||||
Signature: obj.Type().String(),
|
||||
}
|
||||
case *types.TypeName:
|
||||
if obj.Pkg() != nil {
|
||||
@@ -109,6 +113,9 @@ func (d *SingleUseDetector) Detect(ctx context.Context, path string, config *qua
|
||||
var findings []quality.Finding
|
||||
|
||||
for key, def := range funcDefs {
|
||||
if def.Exported || isLikelyEntrypointFile(def.File) {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(def.Name, "Test") || strings.HasPrefix(def.Name, "Test") {
|
||||
continue
|
||||
}
|
||||
@@ -143,9 +150,18 @@ func (d *SingleUseDetector) Detect(ctx context.Context, path string, config *qua
|
||||
}
|
||||
|
||||
for key, def := range typeDefs {
|
||||
if def.Exported || isLikelyEntrypointFile(def.File) {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(def.Name, "Error") || strings.HasSuffix(def.Name, "Options") {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(def.Name, "Config") || strings.HasSuffix(def.Name, "Params") {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(def.Underlying, "struct") && !strings.Contains(def.Underlying, "interface") {
|
||||
continue
|
||||
}
|
||||
|
||||
count := typeUsages[key]
|
||||
if count == 1 {
|
||||
@@ -242,6 +258,22 @@ func (d *SingleUseDetector) getFuncLOC(file string, startLine int) (int, error)
|
||||
return loc, nil
|
||||
}
|
||||
|
||||
func functionKey(fn *types.Func) (string, bool) {
|
||||
if fn == nil || fn.Pkg() == nil {
|
||||
return "", false
|
||||
}
|
||||
sig, ok := fn.Type().(*types.Signature)
|
||||
if ok && sig.Recv() != nil {
|
||||
return "", false
|
||||
}
|
||||
return fn.Pkg().Path() + "." + fn.Name(), true
|
||||
}
|
||||
|
||||
func isLikelyEntrypointFile(path string) bool {
|
||||
p := filepath.ToSlash(path)
|
||||
return strings.HasPrefix(p, "cmd/") || strings.Contains(p, "/cmd/") || strings.HasSuffix(p, "/main.go") || strings.HasSuffix(p, "_test.go")
|
||||
}
|
||||
|
||||
type FuncDef struct {
|
||||
Name string
|
||||
File string
|
||||
@@ -471,33 +503,36 @@ func (d *EnhancedDeadCodeDetector) Detect(ctx context.Context, path string, conf
|
||||
switch o := obj.(type) {
|
||||
case *types.Func:
|
||||
defs[key] = ObjInfo{
|
||||
Name: obj.Name(),
|
||||
Type: "function",
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
Exported: obj.Exported(),
|
||||
Signature: o.Type().String(),
|
||||
Name: obj.Name(),
|
||||
Type: "function",
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
PackageName: pkg.Name,
|
||||
Exported: obj.Exported(),
|
||||
Signature: o.Type().String(),
|
||||
}
|
||||
case *types.TypeName:
|
||||
defs[key] = ObjInfo{
|
||||
Name: obj.Name(),
|
||||
Type: "type",
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
Exported: obj.Exported(),
|
||||
Underlying: o.Type().Underlying().String(),
|
||||
Name: obj.Name(),
|
||||
Type: "type",
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
PackageName: pkg.Name,
|
||||
Exported: obj.Exported(),
|
||||
Underlying: o.Type().Underlying().String(),
|
||||
}
|
||||
case *types.Var:
|
||||
if obj.Exported() {
|
||||
if obj.Exported() && !o.IsField() {
|
||||
defs[key] = ObjInfo{
|
||||
Name: obj.Name(),
|
||||
Type: "variable",
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
Exported: obj.Exported(),
|
||||
Name: obj.Name(),
|
||||
Type: "variable",
|
||||
File: pos.Filename,
|
||||
Line: pos.Line,
|
||||
Package: obj.Pkg().Path(),
|
||||
PackageName: pkg.Name,
|
||||
Exported: obj.Exported(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -521,10 +556,22 @@ func (d *EnhancedDeadCodeDetector) Detect(ctx context.Context, path string, conf
|
||||
if entryPoints[key] {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(def.Package, "/internal/") || def.PackageName == "main" {
|
||||
continue
|
||||
}
|
||||
if isLikelyEntrypointFile(def.File) {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(def.Name, "Test") || strings.HasPrefix(def.Name, "Benchmark") || strings.HasPrefix(def.Name, "Fuzz") {
|
||||
continue
|
||||
}
|
||||
if def.Type == "function" && strings.HasPrefix(def.Name, "New") {
|
||||
continue
|
||||
}
|
||||
if def.Type == "type" && (strings.HasSuffix(def.Name, "Config") || strings.HasSuffix(def.Name, "Options")) {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(def.Name, "Error") && def.Type == "type" {
|
||||
continue
|
||||
@@ -573,12 +620,13 @@ func (d *EnhancedDeadCodeDetector) Detect(ctx context.Context, path string, conf
|
||||
}
|
||||
|
||||
type ObjInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
File string
|
||||
Line int
|
||||
Package string
|
||||
Exported bool
|
||||
Signature string
|
||||
Underlying string
|
||||
Name string
|
||||
Type string
|
||||
File string
|
||||
Line int
|
||||
Package string
|
||||
PackageName string
|
||||
Exported bool
|
||||
Signature string
|
||||
Underlying string
|
||||
}
|
||||
|
||||
@@ -172,8 +172,7 @@ func (d *UnusedImportDetector) analyzeFile(path string) ([]quality.Finding, erro
|
||||
if imp.Name != nil {
|
||||
name = imp.Name.Name
|
||||
} else {
|
||||
parts := strings.Split(pkgPath, "/")
|
||||
name = parts[len(parts)-1]
|
||||
name = inferImportName(pkgPath)
|
||||
}
|
||||
imports[pkgPath] = name
|
||||
}
|
||||
@@ -191,8 +190,7 @@ func (d *UnusedImportDetector) analyzeFile(path string) ([]quality.Finding, erro
|
||||
if imp.Name != nil {
|
||||
name = imp.Name.Name
|
||||
} else {
|
||||
parts := strings.Split(pkgPath, "/")
|
||||
name = parts[len(parts)-1]
|
||||
name = inferImportName(pkgPath)
|
||||
}
|
||||
|
||||
if name == "_" || name == "." {
|
||||
@@ -224,6 +222,42 @@ func (d *UnusedImportDetector) analyzeFile(path string) ([]quality.Finding, erro
|
||||
return findings, nil
|
||||
}
|
||||
|
||||
func inferImportName(pkgPath string) string {
|
||||
parts := strings.Split(pkgPath, "/")
|
||||
if len(parts) == 0 {
|
||||
return pkgPath
|
||||
}
|
||||
|
||||
last := parts[len(parts)-1]
|
||||
if isSemverSegment(last) && len(parts) >= 2 {
|
||||
last = parts[len(parts)-2]
|
||||
}
|
||||
if idx := strings.Index(last, ".v"); idx > 0 && isDigits(last[idx+2:]) {
|
||||
last = last[:idx]
|
||||
}
|
||||
|
||||
return last
|
||||
}
|
||||
|
||||
func isSemverSegment(segment string) bool {
|
||||
if len(segment) < 2 || segment[0] != 'v' {
|
||||
return false
|
||||
}
|
||||
return isDigits(segment[1:])
|
||||
}
|
||||
|
||||
func isDigits(value string) bool {
|
||||
if value == "" {
|
||||
return false
|
||||
}
|
||||
for _, r := range value {
|
||||
if r < '0' || r > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type CycleDetector struct {
|
||||
*quality.BaseDetector
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package analyzers
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInferImportName(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
want string
|
||||
}{
|
||||
{path: "fmt", want: "fmt"},
|
||||
{path: "gopkg.in/yaml.v3", want: "yaml"},
|
||||
{path: "github.com/gocolly/colly/v2", want: "colly"},
|
||||
{path: "golang.org/x/tools/go/packages", want: "packages"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := inferImportName(tt.path)
|
||||
if got != tt.want {
|
||||
t.Fatalf("inferImportName(%q) = %q, want %q", tt.path, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,6 +240,10 @@ func (d *DebugLogDetector) analyzeFile(path string) []quality.Finding {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
normPath := filepath.ToSlash(path)
|
||||
if strings.Contains(normPath, "internal/ui/") || strings.Contains(normPath, "examples/") {
|
||||
return nil
|
||||
}
|
||||
|
||||
debugPatterns := []string{
|
||||
"log.Print",
|
||||
@@ -267,7 +271,7 @@ func (d *DebugLogDetector) analyzeFile(path string) []quality.Finding {
|
||||
|
||||
for _, pattern := range debugPatterns {
|
||||
if callStr == pattern || strings.HasPrefix(callStr, pattern) {
|
||||
if strings.Contains(path, "_test.go") {
|
||||
if strings.HasSuffix(normPath, "_test.go") || strings.HasPrefix(normPath, "cmd/") || strings.Contains(normPath, "/cmd/") {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -291,7 +295,7 @@ func (d *DebugLogDetector) analyzeFile(path string) []quality.Finding {
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(path, "/cmd/") {
|
||||
if strings.HasPrefix(normPath, "cmd/") || strings.Contains(normPath, "/cmd/") {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ func (p *GoPlugin) DefaultSrcDir() string {
|
||||
|
||||
func (p *GoPlugin) CreateDetectors(finder quality.FileFinder) []quality.Detector {
|
||||
return []quality.Detector{
|
||||
analyzers.NewDeadCodeDetector(finder),
|
||||
analyzers.NewEnhancedDeadCodeDetector(finder),
|
||||
analyzers.NewUnusedImportDetector(finder),
|
||||
analyzers.NewCycleDetector(finder),
|
||||
|
||||
Reference in New Issue
Block a user