Repository: golangci/golangci-lint (Go)

Issue:
golines: settings not obeyed during `run`
### Welcome

- [x] Yes, I'm using a binary release within 2 latest [releases](https://github.com/golangci/golangci-lint/releases). Only such installations are supported.
- [x] Yes, I've searched similar [issues on GitHub](https://github.com/golangci/golangci-lint/issues) and didn't find any.
- [x] Yes, I've read the `typecheck` section of the [FAQ](https://golangci-lint.run/welcome/faq/#why-do-you-have-typecheck-errors).
- [x] Yes, I've tried with the standalone [linter](https://golangci-lint.run/usage/linters/) if available (e.g., gocritic, go vet, etc.).
- [x] I agree to follow this project's [Code of Conduct](https://github.com/golangci/golangci-lint?tab=coc-ov-file)

### How did you install golangci-lint?

Deb

### Description of the problem

The `reformat-tags: false` option for the new `golines` formatter is ignored during `run`, only obeyed during `fmt`. This is problematic because [an upstream bug](https://github.com/segmentio/golines/issues/142) causes that reformatting to corrupt tag contents.

Results:
```shell
$ golangci-lint run ./try.go 
try.go:4:1: File is not properly formatted (golines)
        Bar string `a:"b=\"c\"" d:"e"`
^
```

Vs running upstream linter with the same settings:
```shell
$ diff -u ./try.go <(go run github.com/segmentio/golines@latest --max-len=120 --tab-len=2 --no-reformat-tags ./try.go)
# no output
```

Running `golangci-lint fmt ./try.go` _does_ obey the setting and doesn't modify the file.

### Version of golangci-lint

<details>

```console
$ golangci-lint --version
golangci-lint has version 2.0.0 built with go1.24.1 from f1f8343d on 2025-03-24T12:12:51Z
```

</details>

### Configuration

<details>

```yaml
version: "2"
run:
  go: "1.24"
formatters:
  enable:
    - golines
  settings:
    golines:
      max-len: 120
      tab-len: 2
      # tag reformatting is buggy, see:
      # https://github.com/segmentio/golines/issues/161
      # https://github.com/segmentio/golines/issues/142
      # https://github.com/segmentio/golines/pull/143
      reformat-tags: false
```

</details>

### Go environment

<details>

```console
$ go version && go env
go version go1.24.1 linux/amd64
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/mgl/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/mgl/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2151675588=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/mgl/.go/pkg/mod'
GONOPROXY='CENSORED'
GONOSUMDB='CENSORED'
GOOS='linux'
GOPATH='/home/mgl/.go'
GOPRIVATE='CENSORED'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go-1.24'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/mgl/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='path'
GOTOOLDIR='/usr/lib/go-1.24/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.1'
GOWORK=''
PKG_CONFIG='pkg-config'
```

</details>

### Verbose output of running

<details>

```console
$ golangci-lint cache clean
$ golangci-lint run -v
INFO golangci-lint has version 2.0.0 built with go1.24.1 from f1f8343d on 2025-03-24T12:12:51Z 
INFO [config_reader] Config search paths: [./ /tmp/lintbug /tmp / /home/mgl] 
INFO [config_reader] Used config file .golangci.yml 
INFO maxprocs: Leaving GOMAXPROCS=32: CPU quota undefined 
INFO [goenv] Read go env for 4.236426ms: map[string]string{"GOCACHE":"/home/mgl/.cache/go-build", "GOROOT":"/usr/lib/go-1.24"} 
INFO [lintersdb] Active 6 linters: [errcheck golines govet ineffassign staticcheck unused] 
INFO [loader] Go packages loading at mode 8767 (compiled_files|deps|exports_file|files|name|imports|types_sizes) took 77.987065ms 
INFO [runner/filename_unadjuster] Pre-built 0 adjustments in 165.524µs 
INFO [linters_context/goanalysis] analyzers took 8.043541ms with top 10 stages: buildir: 756.353µs, golines: 724.772µs, ctrlflow: 606.745µs, printf: 553.828µs, fact_deprecated: 425.875µs, fact_purity: 352.173µs, nilness: 303.502µs, SA5012: 290.626µs, unused: 236.497µs, typedness: 155.281µs 
INFO [runner] Processors filtering stat (in/out): cgo: 1/1, filename_unadjuster: 1/1, invalid_issue: 1/1, exclusion_rules: 1/1, path_absoluter: 1/1, generated_file_filter: 1/1, fixer: 1/1, severity-rules: 1/1, path_prettifier: 1/1, max_per_file_from_linter: 1/1, path_relativity: 1/1, exclusion_paths: 1/1, diff: 1/1, uniq_by_line: 1/1, max_from_linter: 1/1, source_code: 1/1, path_shortener: 1/1, nolint_filter: 1/1, max_same_issues: 1/1, sort_results: 1/1 
INFO [runner] processing took 104.669µs with stages: generated_file_filter: 32.709µs, source_code: 27.787µs, nolint_filter: 27.135µs, uniq_by_line: 3.725µs, max_same_issues: 2.33µs, path_relativity: 1.891µs, sort_results: 1.289µs, max_from_linter: 1.095µs, path_shortener: 995ns, cgo: 885ns, invalid_issue: 878ns, fixer: 692ns, path_absoluter: 605ns, exclusion_paths: 534ns, filename_unadjuster: 455ns, exclusion_rules: 433ns, max_per_file_from_linter: 374ns, path_prettifier: 361ns, diff: 340ns, severity-rules: 156ns 
INFO [runner] linters took 3.461365ms with stages: goanalysis_metalinter: 3.272232ms 
try.go:4:2: structtag: struct field tag `a:"b="c"" d:"e"` not compatible with reflect.StructTag.Get: key:"value" pairs not separated by spaces (govet)
        Bar string `a:"b="c"" d:"e"`
        ^
1 issues:
* govet: 1
INFO File cache stats: 1 entries of total size 94B 
INFO Memory: 2 samples, avg is 38.0MB, max is 40.1MB 
INFO Execution took 86.297855ms                   
```

</details>

### A minimal reproducible example or link to a public repository

<details>

try.go:
```go
package main

type Foo struct {
	Bar string `a:"b=\"c\"" d:"e"`
	Baz string `a:"f" d:"g"`
}
```

</details>

### Validation

- [x] Yes, I've included all information above (version, config, etc.).

### Supporter

- [ ] I am a sponsor/backer through [GitHub](https://github.com/sponsors/golangci/) or [OpenCollective](https://opencollective.com/golangci-lint)


Discussion:
On further experimentation, it seems like it's not just this one setting, but _none_ of the settings for this formatter are obeyed during `run`, e.g. I have a (private) repo where `run` reports tons of golines complaints (none of which are related to struct tags in that repo), but `fmt` produces no changes. `run --fix` produces the same changes as running the upstream formatter with default settings, vs the settings I have configured in `.golangci.yaml`
Stepping through things in the debugger quickly found the problem, I'll submit a PR
@mgabeler-lee-6rs I have a fix locally, should I wait for your PR?
I want to create a release quickly because this is a critical bug.
@ldez Sorry, I didn't see your comment while I was working on my PR. Feel free to take whichever looks better, I won't take it personally if you discard mine in favor of your fix :)
I will keep you PR, thank you ❤️



Patch:
diff --git a/pkg/config/loader.go b/pkg/config/loader.go
index deace8ff0b17..e153c6adc169 100644
--- a/pkg/config/loader.go
+++ b/pkg/config/loader.go
@@ -223,6 +223,10 @@ func (l *Loader) handleFormatterOverrides() {
 	if slices.Contains(l.cfg.Formatters.Enable, "gci") {
 		l.cfg.Linters.Settings.Gci = l.cfg.Formatters.Settings.Gci
 	}
+
+	if slices.Contains(l.cfg.Formatters.Enable, "golines") {
+		l.cfg.Linters.Settings.GoLines = l.cfg.Formatters.Settings.GoLines
+	}
 }
 
 // Add formatter exclusions to linters exclusions.


Test patch:
diff --git a/pkg/golinters/golines/testdata/fix/in/golines-custom.go b/pkg/golinters/golines/testdata/fix/in/golines-custom.go
new file mode 100644
index 000000000000..d749e27c414f
--- /dev/null
+++ b/pkg/golinters/golines/testdata/fix/in/golines-custom.go
@@ -0,0 +1,16 @@
+//golangcitest:config_path testdata/golines-custom.yml
+//golangcitest:expected_exitcode 0
+package testdata
+
+// the struct tags should not be reformatted here
+type Foo struct {
+	Bar `a:"b=\"c\"" d:"e"`
+	Baz `a:"f" d:"g"`
+}
+
+var (
+	// this ends at 80 columns with tab size 2, and would only be a little wider
+	// with tab size 8, not failing the default line-len, so it checks both
+	// settings are applied properly
+	abc = []string{"a string that is only wrapped at narrow widths and wide tabs"}
+)
diff --git a/pkg/golinters/golines/testdata/fix/out/golines-custom.go b/pkg/golinters/golines/testdata/fix/out/golines-custom.go
new file mode 100644
index 000000000000..c6009373ad17
--- /dev/null
+++ b/pkg/golinters/golines/testdata/fix/out/golines-custom.go
@@ -0,0 +1,18 @@
+//golangcitest:config_path testdata/golines-custom.yml
+//golangcitest:expected_exitcode 0
+package testdata
+
+// the struct tags should not be reformatted here
+type Foo struct {
+	Bar `a:"b=\"c\"" d:"e"`
+	Baz `a:"f" d:"g"`
+}
+
+var (
+	// this ends at 80 columns with tab size 2, and would only be a little wider
+	// with tab size 8, not failing the default line-len, so it checks both
+	// settings are applied properly
+	abc = []string{
+		"a string that is only wrapped at narrow widths and wide tabs",
+	}
+)
diff --git a/pkg/golinters/golines/testdata/golines-custom.yml b/pkg/golinters/golines/testdata/golines-custom.yml
new file mode 100644
index 000000000000..2a728aa320f0
--- /dev/null
+++ b/pkg/golinters/golines/testdata/golines-custom.yml
@@ -0,0 +1,11 @@
+version: "2"
+
+formatters:
+  enable:
+    - golines
+  settings:
+    golines:
+      # override many settings
+      max-len: 80
+      tab-len: 8
+      reformat-tags: false