pairedbrackets

Linter checks formatting of paired brackets (inspired by this article).
Rule
According to the original notation, "a bracket should either start/end a line or be paired on the same line".
With modification for multiline items, the following cases are allowed:
- Both brackets and all items are in one line.
fmt.Printf("%s, %s!", "Hello", "world")
- Left (opening) bracket is the last character on a line and right (closing) bracket starts a new line.
fmt.Printf( // comments and whitespaces are ignored
"%s, %s!", "Hello", "world",
)
- If the last item is multiline, it can start on the same line with the left bracket.
In this case, the right bracket should be on the same line where the last item ends.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
...
})
Linter reports (wordings):
x.go:1:16: left parenthesis should either be the last character on a line or be on the same line with the last argument
⬇
http.HandleFunc("/",
func(w http.ResponseWriter, r *http.Request) {
...
},
)
x.go:4:1: right parenthesis should be on the previous line
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
...
},
)
⬆
x.go:5:3: right parenthesis should be on the next line
http.HandleFunc(
"/",
func(w http.ResponseWriter, r *http.Request) {
...
})
⬆
Examples
Function/method call
| Bad | Good |
fmt.Printf("%s, %s!",
"Hello", "world")
fmt.Printf("%s, %s!",
"Hello", "world",
)
fmt.Printf(
"%s, %s!",
"Hello", "world")
fmt.Printf("%s %s", "Last", `item
is multiline`,
)
|
fmt.Printf("%s, %s!", "Hello", "world")
fmt.Printf(
"%s, %s!", "Hello", "world",
)
fmt.Printf(
"%s, %s!",
"Hello", "world",
)
fmt.Printf("%s %s", "Last", `item
is multiline`)
|
Functions from github.com/stretchr/testify/assert and github.com/stretchr/testify/require are ignored by default (see config).
Composite literal
| Bad | Good |
foo := []int{1,
2, 3}
foo := []int{1,
2, 3,
}
foo := []int{
1,
2,
3}
foo := []string{"Last", "item", `is
multiline`,
}
|
bar := []int{1, 2, 3}
bar := []int{
1,
2,
3,
}
bar := []int{
1, 2, 3,
}
bar := []string{"Last", "item", `is
multiline`}
|
Function parameters
| Bad | Good |
func Foo(a int,
b string, c bool) {
...
}
func Foo(a int,
b string, c bool,
) {
...
}
func Foo(
a int,
b string,
c bool) {
...
}
func Foo(a int, b string,
) {
...
}
func Foo(a int, b struct {
X int
Y string
},
) {
...
}
|
func Bar(a int, b string) {
...
}
func Bar(
a int,
b string,
c bool,
) {
...
}
func Bar(
a int, b string, c bool,
) {
...
}
func Bar(a int, b struct {
X int
Y string
}) {
...
}
|
Function type parameters (generics)
| Bad | Good |
func Foo[T int,
V string]() {
...
}
func Foo[T int,
V string,
]() {
...
}
func Foo[
T int,
V string]() {
...
}
func Foo[T int, V string,
]() {
...
}
func Foo[T int, V interface {
int | string
},
]() {
...
}
|
func Bar[T int, V string]() {
...
}
func Bar[
T int,
V string,
]() {
...
}
func Bar[
T int, V string,
]() {
...
}
func Bar[T int, V interface {
int | string
}]() {
...
}
|
Function returns (output parameters)
| Bad | Good |
func Foo() (int,
error) {
...
}
func Foo() (int,
error,
) {
...
}
func Foo() (
int,
error) {
...
}
func Foo() (int, error,
) {
...
}
func Foo() (int, interface {
Error()
},
) {
...
}
|
func Bar() (int, error) {
...
}
func Bar() (
int,
error,
) {
...
}
func Bar() (
int, error,
) {
...
}
func Bar() (int, interface {
Error()
}) {
...
}
|
gofmt
gofmt fixes many cases, which pairedbrackets complains about. But not all of them. All examples above formatted correctly according to gofmt.
gofumpt
gofumpt is just a slightly better than gofmt. It fixes some composite literal examples above. But not all of them, and it doesn't fix other examples.
Usage
You can use golangci-lint.
Unfortunately, v1 was rejected to be a built-in linter (hopefully v2 will be accepted).
You can configure pairedbrackets as a plugin.
Install golangci-lint
Prebuilt binaries doesn't support plugins (see discussion), so you have to build golangci-lint:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
Install pairedbrackets
go install github.com/maratori/pairedbrackets@latest
Build plugin
pairedbrackets -build-golangci-lint-plugin
pairedbrackets.so will be created in current working directory. You can change the output path with flag -plugin-output (see other flags in help as well).
Config
pairedbrackets is disabled by default.
To enable it, add the following to your .golangci.yml:
linters-settings:
custom:
pairedbrackets:
path: /path/to/plugin/pairedbrackets.so
description: The linter checks formatting of paired brackets
original-url: github.com/maratori/pairedbrackets
linters:
enable:
pairedbrackets
Run
golangci-lint run
Usage as standalone linter
Install
go install github.com/maratori/pairedbrackets@latest
Run
pairedbrackets ./...
Flag -ignore-func-calls
Flag -ignore-func-calls is a comma separated list of regexp patterns of fully qualified function calls to ignore. Default is github.com/stretchr/testify/assert,github.com/stretchr/testify/require.
Fully qualified function examples:
- Function:
github.com/stretchr/testify/require.Equal
- Method:
(*github.com/stretchr/testify/assert.Assertions).Equal
License
MIT License