From c53b3cda1740047b33a4774855233907530e4cd5 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 22:10:45 -0700 Subject: [PATCH 01/16] add golangci-lint --- .github/workflows/golangci-lint.yml | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/golangci-lint.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..ac8eb9c --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,31 @@ +name: golangci-lint +on: [ push ] +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the action will use pre-installed Go. + # skip-go-installation: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true From 6fb97b9c894151ce376537a13311c6019396ea78 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 22:56:20 -0700 Subject: [PATCH 02/16] trying a different linter --- .github/workflows/golangci-lint.yml | 31 ----------------------------- .github/workflows/lint.yml | 3 +++ nject/example_must_consume_test.go | 1 + nject/nject.go | 8 -------- nvelope/decode.go | 1 - nvelope/example_test.go | 1 + 6 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml deleted file mode 100644 index ac8eb9c..0000000 --- a/.github/workflows/golangci-lint.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: golangci-lint -on: [ push ] -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: golangci-lint - uses: golangci/golangci-lint-action@v2 - with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: latest - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the action will use pre-installed Go. - # skip-go-installation: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..cabc732 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,3 @@ +uses: Jerome1337/golint-action@v1.0.2 +with: + golint-path: './src/...' diff --git a/nject/example_must_consume_test.go b/nject/example_must_consume_test.go index 63f8710..2e257c5 100644 --- a/nject/example_must_consume_test.go +++ b/nject/example_must_consume_test.go @@ -11,6 +11,7 @@ type dataSourceName string // openDBErrorReturnRequired is a provider that opens a database. Surface it seems // fine but it has a problem: what if nothing below it returns error? +// nolint:deadcode func openDBErrorReturnRequired(inner func(*sql.DB) error, driver driverName, name dataSourceName) error { db, err := sql.Open(string(driver), string(name)) if err != nil { diff --git a/nject/nject.go b/nject/nject.go index 45b5e81..b6889cb 100644 --- a/nject/nject.go +++ b/nject/nject.go @@ -459,11 +459,3 @@ func (c Collection) reorderNonFinal() { return } } - -func providersToProviders(in []*provider) []Provider { - p := make([]Provider, len(in)) - for i, fm := range in { - p[i] = fm - } - return p -} diff --git a/nvelope/decode.go b/nvelope/decode.go index ffe4b31..57486e7 100644 --- a/nvelope/decode.go +++ b/nvelope/decode.go @@ -409,7 +409,6 @@ var httpRequestType = reflect.TypeOf(&http.Request{}) var bodyType = reflect.TypeOf(Body{}) var textUnmarshallerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() var terminalErrorType = reflect.TypeOf((*nject.TerminalError)(nil)).Elem() -var emptyInterfaceType = reflect.TypeOf((*interface{})(nil)).Elem() var errorType = reflect.TypeOf((*error)(nil)).Elem() // The return value from f only matters when the type of the field is a struct. In diff --git a/nvelope/example_test.go b/nvelope/example_test.go index 84a3700..88b7fdc 100644 --- a/nvelope/example_test.go +++ b/nvelope/example_test.go @@ -14,6 +14,7 @@ import ( "github.com/muir/nject/nvelope" ) +// nolint:deadcode func main() { r := mux.NewRouter() srv := &http.Server{ From 7885b23372f17a738d8b85bcc45226ba7e871394 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 22:58:04 -0700 Subject: [PATCH 03/16] adjusting lint attempt --- .github/workflows/lint.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cabc732..41fb387 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,3 +1,5 @@ +name: Go Lint +on: [ push ] uses: Jerome1337/golint-action@v1.0.2 with: golint-path: './src/...' From 6d14dba82b96d90da11f1c03544e8b852b0bee05 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:00:14 -0700 Subject: [PATCH 04/16] more lint adjustments --- .github/workflows/go.yml | 3 +++ .github/workflows/lint.yml | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 3e6cf46..70838a8 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,3 +16,6 @@ jobs: - name: Test run: go test -v ./... + + - name: Go-linter + uses: Jerome1337/golint-action@v1.0.2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 41fb387..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: Go Lint -on: [ push ] -uses: Jerome1337/golint-action@v1.0.2 -with: - golint-path: './src/...' From 8af9f5702a0ae292cf6f78a0a54b7d9268ee16f5 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:21:58 -0700 Subject: [PATCH 05/16] trying again with golangci-lint --- .github/workflows/go.yml | 3 - .github/workflows/golangci-lint.yml | 31 ++ .golangci.yaml | 764 ++++++++++++++++++++++++++++ 3 files changed, 795 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .golangci.yaml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 70838a8..3e6cf46 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,6 +16,3 @@ jobs: - name: Test run: go test -v ./... - - - name: Go-linter - uses: Jerome1337/golint-action@v1.0.2 diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..5f4d843 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,31 @@ +name: golangci-lint +on: [push ] +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the action will use pre-installed Go. + # skip-go-installation: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..3ad453b --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,764 @@ +# This file contains all available configuration options +# with their default values. + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 1m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # list of build tags, all linters use it. Default is empty list. + build-tags: + - mytag + + # which dirs to skip: issues from them won't be reported; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but default dirs are skipped independently + # from this option's value (see skip-dirs-use-default). + # "/" will be replaced by current OS file path separator to properly work + # on Windows. + skip-dirs: + - src/external_libs + - autogenerated_by_my_lib + + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs-use-default: true + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + # "/" will be replaced by current OS file path separator to properly work + # on Windows. + skip-files: + - ".*\\.my\\.go$" + - lib/bad.go + + # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + modules-download-mode: readonly|vendor|mod + + # Allow multiple parallel golangci-lint instances running. + # If false (default) - golangci-lint acquires file lock on start. + allow-parallel-runners: false + + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions + # default is "colored-line-number" + format: github-actions + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + + # make issues output unique by line, default is true + uniq-by-line: true + + # add a prefix to the output file references; default is no prefix + path-prefix: "" + + # sorts results by: filepath, line and column + sort-results: false + +linters: + disable-all: true + enable: + - megacheck + - govet + enable-all: true + disable: + - maligned + - prealloc + - cyclop + - funlen + - gocognit + - gocyclo + presets: + - bugs + - unused + fast: false + +# all available settings of specific linters +linters-settings: + + cyclop: + # the maximal code complexity to report + max-complexity: 10000 + # the maximal average package complexity. If it's higher than 0.0 (float) the check is enabled (default 0.0) + package-average: 0.0 + # should ignore tests (default false) + skip-tests: false + + dogsled: + # checks assignments with too many blank identifiers; default is 2 + max-blank-identifiers: 4 + + dupl: + # tokens count to trigger issue, 150 by default + threshold: 100 + + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: true + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + # [deprecated] comma-separated list of pairs of the form pkg:regex + # the regex is used to ignore names within pkg. (default "fmt:.*"). + # see https://github.com/kisielk/errcheck#the-deprecated-method for details + ignore: fmt:.*,io/ioutil:^Read.* + + # [deprecated] use exclude-functions instead. + # path to a file containing a list of functions to exclude from checking + # see https://github.com/kisielk/errcheck#excluding-functions for details + exclude: /path/to/file.txt + + # list of functions to exclude from checking, where each entry is a single function to exclude. + # see https://github.com/kisielk/errcheck#excluding-functions for details + exclude-functions: + - io/ioutil.ReadFile + - io.Copy(*bytes.Buffer) + - io.Copy(os.Stdout) + + errorlint: + # Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats + errorf: true + # Check for plain type assertions and type switches + asserts: true + # Check for plain error comparisons + comparison: true + + exhaustive: + # check switch statements in generated files also + check-generated: false + # indicates that switch statements are to be considered exhaustive if a + # 'default' case is present, even if all enum members aren't listed in the + # switch + default-signifies-exhaustive: false + + exhaustivestruct: + # Struct Patterns is list of expressions to match struct packages and names + # The struct packages have the form example.com/package.ExampleStruct + # The matching patterns can use matching syntax from https://pkg.go.dev/path#Match + # If this list is empty, all structs are tested. + struct-patterns: + - '*.Test' + - 'example.com/package.ExampleStruct' + + forbidigo: + # Forbid the following identifiers (identifiers are written using regexp): + forbid: + - ^print.*$ + - 'fmt\.Print.*' + # Exclude godoc examples from forbidigo checks. Default is true. + exclude_godoc_examples: false + + funlen: + lines: 60000 + statements: 40000 + + gci: + # put imports beginning with prefix after 3rd-party packages; + # only support one prefix + # if not set, use goimports.local-prefixes + local-prefixes: github.com/org/project + + gocognit: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 1000 + + nestif: + # minimal complexity of if statements to report, 5 by default + min-complexity: 40 + + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimum occurrences of constant string count to trigger issue, 3 by default + min-occurrences: 3 + # ignore test files, false by default + ignore-tests: false + # look for existing constants matching the values, true by default + match-constant: true + # search also for duplicated numbers, false by default + numbers: false + # minimum value, only works with goconst.numbers, 3 by default + min: 3 + # maximum value, only works with goconst.numbers, 3 by default + max: 3 + # ignore when constant is not used as function argument, true by default + ignore-calls: true + + gocritic: + # Which checks should be enabled; can't be combined with 'disabled-checks'; + # See https://go-critic.github.io/overview#checks-overview + # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` + # By default list of stable checks is used. + enabled-checks: + - rangeValCopy + + # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty + disabled-checks: + - regexpMust + + # 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: + - performance + disabled-tags: + - experimental + + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: # must be valid enabled check name + # whether to restrict checker to params only (default true) + paramsOnly: true + elseif: + # whether to skip balanced if-else pairs (default true) + skipBalanced: true + hugeParam: + # size in bytes that makes the warning trigger (default 80) + sizeThreshold: 80 + nestingReduce: + # min number of statements inside a branch to trigger a warning (default 5) + bodyWidth: 5 + rangeExprCopy: + # size in bytes that makes the warning trigger (default 512) + sizeThreshold: 512 + # whether to check test functions (default true) + skipTestFuncs: true + rangeValCopy: + # size in bytes that makes the warning trigger (default 128) + sizeThreshold: 32 + # whether to check test functions (default true) + skipTestFuncs: true + ruleguard: + # path to a gorules file for the ruleguard checker + rules: '' + truncateCmp: + # whether to skip int/uint/uintptr types (default true) + skipArchDependent: true + underef: + # whether to skip (*x).method() calls where x is a pointer receiver (default true) + skipRecvDeref: true + unnamedResult: + # whether to check exported functions + checkExported: true + + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 1000 + + godot: + # comments to be checked: `declarations`, `toplevel`, or `all` + scope: declarations + # list of regexps for excluding particular comment lines from check + exclude: + # example: exclude comments which contain numbers + # - '[0-9]+' + # check that each sentence starts with a capital letter + capital: false + + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - NOTE + - OPTIMIZE # marks code that should be optimized before merging + - HACK # marks hack-arounds that should be removed before merging + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + + gofumpt: + # Select the Go version to target. The default is `1.15`. + lang-version: "1.15" + + # Choose whether or not to use the extra rules that are disabled + # by default + extra-rules: false + + goheader: + values: + const: + # define here const type values in format k:v, for example: + # COMPANY: MY COMPANY + regexp: + # define here regexp type values, for example + # AUTHOR: .*@mycompany\.com + template: # |- + # put here copyright header template for source code files, for example: + # Note: {{ YEAR }} is a builtin value that returns the year relative to the current machine time. + # + # {{ AUTHOR }} {{ COMPANY }} {{ YEAR }} + # SPDX-License-Identifier: Apache-2.0 + + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at: + + # http://www.apache.org/licenses/LICENSE-2.0 + + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + template-path: + # also as alternative of directive 'template' you may put the path to file with the template source + + goimports: + # put imports beginning with prefix after 3rd-party packages; + # it's a comma-separated list of prefixes + local-prefixes: github.com/muir/nject + + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + + gomnd: + settings: + mnd: + # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. + checks: argument,case,condition,operation,return,assign + # ignored-numbers: 1000 + # ignored-files: magic_.*.go + # ignored-functions: math.* + + gomoddirectives: + # Allow local `replace` directives. Default is false. + replace-local: false + # List of allowed `replace` directives. Default is empty. + replace-allow-list: + - launchpad.net/gocheck + # Allow to not explain why the version has been retracted in the `retract` directives. Default is false. + retract-allow-no-explanation: false + # Forbid the use of the `exclude` directives. Default is false. + exclude-forbidden: false + + gomodguard: + allowed: + modules: # List of allowed modules + # - gopkg.in/yaml.v2 + domains: # List of allowed module domains + # - golang.org + blocked: + modules: # List of blocked modules + # - github.com/uudashr/go-module: # Blocked module + # recommendations: # Recommended modules that should be used instead (Optional) + # - golang.org/x/mod + # reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional) + versions: # List of blocked module version constraints + # - github.com/mitchellh/go-homedir: # Blocked module with version constraint + # version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons + # reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional) + local_replace_directives: false # Set to true to raise lint issues for packages that are loaded from a local path via replace directive + + gosec: + # To select a subset of rules to run. + # Available rules: https://github.com/securego/gosec#available-rules + includes: + - G401 + - G306 + - G101 + # To specify a set of rules to explicitly exclude. + # Available rules: https://github.com/securego/gosec#available-rules + excludes: + - G204 + # To specify the configuration of rules. + # The configuration of rules is not fully documented by gosec: + # https://github.com/securego/gosec#configuration + # https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/rules/rulelist.go#L60-L102 + config: + G306: "0600" + G101: + pattern: "(?i)example" + ignore_entropy: false + entropy_threshold: "80.0" + per_char_threshold: "3.0" + truncate: "32" + + gosimple: + # Select the Go version to target. The default is '1.13'. + go: "1.15" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + govet: + # report about shadowed variables + check-shadowing: true + + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + + # enable or disable analyzers by name + # run `go tool vet help` to see all analyzers + enable: + - atomicalign + enable-all: false + disable: + - shadow + disable-all: false + + depguard: + list-type: blacklist + include-go-root: false + packages: + - github.com/sirupsen/logrus + packages-with-error-message: + # specify an error message to output when a blacklisted package is used + - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + + ifshort: + # Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax. + # Has higher priority than max-decl-chars. + max-decl-lines: 1 + # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax. + max-decl-chars: 30 + + importas: + # if set to `true`, force to use alias. + no-unaliased: true + # List of aliases + alias: + # using `servingv1` alias for `knative.dev/serving/pkg/apis/serving/v1` package + - pkg: knative.dev/serving/pkg/apis/serving/v1 + alias: servingv1 + # using `autoscalingv1alpha1` alias for `knative.dev/serving/pkg/apis/autoscaling/v1alpha1` package + - pkg: knative.dev/serving/pkg/apis/autoscaling/v1alpha1 + alias: autoscalingv1alpha1 + # You can specify the package path by regular expression, + # and alias by regular expression expansion syntax like below. + # see https://github.com/julz/importas#use-regular-expression for details + - pkg: knative.dev/serving/pkg/apis/(\w+)/(v[\w\d]+) + alias: $1$2 + + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 120 + # tab width in spaces. Default to 1. + tab-width: 1 + + makezero: + # Allow only slices initialized with a length of zero. Default is false. + always: false + + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + ignore-words: + - someword + + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + promlinter: + # Promlinter cannot infer all metrics name in static analysis. + # Enable strict mode will also include the errors caused by failing to parse the args. + strict: false + # Please refer to https://github.com/yeya24/promlinter#usage for detailed usage. + disabled-linters: + # - "Help" + # - "MetricUnits" + # - "Counter" + # - "HistogramSummaryReserved" + # - "MetricTypeInName" + # - "ReservedChars" + # - "CamelCase" + # - "lintUnitAbbreviations" + + predeclared: + # comma-separated list of predeclared identifiers to not report on + ignore: "" + # include method names and field names (i.e., qualified names) in checks + q: false + + nolintlint: + # Enable to ensure that nolint directives are all used. Default is true. + allow-unused: false + # Disable to ensure that nolint directives don't have a leading space. Default is true. + allow-leading-space: true + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. + require-specific: true + + rowserrcheck: + packages: + - github.com/jmoiron/sqlx + + revive: + # see https://github.com/mgechev/revive#available-rules for details. + ignore-generated-header: true + severity: warning + rules: + - name: indent-error-flow + severity: warning + - name: add-constant + severity: warning + arguments: + - maxLitCount: "3" + allowStrs: '""' + allowInts: "0,1,2" + allowFloats: "0.0,0.,1.0,1.,2.0,2." + + staticcheck: + # Select the Go version to target. The default is '1.13'. + go: "1.15" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + stylecheck: + # Select the Go version to target. The default is '1.13'. + go: "1.15" + # https://staticcheck.io/docs/options#checks + checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ] + # https://staticcheck.io/docs/options#dot_import_whitelist + dot-import-whitelist: + - fmt + # https://staticcheck.io/docs/options#initialisms + initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ] + # https://staticcheck.io/docs/options#http_status_code_whitelist + http-status-code-whitelist: [ "200", "400", "404", "500" ] + + tagliatelle: + # check the struck tag name case + case: + # use the struct field name to check the name of the struct tag + use-field-name: true + rules: + # any struct tag type can be used. + # support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower` + json: camel + yaml: camel + xml: camel + bson: camel + avro: snake + mapstructure: kebab + + testpackage: + # regexp pattern to skip files + skip-regexp: (export|internal)_test\.go + + thelper: + # The following configurations enable all checks. It can be omitted because all checks are enabled by default. + # You can enable only required checks deleting unnecessary checks. + test: + first: true + name: true + begin: true + benchmark: + first: true + name: true + begin: true + tb: + first: true + name: true + begin: true + + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + unused: + # Select the Go version to target. The default is '1.13'. + go: "1.15" + + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + + wrapcheck: + # An array of strings that specify substrings of signatures to ignore. + # If this set, it will override the default set of ignored signatures. + # See https://github.com/tomarrell/wrapcheck#configuration for more information. + ignoreSigs: + - .Errorf( + - errors.New( + - errors.Unwrap( + - .Wrap( + - .Wrapf( + - .WithMessage( + - .WithMessagef( + - .WithStack( + ignorePackageGlobs: + - encoding/* + - github.com/pkg/* + + wsl: + # See https://github.com/bombsimon/wsl/blob/master/doc/configuration.md for + # documentation of available settings. These are the defaults for + # `golangci-lint`. + allow-assign-and-anything: false + allow-assign-and-call: true + allow-cuddle-declarations: false + allow-multiline-assign: true + allow-separated-leading-comment: false + allow-trailing-comment: false + force-case-trailing-whitespace: 0 + force-err-cuddling: false + force-short-decl-cuddling: false + strict-append: true + + # The custom section can be used to define linter plugins to be loaded at runtime. + # See README doc for more info. + custom: + # Each custom linter should have a unique name. + example: + # The path to the plugin *.so. Can be absolute or local. Required for each custom linter + path: /path/to/example.so + # The description of the linter. Optional, just for documentation purposes. + description: This is an example usage of a plugin linter. + # Intended to point to the repo location of the linter. Optional, just for documentation purposes. + original-url: github.com/golangci/example-linter + +issues: + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: + - abcdef + + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + + # Exclude known linters from partially hard-vendored code, + # which is impossible to exclude via "nolint" comments. + - path: internal/hmac/ + text: "weak cryptographic primitive" + linters: + - gosec + + # Exclude some staticcheck messages + - linters: + - staticcheck + text: "SA9003:" + + # Exclude lll issues for long lines with go:generate + - linters: + - lll + source: "^//go:generate " + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # The default value is false. If set to true exclude and exclude-rules + # regular expressions become case sensitive. + exclude-case-sensitive: false + + # The list of ids of default excludes to include or disable. By default it's empty. + include: + - EXC0002 # disable excluding of issues about comments from golint + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Show only new issues created after git revision `REV` + new-from-rev: REV + + # Show only new issues created in git patch with set file path. + new-from-patch: path/to/patch/file + + # Fix found issues (if it's supported by the linter) + fix: true + +severity: + # Default value is empty string. + # Set the default severity for issues. If severity rules are defined and the issues + # do not match or no severity is provided to the rule this will be the default + # severity applied. Severities should match the supported severity names of the + # selected out format. + # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity + # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + default-severity: error + + # The default value is false. + # If set to true severity-rules regular expressions become case sensitive. + case-sensitive: false + + # Default value is empty list. + # When a list of severity rules are provided, severity information will be added to lint + # issues. Severity rules have the same filtering capability as exclude rules except you + # are allowed to specify one matcher per severity rule. + # Only affects out formats that support setting severity information. + rules: + - linters: + - dupl + severity: info From 11574c7dc45cc4a08dc427f1e037ac03f0ece9c8 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:26:48 -0700 Subject: [PATCH 06/16] adjust lint config --- .golangci.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 3ad453b..1500730 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -84,7 +84,7 @@ linters: enable: - megacheck - govet - enable-all: true + enable-all: false disable: - maligned - prealloc @@ -133,7 +133,7 @@ linters-settings: # [deprecated] use exclude-functions instead. # path to a file containing a list of functions to exclude from checking # see https://github.com/kisielk/errcheck#excluding-functions for details - exclude: /path/to/file.txt + # exclude: /path/to/file.txt # list of functions to exclude from checking, where each entry is a single function to exclude. # see https://github.com/kisielk/errcheck#excluding-functions for details @@ -217,7 +217,11 @@ linters-settings: # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` # By default list of stable checks is used. enabled-checks: - - rangeValCopy + - rangeExprCopy: + - ruleguard + - unnamedresult + - nestingreduce + - truncatecmp # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty disabled-checks: From 497c4d6e2807c8e5e0737a7e58574f3583c71f31 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:30:15 -0700 Subject: [PATCH 07/16] adjust lint config 2 --- .golangci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index 1500730..3f9e052 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -217,7 +217,7 @@ linters-settings: # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` # By default list of stable checks is used. enabled-checks: - - rangeExprCopy: + - rangeExprCopy - ruleguard - unnamedresult - nestingreduce From d54068d62ab31228387403ccb5d54f9da0dfee33 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:32:55 -0700 Subject: [PATCH 08/16] adjust lint config 3 --- .golangci.yaml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 3f9e052..6221eb9 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -217,7 +217,6 @@ linters-settings: # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` # By default list of stable checks is used. enabled-checks: - - rangeExprCopy - ruleguard - unnamedresult - nestingreduce @@ -660,15 +659,15 @@ linters-settings: # The custom section can be used to define linter plugins to be loaded at runtime. # See README doc for more info. - custom: - # Each custom linter should have a unique name. - example: - # The path to the plugin *.so. Can be absolute or local. Required for each custom linter - path: /path/to/example.so - # The description of the linter. Optional, just for documentation purposes. - description: This is an example usage of a plugin linter. - # Intended to point to the repo location of the linter. Optional, just for documentation purposes. - original-url: github.com/golangci/example-linter + #custom: + # # Each custom linter should have a unique name. + # example: + # # The path to the plugin *.so. Can be absolute or local. Required for each custom linter + # path: /path/to/example.so + # # The description of the linter. Optional, just for documentation purposes. + # description: This is an example usage of a plugin linter. + # # Intended to point to the repo location of the linter. Optional, just for documentation purposes. + # original-url: github.com/golangci/example-linter issues: # List of regexps of issue texts to exclude, empty list by default. From 58454e3f45d7688809db79aa3069496bcf27a563 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:34:59 -0700 Subject: [PATCH 09/16] adjust lint config 4 --- .golangci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index 6221eb9..82cebb4 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -80,7 +80,7 @@ output: sort-results: false linters: - disable-all: true + disable-all: false enable: - megacheck - govet From 2f4269c53c17efc481a5ff4f3a2bca891ab10ef0 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:36:18 -0700 Subject: [PATCH 10/16] adjust lint config 5 --- .golangci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index 82cebb4..b132558 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -51,7 +51,7 @@ run: # If invoked with -mod=vendor, the go command assumes that the vendor # directory holds the correct copies of dependencies and ignores # the dependency descriptions in go.mod. - modules-download-mode: readonly|vendor|mod + # modules-download-mode: readonly|vendor|mod # Allow multiple parallel golangci-lint instances running. # If false (default) - golangci-lint acquires file lock on start. From 836fae996e7c1e2921e36e710cf7e79ad10d3009 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:37:58 -0700 Subject: [PATCH 11/16] adjust lint config 6 --- .golangci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index b132558..8aee7df 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -62,7 +62,7 @@ run: output: # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions # default is "colored-line-number" - format: github-actions + format: line-number # print lines of code with issue, default is true print-issued-lines: true From c49d74679047cda2e35cf018793882e199532021 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:46:39 -0700 Subject: [PATCH 12/16] adjust lint config 7 --- .golangci.yaml | 2 +- nject/cache.go | 2 +- nvelope/decode.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 8aee7df..e1ef8a4 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -62,7 +62,7 @@ run: output: # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions # default is "colored-line-number" - format: line-number + format: checkstyle # print lines of code with issue, default is true print-issued-lines: true diff --git a/nject/cache.go b/nject/cache.go index 53a087e..46ca8eb 100644 --- a/nject/cache.go +++ b/nject/cache.go @@ -188,7 +188,7 @@ func fillKeyFromInputs(key []interface{}, in []reflect.Value) { } } -func defineCacher(id int32, fv canCall, l int, okayCheck func([]reflect.Value) bool) cacherFunc { +func defineCacher(_ int32, fv canCall, l int, okayCheck func([]reflect.Value) bool) cacherFunc { var lock sync.Mutex switch { diff --git a/nvelope/decode.go b/nvelope/decode.go index 57486e7..6af4a9f 100644 --- a/nvelope/decode.go +++ b/nvelope/decode.go @@ -50,7 +50,6 @@ type eigo struct { tag string decoders map[string]Decoder modelValidators []func(interface{}) error - methodIfPresent []string defaultContentType string } From 2c998e23b53c731cc0313c987f6c20a4e4a1b309 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:48:45 -0700 Subject: [PATCH 13/16] adjust lint config 8 --- .github/workflows/golangci-lint.yml | 1 + nject/example_must_consume_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 5f4d843..e1b2275 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -14,6 +14,7 @@ jobs: # Optional: working directory, useful for monorepos # working-directory: somedir + output: checkstyle # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 diff --git a/nject/example_must_consume_test.go b/nject/example_must_consume_test.go index 2e257c5..c2a582e 100644 --- a/nject/example_must_consume_test.go +++ b/nject/example_must_consume_test.go @@ -11,7 +11,7 @@ type dataSourceName string // openDBErrorReturnRequired is a provider that opens a database. Surface it seems // fine but it has a problem: what if nothing below it returns error? -// nolint:deadcode +// nolint:deadcode,unused func openDBErrorReturnRequired(inner func(*sql.DB) error, driver driverName, name dataSourceName) error { db, err := sql.Open(string(driver), string(name)) if err != nil { From 43d85c42a030fbee2b86eaa8cad941c3f4a0c41c Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Wed, 22 Sep 2021 23:51:44 -0700 Subject: [PATCH 14/16] adjust lint config 9 --- .github/workflows/golangci-lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index e1b2275..023cc3e 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -18,6 +18,7 @@ jobs: # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 + args: --out-format checkstyle # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true From 36bef8fd59345e2e40ef747614e2a056cfea18cc Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Fri, 24 Sep 2021 22:17:17 -0700 Subject: [PATCH 15/16] many lint fixes --- .golangci.yaml | 6 +-- nject/bind.go | 4 +- nject/cache.go | 4 ++ nject/characterize.go | 2 + nject/debug.go | 10 ++-- nject/error.go | 9 +++- nject/example_generated_test.go | 4 +- nject/example_methodcall_test.go | 2 +- nject/filler.go | 2 +- nject/generate.go | 7 +-- nject/include.go | 1 + nject/match.go | 5 +- nject/match_test.go | 1 + nject/nject.go | 5 +- nject/regressions_test.go | 23 +++------ npoint/endpoint_test.go | 6 +++ npoint/example_preregister_test.go | 2 + npoint/gorilla.go | 4 +- npoint/regression_test.go | 12 ++++- npoint/service_test.go | 78 ++++++++++++++++++++++++------ nserve/example_test.go | 3 +- nserve/hook.go | 5 +- nvelope/decode.go | 37 +++++++++++++- nvelope/deferred.go | 7 ++- nvelope/deferred_test.go | 1 + nvelope/encode.go | 21 +++++--- nvelope/errors.go | 28 ++--------- nvelope/example_middleware_test.go | 3 +- nvelope/example_test.go | 10 ++-- nvelope/logger.go | 6 +-- nvelope/panic.go | 17 ++----- 31 files changed, 207 insertions(+), 118 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index e1ef8a4..e505cd2 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -62,7 +62,7 @@ run: output: # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions # default is "colored-line-number" - format: checkstyle + format: colored-line-number # print lines of code with issue, default is true print-issued-lines: true @@ -733,10 +733,10 @@ issues: new: false # Show only new issues created after git revision `REV` - new-from-rev: REV + # new-from-rev: REV # Show only new issues created in git patch with set file path. - new-from-patch: path/to/patch/file + # new-from-patch: path/to/patch/file # Fix found issues (if it's supported by the linter) fix: true diff --git a/nject/bind.go b/nject/bind.go index 916fc50..5fe8042 100644 --- a/nject/bind.go +++ b/nject/bind.go @@ -41,7 +41,7 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider, d.mustCache = true d, err = characterizeFunc(d, charContext{inputsAreStatic: true}) if err != nil { - return fmt.Errorf("internal error #29: problem with debugging injectors: %s", err) + return fmt.Errorf("internal error #29: problem with debugging injectors: %w", err) } d.isSynthetic = true debuggingProvider = &d @@ -227,6 +227,7 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider, for i := len(collections[runGroup]) - 1; i >= 0; i-- { n := collections[runGroup][i] + // nolint:exhaustive switch n.class { case wrapperFunc: inner := f @@ -239,6 +240,7 @@ func doBind(sc *Collection, originalInvokeF *provider, originalInitF *provider, j := i - 1 Injectors: for j >= 0 { + // nolint:exhaustive switch collections[runGroup][j].class { default: break Injectors diff --git a/nject/cache.go b/nject/cache.go index 46ca8eb..c12af0e 100644 --- a/nject/cache.go +++ b/nject/cache.go @@ -29,6 +29,8 @@ func canSimpleTypeBeMapKey(t reflect.Type) bool { case reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.UnsafePointer: return false + case reflect.Interface, reflect.Struct, reflect.Invalid: + fallthrough default: // we shouldn't be here return false @@ -41,6 +43,7 @@ func canValueBeMapKey(v reflect.Value, recurseOkay bool) bool { // typed nils is too hard return false } + // nolint:exhaustive switch v.Type().Kind() { case reflect.Interface: if v.IsNil() { @@ -77,6 +80,7 @@ func canBeMapKey(in []reflect.Type) (bool, func([]reflect.Value) bool) { var checkers []func([]reflect.Value) bool for i, t := range in { i := i + // nolint:exhaustive switch t.Kind() { case reflect.Struct: for j := 0; j < t.NumField(); j++ { diff --git a/nject/characterize.go b/nject/characterize.go index 4a6174f..5f62177 100644 --- a/nject/characterize.go +++ b/nject/characterize.go @@ -99,6 +99,7 @@ func toTypeCodes(in []reflect.Type) []typeCode { func mappable(inputs ...reflect.Type) bool { ok := true for _, in := range inputs { + // nolint:exhaustive switch in.Kind() { case reflect.Map, reflect.Slice, reflect.Func: ok = false @@ -452,6 +453,7 @@ func (reg typeRegistry) characterizeFuncDetails(fm *provider, cc charContext) (* } else { v := reflect.ValueOf(fm.fn) var isNil bool + // nolint:exhaustive switch v.Type().Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: isNil = v.IsNil() diff --git a/nject/debug.go b/nject/debug.go index 7175f76..e0c50cd 100644 --- a/nject/debug.go +++ b/nject/debug.go @@ -144,17 +144,17 @@ func dumpF(context string, fm *provider) { } } if fm.include { - out += fmt.Sprintf("\n\tincluded") + out += "\n\tincluded" } if fm.required { - out += fmt.Sprintf("\n\trequired") + out += "\n\trequired" } else if fm.wanted { - out += fmt.Sprintf("\n\twanted") + out += "\n\twanted" } else if fm.desired { - out += fmt.Sprintf("\n\tdesired") + out += "\n\tdesired" } if fm.mustConsume { - out += fmt.Sprintf("\n\tmust consume") + out += "\n\tmust consume" } if fm.memoized || fm.memoize { out += fmt.Sprintf("\n\tmemoize %v memoized %v", fm.memoize, fm.memoized) diff --git a/nject/error.go b/nject/error.go index 7fe5bf1..7b9c482 100644 --- a/nject/error.go +++ b/nject/error.go @@ -1,5 +1,9 @@ package nject +import ( + "errors" +) + type njectError struct { err error details string @@ -14,8 +18,9 @@ func (ne *njectError) Error() string { // or something that called Bind() then it will return // a much more detailed error than just calling err.Error() func DetailedError(err error) string { - if njerr, ok := err.(*njectError); ok { - return njerr.err.Error() + "\n\n" + njerr.details + var njectError *njectError + if errors.As(err, &njectError) { + return err.Error() + "\n\n" + njectError.details } return err.Error() } diff --git a/nject/example_generated_test.go b/nject/example_generated_test.go index 4b06131..80993bf 100644 --- a/nject/example_generated_test.go +++ b/nject/example_generated_test.go @@ -48,7 +48,7 @@ func ExampleGenerateFromInjectionChain() { // } -func ExampleCollection_DownFlows_Provider() { +func ExampleCollection_DownFlows_provider() { sequence := nject.Sequence("one provider", func(_ int, _ string) float64 { return 0 }) inputs, outputs := sequence.DownFlows() fmt.Println("inputs", inputs) @@ -57,7 +57,7 @@ func ExampleCollection_DownFlows_Provider() { // outputs [float64] } -func ExampleCollection_DownFlows_Collection() { +func ExampleCollection_DownFlows_collection() { sequence := nject.Sequence("two providers", func(_ int, _ int64) float32 { return 0 }, func(_ int, _ string) float64 { return 0 }, diff --git a/nject/example_methodcall_test.go b/nject/example_methodcall_test.go index 4420676..fbda877 100644 --- a/nject/example_methodcall_test.go +++ b/nject/example_methodcall_test.go @@ -18,7 +18,7 @@ func (s S) Print() { fmt.Println(s.I) } -func ExampleMethodCall() { +func ExampleWithMethodCall() { nject.Run("example", func() int { return 4 diff --git a/nject/filler.go b/nject/filler.go index 381f950..f858308 100644 --- a/nject/filler.go +++ b/nject/filler.go @@ -14,7 +14,6 @@ const debugFiller = false // of Reflective type filler struct { pointer bool - create bool copy bool typ reflect.Type inputs []inputDisposition @@ -498,6 +497,7 @@ func addFieldFiller( var needConvert bool var countEmptyInterfaces int for i, in := range inputs { + i, in := i, in check := func(t reflect.Type, aOf bool) { var s int var c bool diff --git a/nject/generate.go b/nject/generate.go index 677d608..c314d62 100644 --- a/nject/generate.go +++ b/nject/generate.go @@ -96,6 +96,7 @@ func generateInputMapper(fm *provider, start int, param flowType, rmap map[typeC } // generateOutputMapper returns a function that copies values from an array of reflect.Value to a valueCollection +// nolint:unparam func generateOutputMapper(fm *provider, start int, param flowType, vmap map[typeCode]int, purpose string) (func(valueCollection, []reflect.Value), error) { pMap, err := generateParameterMap(fm, param, start, nil, vmap, purpose+" []->valueCollection") if err != nil { @@ -209,10 +210,7 @@ func generateWrappers( // this is not built outside WrapWrapper for thread safety inner := func(i []reflect.Value) []reflect.Value { if callCount > 0 { - // TODO: replace with copy(v, vCopy)? - for i, val := range vCopy { - v[i] = val - } + copy(v, vCopy) } callCount++ outMap(v, i) @@ -238,7 +236,6 @@ func generateWrappers( zero(v) } upMap(v, out) - return } case fallibleInjectorFunc: diff --git a/nject/include.go b/nject/include.go index a974820..3efd662 100644 --- a/nject/include.go +++ b/nject/include.go @@ -139,6 +139,7 @@ func computeDependenciesAndInclusion(funcs []*provider, initF *provider) error { } } debugf("lenth of without before: %d", len(without)) + // nolint:govet err := validateChainMarkIncludeExclude(funcs, false) debugf("lenth of without after: %d", len(without)) for _, fm := range without { diff --git a/nject/match.go b/nject/match.go index bd0e694..ce4677f 100644 --- a/nject/match.go +++ b/nject/match.go @@ -47,10 +47,7 @@ func aGreaterBInts(a []int, b []int) bool { return false } } - if len(a) < len(b) { - return false - } - return true + return len(a) >= len(b) } func (m interfaceMap) bestMatch(match typeCode, purpose string) (typeCode, []*provider, error) { diff --git a/nject/match_test.go b/nject/match_test.go index 7ebaef6..c653057 100644 --- a/nject/match_test.go +++ b/nject/match_test.go @@ -56,6 +56,7 @@ var bestMatchTests = []struct { func TestBestMatch(t *testing.T) { wrapTest(t, func(t *testing.T) { for _, test := range bestMatchTests { + test := test m := make(interfaceMap) for typ, layer := range test.MapData { t.Logf("%s: #%d get type code for %v", test.Name, layer, typ) diff --git a/nject/nject.go b/nject/nject.go index b6889cb..f5d87a6 100644 --- a/nject/nject.go +++ b/nject/nject.go @@ -220,7 +220,8 @@ func (c Collection) characterizeAndFlatten(nonStaticTypes map[typeCode]bool) ([] isLast: ii == len(c.contents)-1, inputsAreStatic: true, } - fm, err := characterizeFunc(fm, cc) + var err error + fm, err = characterizeFunc(fm, cc) if err != nil { return nil, nil, err } @@ -237,6 +238,7 @@ func (c Collection) characterizeAndFlatten(nonStaticTypes map[typeCode]bool) ([] } } } + // nolint:exhaustive switch fm.group { case runGroup, invokeGroup: for _, out := range fm.flows[outputParams] { @@ -244,6 +246,7 @@ func (c Collection) characterizeAndFlatten(nonStaticTypes map[typeCode]bool) ([] } } + // nolint:exhaustive switch fm.group { case staticGroup, literalGroup: afterInit = append(afterInit, fm) diff --git a/nject/regressions_test.go b/nject/regressions_test.go index ef30164..4dc2853 100644 --- a/nject/regressions_test.go +++ b/nject/regressions_test.go @@ -533,18 +533,16 @@ func TestRegressionPrior(t *testing.T) { Cacheable(Provide("QuoteChecker", func() s055 { called["QuoteChecker"]++; return 0 })), // included Cacheable(Provide("QuoteUpdater", func() s056 { called["QuoteUpdater"]++; return 0 })), // included Cacheable(Provide("ServiceLog", func() i018 { called["ServiceLog"]++; return nil })), // included - Provide("CORS", func(_ i016, _ s017) { called["CORS"]++; return }), // included + Provide("CORS", func(_ i016, _ s017) { called["CORS"]++ }), // included Provide("LogBegin", func(inner func(i019), _ s017, _ i018) { called["LogBegin"]++ inner(nil) - return }), // included - Desired(Provide("InjectorChainDebugging", func(_ i019, _ *Debugging) { called["InjectorChainDebugging"]++; return })), // included - Provide("base-collection-2", func(_ i016, _ i019) i020 { called["base-collection-2"]++; return nil }), // included + Desired(Provide("InjectorChainDebugging", func(_ i019, _ *Debugging) { called["InjectorChainDebugging"]++ })), // included + Provide("base-collection-2", func(_ i016, _ i019) i020 { called["base-collection-2"]++; return nil }), // included Provide("WriteJSON", func(inner func() i021, _ i020, _ i019) { called["WriteJSON"]++ inner() - return }), // included Desired(Provide("IfError", func(inner func() (i021, s022), _ i019, _ i020) i021 { called["IfError"]++ @@ -562,7 +560,6 @@ func TestRegressionPrior(t *testing.T) { MustConsume(Provide("DBClose", func(inner func(i029), _ i029) { called["DBClose"]++ inner(nil) - return })), // included Provide("TimeTravelHeader", func() s041 { called["TimeTravelHeader"]++; return 0 }), Provide("ParseClientQuote", func(_ i019, _ s023) (TerminalError, s057) { called["ParseClientQuote"]++; return nil, 0 }), // included @@ -572,7 +569,7 @@ func TestRegressionPrior(t *testing.T) { inner(nil, nil) return nil })), - Provide("ConsumeTxDone", func(_ i031) { called["ConsumeTxDone"]++; return }), + Provide("ConsumeTxDone", func(_ i031) { called["ConsumeTxDone"]++ }), MustConsume(Provide("ParentTx", func(_ i030) i032 { called["ParentTx"]++; return nil })), Provide("GetSessionID", func(_ i019, _ s017) (TerminalError, s043) { called["GetSessionID"]++; return nil, 0 }), // included Provide("AdvisoryLockQuote", func(inner func(s044) error, _ i019, _ i029, _ s043) error { @@ -690,18 +687,16 @@ func TestRegression7642(t *testing.T) { Cacheable(Provide("QuoteChecker", func() s064 { called["QuoteChecker"]++; return 0 })), // included Cacheable(Provide("QuoteUpdater", func() s065 { called["QuoteUpdater"]++; return 0 })), // included Cacheable(Provide("ServiceLog", func() i033 { called["ServiceLog"]++; return nil })), // included - Provide("CORS", func(_ i031, _ s032) { called["CORS"]++; return }), // included + Provide("CORS", func(_ i031, _ s032) { called["CORS"]++ }), // included Provide("LogBegin", func(inner func(i034), _ s032, _ i033) { called["LogBegin"]++ inner(nil) - return }), // included - Desired(Provide("InjectorChainDebugging", func(_ i034, _ *Debugging) { called["InjectorChainDebugging"]++; return })), // included - Provide("base-collection-2", func(_ i031, _ i034) i035 { called["base-collection-2"]++; return nil }), // included + Desired(Provide("InjectorChainDebugging", func(_ i034, _ *Debugging) { called["InjectorChainDebugging"]++ })), // included + Provide("base-collection-2", func(_ i031, _ i034) i035 { called["base-collection-2"]++; return nil }), // included Provide("WriteJSON", func(inner func() i036, _ i035, _ i034) { called["WriteJSON"]++ inner() - return }), // included Desired(Provide("IfError", func(inner func() (i036, s037), _ i034, _ i035) i036 { called["IfError"]++ @@ -718,7 +713,6 @@ func TestRegression7642(t *testing.T) { MustConsume(Provide("DBClose", func(inner func(i044), _ i044) { called["DBClose"]++ inner(nil) - return })), // included Provide("TimeTravelHeader", func() s051 { called["TimeTravelHeader"]++; return 0 }), MustConsume(Provide("ParseClientQuote", func(_ i034, _ s038) (TerminalError, s066) { called["ParseClientQuote"]++; return nil, 0 })), // included @@ -728,7 +722,7 @@ func TestRegression7642(t *testing.T) { inner(nil, nil) return nil })), - Provide("ConsumeTxDone", func(_ i046) { called["ConsumeTxDone"]++; return }), + Provide("ConsumeTxDone", func(_ i046) { called["ConsumeTxDone"]++ }), MustConsume(Provide("ParentTx", func(_ i045) i047 { called["ParentTx"]++; return nil })), Provide("GetSessionID", func(_ i034, _ s032) (TerminalError, s053) { called["GetSessionID"]++; return nil, 0 }), // included Provide("AdvisoryLockQuote", func(inner func(s054) error, _ i034, _ i044, _ s053) error { @@ -740,7 +734,6 @@ func TestRegression7642(t *testing.T) { Desired(MustConsume(Provide("PurchaseFailureAlerter", func(inner func(s068) error, _ i034, _ s067, _ s053) { called["PurchaseFailureAlerter"]++ inner(0) - return }))), Provide("VariationsRuleChecker", func(_ i034, _ s062, _ s053, _ s054) s069 { called["VariationsRuleChecker"]++; return 0 }), // included Provide("ReassembleQuote", func(_ i034, _ s066, _ s067, _ s061) (TerminalError, s070, s071) { diff --git a/npoint/endpoint_test.go b/npoint/endpoint_test.go index c42c33d..1aaf1e1 100644 --- a/npoint/endpoint_test.go +++ b/npoint/endpoint_test.go @@ -45,6 +45,7 @@ func (b *ManualBinder) Call(path string, method string, buf string, h http.Heade panic(fmt.Sprintf("no handler for %s", path)) } url := "http://localhost" + path + // nolint:noctx req, err := http.NewRequest(method, url, bytes.NewReader([]byte(buf))) if err != nil { panic(err) @@ -93,6 +94,7 @@ func TestTestFramework(t *testing.T) { }) h := make(http.Header) h.Set("X-Test-Request", "H2") + // nolint:bodyclose resp := b.Call("/y", "POST", "some data sent", h) buf, err := ioutil.ReadAll(resp.Body) assert.NoError(t, err) @@ -201,6 +203,7 @@ func TestVariablePassing(t *testing.T) { }) b := NewBinder() s.Start(b.Bind) + // nolint:bodyclose _ = b.Call("/x", "GET", "", make(http.Header)) } @@ -242,6 +245,7 @@ func TestInclusion(t *testing.T) { tc.endpoint) b := NewBinder() s.Start(b.Bind) + // nolint:bodyclose _ = b.Call("/x", "GET", "", make(http.Header)) expected := make(map[string]bool) for _, c := range strings.Split(tc.called, " ") { @@ -504,11 +508,13 @@ func TestChains(t *testing.T) { } for _, test := range chainTests { + test := test t.Log("TEST:", test.Name) f := func() { e := npoint.CreateEndpoint(test.Chain...) b := NewBinder() b.Bind("/foo", e) + // nolint:bodyclose b.Call("/foo", "GET", "", nil) } if test.Panics { diff --git a/npoint/example_preregister_test.go b/npoint/example_preregister_test.go index f4d76d4..0339b72 100644 --- a/npoint/example_preregister_test.go +++ b/npoint/example_preregister_test.go @@ -109,6 +109,7 @@ func Example() { service.Start(muxRouter) localServer := httptest.NewServer(muxRouter) defer localServer.Close() + // nolint:noctx r, err := http.Get(localServer.URL + "/example?foo=bar") if err != nil { fmt.Println("get error", err) @@ -119,6 +120,7 @@ func Example() { fmt.Println("read error", err) return } + r.Body.Close() var res map[string]string err = json.Unmarshal(buf, &res) if err != nil { diff --git a/npoint/gorilla.go b/npoint/gorilla.go index f86240f..e8a59a4 100644 --- a/npoint/gorilla.go +++ b/npoint/gorilla.go @@ -137,10 +137,10 @@ func (r *EndpointRegistrationWithMux) URL(pairs ...string) (*url.URL, error) { // URLHost calls the mux.Route method of the same name on the route created for this endpoint. func (r *EndpointRegistrationWithMux) URLHost(pairs ...string) (*url.URL, error) { - return r.URLHost(pairs...) + return r.route.URLHost(pairs...) } // URLPath calls the mux.Route method of the same name on the route created for this endpoint. func (r *EndpointRegistrationWithMux) URLPath(pairs ...string) (*url.URL, error) { - return r.URLPath(pairs...) + return r.route.URLPath(pairs...) } diff --git a/npoint/regression_test.go b/npoint/regression_test.go index d329880..7777838 100644 --- a/npoint/regression_test.go +++ b/npoint/regression_test.go @@ -57,12 +57,20 @@ func TestSaveRequest(t *testing.T) { localServer := httptest.NewServer(muxRouter) defer localServer.Close() - _, err := http.Post(localServer.URL+"/ept", "text/plain", ioutil.NopCloser(bytes.NewBuffer([]byte("some stuff")))) + // nolint:noctx + resp, err := http.Post(localServer.URL+"/ept", "text/plain", ioutil.NopCloser(bytes.NewBuffer([]byte("some stuff")))) assert.NoError(t, err) assert.True(t, calledPost) assert.False(t, calledGet) + if resp != nil { + resp.Body.Close() + } - _, err = http.Get(localServer.URL + "/ept") + // nolint:noctx + resp, err = http.Get(localServer.URL + "/ept") assert.NoError(t, err) assert.True(t, calledGet) + if resp != nil { + resp.Body.Close() + } } diff --git a/npoint/service_test.go b/npoint/service_test.go index 886209f..3c95951 100644 --- a/npoint/service_test.go +++ b/npoint/service_test.go @@ -252,8 +252,10 @@ func multiStartups( b := NewBinder() s.Start(b.Bind) afterStart(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall1(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall2(n) } @@ -267,8 +269,10 @@ func multiStartups( s.RegisterEndpoint(ept, hc) // afterRegister(n) afterStart(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall1(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall2(n) } @@ -282,8 +286,10 @@ func multiStartups( sr.RegisterEndpoint(ept, hc) // afterRegister(n) afterStart(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall1(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall2(n) } @@ -300,12 +306,20 @@ func multiStartups( defer localServer.Close() afterStart(n) t.Logf("GET %s%s\n", localServer.URL, ept) - _, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err := client.Get(localServer.URL + ept) assert.NoError(t, err) + if resp != nil { + resp.Body.Close() + } afterCall1(n) - resp, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err = client.Get(localServer.URL + ept) assert.NoError(t, err, name) - assert.Equal(t, 204, resp.StatusCode, name) + if resp != nil { + assert.Equal(t, 204, resp.StatusCode, name) + resp.Body.Close() + } afterCall2(n) } { @@ -321,12 +335,20 @@ func multiStartups( // afterRegister(n) afterStart(n) t.Logf("GET %s%s\n", localServer.URL, ept) - _, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err := client.Get(localServer.URL + ept) assert.NoError(t, err) + if resp != nil { + resp.Body.Close() + } afterCall1(n) - resp, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err = client.Get(localServer.URL + ept) assert.NoError(t, err, name) - assert.Equal(t, 204, resp.StatusCode, name) + if resp != nil { + assert.Equal(t, 204, resp.StatusCode, name) + resp.Body.Close() + } afterCall2(n) } { @@ -342,12 +364,20 @@ func multiStartups( // afterRegister(n) afterStart(n) t.Logf("GET %s%s\n", localServer.URL, ept) - _, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err := client.Get(localServer.URL + ept) assert.NoError(t, err) + if resp != nil { + resp.Body.Close() + } afterCall1(n) - resp, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err = client.Get(localServer.URL + ept) assert.NoError(t, err, name) - assert.Equal(t, 204, resp.StatusCode, name) + if resp != nil { + assert.Equal(t, 204, resp.StatusCode, name) + resp.Body.Close() + } afterCall2(n) } { @@ -359,8 +389,10 @@ func multiStartups( b.Bind(ept, e) // afterRegister(n) afterStart(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall1(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall2(n) } @@ -373,8 +405,10 @@ func multiStartups( s.RegisterEndpoint(ept, hc) // afterRegister(n) afterStart(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall1(n) + // nolint:bodyclose b.Call(ept, "GET", "", nil) afterCall2(n) } @@ -390,12 +424,20 @@ func multiStartups( defer localServer.Close() afterStart(n) t.Logf("GET %s%s\n", localServer.URL, ept) - _, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err := client.Get(localServer.URL + ept) assert.NoError(t, err) + if resp != nil { + resp.Body.Close() + } afterCall1(n) - resp, err := client.Get(localServer.URL + ept) + // nolint:noctx + resp, err = client.Get(localServer.URL + ept) assert.NoError(t, err, name) - assert.Equal(t, 204, resp.StatusCode, name) + if resp != nil { + assert.Equal(t, 204, resp.StatusCode, name) + resp.Body.Close() + } afterCall2(n) } } @@ -418,11 +460,19 @@ func TestMuxModifiers(t *testing.T) { localServer := httptest.NewServer(muxRouter) defer localServer.Close() + // nolint:noctx resp, err := client.Get(localServer.URL + "/x") - assert.NoError(t, err) + if !assert.NoError(t, err) { + return + } + resp.Body.Close() assert.Equal(t, 204, resp.StatusCode) + // nolint:noctx resp, err = client.Post(localServer.URL+"/x", "application/json", nil) - assert.NoError(t, err) + if !assert.NoError(t, err) { + return + } + resp.Body.Close() assert.Equal(t, 205, resp.StatusCode) } diff --git a/nserve/example_test.go b/nserve/example_test.go index c01aecc..35d656d 100644 --- a/nserve/example_test.go +++ b/nserve/example_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/muir/nject/nserve" + "github.com/pkg/errors" ) type L1 struct{} @@ -45,7 +46,7 @@ func NewL3(_ *L2, app *nserve.App) *L3 { } func ErrorCombiner(e1, e2 error) error { - return fmt.Errorf("%s; %s", e1.Error(), e2.Error()) + return errors.New(e1.Error() + "; " + e2.Error()) } // Example shows the injection, startup, and shutdown of an app with two libraries diff --git a/nserve/hook.go b/nserve/hook.go index 365a3e7..9cbe21f 100644 --- a/nserve/hook.go +++ b/nserve/hook.go @@ -10,6 +10,7 @@ var hookCounter int32 type hookOrder string const ( + // nolint:staticcheck ForwardOrder hookOrder = "forward" ReverseOrder = "forward" ) @@ -19,7 +20,7 @@ type hookId int32 // Hook is the handle/name for a list of callbacks to invoke. type Hook struct { Id hookId - lock sync.Mutex + lock *sync.Mutex Name string Order hookOrder InvokeOnError []*Hook @@ -41,6 +42,7 @@ func (h *Hook) Copy() *Hook { hc.InvokeOnError = oe hc.Id = hookId(atomic.AddInt32(&hookCounter, 1)) hc.Providers = op + hc.lock = new(sync.Mutex) return &hc } @@ -50,6 +52,7 @@ func NewHook(name string, order hookOrder) *Hook { Id: hookId(atomic.AddInt32(&hookCounter, 1)), Name: name, Order: order, + lock: new(sync.Mutex), } } diff --git a/nvelope/decode.go b/nvelope/decode.go index 6af4a9f..2b00965 100644 --- a/nvelope/decode.go +++ b/nvelope/decode.go @@ -25,6 +25,7 @@ type Body []byte var ReadBody = nject.Provide("read-body", readBody) func readBody(r *http.Request) (Body, nject.TerminalError) { + // nolint:errcheck defer r.Body.Close() body, err := io.ReadAll(r.Body) return Body(body), err @@ -49,7 +50,6 @@ type Decoder func([]byte, interface{}) error type eigo struct { tag string decoders map[string]Decoder - modelValidators []func(interface{}) error defaultContentType string } @@ -155,6 +155,7 @@ func GenerateDecoder( returnType := missingType var nonPointer reflect.Type var returnAddress bool + // nolint:exhaustive switch missingType.Kind() { case reflect.Struct: nonPointer = returnType @@ -283,6 +284,7 @@ func GenerateDecoder( outputs := []reflect.Type{returnType, terminalErrorType} reflective := nject.MakeReflective(inputs, outputs, func(in []reflect.Value) []reflect.Value { + // nolint:errcheck r := in[0].Interface().(*http.Request) mp := reflect.New(nonPointer) model := mp.Elem() @@ -346,6 +348,7 @@ func multiUnpack( return nil } +// getUnpacker is used for unpacking headers, query parameters, and path elements func getUnpacker(fieldType reflect.Type, fieldName string, name string, ) (func(from string, target reflect.Value, value string) error, error) { if fieldType.AssignableTo(textUnmarshallerType) { @@ -363,6 +366,16 @@ func getUnpacker(fieldType reflect.Type, fieldName string, name string, }, nil } switch fieldType.Kind() { + case reflect.Ptr: + vu, err := getUnpacker(fieldType.Elem(), fieldName, name) + if err != nil { + return nil, err + } + return func(from string, target reflect.Value, value string) error { + p := reflect.New(fieldType.Elem()) + target.Set(p) + return vu(from, target.Elem(), value) + }, nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return func(from string, target reflect.Value, value string) error { i, err := strconv.ParseInt(value, 10, 64) @@ -395,8 +408,29 @@ func getUnpacker(fieldType reflect.Type, fieldName string, name string, target.SetString(value) return nil }, nil + case reflect.Complex64, reflect.Complex128: + return func(from string, target reflect.Value, value string) error { + c, err := strconv.ParseComplex(value, 128) + if err != nil { + return errors.Wrapf(err, "decode %s %s", from, name) + } + target.SetComplex(c) + return nil + }, nil + case reflect.Bool: + return func(from string, target reflect.Value, value string) error { + b, err := strconv.ParseBool(value) + if err != nil { + return errors.Wrapf(err, "decode %s %s", from, name) + } + target.SetBool(b) + return nil + }, nil // TODO: case reflect.Slice: // TODO: case reflect.Array: + case reflect.Chan, reflect.Interface, reflect.UnsafePointer, reflect.Func, reflect.Invalid, + reflect.Struct, reflect.Map, reflect.Array, reflect.Slice: + fallthrough default: return nil, errors.Errorf( "Cannot decode into %s, %s does not implement UnmarshalText", @@ -419,7 +453,6 @@ func walkStructElements(t reflect.Type, f func(reflect.StructField) bool) { if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { doWalkStructElements(t.Elem(), []int{}, f) } - return } func doWalkStructElements(t reflect.Type, path []int, f func(reflect.StructField) bool) { diff --git a/nvelope/deferred.go b/nvelope/deferred.go index 84d495e..cb63f9a 100644 --- a/nvelope/deferred.go +++ b/nvelope/deferred.go @@ -118,7 +118,7 @@ func (w *DeferredWriter) Flush() error { // Is this handling of short writes necessary? Perhaps // so since a follow-up write will probably give a // more accurate error. - if err == io.ErrShortWrite { + if errors.Is(err, io.ErrShortWrite) { i += amt continue } @@ -129,10 +129,9 @@ func (w *DeferredWriter) Flush() error { return nil } -// TODO: make this public or remove it -// flushIfNotFlushed calls Flush if the DeferredWriter is not in +// FlushIfNotFlushed calls Flush if the DeferredWriter is not in // passthrough mode. -func (w *DeferredWriter) flushIfNotFlushed() error { +func (w *DeferredWriter) FlushIfNotFlushed() error { if !w.passthrough { return w.Flush() } diff --git a/nvelope/deferred_test.go b/nvelope/deferred_test.go index 305217e..973b64a 100644 --- a/nvelope/deferred_test.go +++ b/nvelope/deferred_test.go @@ -24,6 +24,7 @@ func (w *testResponseWriter) Header() http.Header { return w.header } func (w *testResponseWriter) WriteHeader(code int) { w.code = code } func (w *testResponseWriter) Write(b []byte) (int, error) { if w.simulateWriteError != nil { + // nolint:errorlint if w.simulateWriteError == io.ErrShortWrite { if len(b) == 0 { return 0, nil diff --git a/nvelope/encode.go b/nvelope/encode.go index a509996..64c5489 100644 --- a/nvelope/encode.go +++ b/nvelope/encode.go @@ -18,7 +18,7 @@ var InjectWriter = nject.Provide("writer", NewDeferredWriter) var AutoFlushWriter = nject.Provide("autoflush-writer", func(inner func(), w *DeferredWriter) { inner() if !w.Done() { - w.Flush() + _ = w.Flush() } }) @@ -30,8 +30,9 @@ type Response interface{} var EncodeJSON = MakeResponseEncoder("JSON", WithEncoder("application/json", json.Marshal, WithEncoderErrorTransform(func(err error) (interface{}, bool) { - if m, ok := err.(json.Marshaler); ok { - return m, true + var jm json.Marshaler + if errors.As(err, &jm) { + return jm, true } return nil, false }), @@ -41,8 +42,9 @@ var EncodeJSON = MakeResponseEncoder("JSON", var EncodeXML = MakeResponseEncoder("XML", WithEncoder("application/xml", xml.Marshal, WithEncoderErrorTransform(func(err error) (interface{}, bool) { - if m, ok := err.(xml.Marshaler); ok { - return m, true + var me xml.Marshaler + if errors.As(err, &me) { + return me, true } return nil, false }), @@ -211,8 +213,11 @@ func MakeResponseEncoder( handleError(true) } w.WriteHeader(code) - w.Write(enc) - err = w.Flush() + _, err = w.Write(enc) + e2 := w.Flush() + if err == nil { + err = e2 + } if err != nil { log.Warn("Cannot write response", map[string]interface{}{ @@ -236,6 +241,6 @@ func nil204(inner func() (Response, error), w *DeferredWriter) { } if err == nil && model == nil { w.WriteHeader(204) - w.Flush() + _ = w.Flush() } } diff --git a/nvelope/errors.go b/nvelope/errors.go index 8ab12fc..566d52b 100644 --- a/nvelope/errors.go +++ b/nvelope/errors.go @@ -2,18 +2,9 @@ package nvelope import ( "encoding" + "errors" ) -type causer interface { - error - Cause() error -} - -type unwraper interface { - error - Unwrap() error -} - // ReturnCode associates an HTTP return code with a error. // if err is nil, then nil is returned. func ReturnCode(err error, code int) error { @@ -60,20 +51,11 @@ func Forbidden(err error) error { } func GetReturnCode(err error) int { - for { - if rc, ok := err.(returnCode); ok { - return rc.code - } - if c, ok := err.(causer); ok { - err = c.Cause() - continue - } - if u, ok := err.(unwraper); ok { - err = u.Unwrap() - continue - } - return 500 + var rc returnCode + if errors.As(err, &rc) { + return rc.code } + return 500 } type CanModel interface { diff --git a/nvelope/example_middleware_test.go b/nvelope/example_middleware_test.go index 976a20b..41dabab 100644 --- a/nvelope/example_middleware_test.go +++ b/nvelope/example_middleware_test.go @@ -74,7 +74,7 @@ func ServiceWithMiddleware(router *mux.Router) { // Example shows an injection chain handling a single endpoint using nject, // npoint, and nvelope. -func Example_MiddlewareDeferredWriter() { +func ExampleServiceWithMiddleware() { r := mux.NewRouter() ServiceWithMiddleware(r) ts := httptest.NewServer(r) @@ -96,6 +96,7 @@ func Example_MiddlewareDeferredWriter() { fmt.Println("read error:", err) return } + res.Body.Close() fmt.Println(res.StatusCode, "->"+string(b)) } doGet("/a/path/john/37", "good") diff --git a/nvelope/example_test.go b/nvelope/example_test.go index 88b7fdc..4f8a0ea 100644 --- a/nvelope/example_test.go +++ b/nvelope/example_test.go @@ -14,7 +14,7 @@ import ( "github.com/muir/nject/nvelope" ) -// nolint:deadcode +// nolint:deadcode,unused func main() { r := mux.NewRouter() srv := &http.Server{ @@ -33,7 +33,7 @@ type PostBodyModel struct { type ExampleRequestBundle struct { Request PostBodyModel `nvelope:"model"` - With string `nvelope:"path,name=with"` + With *string `nvelope:"path,name=with"` Parameters int64 `nvelope:"path,name=parameters"` Friends []int `nvelope:"query,name=friends"` ContentType string `nvelope:"header,name=Content-Type"` @@ -55,7 +55,7 @@ func HandleExampleEndpoint(req ExampleRequestBundle) (nvelope.Response, error) { return nil, nil default: return ExampleResponse{ - Stuff: "something useful", + Stuff: *req.With, }, nil } } @@ -83,6 +83,7 @@ func Example() { ts := httptest.NewServer(r) client := ts.Client() doPost := func(url string, body string) { + // nolint:noctx res, err := client.Post(ts.URL+url, "application/json", strings.NewReader(body)) if err != nil { @@ -94,6 +95,7 @@ func Example() { fmt.Println("read error:", err) return } + res.Body.Close() fmt.Println(res.StatusCode, "->"+string(b)) } doPost("/a/path/joe/37", `{"Use":"yeah","Exported":"uh hu"}`) @@ -101,7 +103,7 @@ func Example() { doPost("/a/path/joe/38", `invalid json`) doPost("/a/path/joe/666", `{"Use":"yeah","Exported":"uh hu"}`) - // Output: 200 ->{"stuff":"something useful"} + // Output: 200 ->{"stuff":"joe"} // 204 -> // 400 ->nvelope_test.ExampleRequestBundle model: Could not decode application/json into nvelope_test.PostBodyModel: invalid character 'i' looking for beginning of value // 500 ->panic: something is not right diff --git a/nvelope/logger.go b/nvelope/logger.go index dc82700..07d5367 100644 --- a/nvelope/logger.go +++ b/nvelope/logger.go @@ -62,6 +62,6 @@ type nilLogger struct{} var _ BasicLogger = nilLogger{} -func (_ nilLogger) Error(msg string, fields ...map[string]interface{}) { return } -func (_ nilLogger) Warn(msg string, fields ...map[string]interface{}) { return } -func (_ nilLogger) Debug(msg string, fields ...map[string]interface{}) { return } +func (_ nilLogger) Error(msg string, fields ...map[string]interface{}) {} +func (_ nilLogger) Warn(msg string, fields ...map[string]interface{}) {} +func (_ nilLogger) Debug(msg string, fields ...map[string]interface{}) {} diff --git a/nvelope/panic.go b/nvelope/panic.go index ba42bb1..9be9fa2 100644 --- a/nvelope/panic.go +++ b/nvelope/panic.go @@ -77,18 +77,9 @@ func RecoverStack(err error) string { } func isPanicError(err error) (panicError, bool) { - for { - if pe, ok := err.(panicError); ok { - return pe, true - } - if c, ok := err.(causer); ok { - err = c.Cause() - continue - } - if u, ok := err.(unwraper); ok { - err = u.Unwrap() - continue - } - return panicError{}, false + var pe panicError + if errors.As(err, &pe) { + return pe, true } + return panicError{}, false } From 5ca6e57ef66010e29acb440c1f5cde61939ea7f2 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Fri, 24 Sep 2021 22:18:50 -0700 Subject: [PATCH 16/16] still fighting with golangCI-lint --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 023cc3e..067e1a5 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -18,7 +18,7 @@ jobs: # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 - args: --out-format checkstyle + # args: --out-format checkstyle # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true