aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/blizzy78
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/blizzy78')
-rw-r--r--vendor/github.com/blizzy78/varnamelen/.editorconfig13
-rw-r--r--vendor/github.com/blizzy78/varnamelen/.gitignore1
-rw-r--r--vendor/github.com/blizzy78/varnamelen/.golangci.yml70
-rw-r--r--vendor/github.com/blizzy78/varnamelen/LICENSE18
-rw-r--r--vendor/github.com/blizzy78/varnamelen/README.md155
-rw-r--r--vendor/github.com/blizzy78/varnamelen/doc.go3
-rw-r--r--vendor/github.com/blizzy78/varnamelen/flags.go109
-rw-r--r--vendor/github.com/blizzy78/varnamelen/go.mod13
-rw-r--r--vendor/github.com/blizzy78/varnamelen/go.sum32
-rw-r--r--vendor/github.com/blizzy78/varnamelen/typeparam.go35
-rw-r--r--vendor/github.com/blizzy78/varnamelen/typeparam_go1.16.go11
-rw-r--r--vendor/github.com/blizzy78/varnamelen/varnamelen.code-workspace13
-rw-r--r--vendor/github.com/blizzy78/varnamelen/varnamelen.go891
13 files changed, 1364 insertions, 0 deletions
diff --git a/vendor/github.com/blizzy78/varnamelen/.editorconfig b/vendor/github.com/blizzy78/varnamelen/.editorconfig
new file mode 100644
index 000000000..7b6461545
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/.editorconfig
@@ -0,0 +1,13 @@
+root = true
+
+[**]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_style = tab
+indent_size = 4
+trim_trailing_whitespace = true
+
+[**/*.yml]
+indent_style = space
+indent_size = 2
diff --git a/vendor/github.com/blizzy78/varnamelen/.gitignore b/vendor/github.com/blizzy78/varnamelen/.gitignore
new file mode 100644
index 000000000..1a4a71669
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/.gitignore
@@ -0,0 +1 @@
+/cmd/__debug_bin
diff --git a/vendor/github.com/blizzy78/varnamelen/.golangci.yml b/vendor/github.com/blizzy78/varnamelen/.golangci.yml
new file mode 100644
index 000000000..566572363
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/.golangci.yml
@@ -0,0 +1,70 @@
+# https://github.com/golangci/golangci-lint/issues/456#issuecomment-617470264
+issues:
+ exclude-use-default: false
+ exclude:
+ # errcheck: Almost all programs ignore errors on these functions and in most cases it's ok
+ - Error return value of .((os\.)?std(out|err)\..*|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
+ # golint: False positive when tests are defined in package 'test'
+ - func name will be used as test\.Test.* by other packages, and that stutters; consider calling this
+ # gosec: Duplicated errcheck checks
+ - G104
+ # gosec: Too many issues in popular repos
+ - (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)
+ # gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)'
+ - Potential file inclusion via variable
+
+linters:
+ enable:
+ - asciicheck
+ - bodyclose
+ - cyclop
+ - durationcheck
+ - errname
+ - errorlint
+ - exportloopref
+ - forcetypeassert
+ - gocognit
+ - gocritic
+ - goerr113
+ - gofmt
+ - goprintffuncname
+ - gosec
+ - ifshort
+ - nakedret
+ - nestif
+ - nilerr
+ - noctx
+ - nolintlint
+ - prealloc
+ - predeclared
+ - promlinter
+ - revive
+ - rowserrcheck
+ - sqlclosecheck
+ - stylecheck
+ - thelper
+ - tparallel
+ - unconvert
+ - unparam
+ - varnamelen
+ - wastedassign
+ - wrapcheck
+ - wsl
+
+linters-settings:
+ gocognit:
+ min-complexity: 15
+ nakedret:
+ max-func-lines: 0
+ nolintlint:
+ allow-unused: false
+ allow-leading-space: false
+ require-explanation: true
+ require-specific: true
+ unused:
+ go: 1.16
+ varnamelen:
+ check-return: true
+ ignore-type-assert-ok: true
+ ignore-map-index-ok: true
+ ignore-chan-recv-ok: true
diff --git a/vendor/github.com/blizzy78/varnamelen/LICENSE b/vendor/github.com/blizzy78/varnamelen/LICENSE
new file mode 100644
index 000000000..c45156a2c
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/LICENSE
@@ -0,0 +1,18 @@
+Copyright 2021-2022 Maik Schreiber
+
+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/blizzy78/varnamelen/README.md b/vendor/github.com/blizzy78/varnamelen/README.md
new file mode 100644
index 000000000..02131ff29
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/README.md
@@ -0,0 +1,155 @@
+[![GoDoc](https://pkg.go.dev/badge/github.com/blizzy78/varnamelen)](https://pkg.go.dev/github.com/blizzy78/varnamelen)
+
+
+varnamelen
+==========
+
+A Go Analyzer that checks that the length of a variable's name matches its usage scope:
+
+Variables with short names can be hard to use if the variable is used over a longer span of lines of code.
+A longer variable name may be easier to comprehend.
+
+The analyzer also checks method receiver names, named return values, and type parameter names.
+
+Arbitrary declarations such as `f *foo` can be ignored, as well as idiomatic `ok` variables.
+Conventional Go parameters such as `ctx context.Context` or `t *testing.T` will always be ignored.
+
+**Example output**
+
+```
+test.go:4:2: variable name 'x' is too short for the scope of its usage (varnamelen)
+ x := 123
+ ^
+test.go:6:2: variable name 'i' is too short for the scope of its usage (varnamelen)
+ i := 10
+ ^
+```
+
+
+golangci-lint Integration
+-------------------------
+
+varnamelen is integrated into [golangci-lint] (though it may not always be the most recent version.)
+
+Example configuration for golangci-lint:
+
+```yaml
+linters-settings:
+ varnamelen:
+ # The longest distance, in source lines, that is being considered a "small scope." (defaults to 5)
+ # Variables used in at most this many lines will be ignored.
+ max-distance: 5
+ # The minimum length of a variable's name that is considered "long." (defaults to 3)
+ # Variable names that are at least this long will be ignored.
+ min-name-length: 3
+ # Check method receiver. (defaults to false)
+ check-receiver: false
+ # Check named return values. (defaults to false)
+ check-return: false
+ # Check type parameters. (defaults to false)
+ check-type-param: false
+ # Ignore "ok" variables that hold the bool return value of a type assertion. (defaults to false)
+ ignore-type-assert-ok: false
+ # Ignore "ok" variables that hold the bool return value of a map index. (defaults to false)
+ ignore-map-index-ok: false
+ # Ignore "ok" variables that hold the bool return value of a channel receive. (defaults to false)
+ ignore-chan-recv-ok: false
+ # Optional list of variable names that should be ignored completely. (defaults to empty list)
+ ignore-names:
+ - err
+ # Optional list of variable declarations that should be ignored completely. (defaults to empty list)
+ # Entries must be in one of the following forms (see below for examples):
+ # - for variables, parameters, or named return values:
+ # - <name> <type>
+ # - <name> *<type>
+ # - for type parameters:
+ # - <name> <type>
+ # - for constants:
+ # - const <name>
+ ignore-decls:
+ - c echo.Context
+ - t testing.T
+ - f *foo.Bar
+ - e error
+ - i int
+ - const C
+ - T any
+```
+
+
+Standalone Usage
+----------------
+
+The `cmd/` folder provides a standalone command line utility. You can build it like this:
+
+```
+go build -o varnamelen ./cmd/
+```
+
+**Usage**
+
+```
+varnamelen: checks that the length of a variable's name matches its scope
+
+Usage: varnamelen [-flag] [package]
+
+A variable with a short name can be hard to use if the variable is used
+over a longer span of lines of code. A longer variable name may be easier
+to comprehend.
+
+Flags:
+ -V print version and exit
+ -all
+ no effect (deprecated)
+ -c int
+ display offending line with this many lines of context (default -1)
+ -checkReceiver
+ check method receiver names
+ -checkReturn
+ check named return values
+ -checkTypeParam
+ check type parameter names
+ -cpuprofile string
+ write CPU profile to this file
+ -debug string
+ debug flags, any subset of "fpstv"
+ -fix
+ apply all suggested fixes
+ -flags
+ print analyzer flags in JSON
+ -ignoreChanRecvOk
+ ignore 'ok' variables that hold the bool return value of a channel receive
+ -ignoreDecls value
+ comma-separated list of ignored variable declarations
+ -ignoreMapIndexOk
+ ignore 'ok' variables that hold the bool return value of a map index
+ -ignoreNames value
+ comma-separated list of ignored variable names
+ -ignoreTypeAssertOk
+ ignore 'ok' variables that hold the bool return value of a type assertion
+ -json
+ emit JSON output
+ -maxDistance int
+ maximum number of lines of variable usage scope considered 'short' (default 5)
+ -memprofile string
+ write memory profile to this file
+ -minNameLength int
+ minimum length of variable name considered 'long' (default 3)
+ -source
+ no effect (deprecated)
+ -tags string
+ no effect (deprecated)
+ -trace string
+ write trace log to this file
+ -v no effect (deprecated)
+```
+
+
+License
+-------
+
+This package is licensed under the MIT license.
+
+
+
+[golangci-lint]: https://github.com/golangci/golangci-lint
diff --git a/vendor/github.com/blizzy78/varnamelen/doc.go b/vendor/github.com/blizzy78/varnamelen/doc.go
new file mode 100644
index 000000000..d63c71cfb
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/doc.go
@@ -0,0 +1,3 @@
+// Package varnamelen implements an analyzer checking that the length of a variable's name
+// matches its usage scope.
+package varnamelen
diff --git a/vendor/github.com/blizzy78/varnamelen/flags.go b/vendor/github.com/blizzy78/varnamelen/flags.go
new file mode 100644
index 000000000..ee80774f9
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/flags.go
@@ -0,0 +1,109 @@
+package varnamelen
+
+import "strings"
+
+// stringsValue is the value of a list-of-strings flag.
+type stringsValue struct {
+ Values []string
+}
+
+// declarationsValue is the value of a list-of-declarations flag.
+type declarationsValue struct {
+ Values []declaration
+}
+
+// Set implements Value.
+func (sv *stringsValue) Set(values string) error {
+ if strings.TrimSpace(values) == "" {
+ sv.Values = nil
+ return nil
+ }
+
+ parts := strings.Split(values, ",")
+
+ sv.Values = make([]string, len(parts))
+
+ for i, part := range parts {
+ sv.Values[i] = strings.TrimSpace(part)
+ }
+
+ return nil
+}
+
+// String implements Value.
+func (sv *stringsValue) String() string {
+ return strings.Join(sv.Values, ",")
+}
+
+// contains returns true if sv contains s.
+func (sv *stringsValue) contains(s string) bool {
+ for _, v := range sv.Values {
+ if v == s {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Set implements Value.
+func (dv *declarationsValue) Set(values string) error {
+ if strings.TrimSpace(values) == "" {
+ dv.Values = nil
+ return nil
+ }
+
+ parts := strings.Split(values, ",")
+
+ dv.Values = make([]declaration, len(parts))
+
+ for idx, part := range parts {
+ dv.Values[idx] = parseDeclaration(strings.TrimSpace(part))
+ }
+
+ return nil
+}
+
+// String implements Value.
+func (dv *declarationsValue) String() string {
+ parts := make([]string, len(dv.Values))
+
+ for idx, val := range dv.Values {
+ parts[idx] = val.name + " " + val.typ
+ }
+
+ return strings.Join(parts, ",")
+}
+
+// matchVariable returns true if vari matches any of the declarations in dv.
+func (dv *declarationsValue) matchVariable(vari variable) bool {
+ for _, decl := range dv.Values {
+ if vari.match(decl) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// matchParameter returns true if param matches any of the declarations in dv.
+func (dv *declarationsValue) matchParameter(param parameter) bool {
+ for _, decl := range dv.Values {
+ if param.match(decl) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// matchParameter returns true if param matches any of the declarations in dv.
+func (dv *declarationsValue) matchTypeParameter(param typeParam) bool {
+ for _, decl := range dv.Values {
+ if param.match(decl) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/vendor/github.com/blizzy78/varnamelen/go.mod b/vendor/github.com/blizzy78/varnamelen/go.mod
new file mode 100644
index 000000000..29bece721
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/go.mod
@@ -0,0 +1,13 @@
+module github.com/blizzy78/varnamelen
+
+go 1.16
+
+require (
+ github.com/matryer/is v1.4.0
+ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c // indirect
+ golang.org/x/tools v0.1.10
+)
+
+retract (
+ v0.6.1 // see https://github.com/blizzy78/varnamelen/issues/13, use 0.6.2 or later instead
+)
diff --git a/vendor/github.com/blizzy78/varnamelen/go.sum b/vendor/github.com/blizzy78/varnamelen/go.sum
new file mode 100644
index 000000000..6845d4300
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/go.sum
@@ -0,0 +1,32 @@
+github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
+github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c h1:+8miTPjMCTXwih7BQmvWwd0PjdBZq2MKp/qQaahSzEM=
+golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
+golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/vendor/github.com/blizzy78/varnamelen/typeparam.go b/vendor/github.com/blizzy78/varnamelen/typeparam.go
new file mode 100644
index 000000000..a1f3de99a
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/typeparam.go
@@ -0,0 +1,35 @@
+//go:build go1.18
+// +build go1.18
+
+package varnamelen
+
+import "go/ast"
+
+// isTypeParam returns true if field is a type parameter of any of the given funcs.
+func isTypeParam(field *ast.Field, funcs []*ast.FuncDecl, funcLits []*ast.FuncLit) bool { //nolint:gocognit // it's not that complicated
+ for _, f := range funcs {
+ if f.Type.TypeParams == nil {
+ continue
+ }
+
+ for _, p := range f.Type.TypeParams.List {
+ if p == field {
+ return true
+ }
+ }
+ }
+
+ for _, f := range funcLits {
+ if f.Type.TypeParams == nil {
+ continue
+ }
+
+ for _, p := range f.Type.TypeParams.List {
+ if p == field {
+ return true
+ }
+ }
+ }
+
+ return false
+}
diff --git a/vendor/github.com/blizzy78/varnamelen/typeparam_go1.16.go b/vendor/github.com/blizzy78/varnamelen/typeparam_go1.16.go
new file mode 100644
index 000000000..7856651b9
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/typeparam_go1.16.go
@@ -0,0 +1,11 @@
+//go:build (go1.16 && !go1.18) || (go1.17 && !go1.18)
+// +build go1.16,!go1.18 go1.17,!go1.18
+
+package varnamelen
+
+import "go/ast"
+
+// isTypeParam returns true if field is a type parameter of any of the given funcs.
+func isTypeParam(_ *ast.Field, _ []*ast.FuncDecl, _ []*ast.FuncLit) bool {
+ return false
+}
diff --git a/vendor/github.com/blizzy78/varnamelen/varnamelen.code-workspace b/vendor/github.com/blizzy78/varnamelen/varnamelen.code-workspace
new file mode 100644
index 000000000..68c485c96
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/varnamelen.code-workspace
@@ -0,0 +1,13 @@
+{
+ "folders": [
+ {
+ "path": "."
+ }
+ ],
+ "extensions": {
+ "recommendations": [
+ "EditorConfig.EditorConfig",
+ "golang.go"
+ ]
+ }
+}
diff --git a/vendor/github.com/blizzy78/varnamelen/varnamelen.go b/vendor/github.com/blizzy78/varnamelen/varnamelen.go
new file mode 100644
index 000000000..a5b960311
--- /dev/null
+++ b/vendor/github.com/blizzy78/varnamelen/varnamelen.go
@@ -0,0 +1,891 @@
+package varnamelen
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+)
+
+// varNameLen is an analyzer that checks that the length of a variable's name matches its usage scope.
+// It will create a report for a variable's assignment if that variable has a short name, but its
+// usage scope is not considered "small."
+type varNameLen struct {
+ // maxDistance is the longest distance, in source lines, that is being considered a "small scope."
+ maxDistance int
+
+ // minNameLength is the minimum length of a variable's name that is considered "long."
+ minNameLength int
+
+ // ignoreNames is an optional list of variable names that should be ignored completely.
+ ignoreNames stringsValue
+
+ // checkReceiver determines whether method receivers should be checked.
+ checkReceiver bool
+
+ // checkReturn determines whether named return values should be checked.
+ checkReturn bool
+
+ // ignoreTypeAssertOk determines whether "ok" variables that hold the bool return value of a type assertion should be ignored.
+ ignoreTypeAssertOk bool
+
+ // ignoreMapIndexOk determines whether "ok" variables that hold the bool return value of a map index should be ignored.
+ ignoreMapIndexOk bool
+
+ // ignoreChannelReceiveOk determines whether "ok" variables that hold the bool return value of a channel receive should be ignored.
+ ignoreChannelReceiveOk bool
+
+ // ignoreDeclarations is an optional list of variable declarations that should be ignored completely.
+ ignoreDeclarations declarationsValue
+
+ // checkTypeParameters determines whether type parameters should be checked.
+ checkTypeParameters bool
+}
+
+// variable represents a declared variable.
+type variable struct {
+ // name is the name of the variable.
+ name string
+
+ // constant is true if the variable is actually a constant.
+ constant bool
+
+ // typ is the type of the variable.
+ typ string
+
+ // assign is the assign statement that declares the variable.
+ assign *ast.AssignStmt
+
+ // valueSpec is the value specification that declares the variable.
+ valueSpec *ast.ValueSpec
+}
+
+// parameter represents a declared function or method parameter.
+type parameter struct {
+ // name is the name of the parameter.
+ name string
+
+ // typ is the type of the parameter.
+ typ string
+
+ // field is the declaration of the parameter.
+ field *ast.Field
+}
+
+// typeParam represents a declared type parameter.
+type typeParam struct {
+ // name is the name of the type parameter.
+ name string
+
+ // typ is the type of the type parameter.
+ typ string
+
+ // field is the field that declares the type parameter.
+ field *ast.Field
+}
+
+// declaration is a variable declaration.
+type declaration struct {
+ // name is the name of the variable.
+ name string
+
+ // constant is true if the variable is actually a constant.
+ constant bool
+
+ // typ is the type of the variable. Not used for constants.
+ typ string
+}
+
+// importDeclaration is an import declaration.
+type importDeclaration struct {
+ // name is the short name or alias for the imported package. This is either the package's default name,
+ // or the alias specified in the import statement.
+ // Not used if self is true.
+ name string
+
+ // path is the full path to the imported package.
+ path string
+
+ // self is true when this is an implicit import declaration for the current package.
+ self bool
+}
+
+const (
+ // defaultMaxDistance is the default value for the maximum distance between the declaration of a variable and its usage
+ // that is considered a "small scope."
+ defaultMaxDistance = 5
+
+ // defaultMinNameLength is the default value for the minimum length of a variable's name that is considered "long."
+ defaultMinNameLength = 3
+)
+
+// conventionalDecls is a list of conventional variable declarations.
+var conventionalDecls = []declaration{
+ parseDeclaration("ctx context.Context"),
+
+ parseDeclaration("b *testing.B"),
+ parseDeclaration("f *testing.F"),
+ parseDeclaration("m *testing.M"),
+ parseDeclaration("pb *testing.PB"),
+ parseDeclaration("t *testing.T"),
+ parseDeclaration("tb testing.TB"),
+}
+
+// NewAnalyzer returns a new analyzer.
+func NewAnalyzer() *analysis.Analyzer {
+ vnl := varNameLen{
+ maxDistance: defaultMaxDistance,
+ minNameLength: defaultMinNameLength,
+ ignoreNames: stringsValue{},
+ ignoreDeclarations: declarationsValue{},
+ }
+
+ analyzer := analysis.Analyzer{
+ Name: "varnamelen",
+ Doc: "checks that the length of a variable's name matches its scope\n\n" +
+ "A variable with a short name can be hard to use if the variable is used\n" +
+ "over a longer span of lines of code. A longer variable name may be easier\n" +
+ "to comprehend.",
+
+ Run: func(pass *analysis.Pass) (interface{}, error) {
+ (&vnl).run(pass)
+ return nil, nil
+ },
+
+ Requires: []*analysis.Analyzer{
+ inspect.Analyzer,
+ },
+ }
+
+ analyzer.Flags.IntVar(&vnl.maxDistance, "maxDistance", defaultMaxDistance, "maximum number of lines of variable usage scope considered 'short'")
+ analyzer.Flags.IntVar(&vnl.minNameLength, "minNameLength", defaultMinNameLength, "minimum length of variable name considered 'long'")
+ analyzer.Flags.Var(&vnl.ignoreNames, "ignoreNames", "comma-separated list of ignored variable names")
+ analyzer.Flags.BoolVar(&vnl.checkReceiver, "checkReceiver", false, "check method receivers")
+ analyzer.Flags.BoolVar(&vnl.checkReturn, "checkReturn", false, "check named return values")
+ analyzer.Flags.BoolVar(&vnl.ignoreTypeAssertOk, "ignoreTypeAssertOk", false, "ignore 'ok' variables that hold the bool return value of a type assertion")
+ analyzer.Flags.BoolVar(&vnl.ignoreMapIndexOk, "ignoreMapIndexOk", false, "ignore 'ok' variables that hold the bool return value of a map index")
+ analyzer.Flags.BoolVar(&vnl.ignoreChannelReceiveOk, "ignoreChanRecvOk", false, "ignore 'ok' variables that hold the bool return value of a channel receive")
+ analyzer.Flags.Var(&vnl.ignoreDeclarations, "ignoreDecls", "comma-separated list of ignored variable declarations")
+ analyzer.Flags.BoolVar(&vnl.checkTypeParameters, "checkTypeParam", false, "check type parameters")
+
+ return &analyzer
+}
+
+// Run applies v to a package, according to pass.
+func (v *varNameLen) run(pass *analysis.Pass) {
+ varToDist, paramToDist, returnToDist, typeParamToDist := v.distances(pass)
+
+ v.checkVariables(pass, varToDist)
+ v.checkParams(pass, paramToDist)
+ v.checkReturns(pass, returnToDist)
+ v.checkTypeParams(pass, typeParamToDist)
+}
+
+// checkVariables applies v to variables in varToDist.
+func (v *varNameLen) checkVariables(pass *analysis.Pass, varToDist map[variable]int) { //nolint:gocognit // it's not that complicated
+ for variable, dist := range varToDist {
+ if v.ignoreNames.contains(variable.name) {
+ continue
+ }
+
+ if v.ignoreDeclarations.matchVariable(variable) {
+ continue
+ }
+
+ if v.checkNameAndDistance(variable.name, dist) {
+ continue
+ }
+
+ if v.checkTypeAssertOk(variable) {
+ continue
+ }
+
+ if v.checkMapIndexOk(variable) {
+ continue
+ }
+
+ if v.checkChannelReceiveOk(variable) {
+ continue
+ }
+
+ if variable.isConventional() {
+ continue
+ }
+
+ if variable.assign != nil {
+ pass.Reportf(variable.assign.Pos(), "%s name '%s' is too short for the scope of its usage", variable.kindName(), variable.name)
+ continue
+ }
+
+ pass.Reportf(variable.valueSpec.Pos(), "%s name '%s' is too short for the scope of its usage", variable.kindName(), variable.name)
+ }
+}
+
+// checkParams applies v to parameters in paramToDist.
+func (v *varNameLen) checkParams(pass *analysis.Pass, paramToDist map[parameter]int) {
+ for param, dist := range paramToDist {
+ if v.ignoreNames.contains(param.name) {
+ continue
+ }
+
+ if v.ignoreDeclarations.matchParameter(param) {
+ continue
+ }
+
+ if v.checkNameAndDistance(param.name, dist) {
+ continue
+ }
+
+ if param.isConventional() {
+ continue
+ }
+
+ pass.Reportf(param.field.Pos(), "parameter name '%s' is too short for the scope of its usage", param.name)
+ }
+}
+
+// checkReturns applies v to named return values in returnToDist.
+func (v *varNameLen) checkReturns(pass *analysis.Pass, returnToDist map[parameter]int) {
+ for returnValue, dist := range returnToDist {
+ if v.ignoreNames.contains(returnValue.name) {
+ continue
+ }
+
+ if v.ignoreDeclarations.matchParameter(returnValue) {
+ continue
+ }
+
+ if v.checkNameAndDistance(returnValue.name, dist) {
+ continue
+ }
+
+ pass.Reportf(returnValue.field.Pos(), "return value name '%s' is too short for the scope of its usage", returnValue.name)
+ }
+}
+
+// checkTypeParams applies v to type parameters in paramToDist.
+func (v *varNameLen) checkTypeParams(pass *analysis.Pass, paramToDist map[typeParam]int) {
+ for param, dist := range paramToDist {
+ if v.ignoreNames.contains(param.name) {
+ continue
+ }
+
+ if v.ignoreDeclarations.matchTypeParameter(param) {
+ continue
+ }
+
+ if v.checkNameAndDistance(param.name, dist) {
+ continue
+ }
+
+ pass.Reportf(param.field.Pos(), "type parameter name '%s' is too short for the scope of its usage", param.name)
+ }
+}
+
+// checkNameAndDistance returns true if name or dist are considered "short".
+func (v *varNameLen) checkNameAndDistance(name string, dist int) bool {
+ if len(name) >= v.minNameLength {
+ return true
+ }
+
+ if dist <= v.maxDistance {
+ return true
+ }
+
+ return false
+}
+
+// checkTypeAssertOk returns true if "ok" variables that hold the bool return value of a type assertion
+// should be ignored, and if vari is such a variable.
+func (v *varNameLen) checkTypeAssertOk(vari variable) bool {
+ return v.ignoreTypeAssertOk && vari.isTypeAssertOk()
+}
+
+// checkMapIndexOk returns true if "ok" variables that hold the bool return value of a map index
+// should be ignored, and if vari is such a variable.
+func (v *varNameLen) checkMapIndexOk(vari variable) bool {
+ return v.ignoreMapIndexOk && vari.isMapIndexOk()
+}
+
+// checkChannelReceiveOk returns true if "ok" variables that hold the bool return value of a channel receive
+// should be ignored, and if vari is such a variable.
+func (v *varNameLen) checkChannelReceiveOk(vari variable) bool {
+ return v.ignoreChannelReceiveOk && vari.isChannelReceiveOk()
+}
+
+// distances returns maps of variables, parameters, return values, and type parameters mapping to their longest usage distances.
+func (v *varNameLen) distances(pass *analysis.Pass) (map[variable]int, map[parameter]int, map[parameter]int, map[typeParam]int) {
+ assignIdents, valueSpecIdents, paramIdents, returnIdents, typeParamIdents, imports, switches := v.identsAndImports(pass)
+
+ varToDist := map[variable]int{}
+
+ for _, ident := range assignIdents {
+ assign := ident.Obj.Decl.(*ast.AssignStmt) //nolint:forcetypeassert // check is done in identsAndImports
+
+ var typ string
+ if isTypeSwitchAssign(assign, switches) {
+ typ = "<type-switched>"
+ } else {
+ typ = shortTypeName(pass.TypesInfo.TypeOf(ident), imports)
+ }
+
+ variable := variable{
+ name: ident.Name,
+ typ: typ,
+ assign: assign,
+ }
+
+ useLine := pass.Fset.Position(ident.NamePos).Line
+ declLine := pass.Fset.Position(assign.Pos()).Line
+ varToDist[variable] = useLine - declLine
+ }
+
+ for _, ident := range valueSpecIdents {
+ valueSpec := ident.Obj.Decl.(*ast.ValueSpec) //nolint:forcetypeassert // check is done in identsAndImports
+
+ variable := variable{
+ name: ident.Name,
+ constant: ident.Obj.Kind == ast.Con,
+ typ: shortTypeName(pass.TypesInfo.TypeOf(ident), imports),
+ valueSpec: valueSpec,
+ }
+
+ useLine := pass.Fset.Position(ident.NamePos).Line
+ declLine := pass.Fset.Position(valueSpec.Pos()).Line
+ varToDist[variable] = useLine - declLine
+ }
+
+ paramToDist := map[parameter]int{}
+
+ for _, ident := range paramIdents {
+ field := ident.Obj.Decl.(*ast.Field) //nolint:forcetypeassert // check is done in identsAndImports
+
+ param := parameter{
+ name: ident.Name,
+ typ: shortTypeName(pass.TypesInfo.TypeOf(field.Type), imports),
+ field: field,
+ }
+
+ useLine := pass.Fset.Position(ident.NamePos).Line
+ declLine := pass.Fset.Position(field.Pos()).Line
+ paramToDist[param] = useLine - declLine
+ }
+
+ returnToDist := map[parameter]int{}
+
+ for _, ident := range returnIdents {
+ field := ident.Obj.Decl.(*ast.Field) //nolint:forcetypeassert // check is done in identsAndImports
+
+ param := parameter{
+ name: ident.Name,
+ typ: shortTypeName(pass.TypesInfo.TypeOf(ident), imports),
+ field: field,
+ }
+
+ useLine := pass.Fset.Position(ident.NamePos).Line
+ declLine := pass.Fset.Position(field.Pos()).Line
+ returnToDist[param] = useLine - declLine
+ }
+
+ typeParamToDist := map[typeParam]int{}
+
+ for _, ident := range typeParamIdents {
+ field := ident.Obj.Decl.(*ast.Field) //nolint:forcetypeassert // check is done in identsAndImports
+
+ param := typeParam{
+ name: ident.Name,
+ typ: shortTypeName(pass.TypesInfo.TypeOf(field.Type), imports),
+ field: field,
+ }
+
+ useLine := pass.Fset.Position(ident.NamePos).Line
+ declLine := pass.Fset.Position(field.Pos()).Line
+ typeParamToDist[param] = useLine - declLine
+ }
+
+ return varToDist, paramToDist, returnToDist, typeParamToDist
+}
+
+// identsAndImports returns Idents referencing assign statements, value specifications, parameters,
+// return values, and type parameters, respectively, as well as import declarations, and type switch statements.
+func (v *varNameLen) identsAndImports(pass *analysis.Pass) ([]*ast.Ident, []*ast.Ident, []*ast.Ident, []*ast.Ident, //nolint:gocognit,cyclop // this is complex stuff
+ []*ast.Ident, []importDeclaration, []*ast.TypeSwitchStmt) {
+ inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) //nolint:forcetypeassert // inspect.Analyzer always returns *inspector.Inspector
+
+ filter := []ast.Node{
+ (*ast.ImportSpec)(nil),
+ (*ast.FuncDecl)(nil),
+ (*ast.FuncLit)(nil),
+ (*ast.CompositeLit)(nil),
+ (*ast.TypeSwitchStmt)(nil),
+ (*ast.Ident)(nil),
+ }
+
+ assignIdents := []*ast.Ident{}
+ valueSpecIdents := []*ast.Ident{}
+ paramIdents := []*ast.Ident{}
+ returnIdents := []*ast.Ident{}
+ typeParamIdents := []*ast.Ident{}
+ imports := []importDeclaration{}
+ switches := []*ast.TypeSwitchStmt{}
+
+ funcs := []*ast.FuncDecl{}
+ methods := []*ast.FuncDecl{}
+ funcLits := []*ast.FuncLit{}
+ compositeLits := []*ast.CompositeLit{}
+
+ inspector.Preorder(filter, func(node ast.Node) {
+ switch node2 := node.(type) {
+ case *ast.ImportSpec:
+ decl, ok := importSpecToDecl(node2, pass.Pkg.Imports())
+ if !ok {
+ return
+ }
+
+ imports = append(imports, decl)
+
+ case *ast.FuncDecl:
+ funcs = append(funcs, node2)
+
+ if node2.Recv == nil {
+ return
+ }
+
+ methods = append(methods, node2)
+
+ case *ast.FuncLit:
+ funcLits = append(funcLits, node2)
+
+ case *ast.CompositeLit:
+ compositeLits = append(compositeLits, node2)
+
+ case *ast.TypeSwitchStmt:
+ switches = append(switches, node2)
+
+ case *ast.Ident:
+ if node2.Obj == nil {
+ return
+ }
+
+ if isCompositeLitKey(node2, compositeLits) {
+ return
+ }
+
+ switch objDecl := node2.Obj.Decl.(type) {
+ case *ast.AssignStmt:
+ assignIdents = append(assignIdents, node2)
+
+ case *ast.ValueSpec:
+ valueSpecIdents = append(valueSpecIdents, node2)
+
+ case *ast.Field:
+ switch {
+ case isReceiver(objDecl, methods):
+ if !v.checkReceiver {
+ return
+ }
+
+ paramIdents = append(paramIdents, node2)
+
+ case isReturn(objDecl, funcs, funcLits):
+ if !v.checkReturn {
+ return
+ }
+
+ returnIdents = append(returnIdents, node2)
+
+ case isTypeParam(objDecl, funcs, funcLits):
+ if !v.checkTypeParameters {
+ return
+ }
+
+ typeParamIdents = append(typeParamIdents, node2)
+
+ case isParam(objDecl, funcs, funcLits, methods):
+ paramIdents = append(paramIdents, node2)
+ }
+ }
+ }
+ })
+
+ imports = append(imports, importDeclaration{
+ path: pass.Pkg.Path(),
+ self: true,
+ })
+
+ sort.Slice(imports, func(a, b int) bool {
+ // reversed: longest path first
+ return len(imports[a].path) > len(imports[b].path)
+ })
+
+ return assignIdents, valueSpecIdents, paramIdents, returnIdents, typeParamIdents, imports, switches
+}
+
+func importSpecToDecl(spec *ast.ImportSpec, imports []*types.Package) (importDeclaration, bool) {
+ path := strings.TrimSuffix(strings.TrimPrefix(spec.Path.Value, "\""), "\"")
+
+ if spec.Name != nil {
+ return importDeclaration{
+ name: spec.Name.Name,
+ path: path,
+ }, true
+ }
+
+ for _, imp := range imports {
+ if imp.Path() == path {
+ return importDeclaration{
+ name: imp.Name(),
+ path: path,
+ }, true
+ }
+ }
+
+ return importDeclaration{}, false
+}
+
+// isTypeAssertOk returns true if v is an "ok" variable that holds the bool return value of a type assertion.
+func (v variable) isTypeAssertOk() bool {
+ if v.name != "ok" {
+ return false
+ }
+
+ if v.assign == nil {
+ return false
+ }
+
+ if len(v.assign.Lhs) != 2 {
+ return false
+ }
+
+ ident, ok := v.assign.Lhs[1].(*ast.Ident)
+ if !ok {
+ return false
+ }
+
+ if ident.Name != "ok" {
+ return false
+ }
+
+ if len(v.assign.Rhs) != 1 {
+ return false
+ }
+
+ if _, ok := v.assign.Rhs[0].(*ast.TypeAssertExpr); !ok {
+ return false
+ }
+
+ return true
+}
+
+// isMapIndexOk returns true if v is an "ok" variable that holds the bool return value of a map index.
+func (v variable) isMapIndexOk() bool {
+ if v.name != "ok" {
+ return false
+ }
+
+ if v.assign == nil {
+ return false
+ }
+
+ if len(v.assign.Lhs) != 2 {
+ return false
+ }
+
+ ident, ok := v.assign.Lhs[1].(*ast.Ident)
+ if !ok {
+ return false
+ }
+
+ if ident.Name != "ok" {
+ return false
+ }
+
+ if len(v.assign.Rhs) != 1 {
+ return false
+ }
+
+ if _, ok := v.assign.Rhs[0].(*ast.IndexExpr); !ok {
+ return false
+ }
+
+ return true
+}
+
+// isChannelReceiveOk returns true if v is an "ok" variable that holds the bool return value of a channel receive.
+func (v variable) isChannelReceiveOk() bool {
+ if v.name != "ok" {
+ return false
+ }
+
+ if v.assign == nil {
+ return false
+ }
+
+ if len(v.assign.Lhs) != 2 {
+ return false
+ }
+
+ ident, ok := v.assign.Lhs[1].(*ast.Ident)
+ if !ok {
+ return false
+ }
+
+ if ident.Name != "ok" {
+ return false
+ }
+
+ if len(v.assign.Rhs) != 1 {
+ return false
+ }
+
+ unary, ok := v.assign.Rhs[0].(*ast.UnaryExpr)
+ if !ok {
+ return false
+ }
+
+ if unary.Op != token.ARROW {
+ return false
+ }
+
+ return true
+}
+
+// isConventional returns true if v matches a conventional Go variable/parameter name and type,
+// such as "ctx context.Context" or "t *testing.T".
+func (v variable) isConventional() bool {
+ for _, decl := range conventionalDecls {
+ if v.match(decl) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// match returns true if v matches decl.
+func (v variable) match(decl declaration) bool {
+ if v.name != decl.name {
+ return false
+ }
+
+ if v.constant != decl.constant {
+ return false
+ }
+
+ if v.constant {
+ return true
+ }
+
+ if v.typ == "" {
+ return false
+ }
+
+ return decl.matchType(v.typ)
+}
+
+// kindName returns "constant" if v.constant==true, else "variable".
+func (v variable) kindName() string {
+ if v.constant {
+ return "constant"
+ }
+
+ return "variable"
+}
+
+// isReceiver returns true if field is a receiver parameter of any of the given methods.
+func isReceiver(field *ast.Field, methods []*ast.FuncDecl) bool {
+ for _, m := range methods {
+ for _, recv := range m.Recv.List {
+ if recv == field {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// isReturn returns true if field is a return value of any of the given funcs.
+func isReturn(field *ast.Field, funcs []*ast.FuncDecl, funcLits []*ast.FuncLit) bool { //nolint:gocognit // it's not that complicated
+ for _, f := range funcs {
+ if f.Type.Results == nil {
+ continue
+ }
+
+ for _, r := range f.Type.Results.List {
+ if r == field {
+ return true
+ }
+ }
+ }
+
+ for _, f := range funcLits {
+ if f.Type.Results == nil {
+ continue
+ }
+
+ for _, r := range f.Type.Results.List {
+ if r == field {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// isParam returns true if field is a parameter of any of the given funcs.
+func isParam(field *ast.Field, funcs []*ast.FuncDecl, funcLits []*ast.FuncLit, methods []*ast.FuncDecl) bool { //nolint:gocognit,cyclop // it's not that complicated
+ for _, f := range funcs {
+ if f.Type.Params == nil {
+ continue
+ }
+
+ for _, p := range f.Type.Params.List {
+ if p == field {
+ return true
+ }
+ }
+ }
+
+ for _, f := range funcLits {
+ if f.Type.Params == nil {
+ continue
+ }
+
+ for _, p := range f.Type.Params.List {
+ if p == field {
+ return true
+ }
+ }
+ }
+
+ for _, m := range methods {
+ if m.Type.Params == nil {
+ continue
+ }
+
+ for _, p := range m.Type.Params.List {
+ if p == field {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// isCompositeLitKey returns true if ident is a key of any of the given composite literals.
+func isCompositeLitKey(ident *ast.Ident, compositeLits []*ast.CompositeLit) bool {
+ for _, cl := range compositeLits {
+ if _, ok := cl.Type.(*ast.MapType); ok {
+ continue
+ }
+
+ for _, kvExpr := range cl.Elts {
+ kv, ok := kvExpr.(*ast.KeyValueExpr)
+ if !ok {
+ continue
+ }
+
+ if kv.Key == ident {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// isTypeSwitchAssign returns true if assign is an assign statement of any of the given type switch statements.
+func isTypeSwitchAssign(assign *ast.AssignStmt, switches []*ast.TypeSwitchStmt) bool {
+ for _, s := range switches {
+ if s.Assign == assign {
+ return true
+ }
+ }
+
+ return false
+}
+
+// isConventional returns true if v matches a conventional Go variable/parameter name and type,
+// such as "ctx context.Context" or "t *testing.T".
+func (p parameter) isConventional() bool {
+ for _, decl := range conventionalDecls {
+ if p.match(decl) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// match returns whether p matches decl.
+func (p parameter) match(decl declaration) bool {
+ if p.name != decl.name {
+ return false
+ }
+
+ return decl.matchType(p.typ)
+}
+
+// match returns whether p matches decl.
+func (p typeParam) match(decl declaration) bool {
+ if p.name != decl.name {
+ return false
+ }
+
+ return decl.matchType(p.typ)
+}
+
+// parseDeclaration parses and returns a variable declaration parsed from decl.
+func parseDeclaration(decl string) declaration {
+ if strings.HasPrefix(decl, "const ") {
+ return declaration{
+ name: strings.TrimPrefix(decl, "const "),
+ constant: true,
+ }
+ }
+
+ parts := strings.SplitN(decl, " ", 2)
+
+ return declaration{
+ name: parts[0],
+ typ: parts[1],
+ }
+}
+
+// matchType returns true if typ matches d.typ.
+func (d declaration) matchType(typ string) bool {
+ return d.typ == typ
+}
+
+// shortTypeName returns the short name of typ, with respect to imports.
+// For example, if package github.com/matryer/is is imported with alias "x",
+// and typ represents []*github.com/matryer/is.I, shortTypeName will return "[]*x.I".
+// For imports without aliases, the package's default name will be used.
+func shortTypeName(typ types.Type, imports []importDeclaration) string {
+ if typ == nil {
+ return ""
+ }
+
+ typStr := typ.String()
+
+ for _, imp := range imports {
+ prefix := imp.path + "."
+
+ replace := ""
+ if !imp.self {
+ replace = imp.name + "."
+ }
+
+ typStr = strings.ReplaceAll(typStr, prefix, replace)
+ }
+
+ return typStr
+}