If a flag is redefined, go only tells one of the places where the flag is redefined. Is there a way to identify at which place the other flag is defined?
Just to give an instance, see the below program. Here in foo.go, the first flag is defined. Say if I am trying to add a flag with the same name in package main, go panic tells me that the flag is already defined (as package foo was initialized first) and I am redefining it in package main. This is a simple example but in a large code-base where the flag could be defined in imported libraries, its hard to find out where the flag is first defined. Any way to find this out?

package main

import (
    "flag"

    "play.ground/foo"
)

func main() {
    var tf string
    flag.StringVar(&tf, "tf", "", "")
    foo.Bar()
}
-- go.mod --
module play.ground
-- foo/foo.go --
package foo

import (
    "flag"
    "fmt"
)

func init() {
    var tf string
    flag.StringVar(&tf, "tf", "", "")
}

func Bar() {
    fmt.Println("This function lives in an another file!")
}

panic :

/tmpfs/play flag redefined: tf
panic: /tmpfs/play flag redefined: tf

goroutine 1 [running]:
flag.(*FlagSet).Var(0xc0000960c0, {0x4c3148, 0xc0000980a0}, {0x4a2642, 0x2}, {0x0, 0x0})
    /usr/local/go-faketime/src/flag/flag.go:980 +0x2f9
flag.StringVar(...)
    /usr/local/go-faketime/src/flag/flag.go:851
main.main()
    /tmp/sandbox3734065722/prog.go:11 +0x7d

Program exited.

playground :https://go.dev/play/p/jsmMcnEO2hy

推荐答案

正如在上面的注释中所写的,没有工具可以做到这一点.

但你可以用一个简单的技巧来找出答案.

您想要注册一个标志,但您无法注册,因为它之前已经在其他地方注册过(未知在哪里).

如果您可以先注册,则会在错误中报告当前注册的地方.

如何做到这一点呢?创建注册此标志的包:

package first

import "flag"

func init() {
    flag.String("tf", "", "")
}

在你的main包中导入这个first包,就像这样:

package main

import _ "play.gorund/first"

import (
    "flag"

    "play.ground/foo"
)

会发生什么事?首先执行first的包init(),正确地注册tf标志,然后foo将再次try 这样做,失败并报告错误.

输出示例(在Go Playground上试用):

/tmpfs/play flag redefined: tf
panic: /tmpfs/play flag redefined: tf

goroutine 1 [running]:
flag.(*FlagSet).Var(0xc000062180, {0x4c3188, 0xc0000142a0}, {0x4a2642, 0x2}, {0x0, 0x0})
    /usr/local/go-faketime/src/flag/flag.go:980 +0x2f9
flag.StringVar(...)
    /usr/local/go-faketime/src/flag/flag.go:851
play.ground/foo.init.0()
    /tmp/sandbox1464715048/foo/foo.go:10 +0x7d

如您所见,foo.go行10注册了tf标志.任务完成.

Note:

许多编辑器在保存时自动格式化,这可能涉及重新排列/重新分组导入,因此您的play.ground/first导入可能会被下移,而不是第一次.要避免这种情况,请不要使用自动格式化功能,或者为该包 Select 一个在自动格式化后将首先保留的名称.

Note #2:

Spec: Package initialization说明了初始化包的要求和规则,并且没有指定处理导入的顺序(唯一能保证的是所有引用的包在可以使用之前都将被递归地初始化).这意味着,尽管当前的编译器按所列方式处理它们,但您不能Spec: Package initialization%依赖这一点.还有即使对于main包也有多个源文件的问题,将它们以不同的顺序提供给编译器也可能改变初始化顺序.该规范将这一点作为"建议":

为了确保可重现的初始化行为,鼓励构建系统以词法文件名顺序向编译器呈现属于同一包的多个文件.

Go相关问答推荐

如何创建两个连接的io.ReadWriteClosers以用于测试目的

macOS上GoLand 2023.3.4中的代码导航

难以为多个平台添加Go Bazel构建选项

GORM没有从 struct 创建完整的表,如何修复?

ChromeDriver不存在(高朗selenium)

无法获取RPC描述符

文件路径.Abs()未在结果中提供子目录

如何获取集群外go Kubernetes客户端的当前命名空间?

当我使用 CircleCI 构建 Go Image 时,我得到runtime/cgo: pthread_create failed: Operation not allowed

是否可以将 http.ServeHttp 包装在 go 中以使用 alexedwards/scs/v2 添加会话

在 .go 文件中运行一个函数和在 Go 模板中调用它有什么区别?

枚举的 Golang 验证器自定义验证规则

如何在时间范围内规范化数组的元素?

使用 GO 在侧 tar 文件中提取 tar 文件的最快方法

K8s 算子读取原始数据

使用 bolthold 3 条件进行 boltDB 查询

GOENV 只能使用 OS 环境设置

如何在测试中传递用户名和密码等参数

Go:如何通过 GIN-Router 从 AWS S3 将文件作为二进制流发送到浏览器?

Go http标准库中的内存泄漏?