| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- 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)
- }
- }
|