Bump wsl to v1.2.7

This commit is contained in:
Simon Sawert 2019-11-10 15:00:07 +01:00 committed by Trevor Pounds
parent 1248f098de
commit 14ebae2906
11 changed files with 310 additions and 672 deletions

View File

@ -223,10 +223,12 @@ linters-settings:
allow-assign-and-call: true allow-assign-and-call: true
# Allow multiline assignments to be cuddled. Default is true. # Allow multiline assignments to be cuddled. Default is true.
allow-multiline-assign: true allow-multiline-assign: true
# Allow case blocks to end with a whitespace.
allow-case-traling-whitespace: true
# Allow declarations (var) to be cuddled. # Allow declarations (var) to be cuddled.
allow-cuddle-declarations: false allow-cuddle-declarations: false
# Allow trailing comments in ending of blocks
allow-trailing-comment: false
# Force newlines in end of case at this limit (0 = never).
force-case-trailing-whitespace: 0
linters: linters:
enable: enable:

View File

@ -824,10 +824,12 @@ linters-settings:
allow-assign-and-call: true allow-assign-and-call: true
# Allow multiline assignments to be cuddled. Default is true. # Allow multiline assignments to be cuddled. Default is true.
allow-multiline-assign: true allow-multiline-assign: true
# Allow case blocks to end with a whitespace.
allow-case-traling-whitespace: true
# Allow declarations (var) to be cuddled. # Allow declarations (var) to be cuddled.
allow-cuddle-declarations: false allow-cuddle-declarations: false
# Allow trailing comments in ending of blocks
allow-trailing-comment: false
# Force newlines in end of case at this limit (0 = never).
force-case-trailing-whitespace: 0
linters: linters:
enable: enable:

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.12
require ( require (
github.com/OpenPeeDeeP/depguard v1.0.1 github.com/OpenPeeDeeP/depguard v1.0.1
github.com/bombsimon/wsl v1.2.5 github.com/bombsimon/wsl v1.2.7
github.com/fatih/color v1.7.0 github.com/fatih/color v1.7.0
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db
github.com/go-lintpack/lintpack v0.5.2 github.com/go-lintpack/lintpack v0.5.2

4
go.sum
View File

@ -11,8 +11,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bombsimon/wsl v1.2.5 h1:9gTOkIwVtoDZywvX802SDHokeX4kW1cKnV8ZTVAPkRs= github.com/bombsimon/wsl v1.2.7 h1:hckBZYt27e6Ff6yqV/SurMUe48BmXzJk/KVjxDyWtUE=
github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= github.com/bombsimon/wsl v1.2.7/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=

View File

@ -255,11 +255,12 @@ type GocognitSettings struct {
} }
type WSLSettings struct { type WSLSettings struct {
StrictAppend bool `mapstructure:"strict-append"` StrictAppend bool `mapstructure:"strict-append"`
AllowAssignAndCallCuddle bool `mapstructure:"allow-assign-and-call"` AllowAssignAndCallCuddle bool `mapstructure:"allow-assign-and-call"`
AllowMultiLineAssignCuddle bool `mapstructure:"allow-multiline-assign"` AllowMultiLineAssignCuddle bool `mapstructure:"allow-multiline-assign"`
AllowCaseTrailingWhitespace bool `mapstructure:"allow-case-trailing-whitespace"` AllowCuddleDeclaration bool `mapstructure:"allow-cuddle-declarations"`
AllowCuddleDeclaration bool `mapstructure:"allow-cuddle-declarations"` AllowTrailingComment bool `mapstructure:"allow-trailing-comment"`
CaseForceTrailingWhitespaceLimit int `mapstructure:"force-case-trailing-whitespace:"`
} }
var defaultLintersSettings = LintersSettings{ var defaultLintersSettings = LintersSettings{
@ -291,11 +292,12 @@ var defaultLintersSettings = LintersSettings{
MinComplexity: 30, MinComplexity: 30,
}, },
WSL: WSLSettings{ WSL: WSLSettings{
StrictAppend: true, StrictAppend: true,
AllowAssignAndCallCuddle: true, AllowAssignAndCallCuddle: true,
AllowMultiLineAssignCuddle: true, AllowMultiLineAssignCuddle: true,
AllowCaseTrailingWhitespace: true, AllowCuddleDeclaration: false,
AllowCuddleDeclaration: false, AllowTrailingComment: false,
CaseForceTrailingWhitespaceLimit: 0,
}, },
} }

View File

@ -37,13 +37,14 @@ func NewWSL() *goanalysis.Linter {
files = []string{} files = []string{}
linterCfg = lintCtx.Cfg.LintersSettings.WSL linterCfg = lintCtx.Cfg.LintersSettings.WSL
processorCfg = wsl.Configuration{ processorCfg = wsl.Configuration{
StrictAppend: linterCfg.StrictAppend, StrictAppend: linterCfg.StrictAppend,
AllowAssignAndCallCuddle: linterCfg.AllowAssignAndCallCuddle, AllowAssignAndCallCuddle: linterCfg.AllowAssignAndCallCuddle,
AllowMultiLineAssignCuddle: linterCfg.AllowMultiLineAssignCuddle, AllowMultiLineAssignCuddle: linterCfg.AllowMultiLineAssignCuddle,
AllowCaseTrailingWhitespace: linterCfg.AllowCaseTrailingWhitespace, AllowCuddleDeclaration: linterCfg.AllowCuddleDeclaration,
AllowCuddleDeclaration: linterCfg.AllowCuddleDeclaration, AllowTrailingComment: linterCfg.AllowTrailingComment,
AllowCuddleWithCalls: []string{"Lock", "RLock"}, CaseForceTrailingWhitespaceLimit: linterCfg.CaseForceTrailingWhitespaceLimit,
AllowCuddleWithRHS: []string{"Unlock", "RUnlock"}, AllowCuddleWithCalls: []string{"Lock", "RLock"},
AllowCuddleWithRHS: []string{"Unlock", "RUnlock"},
} }
) )

40
test/testdata/wsl.go vendored
View File

@ -159,7 +159,7 @@ func allowTrailing(i int) {
case 2: case 2:
fmt.Println("two") fmt.Println("two")
// Comments OK too!
case 3: case 3:
fmt.Println("three") fmt.Println("three")
} }
@ -172,3 +172,41 @@ func ExampleSomeOutput() {
// Output: // Output:
// Hello, world // Hello, world
} }
func IncDecStmt() {
counter := 0
for range make([]int, 5) {
counter++
}
type t struct {
counter int
}
x := t{5}
x.counter--
if x.counter > 0 {
fmt.Println("not yet 0")
}
}
func AnonymousBlock() {
func(a, b int) { // ERROR "block should not start with a whitespace"
fmt.Println(a + b)
}(1, 1)
}
func MultilineComment() {
if true {
/*
Ok to start block with
a
long
multiline
cmoment
*/
fmt.Println("true")
}
}

View File

@ -2,9 +2,9 @@
language: go language: go
go: go:
- "1.13" - 1.13.x
- "1.12" - 1.12.x
- "1.11" - 1.11.x
env: env:
global: global:

View File

@ -21,595 +21,105 @@ might be bugs or unintentional false positives so I would love an
[issue](https://github.com/bombsimon/wsl/issues/new) to fix, discuss, change or [issue](https://github.com/bombsimon/wsl/issues/new) to fix, discuss, change or
make something configurable! make something configurable!
## Installation
### By `go get` (local installation)
You can do that by using:
```sh
go get -u github.com/bombsimon/wsl/cmd/...
```
### By golangci-lint (CI automation)
`wsl` is already integrated with
[golangci-lint](https://github.com/golangci/golangci-lint). Please refer to the
instructions there.
## Usage ## Usage
Install by using `go get -u github.com/bombsimon/wsl/cmd/...`. How to use depends on how you install `wsl`.
Run with `wsl [--no-test] <file1> [files...]` or `wsl ./package/...`. The "..." ### With local binary
wildcard is not used like other `go` commands but instead can only be to a
relative or absolute path. The general command format for `wsl` is:
```sh
$ wsl [flags] <file1> [files...]
$ wsl [flags] </path/to/package/...>
# Examples
$ wsl ./main.go
$ wsl --no-test ./main.go
$ wsl -allow-declaration ./main.go
$ wsl --no-test --allow-cuddle-declaration ./main.go
$ wsl --no-test --allow-trailing-comment ./myProject/...
```
The "..." wildcard is not used like other `go` commands but instead can only
be to a relative or absolute path.
By default, the linter will run on `./...` which means all go files in the By default, the linter will run on `./...` which means all go files in the
current path and all subsequent paths, including test files. To disable linting current path and all subsequent paths, including test files. To disable linting
test files, use `-n` or `--no-test`. test files, use `-n` or `--no-test`.
This linter can also be used as a part of ### By `golangci-lint` (CI automation)
[golangci-lint](https://github.com/golangci/golangci-lint)
## Rules The recommended command is:
Note that this linter doesn't take in consideration the issues that will be ```sh
fixed with `gofmt` so ensure that the code is properly formatted. golangci-lint --disable-all --enable wsl
### Never use empty lines
Even though this linter was built to **promote** the usage of empty lines, there
are a few places where they should never be used.
Never use empty lines in the start or end of a block!
**Don't**
```go
if someBooleanValue {
fmt.Println("i like starting newlines")
}
if someOtherBooleanValue {
fmt.Println("also trailing")
}
switch {
case 1:
fmt.Println("switch is also a block")
}
switch {
case 1:
fmt.Println("not in a case")
case 2:
fmt.Println("or at the end")
}
func neverNewlineAfterReturn() {
return true
}
func notEvenWithComments() {
return false
// I just forgot to say this...
}
``` ```
**Do** For more information, please refer to
[golangci-lint](https://github.com/golangci/golangci-lint)'s documentation.
```go
if someBooleanValue { ## Issues and configuration
fmt.Println("this is tight and nice")
} The linter suppers a few ways to configure it to satisfy more than one kind of
code style. These settings could be set either with flags or with YAML
switch { configuration if used via `golangci-lint`.
case 1:
fmt.Println("no blank lines here") The supported configuration can be found [in the documentation](doc/configuration.md).
case 2:
// Comments are fine here! Below are the available checklist for any hit from `wsl`. If you do not see any,
fmt.Println("or here") feel free to raise an [issue](https://github.com/bombsimon/wsl/issues/new).
}
> **Note**: this linter doesn't take in consideration the issues that will be
func returnCuddleded() { > fixed with `go fmt -s` so ensure that the code is properly formatted before
// My comment goes above the last statement! > use.
return true
} * [Anonymous switch statements should never be cuddled](doc/rules.md#anonymous-switch-statements-should-never-be-cuddled)
``` * [Append only allowed to cuddle with appended value](doc/rules.md#append-only-allowed-to-cuddle-with-appended-value)
* [Assignments should only be cuddled with other assignments](doc/rules.md#assignments-should-only-be-cuddled-with-other-assignments)
### Use empty lines * [Block should not end with a whitespace (or comment)](doc/rules.md#block-should-not-end-with-a-whitespace-or-comment)
* [Block should not start with a whitespace](doc/rules.md#block-should-not-start-with-a-whitespace)
There's an easy way to improve logic and readability by enforcing whitespaces at * [Case block should end with newline at this size](doc/rules.md#case-block-should-end-with-newline-at-this-size)
the right places. Usually this is around blocks and after declarations. * [Branch statements should not be cuddled if block has more than two lines](doc/rules.md#branch-statements-should-not-be-cuddled-if-block-has-more-than-two-lines)
* [Declarations should never be cuddled](doc/rules.md#declarations-should-never-be-cuddled)
#### If * [Defer statements should only be cuddled with expressions on same variable](doc/rules.md#defer-statements-should-only-be-cuddled-with-expressions-on-same-variable)
* [Expressions should not be cuddled with blocks](doc/rules.md#expressions-should-not-be-cuddled-with-blocks)
If statements should only be cuddled with assignments/declarations of variables * [Expressions should not be cuddled with declarations or returns](doc/rules.md#expressions-should-not-be-cuddled-with-declarations-or-returns)
used in the condition and only one assignment should be cuddled. Never cuddle if * [For statement without condition should never be cuddled](doc/rules.md#for-statement-without-condition-should-never-be-cuddled)
with anything but assignments. * [For statements should only be cuddled with assignments used in the iteration](doc/rules.md#for-statements-should-only-be-cuddled-with-assignments-used-in-the-iteration)
* [Go statements can only invoke functions assigned on line above](doc/rules.md#go-statements-can-only-invoke-functions-assigned-on-line-above)
**Don't** * [If statements should only be cuddled with assignments](doc/rules.md#if-statements-should-only-be-cuddled-with-assignments)
* [If statements should only be cuddled with assignments used in the if
```go statement
notConditional := "x" itself](doc/rules.md#if-statements-should-only-be-cuddled-with-assignments-used-in-the-if-statement-itself)
if somethingElse == "y" { * [Only cuddled expressions if assigning variable or using from line
fmt.Println("what am i checking?") above](doc/rules.md#only-cuddled-expressions-if-assigning-variable-or-using-from-line-above)
} * [Only one cuddle assignment allowed before defer statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-defer-statement)
* [Only one cuddle assginment allowed before for statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-for-statement)
first := 1 * [Only one cuddle assignment allowed before go statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-go-statement)
second := 2 * [Only one cuddle assignment allowed before if statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-if-statement)
third := 3 * [Only one cuddle assignment allowed before range statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-range-statement)
forever := 4 * [Only one cuddle assignment allowed before switch statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-switch-statement)
if forever { * [Only one cuddle assignment allowed before type switch statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-type-switch-statement)
return true * [Ranges should only be cuddled with assignments used in the iteration](doc/rules.md#ranges-should-only-be-cuddled-with-assignments-used-in-the-iteration)
} * [Return statements should not be cuddled if block has more than two lines](doc/rules.md#return-statements-should-not-be-cuddled-if-block-has-more-than-two-lines)
* [Stmt type not implemented](doc/rules.md#stmt-type-not-implemented)
if false { * [Switch statements should only be cuddled with variables switched](doc/rules.md#switch-statements-should-only-be-cuddled-with-variables-switched)
fmt.Println("first if") * [Type switch statements should only be cuddled with variables switched](doc/rules.md#type-switch-statements-should-only-be-cuddled-with-variables-switched)
}
if true {
fmt.Println("second if is cuddled")
}
```
**Do**
```go
val, err := SomeThing()
if err != nil {
// err is assigned on line above
}
first := 1
second := 2
third := 3
forever := 4
if forever > 3 {
return fmt.Sprintf("group multiple assignments away from if")
}
// Or separate from your condition.
first := 1
second := 2
third := 3
forever := 4
if forever > 3 {
return fmt.Sprintf("group multiple assignments away from if")
}
if false {
// This is one statement
}
if true {
// This is another one, don't cuddled them!
}
```
#### Return
Return should be placed on a separate line from other statement unless the block
consists of only two lines (including the return).
**Don't**
```go
doSomething()
add := 1+2
fmt.Sprintf(add)
return false
if true {
stmt.X = true
stmt.Y = false
return true
}
```
**Do**
```go
doSomething()
add := 1+2
fmt.Sprintf(add))
return false
if true {
stmt.X = "only one line without return, may cuddled"
return true
}
if thisWorksToo {
whitespace := true
return false
}
```
#### Branch statement
The same rule as for return
**Don't**
```go
for i := range make([]int, 5) {
if i > 2 {
sendToOne(i)
sendToSecond(i)
continue
}
}
```
**Do**
```go
for i := range make([]int, 5) {
if i > 2 {
sendToOne(i)
sendToSecond(i)
continue
}
if statement == "is short" {
sendToOne(i)
break
}
}
```
#### Assignment
Assignments may only be cuddled with other assignments.
**Don't**
```go
assignOne := "one"
callFunc(assignOne)
assignTwo := "two")
callFunc(assignTwo)
if true {
return
}
assigningClose := "bad"
var x = 2
y := 3
```
**Do**
```go
assignOne := "one"
assignTwo := "two")
callFunc(assignOne)
callFunc(assignTwo)
// Or group assignment and call by usage.
assignOne := "one"
callFunc(assignOne)
assignTwo := "two")
callFunc(assignTwo)
if true {
return
}
notAssigningClose := "not bad"
var x = 2
y := 3
```
#### Declarations
Declarations should never be cuddled with anything, not even other declarations.
**Don't**
```go
var x int
var y int
z := 2
var a
```
**Do**
```go
// Group declarations, they'll align nice and tidy!
var (
x int
y int
)
z := 2
var a
```
#### Expressions
Expressions (function calls) may never be cuddled with declarations or return
statements. Expressions may also not be cuddled with assignments if not passed
to the expression func.
**Don't**
```go
var a bool
fmt.Println(a)
foo := true
someFunc(false)
if someBool() {
fmt.Println("doing things")
}
x.Calling()
```
**Do**
```go
var b bool
fmt.Println(b)
foo := true
someFunc(foo)
bar := false
someFunc(true)
if someBool() {
fmt.Println("doing things")
}
x.Calling()
```
#### Ranges
Range statements may only be cuddled with assignments that are used in the
range. Just like if statements this only applies if it's a single assignment
cuddled and not multiple.
Ranges may also be cuddled with assignments that are used directly in the block
as first statement.
**Don't**
```go
noRangeList := []string{"a", "b", "c"}
for _, x := range anotherList {
fmt.Println(x)
}
oneList := []int{1, 2, 3}
twoList := []int{4, 5, 6}
for i := range twoList {
fmt.Println("too much assignments!")
}
myCount := 0
for _, v := range aList {
fmt.Sprintf("first statement doesn't use assignment")
}
```
**Do**
```go
rangeList := []string{"a", "b", "c"}
for _, x := range rangeList {
fmt.Println(x)
}
oneList := []int{1, 2, 3}
twoList := []int{4, 5, 6}
for i := range twoList {
fmt.Println("too much assignments!")
}
myCount := 0
for _, v := range aList {
myCount += v
fmt.Sprintf("first statement uses cuddled assignment")
}
```
#### Defer
Defer is almost handled like return statements but there are cases where
grouping defer statements with each other or expression calls may improve
readability.
Defer statements may be cuddled with other defer statements as many times as you
like. It may also be cuddled with assignments above or expression variables on
the line above.
**Don't**
```go
first := getFirst()
defer first.Close()
second := getSecond() // This will fail
defer second.Close()
first := getFirst()
second := getSecond()
defer first.Close() // Too many assignments above
defer second.Close()
m1.Lock()
defer m2.RUnlock() // Not the expression above
```
**Do**
```go
first := getFirst()
second := getSecond()
defer first.Close()
defer second.Close()
// Or group by usage.
first := getFirst()
defer first.Close()
second := getSecond()
defer second.Close()
m.Lock()
defer m.Unlock()
```
#### For loops
For statements works similar like ranges except that anonymous (infinite) loops
may never be cuddled. Just like the range statement, variables used in the for
or in first statement in the body may be cuddled.
**Don't**
```go
t := true
for notT {
fmt.Println("where's t used?")
}
n := 0
m := 1
for x < 100 {
n += x // m not used in for or body
}
n := 1
for {
fmt.Println("never cuddled for without condition")
}
```
**Do**
```go
t := true
for t {
fmt.Println("t used in for")
}
n := 0
for x < 100 {
n += x // n used in first block statement.
}
n := 1
for {
fmt.Println("never cuddled for without condition")
}
```
#### Go
Go routines may only be executed if there's a maximum of one assignments above
and that assignment is used in the expression.
**Don't**
```go
first := func() {}
second := func() {}
go second()
notUsed := func() {}
go first()
x := "1"
go func() {
fmt.Println("where's x used!=")
}()
```
**Do**
```go
first := func() {}
go first()
notUsed := func() {}
first := func() {}
go first()
```
#### Switch and Type switch
The same rules applies for switch and type switch statements with the exception
of anonymous type switches. That means type switches where a new variable is not
assigned. It's also allowed to cuddled type switches with variables used if it's
used as the first argument in the first case.
Type switches may only be cuddled with one assignment above and if that
assignment is used in the switch.
**Don't**
```go
notSome := SomeInt()
switch some {
case 1:
fmt.Println("1")
default:
fmt.Println("not 1")
}
notSwitched := SomeInt()
switch {
case 1 > 2:
fmt.Println("whitespace between assignments")
}
n := 0
switch v := some.(type):
case typeOne:
x := v.X // n not used in switch or body
case typeTwo:
x := v.X
}
```
**Do**
```go
some := SomeInt()
switch some {
case 1:
fmt.Println("1")
default:
fmt.Println("not 1")
}
notSwitched := SomeInt()
switch {
case 1 > 2:
fmt.Println("whitespace between assignments")
}
n := 0
switch v := some.(type):
case typeOne:
n = v.X // n is used first in block, OK to cuddle
case typeTwo:
n = v.Y
}
```

View File

@ -50,21 +50,12 @@ type Configuration struct {
// } // }
AllowMultiLineAssignCuddle bool AllowMultiLineAssignCuddle bool
// AllowCaseTrailingWhitespace will allow case blocks to end with a // If the number of lines in a case block is equal to or lager than this
// whitespace. Sometimes this might actually improve readability. This // number, the case *must* end white a newline.
// defaults to false but setting it to true will enable the following CaseForceTrailingWhitespaceLimit int
// example:
// switch { // AllowTrailingComment will allow blocks to end with comments.
// case 1: AllowTrailingComment bool
// fmt.Println(1)
//
// case 2:
// fmt.Println(2)
//
// case 3:
// fmt:println(3)
// }
AllowCaseTrailingWhitespace bool
// AllowCuddleDeclaration will allow multiple var/declaration statements to // AllowCuddleDeclaration will allow multiple var/declaration statements to
// be cuddled. This defaults to false but setting it to true will enable the // be cuddled. This defaults to false but setting it to true will enable the
@ -93,12 +84,13 @@ type Configuration struct {
// DefaultConfig returns default configuration // DefaultConfig returns default configuration
func DefaultConfig() Configuration { func DefaultConfig() Configuration {
return Configuration{ return Configuration{
StrictAppend: true, StrictAppend: true,
AllowAssignAndCallCuddle: true, AllowAssignAndCallCuddle: true,
AllowMultiLineAssignCuddle: true, AllowMultiLineAssignCuddle: true,
AllowCaseTrailingWhitespace: false, AllowTrailingComment: false,
AllowCuddleWithCalls: []string{"Lock", "RLock"}, CaseForceTrailingWhitespaceLimit: 0,
AllowCuddleWithRHS: []string{"Unlock", "RUnlock"}, AllowCuddleWithCalls: []string{"Lock", "RLock"},
AllowCuddleWithRHS: []string{"Unlock", "RUnlock"},
} }
} }
@ -203,14 +195,12 @@ func (p *Processor) parseBlockBody(ident *ast.Ident, block *ast.BlockStmt) {
// nolint: gocognit // nolint: gocognit
func (p *Processor) parseBlockStatements(statements []ast.Stmt) { func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
for i, stmt := range statements { for i, stmt := range statements {
// TODO: How to tell when and where func literals may exist to enforce // Start by checking if this statement is another block (other than if,
// linting. // for and range). This could be assignment to a function, defer or go
if as, isAssignStmt := stmt.(*ast.AssignStmt); isAssignStmt { // call with an inline function or similar. If this is found we start by
for _, rhs := range as.Rhs { // parsing this body block before moving on.
if fl, isFuncLit := rhs.(*ast.FuncLit); isFuncLit { for _, stmtBlocks := range p.findBlockStmt(stmt) {
p.parseBlockBody(nil, fl.Body) p.parseBlockBody(nil, stmtBlocks)
}
}
} }
firstBodyStatement := p.firstBodyStatement(i, statements) firstBodyStatement := p.firstBodyStatement(i, statements)
@ -322,11 +312,15 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
continue continue
} }
if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) { if atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
if !atLeastOneInListsMatch(assignedOnLineAbove, assignedFirstInBlock) { continue
p.addError(t.Pos(), "if statements should only be cuddled with assignments used in the if statement itself")
}
} }
if atLeastOneInListsMatch(assignedOnLineAbove, assignedFirstInBlock) {
continue
}
p.addError(t.Pos(), "if statements should only be cuddled with assignments used in the if statement itself")
case *ast.ReturnStmt: case *ast.ReturnStmt:
if isLastStatementInBlockOfOnlyTwoLines() { if isLastStatementInBlockOfOnlyTwoLines() {
continue continue
@ -473,6 +467,10 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
} }
} }
case *ast.GoStmt: case *ast.GoStmt:
if _, ok := previousStatement.(*ast.GoStmt); ok {
continue
}
if moreThanOneStatementAbove() { if moreThanOneStatementAbove() {
p.addError(t.Pos(), "only one cuddle assignment allowed before go statement") p.addError(t.Pos(), "only one cuddle assignment allowed before go statement")
@ -595,9 +593,10 @@ func (p *Processor) findLHS(node ast.Node) []string {
*ast.ReturnStmt, *ast.GoStmt, *ast.CaseClause, *ast.ReturnStmt, *ast.GoStmt, *ast.CaseClause,
*ast.CommClause, *ast.CallExpr, *ast.UnaryExpr, *ast.CommClause, *ast.CallExpr, *ast.UnaryExpr,
*ast.BranchStmt, *ast.TypeSpec, *ast.ChanType, *ast.BranchStmt, *ast.TypeSpec, *ast.ChanType,
*ast.DeferStmt, *ast.TypeAssertExpr, *ast.IncDecStmt, *ast.DeferStmt, *ast.TypeAssertExpr, *ast.RangeStmt:
*ast.RangeStmt: // Nothing to add to LHS
// Nothing to add to LHS case *ast.IncDecStmt:
return p.findLHS(t.X)
case *ast.Ident: case *ast.Ident:
return []string{t.Name} return []string{t.Name}
case *ast.AssignStmt: case *ast.AssignStmt:
@ -722,6 +721,33 @@ func (p *Processor) findRHS(node ast.Node) []string {
return rhs return rhs
} }
func (p *Processor) findBlockStmt(node ast.Node) []*ast.BlockStmt {
var blocks []*ast.BlockStmt
switch t := node.(type) {
case *ast.AssignStmt:
for _, x := range t.Rhs {
blocks = append(blocks, p.findBlockStmt(x)...)
}
case *ast.CallExpr:
blocks = append(blocks, p.findBlockStmt(t.Fun)...)
case *ast.FuncLit:
blocks = append(blocks, t.Body)
case *ast.ExprStmt:
blocks = append(blocks, p.findBlockStmt(t.X)...)
case *ast.ReturnStmt:
for _, x := range t.Results {
blocks = append(blocks, p.findBlockStmt(x)...)
}
case *ast.DeferStmt:
blocks = append(blocks, p.findBlockStmt(t.Call)...)
case *ast.GoStmt:
blocks = append(blocks, p.findBlockStmt(t.Call)...)
}
return blocks
}
// maybeX extracts the X field from an AST node and returns it with a true value // maybeX extracts the X field from an AST node and returns it with a true value
// if it exists. If the node doesn't have an X field nil and false is returned. // if it exists. If the node doesn't have an X field nil and false is returned.
// Known fields with X that are handled: // Known fields with X that are handled:
@ -839,7 +865,10 @@ func (p *Processor) findLeadingAndTrailingWhitespaces(ident *ast.Ident, stmt, ne
break break
} }
allowedLinesBeforeFirstStatement += len(commentGroup.List) // Support both /* multiline */ and //single line comments
for _, c := range commentGroup.List {
allowedLinesBeforeFirstStatement += len(strings.Split(c.Text, "\n"))
}
} }
} }
@ -850,38 +879,92 @@ func (p *Processor) findLeadingAndTrailingWhitespaces(ident *ast.Ident, stmt, ne
) )
} }
// If the blockEndLine is 0 we're a case clause. If we don't have any // If the blockEndLine is not 0 we're a regular block (not case).
// nextStatement the trailing whitespace will be handled when parsing the if blockEndLine != 0 {
// switch. If we do have a next statement we can see where it starts by if p.config.AllowTrailingComment {
// getting it's colon position. if lastComment, ok := commentMap[lastStatement]; ok {
if blockEndLine == 0 { var (
if nextStatement == nil { lastCommentGroup = lastComment[len(lastComment)-1]
return lastCommentLine = lastCommentGroup.List[len(lastCommentGroup.List)-1]
countNewlines = 0
)
countNewlines += len(strings.Split(lastCommentLine.Text, "\n"))
// No newlines between trailing comments and end of block.
if p.nodeStart(lastCommentLine)+countNewlines != blockEndLine-1 {
return
}
}
} }
// If we allow case to end white whitespace just return. if p.nodeEnd(lastStatement) != blockEndLine-1 && !isExampleFunc(ident) {
if p.config.AllowCaseTrailingWhitespace { p.addError(blockEndPos, "block should not end with a whitespace (or comment)")
return
} }
switch n := nextStatement.(type) { return
case *ast.CaseClause:
blockEndPos = n.Case
case *ast.CommClause:
blockEndPos = n.Case
default:
// We're not at the end of the case?
return
}
blockEndLine = p.fileSet.Position(blockEndPos).Line
} }
if p.nodeEnd(lastStatement) != blockEndLine-1 && !isExampleFunc(ident) { // If we don't have any nextStatement the trailing whitespace will be
p.addError( // handled when parsing the switch. If we do have a next statement we can
blockEndPos, // see where it starts by getting it's colon position. We set the end of the
"block should not end with a whitespace (or comment)", // current case to the position of the next case.
) switch n := nextStatement.(type) {
case *ast.CaseClause:
blockEndPos = n.Case
case *ast.CommClause:
blockEndPos = n.Case
default:
// No more cases
return
}
blockEndLine = p.fileSet.Position(blockEndPos).Line - 1
var (
blockSize = blockEndLine - blockStartLine
caseTrailingCommentLines int
)
// TODO: I don't know what comments are bound to in cases. For regular
// blocks the last comment is bound to the last statement but for cases
// they are bound to the case clause expression. This will however get us all
// comments and depending on the case expression this gets tricky.
//
// To handle this I get the comment map from the current statement (the case
// itself) and iterate through all groups and all comment within all groups.
// I then get the comments after the last statement but before the next case
// clause and just map each line of comment that way.
for _, commentGroups := range commentMap {
for _, commentGroup := range commentGroups {
for _, comment := range commentGroup.List {
commentLine := p.fileSet.Position(comment.Pos()).Line
// Ignore comments before the last statement.
if commentLine <= p.nodeStart(lastStatement) {
continue
}
// Ignore comments after the end of this case.
if commentLine > blockEndLine {
continue
}
// This allows /* multiline */ comments with newlines as well
// as regular (//) ones
caseTrailingCommentLines += len(strings.Split(comment.Text, "\n"))
}
}
}
hasTrailingWhitespace := p.nodeEnd(lastStatement)+caseTrailingCommentLines != blockEndLine
// If the force trailing limit is configured and we don't end with a newline.
if p.config.CaseForceTrailingWhitespaceLimit > 0 && !hasTrailingWhitespace {
// Check if the block size is too big to miss the newline.
if blockSize >= p.config.CaseForceTrailingWhitespaceLimit {
p.addError(lastStatement.Pos(), "case block should end with newline at this size")
}
} }
} }

2
vendor/modules.txt vendored
View File

@ -4,7 +4,7 @@ github.com/BurntSushi/toml
github.com/OpenPeeDeeP/depguard github.com/OpenPeeDeeP/depguard
# github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 # github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6
github.com/StackExchange/wmi github.com/StackExchange/wmi
# github.com/bombsimon/wsl v1.2.5 # github.com/bombsimon/wsl v1.2.7
github.com/bombsimon/wsl github.com/bombsimon/wsl
# github.com/davecgh/go-spew v1.1.1 # github.com/davecgh/go-spew v1.1.1
github.com/davecgh/go-spew/spew github.com/davecgh/go-spew/spew