aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/bombsimon
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2025-01-22 16:07:17 +0100
committerTaras Madan <tarasmadan@google.com>2025-01-23 10:42:36 +0000
commit7b4377ad9d8a7205416df8d6217ef2b010f89481 (patch)
treee6fec4fd12ff807a16d847923f501075bf71d16c /vendor/github.com/bombsimon
parent475a4c203afb8b7d3af51c4fd32bb170ff32a45e (diff)
vendor: delete
Diffstat (limited to 'vendor/github.com/bombsimon')
-rw-r--r--vendor/github.com/bombsimon/wsl/v4/.gitignore72
-rw-r--r--vendor/github.com/bombsimon/wsl/v4/.golangci.yml80
-rw-r--r--vendor/github.com/bombsimon/wsl/v4/LICENSE21
-rw-r--r--vendor/github.com/bombsimon/wsl/v4/README.md98
-rw-r--r--vendor/github.com/bombsimon/wsl/v4/analyzer.go148
-rw-r--r--vendor/github.com/bombsimon/wsl/v4/wsl.go1416
6 files changed, 0 insertions, 1835 deletions
diff --git a/vendor/github.com/bombsimon/wsl/v4/.gitignore b/vendor/github.com/bombsimon/wsl/v4/.gitignore
deleted file mode 100644
index b37c69481..000000000
--- a/vendor/github.com/bombsimon/wsl/v4/.gitignore
+++ /dev/null
@@ -1,72 +0,0 @@
-
-# Created by https://www.gitignore.io/api/go,vim,macos
-
-### Go ###
-# Binaries for programs and plugins
-*.exe
-*.exe~
-*.dll
-*.so
-*.dylib
-
-# Test binary, build with `go test -c`
-*.test
-
-# Output of the go coverage tool, specifically when used with LiteIDE
-*.out
-
-### Go Patch ###
-/vendor/
-/Godeps/
-
-### macOS ###
-# General
-.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
-Icon
-
-# Thumbnails
-._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-.com.apple.timemachine.donotpresent
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-
-### Vim ###
-# Swap
-[._]*.s[a-v][a-z]
-[._]*.sw[a-p]
-[._]s[a-rt-v][a-z]
-[._]ss[a-gi-z]
-[._]sw[a-p]
-
-# Session
-Session.vim
-
-# Temporary
-.netrwhist
-*~
-# Auto-generated tag files
-tags
-# Persistent undo
-[._]*.un~
-
-
-# End of https://www.gitignore.io/api/go,vim,macos
-
-.idea/
diff --git a/vendor/github.com/bombsimon/wsl/v4/.golangci.yml b/vendor/github.com/bombsimon/wsl/v4/.golangci.yml
deleted file mode 100644
index bde0ae54e..000000000
--- a/vendor/github.com/bombsimon/wsl/v4/.golangci.yml
+++ /dev/null
@@ -1,80 +0,0 @@
----
-run:
- timeout: 1m
- issues-exit-code: 1
- tests: true
-
-output:
- print-issued-lines: false
- sort-results: true
- formats:
- - format: colored-line-number
-
-linters-settings:
- gocognit:
- min-complexity: 10
-
- depguard:
- rules:
- main:
- deny:
- - pkg: "github.com/davecgh/go-spew/spew"
- desc: not allowed
-
- misspell:
- locale: US
-
- gocritic:
- # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run`
- # to see all tags and checks. Empty list by default. See
- # https://github.com/go-critic/go-critic#usage -> section "Tags".
- enabled-tags:
- - diagnostic
- - experimental
- - opinionated
- - performance
- - style
-
-linters:
- enable-all: true
- disable:
- - cyclop
- - deadcode
- - depguard
- - dupl
- - dupword
- - exhaustivestruct
- - exhaustruct
- - forbidigo
- - funlen
- - gci
- - gocognit
- - gocyclo
- - godox
- - golint
- - gomnd
- - ifshort
- - interfacer
- - lll
- - maintidx
- - maligned
- - nakedret
- - nestif
- - nlreturn
- - nosnakecase
- - paralleltest
- - prealloc
- - rowserrcheck
- - scopelint
- - structcheck
- - testpackage
- - varcheck
- - varnamelen
- - wastedassign
-
-issues:
- exclude-use-default: true
- max-issues-per-linter: 0
- max-same-issues: 0
-
-# vim: set sw=2 ts=2 et:
diff --git a/vendor/github.com/bombsimon/wsl/v4/LICENSE b/vendor/github.com/bombsimon/wsl/v4/LICENSE
deleted file mode 100644
index 4dade6d1c..000000000
--- a/vendor/github.com/bombsimon/wsl/v4/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2018 Simon Sawert
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/bombsimon/wsl/v4/README.md b/vendor/github.com/bombsimon/wsl/v4/README.md
deleted file mode 100644
index c9c42341e..000000000
--- a/vendor/github.com/bombsimon/wsl/v4/README.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# wsl - Whitespace Linter
-
-[![forthebadge](https://forthebadge.com/images/badges/made-with-go.svg)](https://forthebadge.com)
-[![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)](https://forthebadge.com)
-
-[![GitHub Actions](https://github.com/bombsimon/wsl/actions/workflows/go.yml/badge.svg)](https://github.com/bombsimon/wsl/actions/workflows/go.yml)
-[![Coverage Status](https://coveralls.io/repos/github/bombsimon/wsl/badge.svg?branch=master)](https://coveralls.io/github/bombsimon/wsl?branch=master)
-
-`wsl` is a linter that enforces a very **non scientific** vision of how to make
-code more readable by enforcing empty lines at the right places.
-
-**This linter is aggressive** and a lot of projects I've tested it on have
-failed miserably. For this linter to be useful at all I want to be open to new
-ideas, configurations and discussions! Also note that some of the warnings 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
-make something configurable!
-
-## Installation
-
-```sh
-# Latest release
-go install github.com/bombsimon/wsl/v4/cmd/wsl@latest
-
-# Main branch
-go install github.com/bombsimon/wsl/v4/cmd/wsl@master
-```
-
-## Usage
-
-> **Note**: This linter provides a fixer that can fix most issues with the
-> `--fix` flag. However, currently `golangci-lint` [does not support suggested
-> fixes](https://github.com/golangci/golangci-lint/issues/1779) so the `--fix`
-> flag in `golangci-lint` will **not** work.
-
-`wsl` uses the [analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis)
-package meaning it will operate on package level with the default analysis flags
-and way of working.
-
-```sh
-wsl --help
-wsl [flags] </path/to/package/...>
-
-wsl --allow-cuddle-declarations --fix ./...
-```
-
-`wsl` is also integrated in [`golangci-lint`](https://golangci-lint.run)
-
-```sh
-golangci-lint run --no-config --disable-all --enable wsl
-```
-
-## Issues and configuration
-
-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
-configuration if used via `golangci-lint`.
-
-The supported configuration can be found [in the
-documentation](doc/configuration.md).
-
-Below are the available checklist for any hit from `wsl`. If you do not see any,
-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
-> fixed with `go fmt -s` so ensure that the code is properly formatted before
-> use.
-
-* [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)
-* [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)
-* [Case block should end with newline at this size](doc/rules.md#case-block-should-end-with-newline-at-this-size)
-* [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)
-* [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)
-* [Expressions should not be cuddled with declarations or returns](doc/rules.md#expressions-should-not-be-cuddled-with-declarations-or-returns)
-* [For statement without condition should never be cuddled](doc/rules.md#for-statement-without-condition-should-never-be-cuddled)
-* [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)
-* [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 statement itself](doc/rules.md#if-statements-should-only-be-cuddled-with-assignments-used-in-the-if-statement-itself)
-* [If statements that check an error must be cuddled with the statement that assigned the error](doc/rules.md#if-statements-that-check-an-error-must-be-cuddled-with-the-statement-that-assigned-the-error)
-* [Only cuddled expressions if assigning variable or using from line 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 assignment allowed before for statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-for-statement)
-* [Only one cuddle assignment allowed before go statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-go-statement)
-* [Only one cuddle assignment allowed before if statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-if-statement)
-* [Only one cuddle assignment allowed before range statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-range-statement)
-* [Only one cuddle assignment allowed before switch statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-switch-statement)
-* [Only one cuddle assignment allowed before type switch statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-type-switch-statement)
-* [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)
-* [Short declarations should cuddle only with other short declarations](doc/rules.md#short-declaration-should-cuddle-only-with-other-short-declarations)
-* [Switch statements should only be cuddled with variables switched](doc/rules.md#switch-statements-should-only-be-cuddled-with-variables-switched)
-* [Type switch statements should only be cuddled with variables switched](doc/rules.md#type-switch-statements-should-only-be-cuddled-with-variables-switched)
diff --git a/vendor/github.com/bombsimon/wsl/v4/analyzer.go b/vendor/github.com/bombsimon/wsl/v4/analyzer.go
deleted file mode 100644
index 46d5019a7..000000000
--- a/vendor/github.com/bombsimon/wsl/v4/analyzer.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package wsl
-
-import (
- "flag"
- "go/ast"
- "strings"
-
- "golang.org/x/tools/go/analysis"
-)
-
-func NewAnalyzer(config *Configuration) *analysis.Analyzer {
- wa := &wslAnalyzer{config: config}
-
- return &analysis.Analyzer{
- Name: "wsl",
- Doc: "add or remove empty lines",
- Flags: wa.flags(),
- Run: wa.run,
- RunDespiteErrors: true,
- }
-}
-
-func defaultConfig() *Configuration {
- return &Configuration{
- AllowAssignAndAnythingCuddle: false,
- AllowAssignAndCallCuddle: true,
- AllowCuddleDeclaration: false,
- AllowMultiLineAssignCuddle: true,
- AllowSeparatedLeadingComment: false,
- AllowTrailingComment: false,
- ForceCuddleErrCheckAndAssign: false,
- ForceExclusiveShortDeclarations: false,
- StrictAppend: true,
- IncludeGenerated: false,
- AllowCuddleWithCalls: []string{"Lock", "RLock"},
- AllowCuddleWithRHS: []string{"Unlock", "RUnlock"},
- ErrorVariableNames: []string{"err"},
- ForceCaseTrailingWhitespaceLimit: 0,
- }
-}
-
-// wslAnalyzer is a wrapper around the configuration which is used to be able to
-// set the configuration when creating the analyzer and later be able to update
-// flags and running method.
-type wslAnalyzer struct {
- config *Configuration
-}
-
-func (wa *wslAnalyzer) flags() flag.FlagSet {
- flags := flag.NewFlagSet("", flag.ExitOnError)
-
- // If we have a configuration set we're not running from the command line so
- // we don't use any flags.
- if wa.config != nil {
- return *flags
- }
-
- wa.config = defaultConfig()
-
- flags.BoolVar(&wa.config.AllowAssignAndAnythingCuddle, "allow-assign-and-anything", false, "Allow assignments and anything to be cuddled")
- flags.BoolVar(&wa.config.AllowAssignAndCallCuddle, "allow-assign-and-call", true, "Allow assignments and calls to be cuddled (if using same variable/type)")
- flags.BoolVar(&wa.config.AllowCuddleDeclaration, "allow-cuddle-declarations", false, "Allow declarations to be cuddled")
- flags.BoolVar(&wa.config.AllowMultiLineAssignCuddle, "allow-multi-line-assign", true, "Allow cuddling with multi line assignments")
- flags.BoolVar(&wa.config.AllowSeparatedLeadingComment, "allow-separated-leading-comment", false, "Allow empty newlines in leading comments")
- flags.BoolVar(&wa.config.AllowTrailingComment, "allow-trailing-comment", false, "Allow blocks to end with a comment")
- flags.BoolVar(&wa.config.ForceCuddleErrCheckAndAssign, "force-err-cuddling", false, "Force cuddling of error checks with error var assignment")
- flags.BoolVar(&wa.config.ForceExclusiveShortDeclarations, "force-short-decl-cuddling", false, "Force short declarations to cuddle by themselves")
- flags.BoolVar(&wa.config.StrictAppend, "strict-append", true, "Strict rules for append")
- flags.BoolVar(&wa.config.IncludeGenerated, "include-generated", false, "Include generated files")
- flags.IntVar(&wa.config.ForceCaseTrailingWhitespaceLimit, "force-case-trailing-whitespace", 0, "Force newlines for case blocks > this number.")
-
- flags.Var(&multiStringValue{slicePtr: &wa.config.AllowCuddleWithCalls}, "allow-cuddle-with-calls", "Comma separated list of idents that can have cuddles after")
- flags.Var(&multiStringValue{slicePtr: &wa.config.AllowCuddleWithRHS}, "allow-cuddle-with-rhs", "Comma separated list of idents that can have cuddles before")
- flags.Var(&multiStringValue{slicePtr: &wa.config.ErrorVariableNames}, "error-variable-names", "Comma separated list of error variable names")
-
- return *flags
-}
-
-func (wa *wslAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
- for _, file := range pass.Files {
- if !wa.config.IncludeGenerated && ast.IsGenerated(file) {
- continue
- }
-
- filename := pass.Fset.PositionFor(file.Pos(), false).Filename
- if !strings.HasSuffix(filename, ".go") {
- continue
- }
-
- processor := newProcessorWithConfig(file, pass.Fset, wa.config)
- processor.parseAST()
-
- for pos, fix := range processor.result {
- textEdits := []analysis.TextEdit{}
- for _, f := range fix.fixRanges {
- textEdits = append(textEdits, analysis.TextEdit{
- Pos: f.fixRangeStart,
- End: f.fixRangeEnd,
- NewText: []byte("\n"),
- })
- }
-
- pass.Report(analysis.Diagnostic{
- Pos: pos,
- Category: "whitespace",
- Message: fix.reason,
- SuggestedFixes: []analysis.SuggestedFix{
- {
- TextEdits: textEdits,
- },
- },
- })
- }
- }
-
- //nolint:nilnil // A pass don't need to return anything.
- return nil, nil
-}
-
-// multiStringValue is a flag that supports multiple values. It's implemented to
-// contain a pointer to a string slice that will be overwritten when the flag's
-// `Set` method is called.
-type multiStringValue struct {
- slicePtr *[]string
-}
-
-// Set implements the flag.Value interface and will overwrite the pointer to the
-// slice with a new pointer after splitting the flag by comma.
-func (m *multiStringValue) Set(value string) error {
- s := []string{}
-
- for _, v := range strings.Split(value, ",") {
- s = append(s, strings.TrimSpace(v))
- }
-
- *m.slicePtr = s
-
- return nil
-}
-
-// Set implements the flag.Value interface.
-func (m *multiStringValue) String() string {
- if m.slicePtr == nil {
- return ""
- }
-
- return strings.Join(*m.slicePtr, ", ")
-}
diff --git a/vendor/github.com/bombsimon/wsl/v4/wsl.go b/vendor/github.com/bombsimon/wsl/v4/wsl.go
deleted file mode 100644
index 76f4abf61..000000000
--- a/vendor/github.com/bombsimon/wsl/v4/wsl.go
+++ /dev/null
@@ -1,1416 +0,0 @@
-package wsl
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "reflect"
- "sort"
- "strings"
-)
-
-// Error reason strings.
-const (
- reasonAnonSwitchCuddled = "anonymous switch statements should never be cuddled"
- reasonAppendCuddledWithoutUse = "append only allowed to cuddle with appended value"
- reasonAssignsCuddleAssign = "assignments should only be cuddled with other assignments"
- reasonBlockEndsWithWS = "block should not end with a whitespace (or comment)"
- reasonBlockStartsWithWS = "block should not start with a whitespace"
- reasonCaseBlockTooCuddly = "case block should end with newline at this size"
- reasonDeferCuddledWithOtherVar = "defer statements should only be cuddled with expressions on same variable"
- reasonExprCuddlingNonAssignedVar = "only cuddled expressions if assigning variable or using from line above"
- reasonExpressionCuddledWithBlock = "expressions should not be cuddled with blocks"
- reasonExpressionCuddledWithDeclOrRet = "expressions should not be cuddled with declarations or returns"
- reasonForCuddledAssignWithoutUse = "for statements should only be cuddled with assignments used in the iteration"
- reasonForWithoutCondition = "for statement without condition should never be cuddled"
- reasonGoFuncWithoutAssign = "go statements can only invoke functions assigned on line above"
- reasonMultiLineBranchCuddle = "branch statements should not be cuddled if block has more than two lines"
- reasonMustCuddleErrCheck = "if statements that check an error must be cuddled with the statement that assigned the error"
- reasonNeverCuddleDeclare = "declarations should never be cuddled"
- reasonOnlyCuddle2LineReturn = "return statements should not be cuddled if block has more than two lines"
- reasonOnlyCuddleIfWithAssign = "if statements should only be cuddled with assignments"
- reasonOnlyCuddleWithUsedAssign = "if statements should only be cuddled with assignments used in the if statement itself"
- reasonOnlyOneCuddleBeforeDefer = "only one cuddle assignment allowed before defer statement"
- reasonOnlyOneCuddleBeforeFor = "only one cuddle assignment allowed before for statement"
- reasonOnlyOneCuddleBeforeGo = "only one cuddle assignment allowed before go statement"
- reasonOnlyOneCuddleBeforeIf = "only one cuddle assignment allowed before if statement"
- reasonOnlyOneCuddleBeforeRange = "only one cuddle assignment allowed before range statement"
- reasonOnlyOneCuddleBeforeSwitch = "only one cuddle assignment allowed before switch statement"
- reasonOnlyOneCuddleBeforeTypeSwitch = "only one cuddle assignment allowed before type switch statement"
- reasonRangeCuddledWithoutUse = "ranges should only be cuddled with assignments used in the iteration"
- reasonShortDeclNotExclusive = "short declaration should cuddle only with other short declarations"
- reasonSwitchCuddledWithoutUse = "switch statements should only be cuddled with variables switched"
- reasonTypeSwitchCuddledWithoutUse = "type switch statements should only be cuddled with variables switched"
-)
-
-// Warning strings.
-const (
- warnTypeNotImplement = "type not implemented"
- warnStmtNotImplemented = "stmt type not implemented"
- warnBodyStmtTypeNotImplemented = "body statement type not implemented "
- warnWSNodeTypeNotImplemented = "whitespace node type not implemented "
- warnUnknownLHS = "UNKNOWN LHS"
- warnUnknownRHS = "UNKNOWN RHS"
-)
-
-// Configuration represents configurable settings for the linter.
-type Configuration struct {
- // StrictAppend will do strict checking when assigning from append (x =
- // append(x, y)). If this is set to true the append call must append either
- // a variable assigned, called or used on the line above. Example on not
- // allowed when this is true:
- //
- // x := []string{}
- // y := "not going in X"
- // x = append(x, "not y") // This is not allowed with StrictAppend
- // z := "going in X"
- //
- // x = append(x, z) // This is allowed with StrictAppend
- //
- // m := transform(z)
- // x = append(x, z) // So is this because Z is used above.
- StrictAppend bool
-
- // AllowAssignAndCallCuddle allows assignments to be cuddled with variables
- // used in calls on line above and calls to be cuddled with assignments of
- // variables used in call on line above.
- // Example supported with this set to true:
- //
- // x.Call()
- // x = Assign()
- // x.AnotherCall()
- // x = AnotherAssign()
- AllowAssignAndCallCuddle bool
-
- // AllowAssignAndAnythingCuddle allows assignments to be cuddled with anything.
- // Example supported with this set to true:
- // if x == 1 {
- // x = 0
- // }
- // z := x + 2
- // fmt.Println("x")
- // y := "x"
- AllowAssignAndAnythingCuddle bool
-
- // AllowMultiLineAssignCuddle allows cuddling to assignments even if they
- // span over multiple lines. This defaults to true which allows the
- // following example:
- //
- // err := function(
- // "multiple", "lines",
- // )
- // if err != nil {
- // // ...
- // }
- AllowMultiLineAssignCuddle bool
-
- // If the number of lines in a case block is equal to or lager than this
- // number, the case *must* end white a newline.
- ForceCaseTrailingWhitespaceLimit int
-
- // AllowTrailingComment will allow blocks to end with comments.
- AllowTrailingComment bool
-
- // AllowSeparatedLeadingComment will allow multiple comments in the
- // beginning of a block separated with newline. Example:
- // func () {
- // // Comment one
- //
- // // Comment two
- // fmt.Println("x")
- // }
- AllowSeparatedLeadingComment bool
-
- // AllowCuddleDeclaration will allow multiple var/declaration statements to
- // be cuddled. This defaults to false but setting it to true will enable the
- // following example:
- // var foo bool
- // var err error
- AllowCuddleDeclaration bool
-
- // AllowCuddleWithCalls is a list of call idents that everything can be
- // cuddled with. Defaults to calls looking like locks to support a flow like
- // this:
- //
- // mu.Lock()
- // allow := thisAssignment
- AllowCuddleWithCalls []string
-
- // AllowCuddleWithRHS is a list of right hand side variables that is allowed
- // to be cuddled with anything. Defaults to assignments or calls looking
- // like unlocks to support a flow like this:
- //
- // allow := thisAssignment()
- // mu.Unlock()
- AllowCuddleWithRHS []string
-
- // ForceCuddleErrCheckAndAssign will cause an error when an If statement that
- // checks an error variable doesn't cuddle with the assignment of that variable.
- // This defaults to false but setting it to true will cause the following
- // to generate an error:
- //
- // err := ProduceError()
- //
- // if err != nil {
- // return err
- // }
- ForceCuddleErrCheckAndAssign bool
-
- // When ForceCuddleErrCheckAndAssign is enabled this is a list of names
- // used for error variables to check for in the conditional.
- // Defaults to just "err"
- ErrorVariableNames []string
-
- // ForceExclusiveShortDeclarations will cause an error if a short declaration
- // (:=) cuddles with anything other than another short declaration. For example
- //
- // a := 2
- // b := 3
- //
- // is allowed, but
- //
- // a := 2
- // b = 3
- //
- // is not allowed. This logic overrides ForceCuddleErrCheckAndAssign among others.
- ForceExclusiveShortDeclarations bool
-
- // IncludeGenerated will include generated files in the analysis and report
- // errors even for generated files. Can be useful when developing
- // generators.
- IncludeGenerated bool
-}
-
-// fix is a range to fixup.
-type fix struct {
- fixRangeStart token.Pos
- fixRangeEnd token.Pos
-}
-
-// result represents the result of one error.
-type result struct {
- fixRanges []fix
- reason string
-}
-
-// processor is the type that keeps track of the file and fileset and holds the
-// results from parsing the AST.
-type processor struct {
- config *Configuration
- file *ast.File
- fileSet *token.FileSet
- result map[token.Pos]result
- warnings []string
-}
-
-// newProcessorWithConfig will create a Processor with the passed configuration.
-func newProcessorWithConfig(file *ast.File, fileSet *token.FileSet, cfg *Configuration) *processor {
- return &processor{
- config: cfg,
- file: file,
- fileSet: fileSet,
- result: make(map[token.Pos]result),
- }
-}
-
-// parseAST will parse the AST attached to the Processor instance.
-func (p *processor) parseAST() {
- for _, d := range p.file.Decls {
- switch v := d.(type) {
- case *ast.FuncDecl:
- p.parseBlockBody(v.Name, v.Body)
- case *ast.GenDecl:
- // `go fmt` will handle proper spacing for GenDecl such as imports,
- // constants etc.
- default:
- p.addWarning(warnTypeNotImplement, d.Pos(), v)
- }
- }
-}
-
-// parseBlockBody will parse any kind of block statements such as switch cases
-// and if statements. A list of Result is returned.
-func (p *processor) parseBlockBody(ident *ast.Ident, block *ast.BlockStmt) {
- // Nothing to do if there's no value.
- if reflect.ValueOf(block).IsNil() {
- return
- }
-
- // Start by finding leading and trailing whitespaces.
- p.findLeadingAndTrailingWhitespaces(ident, block, nil)
-
- // Parse the block body contents.
- p.parseBlockStatements(block.List)
-}
-
-// parseBlockStatements will parse all the statements found in the body of a
-// node. A list of Result is returned.
-func (p *processor) parseBlockStatements(statements []ast.Stmt) {
- for i, stmt := range statements {
- // Start by checking if this statement is another block (other than if,
- // for and range). This could be assignment to a function, defer or go
- // call with an inline function or similar. If this is found we start by
- // parsing this body block before moving on.
- for _, stmtBlocks := range p.findBlockStmt(stmt) {
- p.parseBlockBody(nil, stmtBlocks)
- }
-
- firstBodyStatement := p.firstBodyStatement(i, statements)
-
- // First statement, nothing to do.
- if i == 0 {
- continue
- }
-
- previousStatement := statements[i-1]
- previousStatementIsMultiline := p.nodeStart(previousStatement) != p.nodeEnd(previousStatement)
- cuddledWithLastStmt := p.nodeEnd(previousStatement) == p.nodeStart(stmt)-1
-
- // If we're not cuddled and we don't need to enforce err-check cuddling
- // then we can bail out here
- if !cuddledWithLastStmt && !p.config.ForceCuddleErrCheckAndAssign {
- continue
- }
-
- // We don't force error cuddling for multilines. (#86)
- if p.config.ForceCuddleErrCheckAndAssign && previousStatementIsMultiline && !cuddledWithLastStmt {
- continue
- }
-
- // Extract assigned variables on the line above
- // which is the only thing we allow cuddling with. If the assignment is
- // made over multiple lines we should not allow cuddling.
- var assignedOnLineAbove []string
-
- // We want to keep track of what was called on the line above to support
- // special handling of things such as mutexes.
- var calledOnLineAbove []string
-
- // Check if the previous statement spans over multiple lines.
- cuddledWithMultiLineAssignment := cuddledWithLastStmt && p.nodeStart(previousStatement) != p.nodeStart(stmt)-1
-
- // Ensure previous line is not a multi line assignment and if not get
- // rightAndLeftHandSide assigned variables.
- if !cuddledWithMultiLineAssignment {
- assignedOnLineAbove = p.findLHS(previousStatement)
- calledOnLineAbove = p.findRHS(previousStatement)
- }
-
- // If previous assignment is multi line and we allow it, fetch
- // assignments (but only assignments).
- if cuddledWithMultiLineAssignment && p.config.AllowMultiLineAssignCuddle {
- if _, ok := previousStatement.(*ast.AssignStmt); ok {
- assignedOnLineAbove = p.findLHS(previousStatement)
- }
- }
-
- // We could potentially have a block which require us to check the first
- // argument before ruling out an allowed cuddle.
- var calledOrAssignedFirstInBlock []string
-
- if firstBodyStatement != nil {
- calledOrAssignedFirstInBlock = append(p.findLHS(firstBodyStatement), p.findRHS(firstBodyStatement)...)
- }
-
- var (
- leftHandSide = p.findLHS(stmt)
- rightHandSide = p.findRHS(stmt)
- rightAndLeftHandSide = append(leftHandSide, rightHandSide...)
- calledOrAssignedOnLineAbove = append(calledOnLineAbove, assignedOnLineAbove...)
- )
-
- // If we called some kind of lock on the line above we allow cuddling
- // anything.
- if atLeastOneInListsMatch(calledOnLineAbove, p.config.AllowCuddleWithCalls) {
- continue
- }
-
- // If we call some kind of unlock on this line we allow cuddling with
- // anything.
- if atLeastOneInListsMatch(rightHandSide, p.config.AllowCuddleWithRHS) {
- continue
- }
-
- nStatementsBefore := func(n int) bool {
- if i < n {
- return false
- }
-
- for j := 1; j < n; j++ {
- s1 := statements[i-j]
- s2 := statements[i-(j+1)]
-
- if p.nodeStart(s1)-1 != p.nodeEnd(s2) {
- return false
- }
- }
-
- return true
- }
-
- nStatementsAfter := func(n int) bool {
- if len(statements)-1 < i+n {
- return false
- }
-
- for j := 0; j < n; j++ {
- s1 := statements[i+j]
- s2 := statements[i+j+1]
-
- if p.nodeEnd(s1)+1 != p.nodeStart(s2) {
- return false
- }
- }
-
- return true
- }
-
- isLastStatementInBlockOfOnlyTwoLines := func() bool {
- // If we're the last statement, check if there's no more than two
- // lines from the starting statement and the end of this statement.
- // This is to support short return functions such as:
- // func (t *Typ) X() {
- // t.X = true
- // return t
- // }
- if len(statements) == 2 && i == 1 {
- if p.nodeEnd(stmt)-p.nodeStart(previousStatement) <= 2 {
- return true
- }
- }
-
- return false
- }
-
- // If it's a short declaration we should not cuddle with anything else
- // if ForceExclusiveShortDeclarations is set on; either this or the
- // previous statement could be the short decl, so we'll find out which
- // it was and use *that* statement's position
- if p.config.ForceExclusiveShortDeclarations && cuddledWithLastStmt {
- if p.isShortDecl(stmt) && !p.isShortDecl(previousStatement) {
- var reportNode ast.Node = previousStatement
-
- cm := ast.NewCommentMap(p.fileSet, stmt, p.file.Comments)
- if cg, ok := cm[stmt]; ok && len(cg) > 0 {
- for _, c := range cg {
- if c.Pos() > previousStatement.End() && c.End() < stmt.Pos() {
- reportNode = c
- }
- }
- }
-
- p.addErrorRange(
- stmt.Pos(),
- reportNode.End(),
- reportNode.End(),
- reasonShortDeclNotExclusive,
- )
- } else if p.isShortDecl(previousStatement) && !p.isShortDecl(stmt) {
- p.addErrorRange(
- previousStatement.Pos(),
- stmt.Pos(),
- stmt.Pos(),
- reasonShortDeclNotExclusive,
- )
- }
- }
-
- // If it's not an if statement and we're not cuddled move on. The only
- // reason we need to keep going for if statements is to check if we
- // should be cuddled with an error check.
- if _, ok := stmt.(*ast.IfStmt); !ok {
- if !cuddledWithLastStmt {
- continue
- }
- }
-
- reportNewlineTwoLinesAbove := func(n1, n2 ast.Node, reason string) {
- if atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) ||
- atLeastOneInListsMatch(assignedOnLineAbove, calledOrAssignedFirstInBlock) {
- // If both the assignment on the line above _and_ the assignment
- // two lines above is part of line or first in block, add the
- // newline as if non were.
- _, isAssignmentTwoLinesAbove := statements[i-2].(*ast.AssignStmt)
- assignedTwoLinesAbove := p.findLHS(statements[i-2])
-
- if isAssignmentTwoLinesAbove &&
- (atLeastOneInListsMatch(rightAndLeftHandSide, assignedTwoLinesAbove) ||
- atLeastOneInListsMatch(assignedTwoLinesAbove, calledOrAssignedFirstInBlock)) {
- p.addWhitespaceBeforeError(n1, reason)
- } else {
- // If the variable on the line above is allowed to be
- // cuddled, break two lines above so we keep the proper
- // cuddling.
- p.addErrorRange(n1.Pos(), n2.Pos(), n2.Pos(), reason)
- }
- } else {
- // If not, break here so we separate the cuddled variable.
- p.addWhitespaceBeforeError(n1, reason)
- }
- }
-
- switch t := stmt.(type) {
- case *ast.IfStmt:
- checkingErrInitializedInline := func() bool {
- if t.Init == nil {
- return false
- }
-
- // Variables were initialized inline in the if statement
- // Let's make sure it's the err just to be safe
- return atLeastOneInListsMatch(p.findLHS(t.Init), p.config.ErrorVariableNames)
- }
-
- if !cuddledWithLastStmt {
- checkingErr := atLeastOneInListsMatch(rightAndLeftHandSide, p.config.ErrorVariableNames)
- if checkingErr {
- // We only want to enforce cuddling error checks if the
- // error was assigned on the line above. See
- // https://github.com/bombsimon/wsl/issues/78.
- // This is needed since `assignedOnLineAbove` is not
- // actually just assignments but everything from LHS in the
- // previous statement. This means that if previous line was
- // `if err ...`, `err` will now be in the list
- // `assignedOnLineAbove`.
- if _, ok := previousStatement.(*ast.AssignStmt); !ok {
- continue
- }
-
- if checkingErrInitializedInline() {
- continue
- }
-
- if atLeastOneInListsMatch(assignedOnLineAbove, p.config.ErrorVariableNames) {
- p.addErrorRange(
- stmt.Pos(),
- previousStatement.End(),
- stmt.Pos(),
- reasonMustCuddleErrCheck,
- )
- }
- }
-
- continue
- }
-
- if len(assignedOnLineAbove) == 0 {
- p.addWhitespaceBeforeError(t, reasonOnlyCuddleIfWithAssign)
- continue
- }
-
- if nStatementsBefore(2) {
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeIf)
- continue
- }
-
- if atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- continue
- }
-
- if atLeastOneInListsMatch(assignedOnLineAbove, calledOrAssignedFirstInBlock) {
- continue
- }
-
- p.addWhitespaceBeforeError(t, reasonOnlyCuddleWithUsedAssign)
- case *ast.ReturnStmt:
- if isLastStatementInBlockOfOnlyTwoLines() {
- continue
- }
-
- p.addWhitespaceBeforeError(t, reasonOnlyCuddle2LineReturn)
- case *ast.BranchStmt:
- if isLastStatementInBlockOfOnlyTwoLines() {
- continue
- }
-
- p.addWhitespaceBeforeError(t, reasonMultiLineBranchCuddle)
- case *ast.AssignStmt:
- // append is usually an assignment but should not be allowed to be
- // cuddled with anything not appended.
- if len(rightHandSide) > 0 && rightHandSide[len(rightHandSide)-1] == "append" {
- if p.config.StrictAppend {
- if !atLeastOneInListsMatch(calledOrAssignedOnLineAbove, rightHandSide) {
- p.addWhitespaceBeforeError(t, reasonAppendCuddledWithoutUse)
- }
- }
-
- continue
- }
-
- switch previousStatement.(type) {
- case *ast.AssignStmt, *ast.IncDecStmt:
- continue
- }
-
- if p.config.AllowAssignAndAnythingCuddle {
- continue
- }
-
- if _, ok := previousStatement.(*ast.DeclStmt); ok && p.config.AllowCuddleDeclaration {
- continue
- }
-
- // If the assignment is from a type or variable called on the line
- // above we can allow it by setting AllowAssignAndCallCuddle to
- // true.
- // Example (x is used):
- // x.function()
- // a.Field = x.anotherFunction()
- if p.config.AllowAssignAndCallCuddle {
- if atLeastOneInListsMatch(calledOrAssignedOnLineAbove, rightAndLeftHandSide) {
- continue
- }
- }
-
- p.addWhitespaceBeforeError(t, reasonAssignsCuddleAssign)
- case *ast.IncDecStmt:
- switch previousStatement.(type) {
- case *ast.AssignStmt, *ast.IncDecStmt:
- continue
- }
-
- p.addWhitespaceBeforeError(t, reasonAssignsCuddleAssign)
-
- case *ast.DeclStmt:
- if !p.config.AllowCuddleDeclaration {
- p.addWhitespaceBeforeError(t, reasonNeverCuddleDeclare)
- }
- case *ast.ExprStmt:
- switch previousStatement.(type) {
- case *ast.DeclStmt, *ast.ReturnStmt:
- if p.config.AllowAssignAndCallCuddle && p.config.AllowCuddleDeclaration {
- continue
- }
-
- p.addWhitespaceBeforeError(t, reasonExpressionCuddledWithDeclOrRet)
- case *ast.IfStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.ForStmt:
- p.addWhitespaceBeforeError(t, reasonExpressionCuddledWithBlock)
- }
-
- // If the expression is called on a type or variable used or
- // assigned on the line we can allow it by setting
- // AllowAssignAndCallCuddle to true.
- // Example of allowed cuddled (x is used):
- // a.Field = x.func()
- // x.function()
- if p.config.AllowAssignAndCallCuddle {
- if atLeastOneInListsMatch(calledOrAssignedOnLineAbove, rightAndLeftHandSide) {
- continue
- }
- }
-
- // If we assigned variables on the line above but didn't use them in
- // this expression there should probably be a newline between them.
- if len(assignedOnLineAbove) > 0 && !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- p.addWhitespaceBeforeError(t, reasonExprCuddlingNonAssignedVar)
- }
- case *ast.RangeStmt:
- if nStatementsBefore(2) {
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeRange)
- continue
- }
-
- if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- if !atLeastOneInListsMatch(assignedOnLineAbove, calledOrAssignedFirstInBlock) {
- p.addWhitespaceBeforeError(t, reasonRangeCuddledWithoutUse)
- }
- }
- case *ast.DeferStmt:
- if _, ok := previousStatement.(*ast.DeferStmt); ok {
- // We may cuddle multiple defers to group logic.
- continue
- }
-
- if nStatementsBefore(2) {
- // We allow cuddling defer if the defer references something
- // used two lines above.
- // There are several reasons to why we do this.
- // Originally there was a special use case only for "Close"
- //
- // https://github.com/bombsimon/wsl/issues/31 which links to
- // resp, err := client.Do(req)
- // if err != nil {
- // return err
- // }
- // defer resp.Body.Close()
- //
- // After a discussion in a followup issue it makes sense to not
- // only hard code `Close` but for anything that's referenced two
- // statements above.
- //
- // https://github.com/bombsimon/wsl/issues/85
- // db, err := OpenDB()
- // require.NoError(t, err)
- // defer db.Close()
- //
- // All of this is only allowed if there's exactly three cuddled
- // statements, otherwise the regular rules apply.
- if !nStatementsBefore(3) && !nStatementsAfter(1) {
- variablesTwoLinesAbove := append(p.findLHS(statements[i-2]), p.findRHS(statements[i-2])...)
- if atLeastOneInListsMatch(rightHandSide, variablesTwoLinesAbove) {
- continue
- }
- }
-
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeDefer)
-
- continue
- }
-
- // Be extra nice with RHS, it's common to use this for locks:
- // m.Lock()
- // defer m.Unlock()
- previousRHS := p.findRHS(previousStatement)
- if atLeastOneInListsMatch(rightHandSide, previousRHS) {
- continue
- }
-
- // Allow use to cuddled defer func literals with usages on line
- // above. Example:
- // b := getB()
- // defer func() {
- // makesSenseToUse(b)
- // }()
- if c, ok := t.Call.Fun.(*ast.FuncLit); ok {
- funcLitFirstStmt := append(p.findLHS(c.Body), p.findRHS(c.Body)...)
-
- if atLeastOneInListsMatch(assignedOnLineAbove, funcLitFirstStmt) {
- continue
- }
- }
-
- if atLeastOneInListsMatch(assignedOnLineAbove, calledOrAssignedFirstInBlock) {
- continue
- }
-
- if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- p.addWhitespaceBeforeError(t, reasonDeferCuddledWithOtherVar)
- }
- case *ast.ForStmt:
- if len(rightAndLeftHandSide) == 0 {
- p.addWhitespaceBeforeError(t, reasonForWithoutCondition)
- continue
- }
-
- if nStatementsBefore(2) {
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeFor)
- continue
- }
-
- // The same rule applies for ranges as for if statements, see
- // comments regarding variable usages on the line before or as the
- // first line in the block for details.
- if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- if !atLeastOneInListsMatch(assignedOnLineAbove, calledOrAssignedFirstInBlock) {
- p.addWhitespaceBeforeError(t, reasonForCuddledAssignWithoutUse)
- }
- }
- case *ast.GoStmt:
- if _, ok := previousStatement.(*ast.GoStmt); ok {
- continue
- }
-
- if nStatementsBefore(2) {
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeGo)
- continue
- }
-
- if c, ok := t.Call.Fun.(*ast.SelectorExpr); ok {
- goCallArgs := append(p.findLHS(c.X), p.findRHS(c.X)...)
-
- if atLeastOneInListsMatch(calledOnLineAbove, goCallArgs) {
- continue
- }
- }
-
- if c, ok := t.Call.Fun.(*ast.FuncLit); ok {
- goCallArgs := append(p.findLHS(c.Body), p.findRHS(c.Body)...)
-
- if atLeastOneInListsMatch(assignedOnLineAbove, goCallArgs) {
- continue
- }
- }
-
- if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- p.addWhitespaceBeforeError(t, reasonGoFuncWithoutAssign)
- }
- case *ast.SwitchStmt:
- if nStatementsBefore(2) {
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeSwitch)
- continue
- }
-
- if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
- if len(rightAndLeftHandSide) == 0 {
- p.addWhitespaceBeforeError(t, reasonAnonSwitchCuddled)
- } else {
- p.addWhitespaceBeforeError(t, reasonSwitchCuddledWithoutUse)
- }
- }
- case *ast.TypeSwitchStmt:
- if nStatementsBefore(2) {
- reportNewlineTwoLinesAbove(t, statements[i-1], reasonOnlyOneCuddleBeforeTypeSwitch)
- continue
- }
-
- // Allowed to type assert on variable assigned on line above.
- if !atLeastOneInListsMatch(rightHandSide, assignedOnLineAbove) {
- // Allow type assertion on variables used in the first case
- // immediately.
- if !atLeastOneInListsMatch(assignedOnLineAbove, calledOrAssignedFirstInBlock) {
- p.addWhitespaceBeforeError(t, reasonTypeSwitchCuddledWithoutUse)
- }
- }
- case *ast.CaseClause, *ast.CommClause:
- // Case clauses will be checked by not allowing leading of trailing
- // whitespaces within the block. There's nothing in the case itself
- // that may be cuddled.
- default:
- p.addWarning(warnStmtNotImplemented, t.Pos(), t)
- }
- }
-}
-
-// firstBodyStatement returns the first statement inside a body block. This is
-// because variables may be cuddled with conditions or statements if it's used
-// directly as the first argument inside a body.
-// The body will then be parsed as a *ast.BlockStmt (regular block) or as a list
-// of []ast.Stmt (case block).
-func (p *processor) firstBodyStatement(i int, allStmt []ast.Stmt) ast.Node {
- stmt := allStmt[i]
-
- // Start by checking if the statement has a body (probably if-statement,
- // a range, switch case or similar. Whenever a body is found we start by
- // parsing it before moving on in the AST.
- statementBody := reflect.Indirect(reflect.ValueOf(stmt)).FieldByName("Body")
-
- // Some cases allow cuddling depending on the first statement in a body
- // of a block or case. If possible extract the first statement.
- var firstBodyStatement ast.Node
-
- if !statementBody.IsValid() {
- return firstBodyStatement
- }
-
- switch statementBodyContent := statementBody.Interface().(type) {
- case *ast.BlockStmt:
- if len(statementBodyContent.List) > 0 {
- firstBodyStatement = statementBodyContent.List[0]
-
- // If the first body statement is a *ast.CaseClause we're
- // actually interested in the **next** body to know what's
- // inside the first case.
- if x, ok := firstBodyStatement.(*ast.CaseClause); ok {
- if len(x.Body) > 0 {
- firstBodyStatement = x.Body[0]
- }
- }
- }
-
- // If statement bodies will be parsed already when finding block bodies.
- // The reason is because if/else-if/else chains is nested in the AST
- // where the else bit is a part of the if statement. Since if statements
- // is the only statement that can be chained like this we exclude it
- // from parsing it again here.
- if _, ok := stmt.(*ast.IfStmt); !ok {
- p.parseBlockBody(nil, statementBodyContent)
- }
- case []ast.Stmt:
- // The Body field for an *ast.CaseClause or *ast.CommClause is of type
- // []ast.Stmt. We must check leading and trailing whitespaces and then
- // pass the statements to parseBlockStatements to parse it's content.
- var nextStatement ast.Node
-
- // Check if there's more statements (potential cases) after the
- // current one.
- if len(allStmt)-1 > i {
- nextStatement = allStmt[i+1]
- }
-
- p.findLeadingAndTrailingWhitespaces(nil, stmt, nextStatement)
- p.parseBlockStatements(statementBodyContent)
- default:
- p.addWarning(
- warnBodyStmtTypeNotImplemented,
- stmt.Pos(), statementBodyContent,
- )
- }
-
- return firstBodyStatement
-}
-
-func (p *processor) findLHS(node ast.Node) []string {
- var lhs []string
-
- if node == nil {
- return lhs
- }
-
- switch t := node.(type) {
- case *ast.BasicLit, *ast.FuncLit, *ast.SelectStmt,
- *ast.LabeledStmt, *ast.ForStmt, *ast.SwitchStmt,
- *ast.ReturnStmt, *ast.GoStmt, *ast.CaseClause,
- *ast.CommClause, *ast.CallExpr, *ast.UnaryExpr,
- *ast.BranchStmt, *ast.TypeSpec, *ast.ChanType,
- *ast.DeferStmt, *ast.TypeAssertExpr, *ast.RangeStmt:
- // Nothing to add to LHS
- case *ast.IncDecStmt:
- return p.findLHS(t.X)
- case *ast.Ident:
- return []string{t.Name}
- case *ast.AssignStmt:
- for _, v := range t.Lhs {
- lhs = append(lhs, p.findLHS(v)...)
- }
- case *ast.GenDecl:
- for _, v := range t.Specs {
- lhs = append(lhs, p.findLHS(v)...)
- }
- case *ast.ValueSpec:
- for _, v := range t.Names {
- lhs = append(lhs, p.findLHS(v)...)
- }
- case *ast.BlockStmt:
- for _, v := range t.List {
- lhs = append(lhs, p.findLHS(v)...)
- }
- case *ast.BinaryExpr:
- return append(
- p.findLHS(t.X),
- p.findLHS(t.Y)...,
- )
- case *ast.DeclStmt:
- return p.findLHS(t.Decl)
- case *ast.IfStmt:
- return p.findLHS(t.Cond)
- case *ast.TypeSwitchStmt:
- return p.findLHS(t.Assign)
- case *ast.SendStmt:
- return p.findLHS(t.Chan)
- default:
- if x, ok := maybeX(t); ok {
- return p.findLHS(x)
- }
-
- p.addWarning(warnUnknownLHS, t.Pos(), t)
- }
-
- return lhs
-}
-
-func (p *processor) findRHS(node ast.Node) []string {
- var rhs []string
-
- if node == nil {
- return rhs
- }
-
- switch t := node.(type) {
- case *ast.BasicLit, *ast.SelectStmt, *ast.ChanType,
- *ast.LabeledStmt, *ast.DeclStmt, *ast.BranchStmt,
- *ast.TypeSpec, *ast.ArrayType, *ast.CaseClause,
- *ast.CommClause, *ast.MapType, *ast.FuncLit:
- // Nothing to add to RHS
- case *ast.Ident:
- return []string{t.Name}
- case *ast.SelectorExpr:
- // TODO: Should this be RHS?
- // t.X is needed for defer as of now and t.Sel needed for special
- // functions such as Lock()
- rhs = p.findRHS(t.X)
- rhs = append(rhs, p.findRHS(t.Sel)...)
- case *ast.AssignStmt:
- for _, v := range t.Rhs {
- rhs = append(rhs, p.findRHS(v)...)
- }
- case *ast.CallExpr:
- for _, v := range t.Args {
- rhs = append(rhs, p.findRHS(v)...)
- }
-
- rhs = append(rhs, p.findRHS(t.Fun)...)
- case *ast.CompositeLit:
- for _, v := range t.Elts {
- rhs = append(rhs, p.findRHS(v)...)
- }
- case *ast.IfStmt:
- rhs = append(rhs, p.findRHS(t.Cond)...)
- rhs = append(rhs, p.findRHS(t.Init)...)
- case *ast.BinaryExpr:
- return append(
- p.findRHS(t.X),
- p.findRHS(t.Y)...,
- )
- case *ast.TypeSwitchStmt:
- return p.findRHS(t.Assign)
- case *ast.ReturnStmt:
- for _, v := range t.Results {
- rhs = append(rhs, p.findRHS(v)...)
- }
- case *ast.BlockStmt:
- for _, v := range t.List {
- rhs = append(rhs, p.findRHS(v)...)
- }
- case *ast.SwitchStmt:
- return p.findRHS(t.Tag)
- case *ast.GoStmt:
- return p.findRHS(t.Call)
- case *ast.ForStmt:
- return p.findRHS(t.Cond)
- case *ast.DeferStmt:
- return p.findRHS(t.Call)
- case *ast.SendStmt:
- return p.findLHS(t.Value)
- case *ast.IndexExpr:
- rhs = append(rhs, p.findRHS(t.Index)...)
- rhs = append(rhs, p.findRHS(t.X)...)
- case *ast.SliceExpr:
- rhs = append(rhs, p.findRHS(t.X)...)
- rhs = append(rhs, p.findRHS(t.Low)...)
- rhs = append(rhs, p.findRHS(t.High)...)
- case *ast.KeyValueExpr:
- rhs = p.findRHS(t.Key)
- rhs = append(rhs, p.findRHS(t.Value)...)
- default:
- if x, ok := maybeX(t); ok {
- return p.findRHS(x)
- }
-
- p.addWarning(warnUnknownRHS, t.Pos(), t)
- }
-
- return rhs
-}
-
-func (p *processor) isShortDecl(node ast.Node) bool {
- if t, ok := node.(*ast.AssignStmt); ok {
- return t.Tok == token.DEFINE
- }
-
- return false
-}
-
-func (p *processor) findBlockStmt(node ast.Node) []*ast.BlockStmt {
- var blocks []*ast.BlockStmt
-
- switch t := node.(type) {
- case *ast.BlockStmt:
- return []*ast.BlockStmt{t}
- case *ast.AssignStmt:
- for _, x := range t.Rhs {
- blocks = append(blocks, p.findBlockStmt(x)...)
- }
- case *ast.CallExpr:
- blocks = append(blocks, p.findBlockStmt(t.Fun)...)
-
- for _, x := range t.Args {
- blocks = append(blocks, p.findBlockStmt(x)...)
- }
- 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)...)
- case *ast.IfStmt:
- blocks = append([]*ast.BlockStmt{t.Body}, p.findBlockStmt(t.Else)...)
- }
-
- return blocks
-}
-
-// 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.
-// Known fields with X that are handled:
-// IndexExpr, ExprStmt, SelectorExpr, StarExpr, ParentExpr, TypeAssertExpr,
-// RangeStmt, UnaryExpr, ParenExpr, SliceExpr, IncDecStmt.
-func maybeX(node interface{}) (ast.Node, bool) {
- maybeHasX := reflect.Indirect(reflect.ValueOf(node)).FieldByName("X")
- if !maybeHasX.IsValid() {
- return nil, false
- }
-
- n, ok := maybeHasX.Interface().(ast.Node)
- if !ok {
- return nil, false
- }
-
- return n, true
-}
-
-func atLeastOneInListsMatch(listOne, listTwo []string) bool {
- sliceToMap := func(s []string) map[string]struct{} {
- m := map[string]struct{}{}
-
- for _, v := range s {
- m[v] = struct{}{}
- }
-
- return m
- }
-
- m1 := sliceToMap(listOne)
- m2 := sliceToMap(listTwo)
-
- for k1 := range m1 {
- if _, ok := m2[k1]; ok {
- return true
- }
- }
-
- for k2 := range m2 {
- if _, ok := m1[k2]; ok {
- return true
- }
- }
-
- return false
-}
-
-// findLeadingAndTrailingWhitespaces will find leading and trailing whitespaces
-// in a node. The method takes comments in consideration which will make the
-// parser more gentle.
-func (p *processor) findLeadingAndTrailingWhitespaces(ident *ast.Ident, stmt, nextStatement ast.Node) {
- var (
- commentMap = ast.NewCommentMap(p.fileSet, stmt, p.file.Comments)
- blockStatements []ast.Stmt
- blockStartLine int
- blockEndLine int
- blockStartPos token.Pos
- blockEndPos token.Pos
- isCase bool
- )
-
- // Depending on the block type, get the statements in the block and where
- // the block starts (and ends).
- switch t := stmt.(type) {
- case *ast.BlockStmt:
- blockStatements = t.List
- blockStartPos = t.Lbrace
- blockEndPos = t.Rbrace
- case *ast.CaseClause:
- blockStatements = t.Body
- blockStartPos = t.Colon
- isCase = true
- case *ast.CommClause:
- blockStatements = t.Body
- blockStartPos = t.Colon
- isCase = true
- default:
- p.addWarning(warnWSNodeTypeNotImplemented, stmt.Pos(), stmt)
-
- return
- }
-
- // Ignore empty blocks even if they have newlines or just comments.
- if len(blockStatements) < 1 {
- return
- }
-
- blockStartLine = p.fileSet.PositionFor(blockStartPos, false).Line
- blockEndLine = p.fileSet.PositionFor(blockEndPos, false).Line
-
- // No whitespace possible if LBrace and RBrace is on the same line.
- if blockStartLine == blockEndLine {
- return
- }
-
- var (
- firstStatement = blockStatements[0]
- lastStatement = blockStatements[len(blockStatements)-1]
- )
-
- // Get the comment related to the first statement, we do allow comments in
- // the beginning of a block before the first statement.
- var (
- openingNodePos = blockStartPos + 1
- lastLeadingComment ast.Node
- )
-
- var (
- firstStatementCommentGroups []*ast.CommentGroup
- lastStatementCommentGroups []*ast.CommentGroup
- )
-
- if cg, ok := commentMap[firstStatement]; ok && !isCase {
- firstStatementCommentGroups = cg
- } else {
- // TODO: Just like with trailing whitespaces comments in a case block is
- // tied to the last token of the first statement. For now we iterate over
- // all comments in the stmt and grab those that's after colon and before
- // first statement.
- for _, cg := range commentMap {
- if len(cg) < 1 {
- continue
- }
-
- // If we have comments and the last comment ends before the first
- // statement and the node is after the colon, this must be the node
- // mapped to comments.
- for _, c := range cg {
- if c.End() < firstStatement.Pos() && c.Pos() > blockStartPos {
- firstStatementCommentGroups = append(firstStatementCommentGroups, c)
- }
- }
-
- // And same if we have comments where the first comment is after the
- // last statement but before the next statement (next case). As with
- // the other things, if there is not next statement it's no next
- // case and the logic will be handled when parsing the block.
- if nextStatement == nil {
- continue
- }
-
- for _, c := range cg {
- if c.Pos() > lastStatement.End() && c.End() < nextStatement.Pos() {
- lastStatementCommentGroups = append(lastStatementCommentGroups, c)
- }
- }
- }
-
- // Since the comments come from a map they might not be ordered meaning
- // that the last and first comment groups can be in the wrong order. We
- // fix this by sorting all comments by pos after adding them all to the
- // slice.
- sort.Slice(firstStatementCommentGroups, func(i, j int) bool {
- return firstStatementCommentGroups[i].Pos() < firstStatementCommentGroups[j].Pos()
- })
-
- sort.Slice(lastStatementCommentGroups, func(i, j int) bool {
- return lastStatementCommentGroups[i].Pos() < lastStatementCommentGroups[j].Pos()
- })
- }
-
- for _, commentGroup := range firstStatementCommentGroups {
- // If the comment group is on the same line as the block start
- // (LBrace) we should not consider it.
- if p.nodeEnd(commentGroup) == blockStartLine {
- openingNodePos = commentGroup.End()
- continue
- }
-
- // We only care about comments before our statement from the comment
- // map. As soon as we hit comments after our statement let's break
- // out!
- if commentGroup.Pos() > firstStatement.Pos() {
- break
- }
-
- // We never allow leading whitespace for the first comment.
- if lastLeadingComment == nil && p.nodeStart(commentGroup)-1 != blockStartLine {
- p.addErrorRange(
- openingNodePos,
- openingNodePos,
- commentGroup.Pos(),
- reasonBlockStartsWithWS,
- )
- }
-
- // If lastLeadingComment is set this is not the first comment so we
- // should remove whitespace between them if we don't explicitly
- // allow it.
- if lastLeadingComment != nil && !p.config.AllowSeparatedLeadingComment {
- if p.nodeStart(commentGroup)+1 != p.nodeEnd(lastLeadingComment) {
- p.addErrorRange(
- openingNodePos,
- lastLeadingComment.End(),
- commentGroup.Pos(),
- reasonBlockStartsWithWS,
- )
- }
- }
-
- lastLeadingComment = commentGroup
- }
-
- lastNodePos := openingNodePos
- if lastLeadingComment != nil {
- lastNodePos = lastLeadingComment.End()
- blockStartLine = p.nodeEnd(lastLeadingComment)
- }
-
- // Check if we have a whitespace between the last node which can be the
- // Lbrace, a comment on the same line or the last comment if we have
- // comments inside the actual block and the first statement. This is never
- // allowed.
- if p.nodeStart(firstStatement)-1 != blockStartLine {
- p.addErrorRange(
- openingNodePos,
- lastNodePos,
- firstStatement.Pos(),
- reasonBlockStartsWithWS,
- )
- }
-
- // If the blockEndLine is not 0 we're a regular block (not case).
- if blockEndLine != 0 {
- // We don't want to reject example functions since they have to end with
- // a comment.
- if isExampleFunc(ident) {
- return
- }
-
- var (
- lastNode ast.Node = lastStatement
- trailingComments []ast.Node
- )
-
- // Check if we have an comments _after_ the last statement and update
- // the last node if so.
- if c, ok := commentMap[lastStatement]; ok {
- lastComment := c[len(c)-1]
- if lastComment.Pos() > lastStatement.End() && lastComment.Pos() < stmt.End() {
- lastNode = lastComment
- }
- }
-
- // TODO: This should be improved.
- // The trailing comments are mapped to the last statement item which can
- // be anything depending on what the last statement is.
- // In `fmt.Println("hello")`, trailing comments will be mapped to
- // `*ast.BasicLit` for the "hello" string.
- // A short term improvement can be to cache this but for now we naively
- // iterate over all items when we check a block.
- for _, commentGroups := range commentMap {
- for _, commentGroup := range commentGroups {
- if commentGroup.Pos() < lastNode.End() || commentGroup.End() > stmt.End() {
- continue
- }
-
- trailingComments = append(trailingComments, commentGroup)
- }
- }
-
- // TODO: Should this be relaxed?
- // Given the old code we only allowed trailing newline if it was
- // directly tied to the last statement so for backwards compatibility
- // we'll do the same. This means we fail all but the last whitespace
- // even when allowing trailing comments.
- for _, comment := range trailingComments {
- if p.nodeStart(comment)-p.nodeEnd(lastNode) > 1 {
- p.addErrorRange(
- blockEndPos,
- lastNode.End(),
- comment.Pos(),
- reasonBlockEndsWithWS,
- )
- }
-
- lastNode = comment
- }
-
- if !p.config.AllowTrailingComment && p.nodeEnd(stmt)-1 != p.nodeEnd(lastStatement) {
- p.addErrorRange(
- blockEndPos,
- lastNode.End(),
- stmt.End()-1,
- reasonBlockEndsWithWS,
- )
- }
-
- return
- }
-
- // Nothing to do if we're not looking for enforced newline.
- if p.config.ForceCaseTrailingWhitespaceLimit == 0 {
- return
- }
-
- // If we don't have any nextStatement the trailing whitespace will be
- // handled when parsing the switch. If we do have a next statement we can
- // see where it starts by getting it's colon position. We set the end of the
- // current case to the position of the next case.
- switch nextStatement.(type) {
- case *ast.CaseClause, *ast.CommClause:
- default:
- // No more cases
- return
- }
-
- var closingNode ast.Node = lastStatement
- for _, commentGroup := range lastStatementCommentGroups {
- // TODO: In future versions we might want to close the gaps between
- // comments. However this is not currently reported in v3 so we
- // won't add this for now.
- // if p.nodeStart(commentGroup)-1 != p.nodeEnd(closingNode) {}
- closingNode = commentGroup
- }
-
- totalRowsInCase := p.nodeEnd(closingNode) - blockStartLine
- if totalRowsInCase < p.config.ForceCaseTrailingWhitespaceLimit {
- return
- }
-
- if p.nodeEnd(closingNode)+1 == p.nodeStart(nextStatement) {
- p.addErrorRange(
- closingNode.Pos(),
- closingNode.End(),
- closingNode.End(),
- reasonCaseBlockTooCuddly,
- )
- }
-}
-
-func isExampleFunc(ident *ast.Ident) bool {
- return ident != nil && strings.HasPrefix(ident.Name, "Example")
-}
-
-func (p *processor) nodeStart(node ast.Node) int {
- return p.fileSet.PositionFor(node.Pos(), false).Line
-}
-
-func (p *processor) nodeEnd(node ast.Node) int {
- line := p.fileSet.PositionFor(node.End(), false).Line
-
- if isEmptyLabeledStmt(node) {
- return p.fileSet.PositionFor(node.Pos(), false).Line
- }
-
- return line
-}
-
-func isEmptyLabeledStmt(node ast.Node) bool {
- v, ok := node.(*ast.LabeledStmt)
- if !ok {
- return false
- }
-
- _, empty := v.Stmt.(*ast.EmptyStmt)
-
- return empty
-}
-
-func (p *processor) addWhitespaceBeforeError(node ast.Node, reason string) {
- p.addErrorRange(node.Pos(), node.Pos(), node.Pos(), reason)
-}
-
-func (p *processor) addErrorRange(reportAt, start, end token.Pos, reason string) {
- report, ok := p.result[reportAt]
- if !ok {
- report = result{
- reason: reason,
- fixRanges: []fix{},
- }
- }
-
- report.fixRanges = append(report.fixRanges, fix{
- fixRangeStart: start,
- fixRangeEnd: end,
- })
-
- p.result[reportAt] = report
-}
-
-func (p *processor) addWarning(w string, pos token.Pos, t interface{}) {
- position := p.fileSet.PositionFor(pos, false)
-
- p.warnings = append(p.warnings,
- fmt.Sprintf("%s:%d: %s (%T)", position.Filename, position.Line, w, t),
- )
-}