A-letubby
A-letubby

Reputation: 9152

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?

Upvotes: 481

Views: 244915

Answers (12)

baz
baz

Reputation: 1587

You can also do x = x, as a beginner go programmer I have to admit this is the most annoying language I've seen:)

Upvotes: 6

muhammed ozbilici
muhammed ozbilici

Reputation: 772

You can define as package level, still gives warning but can compile without error.

package main
import "fmt"

var i, x int = 1, 2

func main() {
  fmt.Println("Hello")
}

Go playground

Intellij screenshot

Upvotes: 1

yingshao xo
yingshao xo

Reputation: 385

I just made a new Golang compiler, which will simply ignore all variable unused warning and import unused warning by default.

https://github.com/yingshaoxo/go/releases/tag/v1.21

It works for both go run and go build

Upvotes: 5

Daniel Santos
Daniel Santos

Reputation: 3295

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.

Upvotes: 2

Petrus Theron
Petrus Theron

Reputation: 28856

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.

Upvotes: 11

chuacw
chuacw

Reputation: 1863

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.

Upvotes: 72

maxime
maxime

Reputation: 2315

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

Upvotes: 10

Martin Tournoij
Martin Tournoij

Reputation: 27852

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

Upvotes: 45

max pleaner
max pleaner

Reputation: 26788

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()

Upvotes: 14

Florent Bayle
Florent Bayle

Reputation: 11930

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.

Upvotes: 371

miltonb
miltonb

Reputation: 7385

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.

Upvotes: 12

lunux2008
lunux2008

Reputation: 569

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

Upvotes: 56

Related Questions