This commit is contained in:
Tomas Dvorak
2026-02-22 15:41:27 +01:00
parent 0b88627e54
commit 409acd2e08
84 changed files with 65382 additions and 27475 deletions
@@ -268,7 +268,7 @@ type CouplingDetector struct {
func NewCouplingDetector(finder quality.FileFinder) *CouplingDetector {
return &CouplingDetector{
BaseDetector: quality.NewBaseDetector("coupling", quality.SeverityT3, finder),
maxFanOut: 10,
maxFanOut: 20, // Increased from 10 to 20 for more realistic threshold
}
}
@@ -330,7 +330,11 @@ func (d *CouplingDetector) Detect(ctx context.Context, path string, config *qual
for pkg, importedBy := range pkgImportedBy {
fanIn := len(importedBy)
if fanIn > d.maxFanOut*2 {
// Skip standard library packages from fan-in analysis
if d.isStandardLibraryPackage(pkg) {
continue
}
if fanIn > d.maxFanOut*3 { // Increased threshold for fan-in
finding := quality.Finding{
ID: fmt.Sprintf("coupling_fanin::%s", pkg),
Type: "coupling",
@@ -339,7 +343,7 @@ func (d *CouplingDetector) Detect(ctx context.Context, path string, config *qual
File: pkg,
Line: 1,
Severity: quality.SeverityT2,
Score: fanIn/5 - d.maxFanOut/5,
Score: fanIn/10 - d.maxFanOut/10, // Reduced scoring
Status: quality.StatusOpen,
Metadata: map[string]string{
"package": pkg,
@@ -356,6 +360,21 @@ func (d *CouplingDetector) Detect(ctx context.Context, path string, config *qual
return findings, nil
}
func (d *CouplingDetector) isStandardLibraryPackage(pkgPath string) bool {
// Standard library packages that commonly have high fan-in
standardLibs := []string{
"fmt", "time", "strings", "context", "os", "io", "net/http",
"encoding/json", "path/filepath", "sync", "math", "regexp",
}
for _, lib := range standardLibs {
if strings.Contains(pkgPath, lib) && !strings.Contains(pkgPath, "github.com") {
return true
}
}
return false
}
func (d *CouplingDetector) detectHubPackages(pkgImports, pkgImportedBy map[string][]string) []quality.Finding {
var findings []quality.Finding
@@ -30,6 +30,31 @@ func (d *DeadCodeDetector) Severity() quality.Severity {
return quality.SeverityT2
}
func (d *DeadCodeDetector) shouldSkipExport(name, objType string) bool {
// Skip common API surface exports that might be used externally
skipPatterns := []string{
"Version", "License", "APIKey", "Config", "Options",
"Client", "Server", "Handler", "Service", "Manager",
"Store", "Cache", "Index", "Search", "Query",
}
// Skip type definitions and constants
if strings.Contains(objType, "type") ||
strings.Contains(objType, "const") ||
strings.Contains(objType, "var") {
return true
}
// Skip common naming patterns
for _, pattern := range skipPatterns {
if name == pattern {
return true
}
}
return false
}
func (d *DeadCodeDetector) Detect(ctx context.Context, path string, config *quality.Config) ([]quality.Finding, error) {
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedFiles,
@@ -58,18 +83,24 @@ func (d *DeadCodeDetector) Detect(ctx context.Context, path string, config *qual
continue
}
// Skip unexported objects - they're internal
if !obj.Exported() {
continue
}
key := obj.Pkg().Path() + "." + obj.Name()
if !used[key] {
// Skip certain types of exports that are commonly legitimate
if d.shouldSkipExport(obj.Name(), obj.Type().String()) {
continue
}
pos := pkg.Fset.Position(obj.Pos())
finding := quality.Finding{
ID: fmt.Sprintf("dead_code::%s::%s", pos.Filename, obj.Name()),
Type: "dead_code",
Title: fmt.Sprintf("Unused exported identifier: %s", obj.Name()),
Description: fmt.Sprintf("The exported %s '%s' is never used in the codebase. Consider removing it or documenting its intended use.", obj.Type(), obj.Name()),
Description: fmt.Sprintf("The exported %s '%s' is never used in codebase. Consider removing it or documenting its intended use.", obj.Type(), obj.Name()),
File: pos.Filename,
Line: pos.Line,
Severity: quality.SeverityT2,
@@ -290,7 +321,6 @@ func (d *CycleDetector) findCycles(graph map[string][]string) [][]string {
}
}
path = path[:len(path)-1]
recStack[node] = false
}
@@ -43,7 +43,9 @@ func (d *TestCoverageDetector) Detect(ctx context.Context, path string, config *
if _, err := os.Stat(coverFile); os.IsNotExist(err) {
cmd := exec.CommandContext(ctx, "go", "test", "-coverprofile=coverage.out", "-covermode=atomic", "./...")
cmd.Dir = path
cmd.Run()
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("failed to run test coverage: %w", err)
}
if _, err := os.Stat(coverFile); os.IsNotExist(err) {
return nil, nil
@@ -147,7 +149,9 @@ func (d *TestCoverageDetector) parseCoverageFile(path string) (map[string]Covera
countStr := parts[2]
var count int
fmt.Sscanf(countStr, "%d", &count)
if _, err := fmt.Sscanf(countStr, "%d", &count); err != nil {
continue
}
start, end := d.parseRange(rangeStr)
lines := end - start + 1
@@ -169,8 +173,12 @@ func (d *TestCoverageDetector) parseRange(s string) (start, end int) {
return 0, 0
}
fmt.Sscanf(parts[0], "%d", &start)
fmt.Sscanf(parts[1], "%d", &end)
if _, err := fmt.Sscanf(parts[0], "%d", &start); err != nil {
return 0, 0
}
if _, err := fmt.Sscanf(parts[1], "%d", &end); err != nil {
return 0, 0
}
return start, end
}
@@ -220,7 +228,9 @@ func (d *UntestedFuncDetector) Detect(ctx context.Context, path string, config *
countStr := parts[len(parts)-1]
var count int
fmt.Sscanf(countStr, "%d", &count)
if _, err := fmt.Sscanf(countStr, "%d", &count); err != nil {
continue
}
if count == 0 {
fileRange := parts[0]
@@ -283,8 +293,12 @@ func (d *UntestedFuncDetector) parseRange(s string) (start, end int) {
if len(parts) != 2 {
return 0, 0
}
fmt.Sscanf(parts[0], "%d", &start)
fmt.Sscanf(parts[1], "%d", &end)
if _, err := fmt.Sscanf(parts[0], "%d", &start); err != nil {
return 0, 0
}
if _, err := fmt.Sscanf(parts[1], "%d", &end); err != nil {
return 0, 0
}
return start, end
}