Browse Source

test: 新增 AST 死代码门禁(函数逐项检查)

- TestNoDeadFunctions:用 go/ast 统计标识符引用,精确发现未被引用的函数

  (此前文本 grep 会把 configPath/filepath 等误判为 Path 的引用,存在盲点)

- 检查结果:72 个函数全部有引用,无死代码

- 本文件刻意不使用带反斜杠的字符串字面量,避免转义被破坏

- 功能与行为零变化
caesar 17 hours ago
parent
commit
e7471d4138
1 changed files with 72 additions and 0 deletions
  1. 72 0
      deadcode_test.go

+ 72 - 0
deadcode_test.go

@@ -0,0 +1,72 @@
+package main
+
+import (
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"path/filepath"
+	"strings"
+	"testing"
+)
+
+// TestNoDeadFunctions 用 AST 精确检查包内是否存在"定义了但从未被引用"的函数/方法。
+// 相比文本 grep:AST 只统计标识符,不会把字符串或注释里的同名文字误判为引用。
+// main / init 豁免(由运行时调用)。发现问题即测试失败,作为长期质量门禁。
+//
+// 注意:本文件刻意不用任何带反斜杠的字符串字面量(逐条 Errorf 代替拼接换行),
+// 以免"转义序列"在生成/复制过程中被破坏。
+func TestNoDeadFunctions(t *testing.T) {
+	fset := token.NewFileSet()
+	files, err := filepath.Glob("*.go")
+	if err != nil {
+		t.Fatalf("扫描源文件失败: %v", err)
+	}
+
+	defs := map[string]string{} // 函数名 -> 定义位置
+	counts := map[string]int{}  // 标识符出现次数(含测试文件)
+
+	for _, path := range files {
+		if strings.HasSuffix(path, "_test.go") {
+			continue
+		}
+		file, err := parser.ParseFile(fset, path, nil, 0)
+		if err != nil {
+			t.Fatalf("解析 %s 失败: %v", path, err)
+		}
+		for _, decl := range file.Decls {
+			fn, ok := decl.(*ast.FuncDecl)
+			if !ok {
+				continue
+			}
+			name := fn.Name.Name
+			if name == "main" || name == "init" {
+				continue
+			}
+			defs[name] = fset.Position(fn.Pos()).String()
+		}
+	}
+
+	for _, path := range files {
+		file, err := parser.ParseFile(fset, path, nil, 0)
+		if err != nil {
+			continue
+		}
+		ast.Inspect(file, func(n ast.Node) bool {
+			if id, ok := n.(*ast.Ident); ok {
+				counts[id.Name]++
+			}
+			return true
+		})
+	}
+
+	deadCount := 0
+	for name, where := range defs {
+		if counts[name] <= 1 { // 仅定义处出现 => 从未被引用
+			deadCount++
+			t.Errorf("未被引用的函数: %s (%s)", name, where)
+		}
+	}
+	if deadCount > 0 {
+		t.Errorf("共 %d 个函数未被引用(建议删除,保持最小)", deadCount)
+	}
+}