deadcode 是目前最实用的私有函数扫描工具,它从前向可达性分析出发精准定位未被引用的私有函数,但对反射、插件、JSON 配置等隐式调用无感知,需人工核对 init、接口实现和包级变量初始化三类场景。

直接用 deadcode 扫描私有函数
Go 编译器不报未调用的私有函数(小写首字母),go build 也不会删——它只做 SSA 层面的保守 DCE,不分析调用图。所以必须靠外部工具。deadcode 是目前最实用的选择,它从 main 和测试入口出发做前向可达性分析,能准确定位本包内完全没被引用的私有函数。
安装和运行很简单:
go install github.com/tsenart/deadcode@latest-
deadcode ./(整个模块)或deadcode ./pkg(子目录)
输出类似:pkg/util.go:12:6:unused func cleanupTempFiles,行号精确,可直接跳转。
注意:deadcode 对反射调用(reflect.Value.Call)、插件加载、JSON 配置驱动的函数无感知,这类需人工确认;若函数被 var _ = helper 或空接口赋值(var _ interface{} = helper)引用,也可能误报。
别依赖 GoLand 的 “Find Usages” 查私有函数
Find Usages(快捷键 Alt+F7)对导出符号(大写首字母)很准,但对私有函数基本不可靠:它只查当前文件或显式引用,不跨文件分析包级调用关系,更不处理变量赋值、取地址(&helper)、接口实现等间接引用场景。
比如一个私有函数在 init() 里被调用,或用于包级变量初始化(var x = helper()),Find Usages 很可能返回 “No usages found”,但删掉就 panic。
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
真正想验证是否真没人用,得结合:deadcode 结果 + 手动 grep:grep -r "helper" ./ --include="*.go",重点看 init、包级 var、reflect.、字符串硬编码(如日志里的 "helper")。
全局搜索不能替代静态分析
按两次 Shift 打开全局搜索,输函数名,看似能“查有没有人用”,但问题很大:
- 搜不到被字符串拼接调用的(
name := "helper"; callByName(name)) - 搜不到被构建标签屏蔽的文件里的引用(
//go:build !prod) - 搜不到测试文件以外的间接引用(比如某个
testutil包里调用了它,但该包没被当前模块 import)
而且全局搜索默认包含注释、字符串字面量、甚至 .md 文件,噪音极大。真要用,得手动加过滤:Ctrl+Shift+F → 勾选 “In comments” 关闭、“In strings” 关闭、“File mask” 设为 *.go,再搜。
删之前必须人工核对三个隐式调用点
哪怕 deadcode 报了 “unused”,也不能直接删。以下三类情况极易被工具漏掉,但删了就炸:
-
init()函数里调用了它(init不在主调用图里) - 函数名被硬编码在
runtime.FuncForPC、错误包装(fmt.Errorf("in %s", "helper"))或日志中 - 它实现了某个接口(哪怕没显式赋值),而该接口被其他包通过类型断言或反射使用
尤其要注意包级变量初始化:比如 var _ io.Closer = &MyStruct{} 虽然没直接调函数,但若 MyStruct.Close 是那个“未用函数”,删掉就破坏接口契约。

















