error: "declared and not used" even if I assign to it - go

I can't find why below code gives compile error "alive declared and not used".
func ping(ip string) {
var alive bool
_, err := exec.Command("ping", "-n 1", "-w 1000", ip).Output()
if err != nil {
alive = false
} else {
alive = true
}
}

The compile error you're seeing is exactly what is happening. That var alive bool is unused. You declare it and assign a value to it but you never do anything with it.
Here is a playground-friendly modification of your code that will run:
package main
import (
"fmt"
"strconv"
)
func main() {
fmt.Println(isInt("Hello, playground")) // prints false
fmt.Println(isInt("1234567890")) // prints true
}
func isInt(s string) bool {
var alive bool
_, err := strconv.Atoi(s) // simply to demonstrate an error case
if err != nil {
alive = false
} else {
alive = true
}
return alive
}
Notice that I return alive. The function is useless and not something I would suggest in and of itself but it should help illustrate what is missing in your example.

Since it is a local variable, it will exit scope at the end of the function. alive is neither evaluated nor returned inside the function. Hence the compiler complains.

Those strange limitations in Golang Devs team heads are terribly annoying.
Why not to allow people to disable their limitations with options?
Answer is simple: they write the lang for themselves (for guugle), not for the community.
Fortunately, Go is OpenSource and even written in Go.
So, here is a simple patch removing the "declared and not used" or "declared but not used" errors raising:
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 770210f..78c0cbc 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
## -49,10 +49,7 ## func walk(fn *Node) {
if defn.Left.Name.Used() {
continue
}
- yyerrorl(defn.Left.Pos, "%v declared and not used", ln.Sym)
defn.Left.Name.SetUsed(true) // suppress repeats
- } else {
- yyerrorl(ln.Pos, "%v declared and not used", ln.Sym)
}
}
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index abd9d05..8b15786 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
## -55,6 +55,7 ## func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
}
func (check *Checker) usage(scope *Scope) {
+ return
var unused []*Var
for _, elem := range scope.elems {
if v, _ := elem.(*Var); v != nil && !v.used {
(Actual for go1.12)
Unpack a fresh Golang into /usr/local/go and apply the patch.
Then compile:
export GOROOT_BOOTSTRAP=/usr/local/go2
cp -a /usr/local/go /usr/local/go2
cd /usr/local/go/src
sed -e 's#^bash run.bash.*##' -i all.bash
./all.bash
rm -rf /usr/local/go2
unset GOROOT_BOOTSTRAP
It's much faster to apply the patch once [per new version] than every time deal with every missed variable.

Related

Is there a way to determine whether a flag was set when using `flag.VisitAll`?

I'm using go's native "flag" package.
Built into it is the ability to visit all currently defined flags, using flag.VisitAll.
I'm trying to build a snippet that tries to fetch the value for that flag from an environment variable if one exists and in-case the flag was not set, and I can't find a way to determine whether a specific flag was set or not.
Is there any way to achieve that without implementing new parameter types?
Using flag.VisitAll sounds a bit convoluted; I'd suggest getting the environment variable with a sane default and using it as the flag's default value - meaning the environment variable will be the fallback if the flag isn't set:
package main
import (
"flag"
"fmt"
"os"
)
func GetEnvDefault(key, def string) string {
v := os.Getenv(key)
if v == "" {
return def
}
return v
}
func main() {
// Uncomment to test behaviour
// os.Setenv("SERVER_NAME", "donaldduck")
var serverName string
flag.StringVar(&serverName, "n", GetEnvDefault("SERVER_NAME", "mickeymouse"), "The human name for the server")
flag.Parse()
fmt.Println(serverName)
}
See: https://play.golang.org/p/ixDsXH31cBF
There is no function to walk over the unset command line flags. This functionality can be implemented, however, by taking the difference between the flags returned by VisitAll and Visit; the former walks over all flags, while the latter walks over set flags:
func UnsetFlags(fs *flag.FlagSet) []*flag.Flag {
var unset []*flag.Flag
fs.VisitAll(func(f *flag.Flag) {
unset = append(unset, f)
})
fs.Visit(func(f *flag.Flag) {
for i, h := range unset {
if f == h {
unset = append(unset[:i], unset[i+1:]...)
}
}
})
return unset
}
You can use that function after your flag.Parse call to set any unset flags to their environment value:
for _, f := range UnsetFlags(flag.CommandLine) {
v := os.Getenv(f.Name)
f.Value.Set(v)
}

Using default value in golang func

I'm trying to implement a default value according to the option 1 of the post Golang and default values. But when I try to do go install the following error pops up in the terminal:
not enough arguments in call to test.Concat1
have ()
want (string)
Code:
package test
func Concat1(a string) string {
if a == "" {
a = "default-a"
}
return fmt.Sprintf("%s", a)
}
// other package
package main
func main() {
test.Concat1()
}
Thanks in advance.
I don't think what you are trying to do will work that way. You may want to opt for option #4 from the page you cited, which uses variadic variables. In your case looks to me like you want just a string, so it'd be something like this:
func Concat1(a ...string) string {
if len(a) == 0 {
return "a-default"
}
return a[0]
}
Go does not have optional defaults for function arguments.
You may emulate them to some extent by having a special type
to contain the set of parameters for a function.
In your toy example that would be something like
type Concat1Args struct {
a string
}
func Concat1(args Concat1Args) string {
if args.a == "" {
args.a = "default-a"
}
return fmt.Sprintf("%s", args.a)
}
The "trick" here is that in Go each type has its respective
"zero value", and when producing a value of a composite type
using the so-called literal, it's possible to initialize only some of the type's fields, so in our example that would be
s := Concat1(Concat1Args{})
vs
s := Concat1(Concat1Args{"whatever"})
I know that looks clumsy, and I have showed this mostly for
demonstration purpose. In real production code, where a function
might have a dozen of parameters or more, having them packed
in a dedicate composite type is usually the only sensible way
to go but for a case like yours it's better to just explicitly
pass "" to the function.
Golang does not support default parameters. Accordingly, variadic arguments by themselves are not analogous. However, variadic functions with the use of error handling can 'resemble' the pattern. Try the following as a simple example:
package main
import (
"errors"
"log"
)
func createSeries(p ...int) ([]int, error) {
usage := "Usage: createSeries(<length>, <optional starting value>), length should be > 0"
if len(p) == 0 {
return nil, errors.New(usage)
}
n := p[0]
if n <= 0 {
return nil, errors.New(usage)
}
var base int
if len(p) == 2 {
base = p[1]
} else if len(p) > 2 {
return nil, errors.New(usage)
}
vals := make([]int, n)
for i := 0; i < n; i++ {
vals[i] = base + i
}
return vals, nil
}
func main() {
answer, err := createSeries(4, -9)
if err != nil {
log.Fatal(err)
}
log.Println(answer)
}
Default parameters work differently in Go than they do in other languages. In a function there can be one ellipsis, always at the end, which will keep a slice of values of the same type so in your case this would be:
func Concat1(a ...string) string {
but that means that the caller may pass in any number of arguments >= 0. Also you need to check that the arguments in the slice are not empty and then assign them yourself. This means they do not get assigned a default value through any kind of special syntax in Go. This is not possible but you can do
if a[0] == "" {
a[0] = "default value"
}
If you want to make sure that the user passes either zero or one strings, just create two functions in your API, e.g.
func Concat(a string) string { // ...
func ConcatDefault() string {
Concat("default value")
}

Can command line flags in Go be set to mandatory?

Is there a way how to set that certain flags are mandatory, or do I have to check for their presence on my own?
The flag package does not support mandatory or required flags (meaning the flag must be specified explicitly).
What you can do is use sensible default values for (all) flags. And if a flag is something like there is no sensible default, check the value at the start of your application and halt with an error message. You should do flag value validation anyway (not just for required flags), so this shouldn't mean any (big) overhead, and this is a good practice in general.
As already mentioned, the flag package does not provide this feature directly and usually you can (and should) be able to provide a sensible default. For cases where you only need a small number of explicit arguments (e.g. an input and output filename) you could use positional arguments (e.g. after flag.Parse() check that flag.NArg()==2 and then input, output := flag.Arg(0), flag.Arg(1)).
If however, you have a case where this isn't sensible; say a few integer flags you want to accept in any order, where any integer value is reasonable, but no default is. Then you can use the flag.Visit function to check if the flags you care about were explicitly set or not. I think this is the only way to tell if a flag was explicitly set to it's default value (not counting a custom flag.Value type with a Set implementation that kept state).
For example, perhaps something like:
required := []string{"b", "s"}
flag.Parse()
seen := make(map[string]bool)
flag.Visit(func(f *flag.Flag) { seen[f.Name] = true })
for _, req := range required {
if !seen[req] {
// or possibly use `log.Fatalf` instead of:
fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
os.Exit(2) // the same exit code flag.Parse uses
}
}
Playground
This would produce an error if either the "-b" or "-s" flag was not explicitly set.
go-flags lets you declare both required flags and required positional arguments:
var opts struct {
Flag string `short:"f" required:"true" name:"a flag"`
Args struct {
First string `positional-arg-name:"first arg"`
Sencond string `positional-arg-name:"second arg"`
} `positional-args:"true" required:"2"`
}
args, err := flags.Parse(&opts)
I like github.com/jessevdk/go-flags package to use in CLI. It provides a required attribute, to set a mandatory flag:
var opts struct {
...
// Example of a required flag
Name string `short:"n" long:"name" description:"A name" required:"true"`
...
}
If you have flag path, simply check if *path contains some value
var path = flag.String("f", "", "/path/to/access.log")
flag.Parse()
if *path == "" {
usage()
os.Exit(1)
}
I agree with this solution but, in my case default values are usually environment values. For example,
dsn := flag.String("dsn", os.Getenv("MYSQL_DSN"), "data source name")
And in this case, I want to check if the values are set from invocation (usually local development) or environment var (prod environment).
So with some minor modifications, it worked for my case.
Using flag.VisitAll to check the value of all flags.
required := []string{"b", "s"}
flag.Parse()
seen := make(map[string]bool)
flag.VisitAll(func(f *flag.Flag) {
if f.Value.String() != "" {
seen[f.Name] = true
}
})
for _, req := range required {
if !seen[req] {
// or possibly use `log.Fatalf` instead of:
fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
os.Exit(2) // the same exit code flag.Parse uses
}
}
Test example in plauground
Or you could docopt, where you only have to write the "usage" text. Docopt interprets the usage text and creates an argument map. This opens up a whole lot of possibilities, all following the POSIX usage text standard. This library is available for about 20 languages already.
https://github.com/docopt/docopt.go
package main
import (
"fmt"
"github.com/docopt/docopt-go"
)
const (
Usage = `Naval Fate.
Usage:
naval_fate ship new <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
naval_fate -h | --help
naval_fate --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.`
)
func main() {
args, _ := docopt.ParseDoc(Usage)
fmt.Println(args)
}
A thing to note is that when you do:
password := flag.String("password", "", "the password")
flag.Parse()
The default is set by flag.String, not flag.Parse.
So if you instead do:
const unspecified = "\x00"
password := flag.String("password", "", "the password")
*password = unspecified
flag.Parse()
Then *password == unspecified if you don't specify it explicitly in the command line. This is my go to for strings when I want to distinguish "empty" from "unspecified".
Here's a full working example.
Work around the Usage and DefValue attributes, and wrap your flags into a struct.
package main
import (
"flag"
"fmt"
"os"
)
type CliFlag struct {
Required bool
Usage string
Name string
Address *string
}
func init() {
flags := [...]CliFlag{
{
Required: true,
Usage: "(github.com) repository URI",
Name: "repo-uri",
Address: nil,
},
{
Required: true,
Usage: "(Zombro) repository workspace",
Name: "repo-workspace",
Address: nil,
},
{
Required: true,
Usage: "(zzio) repository slug",
Name: "repo-slug",
Address: nil,
},
}
for i, f := range flags {
f.Address = flag.String(f.Name, "", f.Usage)
flags[i] = f
}
flag.Parse()
missing := make([]string, 0)
for _, f := range flags {
if *f.Address == "" && f.Required {
missing = append(missing, f.Name)
}
}
if len(missing) > 0 {
fmt.Printf("missing required flags: %v \n", missing)
flag.Usage()
os.Exit(1)
}
}
func main() {
fmt.Println("main")
}
missing
$ go run . -repo-slug test
missing required flags: [repo-uri repo-workspace]
Usage of /var/folders/mg/86n5kszs27bdqj0fpswvr0m00000gn/T/go-build2061541798/b001/exe/zzio:
-repo-slug string
(zzio) repository slug
-repo-uri string
(github.com) repository URI
-repo-workspace string
(Zombro) repository workspace
exit status 1

golang flag stops parsing after the first non-option

i am building a little cli tool that boots my app in development or production.
the way i want it to work is like this:
app run --dev or app run --prod
Atm it doest parses the flags after my command but only before my command. So this works
app --dev run or app --prod run
Any idee how to fix it this so i can use it after my command? here is my code
func main() {
//flag.Usage := usage
flag.Parse()
args := flag.Args()
if len(args) == 0 {
Usage()
os.Exit(0)
}
if *dev {
os.Setenv("ENV", "development")
}
if *prod {
os.Setenv("ENV", "production")
}
switch {
// Run
case args[0] == "run" && len(args) == 1:
os.Setenv("port", *port)
log.Printf("Booting in %s", os.Getenv("ENV"))
Run()
// Help
case args[0] == "help" && len(args) == 1:
Usage()
}
}
Traditionally, the UNIX option parser getopt() stops parsing after the first non-option. The glibc altered this behavior to support options in arbitrary positions, a controversial decision. The flag package implements the traditional behavior.
One thing you could do is permuting the arguments array before parsing the flags. That's what the glibc does:
func permutateArgs(args []string) int {
args = args[1:]
optind := 0
for i := range args {
if args[i][0] == '-' {
tmp := args[i]
args[i] = args[optind]
args[optind] = tmp
optind++
}
}
return optind + 1
}
This code permutates args such that options are in front, leaving the program name untouched. permutateArgs returns the index of the first non-option after permutation. Use this code like this:
optind := permutateArgs(os.Args)
flags.Parse()
// process non-options
for i := range os.Args[optind:] {
// ...
}
This is simply the way the flag package deals with arguments. Argument handling is always somewhat convention driven, and the flag package definitely does not follow the gnu-style that many are accustomed to.
One way is to sort the options before the commands:
// example argument sorting using sort.Stable
type ArgSlice []string
func (p ArgSlice) Len() int { return len(p) }
func (p ArgSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p ArgSlice) Less(i, j int) bool {
if len(p[i]) == 0 {
return false
}
if len(p[j]) == 0 {
return true
}
return p[i][0] == '-' && p[j][0] != '-'
}
func main() {
args := []string{"cmd", "-a", "arg", "-b", "-c"}
sort.Stable(ArgSlice(args))
// if sorting os.Args, make sure to omit the first argument
// sort.Stable(ArgSlice(os.Args[1:]))
fmt.Println(args)
}
Many packages use separate FlagSets for subcommands, which provides nice separation, but this would not work when there are possibly different flags between subcommands. The only solution there is to duplicate the flags across each level.
However, the best answer is still to follow the conventions used by the flag package, and not try to fight it.

How to avoid annoying error "declared and not used"

I'm learning Go but I feel it is a bit annoying that when compiling, I should not leave any variable or package unused.
This is really quite slowing me down. For example, I just wanted to declare a new package and plan to use it later or just uncomment some command to test. I always get the error and need to go comment all of those uses.
Is there any way to avoid this kind of check in Go?
That error is here to force you to write better code, and be sure to use everything you declare or import. It makes it easier to read code written by other people (you are always sure that all declared variables will be used), and avoid some possible dead code.
But, if you really want to skip this error, you can use the blank identifier (_) :
package main
import (
"fmt" // imported and not used: "fmt"
)
func main() {
i := 1 // i declared and not used
}
becomes
package main
import (
_ "fmt" // no more error
)
func main() {
i := 1 // no more error
_ = i
}
As said by kostix in the comments below, you can find the official position of the Go team in the FAQ:
The presence of an unused variable may indicate a bug, while unused imports just slow down compilation. Accumulate enough unused imports in your code tree and things can get very slow. For these reasons, Go allows neither.
You can use a simple "null function" for this, for example:
func Use(vals ...interface{}) {
for _, val := range vals {
_ = val
}
}
Which you can use like so:
package main
func main() {
a := "declared and not used"
b := "another declared and not used"
c := 123
Use(a, b, c)
}
There's also a package for this so you don't have to define the Use function every time:
import (
"github.com/lunux2008/xulu"
)
func main() {
// [..]
xulu.Use(a, b, c)
}
I ran into this while I was learning Go 2 years ago, so I declared my own function.
// UNUSED allows unused variables to be included in Go programs
func UNUSED(x ...interface{}) {}
And then you can use it like so:
UNUSED(x)
UNUSED(x, y)
UNUSED(x, y, z)
The great thing about it is, you can pass anything into UNUSED.
Is it better than the following?
_, _, _ = x, y, z
That's up to you.
According to the FAQ:
Some have asked for a compiler option to turn those checks off or at least reduce them to warnings. Such an option has not been added, though, because compiler options should not affect the semantics of the language and because the Go compiler does not report warnings, only errors that prevent compilation.
There are two reasons for having no warnings. First, if it's worth complaining about, it's worth fixing in the code. (And if it's not worth fixing, it's not worth mentioning.) Second, having the compiler generate warnings encourages the implementation to warn about weak cases that can make compilation noisy, masking real errors that should be fixed.
I don't necessarily agree with this for various reasons not worth going into. It is what it is, and it's not likely to change in the near future.
For packages, there's the goimports tool which automatically adds missing packages and removes unused ones. For example:
# Install it
$ go get golang.org/x/tools/cmd/goimports
# -w to write the source file instead of stdout
$ goimports -w my_file.go
You should be able to run this from any half-way decent editor − for example for Vim:
:!goimports -w %
The goimports page lists some commands for other editors, and you typically set it to be run automatically when you save the buffer to disk.
Note that goimports will also run gofmt.
As was already mentioned, for variables the easiest way is to (temporarily) assign them to _ :
// No errors
tasty := "ice cream"
horrible := "marmite"
// Commented out for debugging
//eat(tasty, horrible)
_, _ = tasty, horrible
In case others have a hard time making sense of this, I think it might help to explain it in very straightforward terms. If you have a variable that you don't use, for example a function for which you've commented out the invocation (a common use-case):
myFn := func () { }
// myFn()
You can assign a useless/blank variable to the function so that it's no longer unused:
myFn := func () { }
_ = myFn
// myFn()
One angle not so far mentioned is tool sets used for editing the code.
Using Visual Studio Code along with the Extension from lukehoban called Go will do some auto-magic for you. The Go extension automatically runs gofmt, golint etc, and removes and adds import entries. So at least that part is now automatic.
I will admit its not 100% of the solution to the question, but however useful enough.
As far as I can tell, these lines in the Go compiler look like the ones to comment out. You should be able to build your own toolchain that ignores these counterproductive warnings.
I ran into this issue when I wanted to temporarily disable the sending of an email while working on another part of the code.
Commenting the use of the service triggered a lot of cascade errors, so instead of commenting I used a condition
if false {
// Technically, svc still be used so no yelling
_, err = svc.SendRawEmail(input)
Check(err)
}
My answer is to hack the f-ing sources. This patch works against 1.19.4 and then you build your sources with -gcflags all=-nounusederrors (you'll need help elsewhere in order to build Golang from sources):
tty/tty.go:98:6: hello declared but not used, but nobody cares
It doesn't suppress some unused labels and the grammar of the appended message is a bit sloppy, but nobody cares.
From 6eb19713fb5302ef2d5eb4af0c05e86c88d055c7 Mon Sep 17 00:00:00 2001
From: Daniel Santos <daniel.santos#pobox.com>
Date: Mon, 9 Jan 2023 21:56:03 -0600
Subject: Add -nounusedwarnings
---
src/cmd/compile/internal/base/flag.go | 1 +
src/cmd/compile/internal/types2/errors.go | 10 ++++++++++
src/cmd/compile/internal/types2/labels.go | 2 +-
src/cmd/compile/internal/types2/resolver.go | 8 ++++----
src/cmd/compile/internal/types2/stmt.go | 4 ++--
src/cmd/go/alldocs.go | 2 ++
src/cmd/go/internal/work/build.go | 2 ++
src/go/types/gotype.go | 3 +++
8 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go
index a363b83984..f295746f64 100644
--- a/src/cmd/compile/internal/base/flag.go
+++ b/src/cmd/compile/internal/base/flag.go
## -111,6 +111,7 ## type CmdFlags struct {
MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\""
MutexProfile string "help:\"write mutex profile to `file`\""
NoLocalImports bool "help:\"reject local (relative) imports\""
+ NoUnusedErrors bool "help:\"no errors for unused imports and variables\""
Pack bool "help:\"write to file.a instead of file.o\""
Race bool "help:\"enable race detector\""
Shared *bool "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go
index 2a3e88a2fe..0405fa26de 100644
--- a/src/cmd/compile/internal/types2/errors.go
+++ b/src/cmd/compile/internal/types2/errors.go
## -8,6 +8,7 ## package types2
import (
"bytes"
+ "cmd/compile/internal/base"
"cmd/compile/internal/syntax"
"fmt"
"runtime"
## -275,6 +276,15 ## func (check *Checker) softErrorf(at poser, format string, args ...interface{}) {
check.err(at, check.sprintf(format, args...), true)
}
+func (check *Checker) unusedf(at poser, format string, args ...interface{}) {
+ if base.Flag.NoUnusedErrors {
+ pos := posFor(at)
+ fmt.Printf("%s: %s, but nobody cares\n", pos, check.sprintf(format, args...))
+ } else {
+ check.softErrorf(at, format, args)
+ }
+}
+
func (check *Checker) versionErrorf(at poser, goVersion string, format string, args ...interface{}) {
msg := check.sprintf(format, args...)
if check.conf.CompilerErrorMessages {
diff --git a/src/cmd/compile/internal/types2/labels.go b/src/cmd/compile/internal/types2/labels.go
index 6f02e2fc96..d3ae602549 100644
--- a/src/cmd/compile/internal/types2/labels.go
+++ b/src/cmd/compile/internal/types2/labels.go
## -35,7 +35,7 ## func (check *Checker) labels(body *syntax.BlockStmt) {
for name, obj := range all.elems {
obj = resolve(name, obj)
if lbl := obj.(*Label); !lbl.used {
- check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
+ check.unusedf(lbl.pos, "label %s declared but not used", lbl.name)
}
}
}
diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go
index 5d498b6b2b..935435b03f 100644
--- a/src/cmd/compile/internal/types2/resolver.go
+++ b/src/cmd/compile/internal/types2/resolver.go
## -731,15 +731,15 ## func (check *Checker) errorUnusedPkg(obj *PkgName) {
}
if obj.name == "" || obj.name == "." || obj.name == elem {
if check.conf.CompilerErrorMessages {
- check.softErrorf(obj, "imported and not used: %q", path)
+ check.unusedf(obj, "imported and not used: %q", path)
} else {
- check.softErrorf(obj, "%q imported but not used", path)
+ check.unusedf(obj, "%q imported but not used", path)
}
} else {
if check.conf.CompilerErrorMessages {
- check.softErrorf(obj, "imported and not used: %q as %s", path, obj.name)
+ check.unusedf(obj, "imported and not used: %q as %s", path, obj.name)
} else {
- check.softErrorf(obj, "%q imported but not used as %s", path, obj.name)
+ check.unusedf(obj, "%q imported but not used as %s", path, obj.name)
}
}
}
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index 74d4164ba9..c4255e4413 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
## -66,7 +66,7 ## func (check *Checker) usage(scope *Scope) {
return unused[i].pos.Cmp(unused[j].pos) < 0
})
for _, v := range unused {
- check.softErrorf(v.pos, "%s declared but not used", v.name)
+ check.unusedf(v.pos, "%s declared but not used", v.name)
}
for _, scope := range scope.children {
## -804,7 +804,7 ## func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
v.used = true // avoid usage error when checking entire function
}
if !used {
- check.softErrorf(lhs, "%s declared but not used", lhs.Value)
+ check.unusedf(lhs, "%s declared but not used", lhs.Value)
}
}
}
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index a3c1fecb91..1f4c5c7b5c 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
## -179,6 +179,8 ##
// directory, but it is not accessed. When -modfile is specified, an
// alternate go.sum file is also used: its path is derived from the
// -modfile flag by trimming the ".mod" extension and appending ".sum".
+// -nounusederrors
+// do not error on unused functions, imports, variables, etc.
// -overlay file
// read a JSON config file that provides an overlay for build operations.
// The file is a JSON struct with a single field, named 'Replace', that
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 5f11cdabaf..b37f1c8a01 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
## -135,6 +135,8 ## and test commands:
directory, but it is not accessed. When -modfile is specified, an
alternate go.sum file is also used: its path is derived from the
-modfile flag by trimming the ".mod" extension and appending ".sum".
+ -nounusederrors
+ do not error on unused functions, imports, variables, etc.
-overlay file
read a JSON config file that provides an overlay for build operations.
The file is a JSON struct with a single field, named 'Replace', that
diff --git a/src/go/types/gotype.go b/src/go/types/gotype.go
index e8ff9658da..5a60b83346 100644
--- a/src/go/types/gotype.go
+++ b/src/go/types/gotype.go
## -47,6 +47,8 ## The flags are:
verbose mode
-c
compiler used for installed packages (gc, gccgo, or source); default: source
+ -nounusederrors
+ treat "unused" errors as warnings
Flags controlling additional output:
## -104,6 +106,7 ## var (
allErrors = flag.Bool("e", false, "report all errors, not just the first 10")
verbose = flag.Bool("v", false, "verbose mode")
compiler = flag.String("c", "source", "compiler used for installed packages (gc, gccgo, or source)")
+ nounusederr= flag.Bool("nounusederrors", false, "treat unused objects as warnings")
// additional output control
printAST = flag.Bool("ast", false, "print AST")
--
2.38.2
Disclaimer: this isn't a comprehensive patch -- it only affects running go build or the compile tool. Should still have errors when using ast (tree parser), tracing and a few others tools because I didn't mess with internal/types, only cmd/compile/internal/types2 -- the Golang sources are a bit messy like that. I'm hoping they can refactor at some point and get rid of these redundancies.

Resources