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
CodePudding user response:
As written in the comments above, there's no tooling for that.
But you can use a simple trick to find out.
You want to register a flag, but you can't because it has been registered earlier somewhere else (unknown where).
If you can register it first, then the place where it currently gets registered at will be reported in the error.
How to do that? Create a package which registers this flag:
package first
import "flag"
func init() {
flag.String("tf", "", "")
}
And import this package first thing in your main
package like this:
package main
import _ "play.gorund/first"
import (
"flag"
"play.ground/foo"
)
What will happen? Package init()
of first
is executed first, properly registering the tf
flag, then foo
will attempt to do that again, failing, and reporting the error.
Example output (try it on the 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
As you can see, foo.go
line 10 registers the tf
flag. Mission accomplished.
Note:
Many editors auto-format on save, which may involve rearranging / regrouping imports, so your play.ground/first
import may be moved down, not being the first. To avoid this, don't use the auto-format feature, or choose a name for this package that will remain first after auto-formatting.
Note #2:
The Spec: Package initialization states the requirements and rules of initializing packages, and the order in which imports are processed is not specified (only thing guaranteed is that all referenced package will be initialized recursively before it can be used). This means that although current compilers process them as listed, you cannot rely on this for 100%. There's also the issue of having multiple source files even for the main
package, supplying them in different order to the compiler may also change the initialization order. The spec has this as a "recommendation":
To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.