Repository: alecthomas/kong
Pull request #499 (2025-02-14 09:40:36)

feat: Allow ignoring fields from embedded structs
If a field in a struct is ignored with `kong:"-"`, any embedded fields
with the same name are also ignored. This allows an outer struct to
remove flags from an embedded struct by redefining it and adding a kong
ignore tag.



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3396e46..4483d4b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,7 +13,7 @@ jobs:
       matrix:
         # These are the release channels.
         # Hermit will handle installing the right patch.
-        go: ["1.20", "1.21"]
+        go: ["1.23", "1.24"]
     steps:
       - name: Checkout code
         uses: actions/checkout@v4
@@ -35,7 +35,7 @@ jobs:
       matrix:
         # These are versions for GitHub's setup-go.
         # '.x' will pick the latest patch release.
-        go: ["1.20.x", "1.21.x"]
+        go: ["1.23.x", "1.24.x"]
     steps:
       - name: Checkout code
         uses: actions/checkout@v4
diff --git a/.golangci.yml b/.golangci.yml
index 3a05633..1eb0b92 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,7 +12,6 @@ linters:
     - wsl
     - funlen
     - gocognit
-    - gomnd
     - goprintffuncname
     - paralleltest
     - nlreturn
@@ -36,13 +35,14 @@ linters:
     - nilnil
     - depguard    # nothing to guard against yet
     - tagalign    # hurts readability of kong tags
+    - tenv        # deprecated since v1.64, but not removed yet
     - mnd
     - perfsprint
     - err113
     - copyloopvar
     - intrange
-    - execinquery
     - nakedret
+    - recvcheck   # value receivers are intentionally used for copies
 
 linters-settings:
   govet:
diff --git a/_examples/server/go.mod b/_examples/server/go.mod
index bbdafbc..39fccfe 100644
--- a/_examples/server/go.mod
+++ b/_examples/server/go.mod
@@ -4,7 +4,7 @@ go 1.13
 
 require (
 	github.com/alecthomas/colour v0.1.0
-	github.com/alecthomas/kong v1.7.0
+	github.com/alecthomas/kong v1.8.1
 	github.com/chzyer/readline v1.5.1
 	github.com/chzyer/test v1.0.0 // indirect
 	github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
diff --git a/_examples/server/go.sum b/_examples/server/go.sum
index 23d377f..2449645 100644
--- a/_examples/server/go.sum
+++ b/_examples/server/go.sum
@@ -24,6 +24,8 @@ github.com/alecthomas/kong v1.6.1 h1:/7bVimARU3uxPD0hbryPE8qWrS3Oz3kPQoxA/H2NKG8
 github.com/alecthomas/kong v1.6.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
 github.com/alecthomas/kong v1.7.0 h1:MnT8+5JxFDCvISeI6vgd/mFbAJwueJ/pqQNzZMsiqZE=
 github.com/alecthomas/kong v1.7.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
+github.com/alecthomas/kong v1.8.1 h1:6aamvWBE/REnR/BCq10EcozmcpUPc5aGI1lPAWdB0EE=
+github.com/alecthomas/kong v1.8.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
 github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
 github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
diff --git a/bin/.go-1.23.6.pkg b/bin/.go-1.24.0.pkg
similarity index 100%
rename from bin/.go-1.23.6.pkg
rename to bin/.go-1.24.0.pkg
diff --git a/bin/.golangci-lint-1.60.1.pkg b/bin/.golangci-lint-1.64.5.pkg
similarity index 100%
rename from bin/.golangci-lint-1.60.1.pkg
rename to bin/.golangci-lint-1.64.5.pkg
diff --git a/bin/go b/bin/go
index 8fb1c41..42b4aa3 120000
--- a/bin/go
+++ b/bin/go
@@ -1,1 +1,1 @@
-.go-1.23.6.pkg
\ No newline at end of file
+.go-1.24.0.pkg
\ No newline at end of file
diff --git a/bin/gofmt b/bin/gofmt
index 8fb1c41..42b4aa3 120000
--- a/bin/gofmt
+++ b/bin/gofmt
@@ -1,1 +1,1 @@
-.go-1.23.6.pkg
\ No newline at end of file
+.go-1.24.0.pkg
\ No newline at end of file
diff --git a/bin/golangci-lint b/bin/golangci-lint
index 4857716..1bb9797 120000
--- a/bin/golangci-lint
+++ b/bin/golangci-lint
@@ -1,1 +1,1 @@
-.golangci-lint-1.60.1.pkg
\ No newline at end of file
+.golangci-lint-1.64.5.pkg
\ No newline at end of file
diff --git a/build.go b/build.go
index 5d17f53..63afcd4 100644
--- a/build.go
+++ b/build.go
@@ -54,6 +54,7 @@ func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err erro
 	if v.Kind() != reflect.Struct {
 		return out, nil
 	}
+	ignored := map[string]bool{}
 	for i := 0; i < v.NumField(); i++ {
 		ft := v.Type().Field(i)
 		fv := v.Field(i)
@@ -61,7 +62,8 @@ func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err erro
 		if err != nil {
 			return nil, err
 		}
-		if tag.Ignored {
+		if tag.Ignored || ignored[ft.Name] {
+			ignored[ft.Name] = true
 			continue
 		}
 		// Assign group if it's not already set.
@@ -106,9 +108,27 @@ func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err erro
 		}
 		out = append(out, sub...)
 	}
+	out = removeIgnored(out, ignored)
 	return out, nil
 }
 
+func removeIgnored(fields []flattenedField, ignored map[string]bool) []flattenedField {
+	j := 0
+	for i := 0; i < len(fields); i++ {
+		if ignored[fields[i].field.Name] {
+			continue
+		}
+		if i != j {
+			fields[j] = fields[i]
+		}
+		j++
+	}
+	if j != len(fields) {
+		fields = fields[:j]
+	}
+	return fields
+}
+
 // Build a Node in the Kong data model.
 //
 // "v" is the value to create the node from, "typ" is the output Node type.
diff --git a/callbacks.go b/callbacks.go
index 2a296d0..6096a26 100644
--- a/callbacks.go
+++ b/callbacks.go
@@ -6,10 +6,59 @@ import (
 	"strings"
 )
 
+// binding is a single binding registered with Kong.
+type binding struct {
+	// fn is a function that returns a value of the target type.
+	fn reflect.Value
+
+	// val is a value of the target type.
+	// Must be set if done and singleton are true.
+	val reflect.Value
+
+	// singleton indicates whether the binding is a singleton.
+	// If true, the binding will be resolved once and cached.
+	singleton bool
+
+	// done indicates whether a singleton binding has been resolved.
+	// If singleton is false, this field is ignored.
+	done bool
+}
+
+// newValueBinding builds a binding with an already resolved value.
+func newValueBinding(v reflect.Value) *binding {
+	return &binding{val: v, done: true, singleton: true}
+}
+
+// newFunctionBinding builds a binding with a function
+// that will return a value of the target type.
+//
+// The function signature must be func(...) (T, error) or func(...) T
+// where parameters are recursively resolved.
+func newFunctionBinding(f reflect.Value, singleton bool) *binding {
+	return &binding{fn: f, singleton: singleton}
+}
+
+// Get returns the pre-resolved value for the binding,
+// or false if the binding is not resolved.
+func (b *binding) Get() (v reflect.Value, ok bool) {
+	return b.val, b.done
+}
+
+// Set sets the value of the binding to the given value,
+// marking it as resolved.
+//
+// If the binding is not a singleton, this method does nothing.
+func (b *binding) Set(v reflect.Value) {
+	if b.singleton {
+		b.val = v
+		b.done = true
+	}
+}
+
 // A map of type to function that returns a value of that type.
 //
 // The function should have the signature func(...) (T, error). Arguments are recursively resolved.
-type bindings map[reflect.Type]any
+type bindings map[reflect.Type]*binding
 
 func (b bindings) String() string {
 	out := []string{}
@@ -21,17 +70,18 @@ func (b bindings) String() string {
 
 func (b bindings) add(values ...any) bindings {
 	for _, v := range values {
-		v := v
-		b[reflect.TypeOf(v)] = func() (any, error) { return v, nil }
+		val := reflect.ValueOf(v)
+		b[val.Type()] = newValueBinding(val)
 	}
 	return b
 }
 
 func (b bindings) addTo(impl, iface any) {
-	b[reflect.TypeOf(iface).Elem()] = func() (any, error) { return impl, nil }
+	val := reflect.ValueOf(impl)
+	b[reflect.TypeOf(iface).Elem()] = newValueBinding(val)
 }
 
-func (b bindings) addProvider(provider any) error {
+func (b bindings) addProvider(provider any, singleton bool) error {
 	pv := reflect.ValueOf(provider)
 	t := pv.Type()
 	if t.Kind() != reflect.Func {
@@ -47,7 +97,7 @@ func (b bindings) addProvider(provider any) error {
 		}
 	}
 	rt := pv.Type().Out(0)
-	b[rt] = provider
+	b[rt] = newFunctionBinding(pv, singleton)
 	return nil
 }
 
@@ -148,19 +198,29 @@ func callAnyFunction(f reflect.Value, bindings bindings) (out []any, err error)
 	t := f.Type()
 	for i := 0; i < t.NumIn(); i++ {
 		pt := t.In(i)
-		argf, ok := bindings[pt]
+		binding, ok := bindings[pt]
 		if !ok {
 			return nil, fmt.Errorf("couldn't find binding of type %s for parameter %d of %s(), use kong.Bind(%s)", pt, i, t, pt)
 		}
+
+		// Don't need to call the function if the value is already resolved.
+		if val, ok := binding.Get(); ok {
+			in = append(in, val)
+			continue
+		}
+
 		// Recursively resolve binding functions.
-		argv, err := callAnyFunction(reflect.ValueOf(argf), bindings)
+		argv, err := callAnyFunction(binding.fn, bindings)
 		if err != nil {
 			return nil, fmt.Errorf("%s: %w", pt, err)
 		}
 		if ferrv := reflect.ValueOf(argv[len(argv)-1]); ferrv.IsValid() && ferrv.Type().Implements(callbackReturnSignature) && !ferrv.IsNil() {
 			return nil, ferrv.Interface().(error) //nolint:forcetypeassert
 		}
-		in = append(in, reflect.ValueOf(argv[0]))
+
+		val := reflect.ValueOf(argv[0])
+		binding.Set(val)
+		in = append(in, val)
 	}
 	outv := f.Call(in)
 	out = make([]any, len(outv))
diff --git a/context.go b/context.go
index ebf4c31..7b1d482 100644
--- a/context.go
+++ b/context.go
@@ -120,10 +120,19 @@ func (c *Context) BindTo(impl, iface any) {
 // This is useful when the Run() function of different commands require different values that may
 // not all be initialisable from the main() function.
 //
-// "provider" must be a function with the signature func(...) (T, error) or func(...) T, where
-// ... will be recursively injected with bound values.
+// "provider" must be a function with the signature func(...) (T, error) or func(...) T,
+// where ... will be recursively injected with bound values.
 func (c *Context) BindToProvider(provider any) error {
-	return c.bindings.addProvider(provider)
+	return c.bindings.addProvider(provider, false /* singleton */)
+}
+
+// BindSingletonProvider allows binding of provider functions.
+// The provider will be called once and the result cached.
+//
+// "provider" must be a function with the signature func(...) (T, error) or func(...) T,
+// where ... will be recursively injected with bound values.
+func (c *Context) BindSingletonProvider(provider any) error {
+	return c.bindings.addProvider(provider, true /* singleton */)
 }
 
 // Value returns the value for a particular path element.
@@ -792,7 +801,7 @@ func (c *Context) RunNode(node *Node, binds ...any) (err error) {
 					methodt := t.Method(i)
 					if strings.HasPrefix(methodt.Name, "Provide") {
 						method := p.Method(i)
-						if err := methodBinds.addProvider(method.Interface()); err != nil {
+						if err := methodBinds.addProvider(method.Interface(), false /* singleton */); err != nil {
 							return fmt.Errorf("%s.%s: %w", t.Name(), methodt.Name, err)
 						}
 					}
diff --git a/help.go b/help.go
index 6363ea2..28290f6 100644
--- a/help.go
+++ b/help.go
@@ -415,7 +415,7 @@ func (h *helpWriter) Write(w io.Writer) error {
 
 func (h *helpWriter) Wrap(text string) {
 	w := bytes.NewBuffer(nil)
-	doc.ToText(w, strings.TrimSpace(text), "", "    ", h.width)
+	doc.ToText(w, strings.TrimSpace(text), "", "    ", h.width) //nolint:staticcheck // cross-package links not possible
 	for _, line := range strings.Split(strings.TrimSpace(w.String()), "\n") {
 		h.Print(line)
 	}
@@ -470,7 +470,7 @@ func writeTwoColumns(w *helpWriter, rows [][2]string) {
 
 	for _, row := range rows {
 		buf := bytes.NewBuffer(nil)
-		doc.ToText(buf, row[1], "", strings.Repeat(" ", defaultIndent), w.width-leftSize-defaultColumnPadding)
+		doc.ToText(buf, row[1], "", strings.Repeat(" ", defaultIndent), w.width-leftSize-defaultColumnPadding) //nolint:staticcheck // cross-package links not possible
 		lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
 
 		line := fmt.Sprintf("%-*s", leftSize, row[0])
diff --git a/options.go b/options.go
index 6263202..d20b2fb 100644
--- a/options.go
+++ b/options.go
@@ -210,15 +210,33 @@ func BindTo(impl, iface any) Option {
 
 // BindToProvider binds an injected value to a provider function.
 //
-// The provider function must have the signature:
+// The provider function must have one of the following signatures:
+//
+//	func(...) (T, error)
+//	func(...) T
 //
-//	func() (any, error)
+// Where arguments to the function are injected by Kong.
 //
 // This is useful when the Run() function of different commands require different values that may
 // not all be initialisable from the main() function.
 func BindToProvider(provider any) Option {
 	return OptionFunc(func(k *Kong) error {
-		return k.bindings.addProvider(provider)
+		return k.bindings.addProvider(provider, false /* singleton */)
+	})
+}
+
+// BindSingletonProvider binds an injected value to a provider function.
+// The provider function must have the signature:
+//
+//	func(...) (T, error)
+//	func(...) T
+//
+// Unlike [BindToProvider], the provider function will only be called
+// at most once, and the result will be cached and reused
+// across multiple recipients of the injected value.
+func BindSingletonProvider(provider any) Option {
+	return OptionFunc(func(k *Kong) error {
+		return k.bindings.addProvider(provider, true /* singleton */)
 	})
 }
 
diff --git a/resolver.go b/resolver.go
index 29be1b9..3e37ca7 100644
--- a/resolver.go
+++ b/resolver.go
@@ -63,6 +63,6 @@ func JSON(r io.Reader) (Resolver, error) {
 }
 
 func snakeCase(name string) string {
-	name = strings.Join(strings.Split(strings.Title(name), "-"), "")
+	name = strings.Join(strings.Split(strings.Title(name), "-"), "") //nolint:staticcheck // Unicode punctuation not an issue
 	return strings.ToLower(name[:1]) + name[1:]
 }


Test patch:
diff --git a/config_test.go b/config_test.go
index 570f142..fd303af 100644
--- a/config_test.go
+++ b/config_test.go
@@ -15,12 +15,10 @@ func TestMultipleConfigLoading(t *testing.T) {
 	}
 
 	cli.Flag = "first"
-	first, cleanFirst := makeConfig(t, &cli)
-	defer cleanFirst()
+	first := makeConfig(t, &cli)
 
 	cli.Flag = ""
-	second, cleanSecond := makeConfig(t, &cli)
-	defer cleanSecond()
+	second := makeConfig(t, &cli)
 
 	p := mustNew(t, &cli, kong.Configuration(kong.JSON, first, second))
 	_, err := p.Parse(nil)
@@ -34,20 +32,19 @@ func TestConfigValidation(t *testing.T) {
 	}
 
 	cli.Flag = "invalid"
-	conf, cleanConf := makeConfig(t, &cli)
-	defer cleanConf()
+	conf := makeConfig(t, &cli)
 
 	p := mustNew(t, &cli, kong.Configuration(kong.JSON, conf))
 	_, err := p.Parse(nil)
 	assert.Error(t, err)
 }
 
-func makeConfig(t *testing.T, config any) (path string, cleanup func()) {
+func makeConfig(t *testing.T, config any) (path string) {
 	t.Helper()
-	w, err := os.CreateTemp("", "")
+	w, err := os.CreateTemp(t.TempDir(), "")
 	assert.NoError(t, err)
 	defer w.Close()
 	err = json.NewEncoder(w).Encode(config)
 	assert.NoError(t, err)
-	return w.Name(), func() { os.Remove(w.Name()) }
+	return w.Name()
 }
diff --git a/help_test.go b/help_test.go
index e680be2..f8aebab 100644
--- a/help_test.go
+++ b/help_test.go
@@ -83,8 +83,7 @@ func TestHelp(t *testing.T) {
 
 			Three threeArg `arg help:"Sub-sub-arg."`
 
-			Four struct {
-			} `cmd help:"Sub-sub-command."`
+			Four struct{} `cmd help:"Sub-sub-command."`
 		} `cmd help:"Another subcommand."`
 	}
 
@@ -192,8 +191,7 @@ func TestFlagsLast(t *testing.T) {
 
 			Three threeArg `arg help:"Sub-sub-arg."`
 
-			Four struct {
-			} `cmd help:"Sub-sub-command."`
+			Four struct{} `cmd help:"Sub-sub-command."`
 		} `cmd help:"Another subcommand."`
 	}
 
@@ -296,8 +294,7 @@ func TestHelpTree(t *testing.T) {
 		Two struct {
 			Three threeArg `arg help:"Sub-sub-arg."`
 
-			Four struct {
-			} `cmd help:"Sub-sub-command." aliases:"for,fore"`
+			Four struct{} `cmd help:"Sub-sub-command." aliases:"for,fore"`
 		} `cmd help:"Another subcommand."`
 	}
 
@@ -390,8 +387,7 @@ func TestHelpCompactNoExpand(t *testing.T) {
 		Two struct {
 			Three threeArg `arg help:"Sub-sub-arg."`
 
-			Four struct {
-			} `cmd help:"Sub-sub-command." aliases:"for,fore"`
+			Four struct{} `cmd help:"Sub-sub-command." aliases:"for,fore"`
 		} `cmd help:"Another subcommand."`
 	}
 
@@ -603,7 +599,7 @@ func TestAutoGroup(t *testing.T) {
 			if node, ok := parent.(*kong.Node); ok {
 				return &kong.Group{
 					Key:   node.Name,
-					Title: strings.Title(node.Name) + " flags:",
+					Title: strings.Title(node.Name) + " flags:", //nolint:staticcheck // strings.Title in test is okay
 				}
 			}
 			return nil
diff --git a/kong_test.go b/kong_test.go
index 6b5f5d6..15d1f51 100644
--- a/kong_test.go
+++ b/kong_test.go
@@ -856,6 +856,43 @@ func TestExcludedField(t *testing.T) {
 	assert.Error(t, err)
 }
 
+func TestExcludeEmbeddedField(t *testing.T) {
+	type Embedded struct {
+		Flag     string
+		Excluded string
+	}
+	type Embedded2 struct {
+		Flag2    string
+		Excluded string
+	}
+	var cli struct {
+		Embedded
+		Excluded string `kong:"-"`
+		Embedded2
+	}
+	var cli2 struct {
+		Embedded  Embedded  `kong:"embed"`
+		Excluded  string    `kong:"-"`
+		Embedded2 Embedded2 `kong:"embed"`
+	}
+
+	p := mustNew(t, &cli)
+	_, err := p.Parse([]string{"--flag=foo"})
+	assert.NoError(t, err)
+	_, err = p.Parse([]string{"--flag-2=foo"})
+	assert.NoError(t, err)
+	_, err = p.Parse([]string{"--excluded=foo"})
+	assert.Error(t, err)
+
+	p = mustNew(t, &cli2)
+	_, err = p.Parse([]string{"--flag=foo"})
+	assert.NoError(t, err)
+	_, err = p.Parse([]string{"--flag-2=foo"})
+	assert.NoError(t, err)
+	_, err = p.Parse([]string{"--excluded=foo"})
+	assert.Error(t, err)
+}
+
 func TestUnnamedFieldEmbeds(t *testing.T) {
 	type Embed struct {
 		Flag string
diff --git a/mapper_test.go b/mapper_test.go
index a6ddbe3..947f836 100644
--- a/mapper_test.go
+++ b/mapper_test.go
@@ -268,9 +268,8 @@ func TestFileContentFlag(t *testing.T) {
 	var cli struct {
 		File kong.FileContentFlag
 	}
-	f, err := os.CreateTemp("", "")
+	f, err := os.CreateTemp(t.TempDir(), "")
 	assert.NoError(t, err)
-	defer os.Remove(f.Name())
 	fmt.Fprint(f, "hello world")
 	f.Close()
 	_, err = mustNew(t, &cli).Parse([]string{"--file", f.Name()})
@@ -282,9 +281,8 @@ func TestNamedFileContentFlag(t *testing.T) {
 	var cli struct {
 		File kong.NamedFileContentFlag
 	}
-	f, err := os.CreateTemp("", "")
+	f, err := os.CreateTemp(t.TempDir(), "")
 	assert.NoError(t, err)
-	defer os.Remove(f.Name())
 	fmt.Fprint(f, "hello world")
 	f.Close()
 	_, err = mustNew(t, &cli).Parse([]string{"--file", f.Name()})
diff --git a/options_test.go b/options_test.go
index e549475..791cb64 100644
--- a/options_test.go
+++ b/options_test.go
@@ -119,6 +119,43 @@ func TestBindToProvider(t *testing.T) {
 	assert.True(t, cli.Called)
 }
 
+func TestBindSingletonProvider(t *testing.T) {
+	type (
+		Connection struct{}
+		ClientA    struct{ conn *Connection }
+		ClientB    struct{ conn *Connection }
+	)
+
+	var numConnections int
+	newConnection := func() *Connection {
+		numConnections++
+		return &Connection{}
+	}
+
+	var cli struct{}
+	app, err := New(&cli,
+		BindSingletonProvider(newConnection),
+		BindToProvider(func(conn *Connection) *ClientA {
+			return &ClientA{conn: conn}
+		}),
+		BindToProvider(func(conn *Connection) *ClientB {
+			return &ClientB{conn: conn}
+		}),
+	)
+	assert.NoError(t, err)
+
+	ctx, err := app.Parse([]string{})
+	assert.NoError(t, err)
+
+	_, err = ctx.Call(func(a *ClientA, b *ClientB) {
+		assert.NotZero(t, a.conn)
+		assert.NotZero(t, b.conn)
+
+		assert.Equal(t, 1, numConnections, "expected newConnection to be called only once")
+	})
+	assert.NoError(t, err)
+}
+
 func TestFlagNamer(t *testing.T) {
 	var cli struct {
 		SomeFlag string
diff --git a/util_test.go b/util_test.go
index 5ec0131..78302ef 100644
--- a/util_test.go
+++ b/util_test.go
@@ -16,9 +16,8 @@ func TestConfigFlag(t *testing.T) {
 		Flag   string
 	}
 
-	w, err := os.CreateTemp("", "")
+	w, err := os.CreateTemp(t.TempDir(), "")
 	assert.NoError(t, err)
-	defer os.Remove(w.Name())
 	w.WriteString(`{"flag": "hello world"}`) //nolint: errcheck
 	w.Close()