From 7c3db81921ceea352412011ea6cd8961e7224fb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:15:39 +0000 Subject: mod: bump github.com/golangci/golangci-lint from 1.62.0 to 1.62.2 Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.62.0 to 1.62.2. - [Release notes](https://github.com/golangci/golangci-lint/releases) - [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md) - [Commits](https://github.com/golangci/golangci-lint/compare/v1.62.0...v1.62.2) --- updated-dependencies: - dependency-name: github.com/golangci/golangci-lint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 16 +- go.sum | 32 +- .../internal/checkers/encoded_compare.go | 2 +- .../testifylint/internal/checkers/formatter.go | 8 +- .../internal/checkers/helpers_basic_type.go | 6 + .../internal/checkers/helpers_encoded.go | 24 +- .../testifylint/internal/config/config.go | 2 +- .../Crocmagnon/fatcontext/pkg/analyzer/analyzer.go | 8 +- .../golangci-lint/internal/go/cache/cache.go | 2 +- .../pkg/result/processors/sort_results.go | 211 +++------- vendor/github.com/mgechev/revive/lint/linter.go | 10 +- vendor/github.com/mgechev/revive/lint/package.go | 2 +- .../github.com/mgechev/revive/rule/add-constant.go | 269 ------------- .../github.com/mgechev/revive/rule/add_constant.go | 267 +++++++++++++ .../mgechev/revive/rule/argument-limit.go | 89 ----- .../mgechev/revive/rule/argument_limit.go | 84 ++++ .../mgechev/revive/rule/banned-characters.go | 90 ----- .../mgechev/revive/rule/banned_characters.go | 89 +++++ .../github.com/mgechev/revive/rule/bare-return.go | 84 ---- .../github.com/mgechev/revive/rule/bare_return.go | 84 ++++ .../mgechev/revive/rule/blank-imports.go | 75 ---- .../mgechev/revive/rule/blank_imports.go | 75 ++++ .../mgechev/revive/rule/bool-literal-in-expr.go | 72 ---- .../mgechev/revive/rule/bool_literal_in_expr.go | 72 ++++ .../github.com/mgechev/revive/rule/call-to-gc.go | 70 ---- .../github.com/mgechev/revive/rule/call_to_gc.go | 70 ++++ .../mgechev/revive/rule/cognitive-complexity.go | 214 ----------- .../mgechev/revive/rule/cognitive_complexity.go | 209 ++++++++++ .../mgechev/revive/rule/comment-spacings.go | 87 ----- .../mgechev/revive/rule/comment_spacings.go | 82 ++++ .../mgechev/revive/rule/comments-density.go | 96 ----- .../mgechev/revive/rule/comments_density.go | 87 +++++ .../mgechev/revive/rule/confusing-naming.go | 207 ---------- .../mgechev/revive/rule/confusing-results.go | 66 ---- .../mgechev/revive/rule/confusing_naming.go | 207 ++++++++++ .../mgechev/revive/rule/confusing_results.go | 66 ++++ .../mgechev/revive/rule/constant-logical-expr.go | 101 ----- .../mgechev/revive/rule/constant_logical_expr.go | 101 +++++ .../mgechev/revive/rule/context-as-argument.go | 110 ------ .../mgechev/revive/rule/context-keys-type.go | 81 ---- .../mgechev/revive/rule/context_as_argument.go | 109 ++++++ .../mgechev/revive/rule/context_keys_type.go | 81 ++++ .../github.com/mgechev/revive/rule/cyclomatic.go | 11 +- vendor/github.com/mgechev/revive/rule/deep-exit.go | 95 ----- vendor/github.com/mgechev/revive/rule/deep_exit.go | 95 +++++ vendor/github.com/mgechev/revive/rule/defer.go | 11 +- .../github.com/mgechev/revive/rule/dot-imports.go | 107 ------ .../github.com/mgechev/revive/rule/dot_imports.go | 101 +++++ .../mgechev/revive/rule/duplicated-imports.go | 39 -- .../mgechev/revive/rule/duplicated_imports.go | 39 ++ .../github.com/mgechev/revive/rule/early-return.go | 51 --- .../github.com/mgechev/revive/rule/early_return.go | 51 +++ .../github.com/mgechev/revive/rule/empty-block.go | 75 ---- .../github.com/mgechev/revive/rule/empty-lines.go | 105 ----- .../github.com/mgechev/revive/rule/empty_block.go | 75 ++++ .../github.com/mgechev/revive/rule/empty_lines.go | 105 +++++ .../mgechev/revive/rule/enforce-map-style.go | 163 -------- .../revive/rule/enforce-repeated-arg-type-style.go | 187 --------- .../mgechev/revive/rule/enforce-slice-style.go | 213 ---------- .../mgechev/revive/rule/enforce_map_style.go | 155 ++++++++ .../revive/rule/enforce_repeated_arg_type_style.go | 178 +++++++++ .../mgechev/revive/rule/enforce_slice_style.go | 205 ++++++++++ .../github.com/mgechev/revive/rule/error-naming.go | 79 ---- .../github.com/mgechev/revive/rule/error-return.go | 67 ---- .../mgechev/revive/rule/error-strings.go | 199 ---------- .../github.com/mgechev/revive/rule/error_naming.go | 79 ++++ .../github.com/mgechev/revive/rule/error_return.go | 67 ++++ .../mgechev/revive/rule/error_strings.go | 193 ++++++++++ vendor/github.com/mgechev/revive/rule/exported.go | 13 +- .../github.com/mgechev/revive/rule/file-header.go | 89 ----- .../mgechev/revive/rule/file-length-limit.go | 138 ------- .../github.com/mgechev/revive/rule/file_header.go | 84 ++++ .../mgechev/revive/rule/file_length_limit.go | 132 +++++++ .../mgechev/revive/rule/filename-format.go | 87 ----- .../mgechev/revive/rule/filename_format.go | 81 ++++ .../github.com/mgechev/revive/rule/flag-param.go | 105 ----- .../github.com/mgechev/revive/rule/flag_param.go | 105 +++++ .../mgechev/revive/rule/function-length.go | 181 --------- .../mgechev/revive/rule/function-result-limit.go | 90 ----- .../mgechev/revive/rule/function_length.go | 174 +++++++++ .../mgechev/revive/rule/function_result_limit.go | 85 ++++ .../github.com/mgechev/revive/rule/get-return.go | 80 ---- .../github.com/mgechev/revive/rule/get_return.go | 80 ++++ .../mgechev/revive/rule/identical-branches.go | 87 ----- .../mgechev/revive/rule/identical_branches.go | 87 +++++ vendor/github.com/mgechev/revive/rule/if-return.go | 115 ------ vendor/github.com/mgechev/revive/rule/if_return.go | 115 ++++++ .../mgechev/revive/rule/import-alias-naming.go | 126 ------ .../mgechev/revive/rule/import-shadowing.go | 115 ------ .../mgechev/revive/rule/import_alias_naming.go | 120 ++++++ .../mgechev/revive/rule/import_shadowing.go | 115 ++++++ .../mgechev/revive/rule/imports-blocklist.go | 73 ---- .../mgechev/revive/rule/imports_blocklist.go | 68 ++++ .../mgechev/revive/rule/increment-decrement.go | 73 ---- .../mgechev/revive/rule/increment_decrement.go | 73 ++++ .../mgechev/revive/rule/indent-error-flow.go | 45 --- .../mgechev/revive/rule/indent_error_flow.go | 45 +++ .../mgechev/revive/rule/line-length-limit.go | 104 ----- .../mgechev/revive/rule/line_length_limit.go | 99 +++++ .../mgechev/revive/rule/max-control-nesting.go | 128 ------ .../mgechev/revive/rule/max-public-structs.go | 90 ----- .../mgechev/revive/rule/max_control_nesting.go | 123 ++++++ .../mgechev/revive/rule/max_public_structs.go | 90 +++++ .../mgechev/revive/rule/modifies-param.go | 80 ---- .../mgechev/revive/rule/modifies-value-receiver.go | 129 ------- .../mgechev/revive/rule/modifies_param.go | 80 ++++ .../mgechev/revive/rule/modifies_value_receiver.go | 179 +++++++++ .../mgechev/revive/rule/nested-structs.go | 75 ---- .../mgechev/revive/rule/nested_structs.go | 75 ++++ .../mgechev/revive/rule/optimize-operands-order.go | 86 ----- .../mgechev/revive/rule/optimize_operands_order.go | 86 +++++ .../mgechev/revive/rule/package-comments.go | 164 -------- .../mgechev/revive/rule/package_comments.go | 164 ++++++++ .../mgechev/revive/rule/range-val-address.go | 166 -------- .../mgechev/revive/rule/range-val-in-closure.go | 125 ------ .../mgechev/revive/rule/range_val_address.go | 166 ++++++++ .../mgechev/revive/rule/range_val_in_closure.go | 125 ++++++ .../mgechev/revive/rule/receiver-naming.go | 133 ------- .../mgechev/revive/rule/receiver_naming.go | 128 ++++++ .../mgechev/revive/rule/redefines-builtin-id.go | 224 ----------- .../mgechev/revive/rule/redefines_builtin_id.go | 224 +++++++++++ .../mgechev/revive/rule/redundant-import-alias.go | 52 --- .../mgechev/revive/rule/redundant_import_alias.go | 52 +++ .../mgechev/revive/rule/string-format.go | 328 ---------------- .../mgechev/revive/rule/string-of-int.go | 95 ----- .../mgechev/revive/rule/string_format.go | 328 ++++++++++++++++ .../mgechev/revive/rule/string_of_int.go | 95 +++++ .../github.com/mgechev/revive/rule/struct-tag.go | 427 --------------------- .../github.com/mgechev/revive/rule/struct_tag.go | 424 ++++++++++++++++++++ .../mgechev/revive/rule/superfluous-else.go | 47 --- .../mgechev/revive/rule/superfluous_else.go | 47 +++ .../github.com/mgechev/revive/rule/time-equal.go | 73 ---- .../github.com/mgechev/revive/rule/time-naming.go | 96 ----- .../github.com/mgechev/revive/rule/time_equal.go | 73 ++++ .../github.com/mgechev/revive/rule/time_naming.go | 96 +++++ .../revive/rule/unchecked-type-assertion.go | 194 ---------- .../revive/rule/unchecked_type_assertion.go | 189 +++++++++ .../mgechev/revive/rule/unconditional-recursion.go | 200 ---------- .../mgechev/revive/rule/unconditional_recursion.go | 200 ++++++++++ .../mgechev/revive/rule/unexported-naming.go | 115 ------ .../mgechev/revive/rule/unexported-return.go | 107 ------ .../mgechev/revive/rule/unexported_naming.go | 115 ++++++ .../mgechev/revive/rule/unexported_return.go | 107 ++++++ .../mgechev/revive/rule/unhandled-error.go | 181 --------- .../mgechev/revive/rule/unhandled_error.go | 175 +++++++++ .../mgechev/revive/rule/unnecessary-stmt.go | 107 ------ .../mgechev/revive/rule/unnecessary_stmt.go | 107 ++++++ .../mgechev/revive/rule/unreachable-code.go | 122 ------ .../mgechev/revive/rule/unreachable_code.go | 122 ++++++ .../github.com/mgechev/revive/rule/unused-param.go | 167 -------- .../mgechev/revive/rule/unused-receiver.go | 133 ------- .../github.com/mgechev/revive/rule/unused_param.go | 159 ++++++++ .../mgechev/revive/rule/unused_receiver.go | 125 ++++++ vendor/github.com/mgechev/revive/rule/use-any.go | 54 --- vendor/github.com/mgechev/revive/rule/use_any.go | 54 +++ .../mgechev/revive/rule/useless-break.go | 82 ---- .../mgechev/revive/rule/useless_break.go | 82 ++++ .../mgechev/revive/rule/var-declarations.go | 122 ------ .../github.com/mgechev/revive/rule/var-naming.go | 280 -------------- .../mgechev/revive/rule/var_declarations.go | 122 ++++++ .../github.com/mgechev/revive/rule/var_naming.go | 273 +++++++++++++ .../mgechev/revive/rule/waitgroup-by-value.go | 66 ---- .../mgechev/revive/rule/waitgroup_by_value.go | 66 ++++ vendor/github.com/nunnatsa/ginkgolinter/README.md | 6 +- .../internal/expression/actual/actual.go | 4 +- .../internal/expression/actual/actualarg.go | 27 +- .../ginkgolinter/internal/expression/expression.go | 6 +- .../internal/gomegahandler/dothandler.go | 8 +- .../ginkgolinter/internal/gomegahandler/handler.go | 2 +- .../internal/gomegahandler/namedhandler.go | 7 +- .../internal/rules/errorequalnilrule.go | 11 +- .../polyfloyd/go-errorlint/errorlint/allowed.go | 2 + .../polyfloyd/go-errorlint/errorlint/lint.go | 2 +- vendor/github.com/uudashr/iface/opaque/opaque.go | 7 +- vendor/modules.txt | 18 +- 175 files changed, 9140 insertions(+), 9305 deletions(-) delete mode 100644 vendor/github.com/mgechev/revive/rule/add-constant.go create mode 100644 vendor/github.com/mgechev/revive/rule/add_constant.go delete mode 100644 vendor/github.com/mgechev/revive/rule/argument-limit.go create mode 100644 vendor/github.com/mgechev/revive/rule/argument_limit.go delete mode 100644 vendor/github.com/mgechev/revive/rule/banned-characters.go create mode 100644 vendor/github.com/mgechev/revive/rule/banned_characters.go delete mode 100644 vendor/github.com/mgechev/revive/rule/bare-return.go create mode 100644 vendor/github.com/mgechev/revive/rule/bare_return.go delete mode 100644 vendor/github.com/mgechev/revive/rule/blank-imports.go create mode 100644 vendor/github.com/mgechev/revive/rule/blank_imports.go delete mode 100644 vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go create mode 100644 vendor/github.com/mgechev/revive/rule/bool_literal_in_expr.go delete mode 100644 vendor/github.com/mgechev/revive/rule/call-to-gc.go create mode 100644 vendor/github.com/mgechev/revive/rule/call_to_gc.go delete mode 100644 vendor/github.com/mgechev/revive/rule/cognitive-complexity.go create mode 100644 vendor/github.com/mgechev/revive/rule/cognitive_complexity.go delete mode 100644 vendor/github.com/mgechev/revive/rule/comment-spacings.go create mode 100644 vendor/github.com/mgechev/revive/rule/comment_spacings.go delete mode 100644 vendor/github.com/mgechev/revive/rule/comments-density.go create mode 100644 vendor/github.com/mgechev/revive/rule/comments_density.go delete mode 100644 vendor/github.com/mgechev/revive/rule/confusing-naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/confusing-results.go create mode 100644 vendor/github.com/mgechev/revive/rule/confusing_naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/confusing_results.go delete mode 100644 vendor/github.com/mgechev/revive/rule/constant-logical-expr.go create mode 100644 vendor/github.com/mgechev/revive/rule/constant_logical_expr.go delete mode 100644 vendor/github.com/mgechev/revive/rule/context-as-argument.go delete mode 100644 vendor/github.com/mgechev/revive/rule/context-keys-type.go create mode 100644 vendor/github.com/mgechev/revive/rule/context_as_argument.go create mode 100644 vendor/github.com/mgechev/revive/rule/context_keys_type.go delete mode 100644 vendor/github.com/mgechev/revive/rule/deep-exit.go create mode 100644 vendor/github.com/mgechev/revive/rule/deep_exit.go delete mode 100644 vendor/github.com/mgechev/revive/rule/dot-imports.go create mode 100644 vendor/github.com/mgechev/revive/rule/dot_imports.go delete mode 100644 vendor/github.com/mgechev/revive/rule/duplicated-imports.go create mode 100644 vendor/github.com/mgechev/revive/rule/duplicated_imports.go delete mode 100644 vendor/github.com/mgechev/revive/rule/early-return.go create mode 100644 vendor/github.com/mgechev/revive/rule/early_return.go delete mode 100644 vendor/github.com/mgechev/revive/rule/empty-block.go delete mode 100644 vendor/github.com/mgechev/revive/rule/empty-lines.go create mode 100644 vendor/github.com/mgechev/revive/rule/empty_block.go create mode 100644 vendor/github.com/mgechev/revive/rule/empty_lines.go delete mode 100644 vendor/github.com/mgechev/revive/rule/enforce-map-style.go delete mode 100644 vendor/github.com/mgechev/revive/rule/enforce-repeated-arg-type-style.go delete mode 100644 vendor/github.com/mgechev/revive/rule/enforce-slice-style.go create mode 100644 vendor/github.com/mgechev/revive/rule/enforce_map_style.go create mode 100644 vendor/github.com/mgechev/revive/rule/enforce_repeated_arg_type_style.go create mode 100644 vendor/github.com/mgechev/revive/rule/enforce_slice_style.go delete mode 100644 vendor/github.com/mgechev/revive/rule/error-naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/error-return.go delete mode 100644 vendor/github.com/mgechev/revive/rule/error-strings.go create mode 100644 vendor/github.com/mgechev/revive/rule/error_naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/error_return.go create mode 100644 vendor/github.com/mgechev/revive/rule/error_strings.go delete mode 100644 vendor/github.com/mgechev/revive/rule/file-header.go delete mode 100644 vendor/github.com/mgechev/revive/rule/file-length-limit.go create mode 100644 vendor/github.com/mgechev/revive/rule/file_header.go create mode 100644 vendor/github.com/mgechev/revive/rule/file_length_limit.go delete mode 100644 vendor/github.com/mgechev/revive/rule/filename-format.go create mode 100644 vendor/github.com/mgechev/revive/rule/filename_format.go delete mode 100644 vendor/github.com/mgechev/revive/rule/flag-param.go create mode 100644 vendor/github.com/mgechev/revive/rule/flag_param.go delete mode 100644 vendor/github.com/mgechev/revive/rule/function-length.go delete mode 100644 vendor/github.com/mgechev/revive/rule/function-result-limit.go create mode 100644 vendor/github.com/mgechev/revive/rule/function_length.go create mode 100644 vendor/github.com/mgechev/revive/rule/function_result_limit.go delete mode 100644 vendor/github.com/mgechev/revive/rule/get-return.go create mode 100644 vendor/github.com/mgechev/revive/rule/get_return.go delete mode 100644 vendor/github.com/mgechev/revive/rule/identical-branches.go create mode 100644 vendor/github.com/mgechev/revive/rule/identical_branches.go delete mode 100644 vendor/github.com/mgechev/revive/rule/if-return.go create mode 100644 vendor/github.com/mgechev/revive/rule/if_return.go delete mode 100644 vendor/github.com/mgechev/revive/rule/import-alias-naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/import-shadowing.go create mode 100644 vendor/github.com/mgechev/revive/rule/import_alias_naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/import_shadowing.go delete mode 100644 vendor/github.com/mgechev/revive/rule/imports-blocklist.go create mode 100644 vendor/github.com/mgechev/revive/rule/imports_blocklist.go delete mode 100644 vendor/github.com/mgechev/revive/rule/increment-decrement.go create mode 100644 vendor/github.com/mgechev/revive/rule/increment_decrement.go delete mode 100644 vendor/github.com/mgechev/revive/rule/indent-error-flow.go create mode 100644 vendor/github.com/mgechev/revive/rule/indent_error_flow.go delete mode 100644 vendor/github.com/mgechev/revive/rule/line-length-limit.go create mode 100644 vendor/github.com/mgechev/revive/rule/line_length_limit.go delete mode 100644 vendor/github.com/mgechev/revive/rule/max-control-nesting.go delete mode 100644 vendor/github.com/mgechev/revive/rule/max-public-structs.go create mode 100644 vendor/github.com/mgechev/revive/rule/max_control_nesting.go create mode 100644 vendor/github.com/mgechev/revive/rule/max_public_structs.go delete mode 100644 vendor/github.com/mgechev/revive/rule/modifies-param.go delete mode 100644 vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go create mode 100644 vendor/github.com/mgechev/revive/rule/modifies_param.go create mode 100644 vendor/github.com/mgechev/revive/rule/modifies_value_receiver.go delete mode 100644 vendor/github.com/mgechev/revive/rule/nested-structs.go create mode 100644 vendor/github.com/mgechev/revive/rule/nested_structs.go delete mode 100644 vendor/github.com/mgechev/revive/rule/optimize-operands-order.go create mode 100644 vendor/github.com/mgechev/revive/rule/optimize_operands_order.go delete mode 100644 vendor/github.com/mgechev/revive/rule/package-comments.go create mode 100644 vendor/github.com/mgechev/revive/rule/package_comments.go delete mode 100644 vendor/github.com/mgechev/revive/rule/range-val-address.go delete mode 100644 vendor/github.com/mgechev/revive/rule/range-val-in-closure.go create mode 100644 vendor/github.com/mgechev/revive/rule/range_val_address.go create mode 100644 vendor/github.com/mgechev/revive/rule/range_val_in_closure.go delete mode 100644 vendor/github.com/mgechev/revive/rule/receiver-naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/receiver_naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go create mode 100644 vendor/github.com/mgechev/revive/rule/redefines_builtin_id.go delete mode 100644 vendor/github.com/mgechev/revive/rule/redundant-import-alias.go create mode 100644 vendor/github.com/mgechev/revive/rule/redundant_import_alias.go delete mode 100644 vendor/github.com/mgechev/revive/rule/string-format.go delete mode 100644 vendor/github.com/mgechev/revive/rule/string-of-int.go create mode 100644 vendor/github.com/mgechev/revive/rule/string_format.go create mode 100644 vendor/github.com/mgechev/revive/rule/string_of_int.go delete mode 100644 vendor/github.com/mgechev/revive/rule/struct-tag.go create mode 100644 vendor/github.com/mgechev/revive/rule/struct_tag.go delete mode 100644 vendor/github.com/mgechev/revive/rule/superfluous-else.go create mode 100644 vendor/github.com/mgechev/revive/rule/superfluous_else.go delete mode 100644 vendor/github.com/mgechev/revive/rule/time-equal.go delete mode 100644 vendor/github.com/mgechev/revive/rule/time-naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/time_equal.go create mode 100644 vendor/github.com/mgechev/revive/rule/time_naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unchecked-type-assertion.go create mode 100644 vendor/github.com/mgechev/revive/rule/unchecked_type_assertion.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unconditional-recursion.go create mode 100644 vendor/github.com/mgechev/revive/rule/unconditional_recursion.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unexported-naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unexported-return.go create mode 100644 vendor/github.com/mgechev/revive/rule/unexported_naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/unexported_return.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unhandled-error.go create mode 100644 vendor/github.com/mgechev/revive/rule/unhandled_error.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go create mode 100644 vendor/github.com/mgechev/revive/rule/unnecessary_stmt.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unreachable-code.go create mode 100644 vendor/github.com/mgechev/revive/rule/unreachable_code.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unused-param.go delete mode 100644 vendor/github.com/mgechev/revive/rule/unused-receiver.go create mode 100644 vendor/github.com/mgechev/revive/rule/unused_param.go create mode 100644 vendor/github.com/mgechev/revive/rule/unused_receiver.go delete mode 100644 vendor/github.com/mgechev/revive/rule/use-any.go create mode 100644 vendor/github.com/mgechev/revive/rule/use_any.go delete mode 100644 vendor/github.com/mgechev/revive/rule/useless-break.go create mode 100644 vendor/github.com/mgechev/revive/rule/useless_break.go delete mode 100644 vendor/github.com/mgechev/revive/rule/var-declarations.go delete mode 100644 vendor/github.com/mgechev/revive/rule/var-naming.go create mode 100644 vendor/github.com/mgechev/revive/rule/var_declarations.go create mode 100644 vendor/github.com/mgechev/revive/rule/var_naming.go delete mode 100644 vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go create mode 100644 vendor/github.com/mgechev/revive/rule/waitgroup_by_value.go diff --git a/go.mod b/go.mod index 3752dca81..35fbcd201 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( cloud.google.com/go/storage v1.43.0 github.com/VividCortex/gohistogram v1.0.0 github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6 - github.com/golangci/golangci-lint v1.62.0 + github.com/golangci/golangci-lint v1.62.2 github.com/google/flatbuffers v24.3.25+incompatible github.com/google/generative-ai-go v0.18.0 github.com/google/go-cmp v0.6.0 @@ -54,9 +54,9 @@ require ( github.com/Abirdcfly/dupword v0.1.3 // indirect github.com/Antonboom/errname v1.0.0 // indirect github.com/Antonboom/nilnil v1.0.0 // indirect - github.com/Antonboom/testifylint v1.5.0 // indirect + github.com/Antonboom/testifylint v1.5.2 // indirect github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect - github.com/Crocmagnon/fatcontext v0.5.2 // indirect + github.com/Crocmagnon/fatcontext v0.5.3 // indirect github.com/Djarvur/go-err113 v0.1.0 // indirect github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect @@ -166,7 +166,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mgechev/revive v1.5.0 // indirect + github.com/mgechev/revive v1.5.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moricho/tparallel v0.3.2 // indirect @@ -174,13 +174,13 @@ require ( github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.18.0 // indirect + github.com/nunnatsa/ginkgolinter v0.18.3 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.6.0 // indirect + github.com/polyfloyd/go-errorlint v1.7.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -225,7 +225,7 @@ require ( github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.1.1 // indirect github.com/uudashr/gocognit v1.1.3 // indirect - github.com/uudashr/iface v1.2.0 // indirect + github.com/uudashr/iface v1.2.1 // indirect github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect @@ -245,7 +245,7 @@ require ( go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.29.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 // indirect + golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.31.0 // indirect golang.org/x/term v0.26.0 // indirect diff --git a/go.sum b/go.sum index 745206c16..030872509 100644 --- a/go.sum +++ b/go.sum @@ -644,14 +644,14 @@ github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoT github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI= github.com/Antonboom/nilnil v1.0.0 h1:n+v+B12dsE5tbAqRODXmEKfZv9j2KcTBrp+LkoM4HZk= github.com/Antonboom/nilnil v1.0.0/go.mod h1:fDJ1FSFoLN6yoG65ANb1WihItf6qt9PJVTn/s2IrcII= -github.com/Antonboom/testifylint v1.5.0 h1:dlUIsDMtCrZWUnvkaCz3quJCoIjaGi41GzjPBGkkJ8A= -github.com/Antonboom/testifylint v1.5.0/go.mod h1:wqaJbu0Blb5Wag2wv7Z5xt+CIV+eVLxtGZrlK13z3AE= +github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk= +github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA= -github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74= +github.com/Crocmagnon/fatcontext v0.5.3 h1:zCh/wjc9oyeF+Gmp+V60wetm8ph2tlsxocgg/J0hOps= +github.com/Crocmagnon/fatcontext v0.5.3/go.mod h1:XoCQYY1J+XTfyv74qLXvNw4xFunr3L1wkopIIKG7wGM= github.com/Djarvur/go-err113 v0.1.0 h1:uCRZZOdMQ0TZPHYTdYpoC0bLYJKPEHPUJ8MeAa51lNU= github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= @@ -915,8 +915,8 @@ github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUP github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME= github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE= -github.com/golangci/golangci-lint v1.62.0 h1:/G0g+bi1BhmGJqLdNQkKBWjcim8HjOPc4tsKuHDOhcI= -github.com/golangci/golangci-lint v1.62.0/go.mod h1:jtoOhQcKTz8B6dGNFyfQV3WZkQk+YvBDewDtNpiAJts= +github.com/golangci/golangci-lint v1.62.2 h1:b8K5K9PN+rZN1+mKLtsZHz2XXS9aYKzQ9i25x3Qnxxw= +github.com/golangci/golangci-lint v1.62.2/go.mod h1:ILWWyeFUrctpHVGMa1dg2xZPKoMUTc5OIMgW7HZr34g= github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= @@ -1136,8 +1136,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mgechev/revive v1.5.0 h1:oaSmjA7rP8+HyoRuCgC531VHwnLH1AlJdjj+1AnQceQ= -github.com/mgechev/revive v1.5.0/go.mod h1:L6T3H8EoerRO86c7WuGpvohIUmiploGiyoYbtIWFmV8= +github.com/mgechev/revive v1.5.1 h1:hE+QPeq0/wIzJwOphdVyUJ82njdd8Khp4fUIHGZHW3M= +github.com/mgechev/revive v1.5.1/go.mod h1:lC9AhkJIBs5zwx8wkudyHrU+IJkrEKmpCmGMnIJPk4o= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -1154,8 +1154,8 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.18.0 h1:ZXO1wKhPg3A6LpbN5dMuqwhfOjN5c3ous8YdKOuqk9k= -github.com/nunnatsa/ginkgolinter v0.18.0/go.mod h1:vPrWafSULmjMGCMsfGA908if95VnHQNAahvSBOjTuWs= +github.com/nunnatsa/ginkgolinter v0.18.3 h1:WgS7X3zzmni3vwHSBhvSgqrRgUecN6PQUcfB0j1noDw= +github.com/nunnatsa/ginkgolinter v0.18.3/go.mod h1:BE1xyB/PNtXXG1azrvrqJW5eFH0hSRylNzFy8QHPwzs= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= @@ -1187,8 +1187,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgm github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= -github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= +github.com/polyfloyd/go-errorlint v1.7.0 h1:Zp6lzCK4hpBDj8y8a237YK4EPrMXQWvOe3nGoH4pFrU= +github.com/polyfloyd/go-errorlint v1.7.0/go.mod h1:dGWKu85mGHnegQ2SWpEybFityCg3j7ZbwsVUxAOk9gY= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= @@ -1326,8 +1326,8 @@ github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/ github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= -github.com/uudashr/iface v1.2.0 h1:ECJjh5q/1Zmnv/2yFpWV6H3oMg5+Mo+vL0aqw9Gjazo= -github.com/uudashr/iface v1.2.0/go.mod h1:Ux/7d/rAF3owK4m53cTVXL4YoVHKNqnoOeQHn2xrlp0= +github.com/uudashr/iface v1.2.1 h1:vHHyzAUmWZ64Olq6NZT3vg/z1Ws56kyPdBOd5kTXDF8= +github.com/uudashr/iface v1.2.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= github.com/vektra/mockery/v2 v2.45.1 h1:6HpdnKiLCjVtzlRLQPUNIM0u7yrvAoZ7VWF1TltJvTM= github.com/vektra/mockery/v2 v2.45.1/go.mod h1:XNTE9RIu3deGAGQRVjP1VZxGpQNm0YedZx4oDs3prr8= github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= @@ -1424,8 +1424,8 @@ golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWB golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 h1:bVwtbF629Xlyxk6xLQq2TDYmqP0uiWaet5LwRebuY0k= -golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f h1:WTyX8eCCyfdqiPYkRGm0MqElSfYFH3yR1+rl/mct9sA= +golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go index 53c74ac45..1464fd640 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go @@ -48,7 +48,7 @@ func (checker EncodedCompare) Check(pass *analysis.Pass, call *CallMeta) *analys switch { case aIsExplicitJSON, bIsExplicitJSON, isJSONStyleExpr(pass, a), isJSONStyleExpr(pass, b): proposed = "JSONEq" - case isYAMLStyleExpr(a), isYAMLStyleExpr(b): + case isYAMLStyleExpr(pass, a), isYAMLStyleExpr(pass, b): proposed = "YAMLEq" } diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go index 896b6bf5f..7ff4de470 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go @@ -115,7 +115,11 @@ func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta) func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta) (int, bool) { msgAndArgsPos := getMsgAndArgsPosition(call.Fn.Signature) - if msgAndArgsPos < 0 { + if msgAndArgsPos <= 0 { + return -1, false + } + + if !(len(call.ArgsRaw) > msgAndArgsPos && hasStringType(pass, call.ArgsRaw[msgAndArgsPos])) { return -1, false } @@ -123,7 +127,7 @@ func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta) (int, bool) { return -1, false } - return msgAndArgsPos, len(call.ArgsRaw) > msgAndArgsPos + return msgAndArgsPos, true } func assertHasFormattedAnalogue(pass *analysis.Pass, call *CallMeta) bool { diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go index 9b43e914c..b4bb56321 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go @@ -166,6 +166,12 @@ func hasBytesType(pass *analysis.Pass, e ast.Expr) bool { return ok && el.Kind() == types.Uint8 } +// hasStringType returns true if the expression is of `string` type. +func hasStringType(pass *analysis.Pass, e ast.Expr) bool { + basicType, ok := pass.TypesInfo.TypeOf(e).(*types.Basic) + return ok && basicType.Kind() == types.String +} + // untype returns v from type(v) expression or v itself if there is no type conversion. func untype(e ast.Expr) ast.Expr { ce, ok := e.(*ast.CallExpr) diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go index 35a497a72..c366f8563 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go @@ -11,13 +11,15 @@ import ( ) var ( + wordsRe = regexp.MustCompile(`[A-Z]+(?:[a-z]*|$)|[a-z]+`) // NOTE(a.telyshev): ChatGPT. + jsonIdentRe = regexp.MustCompile(`json|JSON|Json`) - yamlIdentRe = regexp.MustCompile(`yaml|YAML|Yaml|yml|YML|Yml`) + yamlWordRe = regexp.MustCompile(`yaml|YAML|Yaml|^(yml|YML|Yml)$`) ) func isJSONStyleExpr(pass *analysis.Pass, e ast.Expr) bool { if isIdentNamedAfterPattern(jsonIdentRe, e) { - return true + return hasBytesType(pass, e) || hasStringType(pass, e) } if t, ok := pass.TypesInfo.Types[e]; ok && t.Value != nil { @@ -35,6 +37,20 @@ func isJSONStyleExpr(pass *analysis.Pass, e ast.Expr) bool { return false } -func isYAMLStyleExpr(e ast.Expr) bool { - return isIdentNamedAfterPattern(yamlIdentRe, e) +func isYAMLStyleExpr(pass *analysis.Pass, e ast.Expr) bool { + id, ok := e.(*ast.Ident) + return ok && (hasBytesType(pass, e) || hasStringType(pass, e)) && hasWordAfterPattern(id.Name, yamlWordRe) +} + +func hasWordAfterPattern(s string, re *regexp.Regexp) bool { + for _, w := range splitIntoWords(s) { + if re.MatchString(w) { + return true + } + } + return false +} + +func splitIntoWords(s string) []string { + return wordsRe.FindAllString(s, -1) } diff --git a/vendor/github.com/Antonboom/testifylint/internal/config/config.go b/vendor/github.com/Antonboom/testifylint/internal/config/config.go index a8812e6d0..23b673428 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/config/config.go +++ b/vendor/github.com/Antonboom/testifylint/internal/config/config.go @@ -133,7 +133,7 @@ func BindToFlags(cfg *Config, fs *flag.FlagSet) { "to enable go vet's printf checks") fs.BoolVar(&cfg.Formatter.RequireFFuncs, "formatter.require-f-funcs", false, - "to require f-assertions if format string is used") + "to require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.") fs.BoolVar(&cfg.GoRequire.IgnoreHTTPHandlers, "go-require.ignore-http-handlers", false, diff --git a/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go b/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go index a65efbba8..7b88bf56e 100644 --- a/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go +++ b/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go @@ -59,7 +59,7 @@ func run(pass *analysis.Pass) (interface{}, error) { { Pos: assignStmt.Pos(), End: assignStmt.End(), - NewText: []byte(suggested), + NewText: suggested, }, }, }) @@ -215,10 +215,10 @@ func getRootIdent(pass *analysis.Pass, node ast.Node) *ast.Ident { } // render returns the pretty-print of the given node -func render(fset *token.FileSet, x interface{}) (string, error) { +func render(fset *token.FileSet, x interface{}) ([]byte, error) { var buf bytes.Buffer if err := printer.Fprint(&buf, fset, x); err != nil { - return "", fmt.Errorf("printing node: %w", err) + return nil, fmt.Errorf("printing node: %w", err) } - return buf.String(), nil + return buf.Bytes(), nil } diff --git a/vendor/github.com/golangci/golangci-lint/internal/go/cache/cache.go b/vendor/github.com/golangci/golangci-lint/internal/go/cache/cache.go index 7bf4f1d66..85899ebc9 100644 --- a/vendor/github.com/golangci/golangci-lint/internal/go/cache/cache.go +++ b/vendor/github.com/golangci/golangci-lint/internal/go/cache/cache.go @@ -293,7 +293,7 @@ func GetBytes(c Cache, id ActionID) ([]byte, Entry, error) { } data, err := robustio.ReadFile(c.OutputFile(entry.OutputID)) if err != nil { - return nil, entry, err + return nil, entry, &entryNotFoundError{Err: err} } if sha256.Sum256(data) != entry.OutputID { return nil, entry, &entryNotFoundError{Err: errors.New("bad checksum")} diff --git a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go index 4da73c72e..7eebea631 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go @@ -1,10 +1,9 @@ package processors import ( - "errors" + "cmp" "fmt" "slices" - "sort" "strings" "github.com/golangci/golangci-lint/pkg/config" @@ -22,24 +21,32 @@ const ( orderNameSeverity = "severity" ) +const ( + less = iota - 1 + equal + greater +) + var _ Processor = (*SortResults)(nil) +type issueComparator func(a, b *result.Issue) int + type SortResults struct { - cmps map[string]*comparator + cmps map[string][]issueComparator cfg *config.Output } func NewSortResults(cfg *config.Config) *SortResults { return &SortResults{ - cmps: map[string]*comparator{ + cmps: map[string][]issueComparator{ // For sorting we are comparing (in next order): // file names, line numbers, position, and finally - giving up. - orderNameFile: byFileName().SetNext(byLine().SetNext(byColumn())), + orderNameFile: {byFileName, byLine, byColumn}, // For sorting we are comparing: linter name - orderNameLinter: byLinter(), + orderNameLinter: {byLinter}, // For sorting we are comparing: severity - orderNameSeverity: bySeverity(), + orderNameSeverity: {bySeverity}, }, cfg: &cfg.Output, } @@ -57,23 +64,21 @@ func (p SortResults) Process(issues []result.Issue) ([]result.Issue, error) { p.cfg.SortOrder = []string{orderNameFile} } - var cmps []*comparator + var cmps []issueComparator + for _, name := range p.cfg.SortOrder { c, ok := p.cmps[name] if !ok { return nil, fmt.Errorf("unsupported sort-order name %q", name) } - cmps = append(cmps, c) + cmps = append(cmps, c...) } - cmp, err := mergeComparators(cmps) - if err != nil { - return nil, err - } + comp := mergeComparators(cmps...) - sort.Slice(issues, func(i, j int) bool { - return cmp.Compare(&issues[i], &issues[j]) == less + slices.SortFunc(issues, func(a, b result.Issue) int { + return comp(&a, &b) }) return issues, nil @@ -81,147 +86,32 @@ func (p SortResults) Process(issues []result.Issue) ([]result.Issue, error) { func (SortResults) Finish() {} -type compareResult int - -const ( - less compareResult = iota - 1 - equal - greater - none -) - -func (c compareResult) isNeutral() bool { - // return true if compare result is incomparable or equal. - return c == none || c == equal -} - -func (c compareResult) String() string { - switch c { - case less: - return "less" - case equal: - return "equal" - case greater: - return "greater" - default: - return "none" - } -} - -// comparator describes how to implement compare for two "issues". -type comparator struct { - name string - compare func(a, b *result.Issue) compareResult - next *comparator -} - -func (cmp *comparator) Next() *comparator { return cmp.next } - -func (cmp *comparator) SetNext(c *comparator) *comparator { - cmp.next = c - return cmp +func byFileName(a, b *result.Issue) int { + return strings.Compare(a.FilePath(), b.FilePath()) } -func (cmp *comparator) String() string { - s := cmp.name - if cmp.Next() != nil { - s += " > " + cmp.Next().String() - } - - return s +func byLine(a, b *result.Issue) int { + return numericCompare(a.Line(), b.Line()) } -func (cmp *comparator) Compare(a, b *result.Issue) compareResult { - res := cmp.compare(a, b) - if !res.isNeutral() { - return res - } - - if next := cmp.Next(); next != nil { - return next.Compare(a, b) - } - - return res +func byColumn(a, b *result.Issue) int { + return numericCompare(a.Column(), b.Column()) } -func byFileName() *comparator { - return &comparator{ - name: "byFileName", - compare: func(a, b *result.Issue) compareResult { - return compareResult(strings.Compare(a.FilePath(), b.FilePath())) - }, - } +func byLinter(a, b *result.Issue) int { + return strings.Compare(a.FromLinter, b.FromLinter) } -func byLine() *comparator { - return &comparator{ - name: "byLine", - compare: func(a, b *result.Issue) compareResult { - return numericCompare(a.Line(), b.Line()) - }, - } +func bySeverity(a, b *result.Issue) int { + return severityCompare(a.Severity, b.Severity) } -func byColumn() *comparator { - return &comparator{ - name: "byColumn", - compare: func(a, b *result.Issue) compareResult { - return numericCompare(a.Column(), b.Column()) - }, - } -} - -func byLinter() *comparator { - return &comparator{ - name: "byLinter", - compare: func(a, b *result.Issue) compareResult { - return compareResult(strings.Compare(a.FromLinter, b.FromLinter)) - }, - } -} - -func bySeverity() *comparator { - return &comparator{ - name: "bySeverity", - compare: func(a, b *result.Issue) compareResult { - return severityCompare(a.Severity, b.Severity) - }, - } -} - -func mergeComparators(cmps []*comparator) (*comparator, error) { - if len(cmps) == 0 { - return nil, errors.New("no comparator") - } - - for i := range len(cmps) - 1 { - findComparatorTip(cmps[i]).SetNext(cmps[i+1]) - } - - return cmps[0], nil -} - -func findComparatorTip(cmp *comparator) *comparator { - if cmp.Next() != nil { - return findComparatorTip(cmp.Next()) - } - - return cmp -} - -func severityCompare(a, b string) compareResult { +func severityCompare(a, b string) int { // The position inside the slice define the importance (lower to higher). classic := []string{"low", "medium", "high", "warning", "error"} if slices.Contains(classic, a) && slices.Contains(classic, b) { - switch { - case slices.Index(classic, a) > slices.Index(classic, b): - return greater - case slices.Index(classic, a) < slices.Index(classic, b): - return less - default: - return equal - } + return cmp.Compare(slices.Index(classic, a), slices.Index(classic, b)) } if slices.Contains(classic, a) { @@ -232,28 +122,27 @@ func severityCompare(a, b string) compareResult { return less } - return compareResult(strings.Compare(a, b)) + return strings.Compare(a, b) } -func numericCompare(a, b int) compareResult { - var ( - isValuesInvalid = a < 0 || b < 0 - isZeroValuesBoth = a == 0 && b == 0 - isEqual = a == b - isZeroValueInA = b > 0 && a == 0 - isZeroValueInB = a > 0 && b == 0 - ) - - switch { - case isZeroValuesBoth || isEqual: +func numericCompare(a, b int) int { + // Negative values and zeros are skipped (equal) because they either invalid or "neutral" (default int value). + if a <= 0 || b <= 0 { return equal - case isValuesInvalid || isZeroValueInA || isZeroValueInB: - return none - case a > b: - return greater - case a < b: - return less } - return equal + return cmp.Compare(a, b) +} + +func mergeComparators(comps ...issueComparator) issueComparator { + return func(a, b *result.Issue) int { + for _, comp := range comps { + i := comp(a, b) + if i != equal { + return i + } + } + + return equal + } } diff --git a/vendor/github.com/mgechev/revive/lint/linter.go b/vendor/github.com/mgechev/revive/lint/linter.go index 56036e83d..b777f9251 100644 --- a/vendor/github.com/mgechev/revive/lint/linter.go +++ b/vendor/github.com/mgechev/revive/lint/linter.go @@ -109,7 +109,7 @@ func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-cha fmt.Fprintln(os.Stderr, err) os.Exit(1) } - defer wg.Done() + wg.Done() }(packages[n], perPkgVersions[n]) } @@ -173,6 +173,10 @@ func detectGoMod(dir string) (rootDir string, ver *goversion.Version, err error) return "", nil, fmt.Errorf("failed to parse %q, got %v", modFileName, err) } + if modAst.Go == nil { + return "", nil, fmt.Errorf("%q does not specify a Go version", modFileName) + } + ver, err = goversion.NewVersion(modAst.Go.Version) return filepath.Dir(modFileName), ver, err } @@ -180,7 +184,9 @@ func detectGoMod(dir string) (rootDir string, ver *goversion.Version, err error) func retrieveModFile(dir string) (string, error) { const lookingForFile = "go.mod" for { - if dir == "." || dir == "/" { + // filepath.Dir returns 'C:\' on Windows, and '/' on Unix + isRootDir := (dir == filepath.VolumeName(dir)+string(filepath.Separator)) + if dir == "." || isRootDir { return "", fmt.Errorf("did not found %q file", lookingForFile) } diff --git a/vendor/github.com/mgechev/revive/lint/package.go b/vendor/github.com/mgechev/revive/lint/package.go index 2ab035f16..4a633f35a 100644 --- a/vendor/github.com/mgechev/revive/lint/package.go +++ b/vendor/github.com/mgechev/revive/lint/package.go @@ -189,7 +189,7 @@ func (p *Package) lint(rules []Rule, config Config, failures chan Failure) { wg.Add(1) go (func(file *File) { file.lint(rules, config, failures) - defer wg.Done() + wg.Done() })(file) } wg.Wait() diff --git a/vendor/github.com/mgechev/revive/rule/add-constant.go b/vendor/github.com/mgechev/revive/rule/add-constant.go deleted file mode 100644 index 233f1d848..000000000 --- a/vendor/github.com/mgechev/revive/rule/add-constant.go +++ /dev/null @@ -1,269 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "regexp" - "strconv" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -const ( - defaultStrLitLimit = 2 - kindFLOAT = "FLOAT" - kindINT = "INT" - kindSTRING = "STRING" -) - -type allowList map[string]map[string]bool - -func newAllowList() allowList { - return map[string]map[string]bool{kindINT: {}, kindFLOAT: {}, kindSTRING: {}} -} - -func (wl allowList) add(kind, list string) { - elems := strings.Split(list, ",") - for _, e := range elems { - wl[kind][e] = true - } -} - -// AddConstantRule lints unused params in functions. -type AddConstantRule struct { - allowList allowList - ignoreFunctions []*regexp.Regexp - strLitLimit int - sync.Mutex -} - -// Apply applies the rule to given file. -func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := &lintAddConstantRule{ - onFailure: onFailure, - strLits: make(map[string]int), - strLitLimit: r.strLitLimit, - allowList: r.allowList, - ignoreFunctions: r.ignoreFunctions, - structTags: make(map[*ast.BasicLit]struct{}), - } - - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*AddConstantRule) Name() string { - return "add-constant" -} - -type lintAddConstantRule struct { - onFailure func(lint.Failure) - strLits map[string]int - strLitLimit int - allowList allowList - ignoreFunctions []*regexp.Regexp - structTags map[*ast.BasicLit]struct{} -} - -func (w *lintAddConstantRule) Visit(node ast.Node) ast.Visitor { - if node == nil { - return nil - } - - switch n := node.(type) { - case *ast.CallExpr: - w.checkFunc(n) - return nil - case *ast.GenDecl: - return nil // skip declarations - case *ast.BasicLit: - if !w.isStructTag(n) { - w.checkLit(n) - } - case *ast.StructType: - if n.Fields != nil { - for _, field := range n.Fields.List { - if field.Tag != nil { - w.structTags[field.Tag] = struct{}{} - } - } - } - } - - return w -} - -func (w *lintAddConstantRule) checkFunc(expr *ast.CallExpr) { - fName := w.getFuncName(expr) - - for _, arg := range expr.Args { - switch t := arg.(type) { - case *ast.CallExpr: - w.checkFunc(t) - case *ast.BasicLit: - if w.isIgnoredFunc(fName) { - continue - } - w.checkLit(t) - } - } -} - -func (*lintAddConstantRule) getFuncName(expr *ast.CallExpr) string { - switch f := expr.Fun.(type) { - case *ast.SelectorExpr: - switch prefix := f.X.(type) { - case *ast.Ident: - return prefix.Name + "." + f.Sel.Name - case *ast.CallExpr: - // If the selector is an CallExpr, like `fn().Info`, we return `.Info` as function name - if f.Sel != nil { - return "." + f.Sel.Name - } - } - case *ast.Ident: - return f.Name - } - - return "" -} - -func (w *lintAddConstantRule) checkLit(n *ast.BasicLit) { - switch kind := n.Kind.String(); kind { - case kindFLOAT, kindINT: - w.checkNumLit(kind, n) - case kindSTRING: - w.checkStrLit(n) - } -} - -func (w *lintAddConstantRule) isIgnoredFunc(fName string) bool { - for _, pattern := range w.ignoreFunctions { - if pattern.MatchString(fName) { - return true - } - } - - return false -} - -func (w *lintAddConstantRule) checkStrLit(n *ast.BasicLit) { - const ignoreMarker = -1 - - if w.allowList[kindSTRING][n.Value] { - return - } - - count := w.strLits[n.Value] - mustCheck := count > ignoreMarker - if mustCheck { - w.strLits[n.Value] = count + 1 - if w.strLits[n.Value] > w.strLitLimit { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: n, - Category: "style", - Failure: fmt.Sprintf("string literal %s appears, at least, %d times, create a named constant for it", n.Value, w.strLits[n.Value]), - }) - w.strLits[n.Value] = -1 // mark it to avoid failing again on the same literal - } - } -} - -func (w *lintAddConstantRule) checkNumLit(kind string, n *ast.BasicLit) { - if w.allowList[kind][n.Value] { - return - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: n, - Category: "style", - Failure: fmt.Sprintf("avoid magic numbers like '%s', create a named constant for it", n.Value), - }) -} - -func (w *lintAddConstantRule) isStructTag(n *ast.BasicLit) bool { - _, ok := w.structTags[n] - return ok -} - -func (r *AddConstantRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.allowList == nil { - r.strLitLimit = defaultStrLitLimit - r.allowList = newAllowList() - if len(arguments) > 0 { - args, ok := arguments[0].(map[string]any) - if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0])) - } - for k, v := range args { - kind := "" - switch k { - case "allowFloats": - kind = kindFLOAT - fallthrough - case "allowInts": - if kind == "" { - kind = kindINT - } - fallthrough - case "allowStrs": - if kind == "" { - kind = kindSTRING - } - list, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)) - } - r.allowList.add(kind, list) - case "maxLitCount": - sl, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)) - } - - limit, err := strconv.Atoi(sl) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) - } - r.strLitLimit = limit - case "ignoreFuncs": - excludes, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)) - } - - for _, exclude := range strings.Split(excludes, ",") { - exclude = strings.Trim(exclude, " ") - if exclude == "" { - panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.") - } - - exp, err := regexp.Compile(exclude) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err)) - } - - r.ignoreFunctions = append(r.ignoreFunctions, exp) - } - } - } - } - } -} diff --git a/vendor/github.com/mgechev/revive/rule/add_constant.go b/vendor/github.com/mgechev/revive/rule/add_constant.go new file mode 100644 index 000000000..399382c8b --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/add_constant.go @@ -0,0 +1,267 @@ +package rule + +import ( + "fmt" + "go/ast" + "regexp" + "strconv" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +const ( + defaultStrLitLimit = 2 + kindFLOAT = "FLOAT" + kindINT = "INT" + kindSTRING = "STRING" +) + +type allowList map[string]map[string]bool + +func newAllowList() allowList { + return map[string]map[string]bool{kindINT: {}, kindFLOAT: {}, kindSTRING: {}} +} + +func (wl allowList) add(kind, list string) { + elems := strings.Split(list, ",") + for _, e := range elems { + wl[kind][e] = true + } +} + +// AddConstantRule lints unused params in functions. +type AddConstantRule struct { + allowList allowList + ignoreFunctions []*regexp.Regexp + strLitLimit int + + configureOnce sync.Once +} + +// Apply applies the rule to given file. +func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := &lintAddConstantRule{ + onFailure: onFailure, + strLits: make(map[string]int), + strLitLimit: r.strLitLimit, + allowList: r.allowList, + ignoreFunctions: r.ignoreFunctions, + structTags: make(map[*ast.BasicLit]struct{}), + } + + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*AddConstantRule) Name() string { + return "add-constant" +} + +type lintAddConstantRule struct { + onFailure func(lint.Failure) + strLits map[string]int + strLitLimit int + allowList allowList + ignoreFunctions []*regexp.Regexp + structTags map[*ast.BasicLit]struct{} +} + +func (w *lintAddConstantRule) Visit(node ast.Node) ast.Visitor { + if node == nil { + return nil + } + + switch n := node.(type) { + case *ast.CallExpr: + w.checkFunc(n) + return nil + case *ast.GenDecl: + return nil // skip declarations + case *ast.BasicLit: + if !w.isStructTag(n) { + w.checkLit(n) + } + case *ast.StructType: + if n.Fields != nil { + for _, field := range n.Fields.List { + if field.Tag != nil { + w.structTags[field.Tag] = struct{}{} + } + } + } + } + + return w +} + +func (w *lintAddConstantRule) checkFunc(expr *ast.CallExpr) { + fName := w.getFuncName(expr) + + for _, arg := range expr.Args { + switch t := arg.(type) { + case *ast.CallExpr: + w.checkFunc(t) + case *ast.BasicLit: + if w.isIgnoredFunc(fName) { + continue + } + w.checkLit(t) + } + } +} + +func (*lintAddConstantRule) getFuncName(expr *ast.CallExpr) string { + switch f := expr.Fun.(type) { + case *ast.SelectorExpr: + switch prefix := f.X.(type) { + case *ast.Ident: + return prefix.Name + "." + f.Sel.Name + case *ast.CallExpr: + // If the selector is an CallExpr, like `fn().Info`, we return `.Info` as function name + if f.Sel != nil { + return "." + f.Sel.Name + } + } + case *ast.Ident: + return f.Name + } + + return "" +} + +func (w *lintAddConstantRule) checkLit(n *ast.BasicLit) { + switch kind := n.Kind.String(); kind { + case kindFLOAT, kindINT: + w.checkNumLit(kind, n) + case kindSTRING: + w.checkStrLit(n) + } +} + +func (w *lintAddConstantRule) isIgnoredFunc(fName string) bool { + for _, pattern := range w.ignoreFunctions { + if pattern.MatchString(fName) { + return true + } + } + + return false +} + +func (w *lintAddConstantRule) checkStrLit(n *ast.BasicLit) { + const ignoreMarker = -1 + + if w.allowList[kindSTRING][n.Value] { + return + } + + count := w.strLits[n.Value] + mustCheck := count > ignoreMarker + if mustCheck { + w.strLits[n.Value] = count + 1 + if w.strLits[n.Value] > w.strLitLimit { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: n, + Category: "style", + Failure: fmt.Sprintf("string literal %s appears, at least, %d times, create a named constant for it", n.Value, w.strLits[n.Value]), + }) + w.strLits[n.Value] = -1 // mark it to avoid failing again on the same literal + } + } +} + +func (w *lintAddConstantRule) checkNumLit(kind string, n *ast.BasicLit) { + if w.allowList[kind][n.Value] { + return + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: n, + Category: "style", + Failure: fmt.Sprintf("avoid magic numbers like '%s', create a named constant for it", n.Value), + }) +} + +func (w *lintAddConstantRule) isStructTag(n *ast.BasicLit) bool { + _, ok := w.structTags[n] + return ok +} + +func (r *AddConstantRule) configure(arguments lint.Arguments) { + if r.allowList == nil { + r.strLitLimit = defaultStrLitLimit + r.allowList = newAllowList() + if len(arguments) > 0 { + args, ok := arguments[0].(map[string]any) + if !ok { + panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0])) + } + for k, v := range args { + kind := "" + switch k { + case "allowFloats": + kind = kindFLOAT + fallthrough + case "allowInts": + if kind == "" { + kind = kindINT + } + fallthrough + case "allowStrs": + if kind == "" { + kind = kindSTRING + } + list, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)) + } + r.allowList.add(kind, list) + case "maxLitCount": + sl, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)) + } + + limit, err := strconv.Atoi(sl) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) + } + r.strLitLimit = limit + case "ignoreFuncs": + excludes, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)) + } + + for _, exclude := range strings.Split(excludes, ",") { + exclude = strings.Trim(exclude, " ") + if exclude == "" { + panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.") + } + + exp, err := regexp.Compile(exclude) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err)) + } + + r.ignoreFunctions = append(r.ignoreFunctions, exp) + } + } + } + } + } +} diff --git a/vendor/github.com/mgechev/revive/rule/argument-limit.go b/vendor/github.com/mgechev/revive/rule/argument-limit.go deleted file mode 100644 index b6ce0e81a..000000000 --- a/vendor/github.com/mgechev/revive/rule/argument-limit.go +++ /dev/null @@ -1,89 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -// ArgumentsLimitRule lints given else constructs. -type ArgumentsLimitRule struct { - max int - sync.Mutex -} - -const defaultArgumentsLimit = 8 - -func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.max != 0 { - return - } - - if len(arguments) < 1 { - r.max = defaultArgumentsLimit - return - } - - maxArguments, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(`invalid value passed as argument number to the "argument-limit" rule`) - } - r.max = int(maxArguments) -} - -// Apply applies the rule to given file. -func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - walker := lintArgsNum{ - max: r.max, - onFailure: onFailure, - } - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*ArgumentsLimitRule) Name() string { - return "argument-limit" -} - -type lintArgsNum struct { - max int - onFailure func(lint.Failure) -} - -func (w lintArgsNum) Visit(n ast.Node) ast.Visitor { - node, ok := n.(*ast.FuncDecl) - if !ok { - return w - } - - num := 0 - for _, l := range node.Type.Params.List { - for range l.Names { - num++ - } - } - - if num > w.max { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("maximum number of arguments per function exceeded; max %d but got %d", w.max, num), - Node: node.Type, - }) - } - - return nil // skip visiting the body of the function -} diff --git a/vendor/github.com/mgechev/revive/rule/argument_limit.go b/vendor/github.com/mgechev/revive/rule/argument_limit.go new file mode 100644 index 000000000..b4d56de0e --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/argument_limit.go @@ -0,0 +1,84 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +// ArgumentsLimitRule lints given else constructs. +type ArgumentsLimitRule struct { + max int + + configureOnce sync.Once +} + +const defaultArgumentsLimit = 8 + +func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.max = defaultArgumentsLimit + return + } + + maxArguments, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(`invalid value passed as argument number to the "argument-limit" rule`) + } + r.max = int(maxArguments) +} + +// Apply applies the rule to given file. +func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + walker := lintArgsNum{ + max: r.max, + onFailure: onFailure, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*ArgumentsLimitRule) Name() string { + return "argument-limit" +} + +type lintArgsNum struct { + max int + onFailure func(lint.Failure) +} + +func (w lintArgsNum) Visit(n ast.Node) ast.Visitor { + node, ok := n.(*ast.FuncDecl) + if !ok { + return w + } + + num := 0 + for _, l := range node.Type.Params.List { + for range l.Names { + num++ + } + } + + if num > w.max { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("maximum number of arguments per function exceeded; max %d but got %d", w.max, num), + Node: node.Type, + }) + } + + return nil // skip visiting the body of the function +} diff --git a/vendor/github.com/mgechev/revive/rule/banned-characters.go b/vendor/github.com/mgechev/revive/rule/banned-characters.go deleted file mode 100644 index 12997bae1..000000000 --- a/vendor/github.com/mgechev/revive/rule/banned-characters.go +++ /dev/null @@ -1,90 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// BannedCharsRule checks if a file contains banned characters. -type BannedCharsRule struct { - bannedCharList []string - sync.Mutex -} - -const bannedCharsRuleName = "banned-characters" - -func (r *BannedCharsRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.bannedCharList == nil && len(arguments) > 0 { - checkNumberOfArguments(1, arguments, bannedCharsRuleName) - r.bannedCharList = r.getBannedCharsList(arguments) - } -} - -// Apply applied the rule to the given file. -func (r *BannedCharsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintBannedCharsRule{ - bannedChars: r.bannedCharList, - onFailure: onFailure, - } - - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name -func (*BannedCharsRule) Name() string { - return bannedCharsRuleName -} - -// getBannedCharsList converts arguments into the banned characters list -func (r *BannedCharsRule) getBannedCharsList(args lint.Arguments) []string { - var bannedChars []string - for _, char := range args { - charStr, ok := char.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), char)) - } - bannedChars = append(bannedChars, charStr) - } - - return bannedChars -} - -type lintBannedCharsRule struct { - bannedChars []string - onFailure func(lint.Failure) -} - -// Visit checks for each node if an identifier contains banned characters -func (w lintBannedCharsRule) Visit(node ast.Node) ast.Visitor { - n, ok := node.(*ast.Ident) - if !ok { - return w - } - for _, c := range w.bannedChars { - ok := strings.Contains(n.Name, c) - if ok { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("banned character found: %s", c), - RuleName: bannedCharsRuleName, - Node: n, - }) - } - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/banned_characters.go b/vendor/github.com/mgechev/revive/rule/banned_characters.go new file mode 100644 index 000000000..926b32c21 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/banned_characters.go @@ -0,0 +1,89 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// BannedCharsRule checks if a file contains banned characters. +type BannedCharsRule struct { + bannedCharList []string + + configureOnce sync.Once +} + +const bannedCharsRuleName = "banned-characters" + +func (r *BannedCharsRule) configure(arguments lint.Arguments) { + if len(arguments) > 0 { + checkNumberOfArguments(1, arguments, bannedCharsRuleName) + r.bannedCharList = r.getBannedCharsList(arguments) + } +} + +// Apply applied the rule to the given file. +func (r *BannedCharsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintBannedCharsRule{ + bannedChars: r.bannedCharList, + onFailure: onFailure, + } + + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name +func (*BannedCharsRule) Name() string { + return bannedCharsRuleName +} + +// getBannedCharsList converts arguments into the banned characters list +func (r *BannedCharsRule) getBannedCharsList(args lint.Arguments) []string { + var bannedChars []string + for _, char := range args { + charStr, ok := char.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), char)) + } + bannedChars = append(bannedChars, charStr) + } + + return bannedChars +} + +type lintBannedCharsRule struct { + bannedChars []string + onFailure func(lint.Failure) +} + +// Visit checks for each node if an identifier contains banned characters +func (w lintBannedCharsRule) Visit(node ast.Node) ast.Visitor { + n, ok := node.(*ast.Ident) + if !ok { + return w + } + for _, c := range w.bannedChars { + ok := strings.Contains(n.Name, c) + if ok { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("banned character found: %s", c), + RuleName: bannedCharsRuleName, + Node: n, + }) + } + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/bare-return.go b/vendor/github.com/mgechev/revive/rule/bare-return.go deleted file mode 100644 index 147fa84db..000000000 --- a/vendor/github.com/mgechev/revive/rule/bare-return.go +++ /dev/null @@ -1,84 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// BareReturnRule lints given else constructs. -type BareReturnRule struct{} - -// Apply applies the rule to given file. -func (*BareReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintBareReturnRule{onFailure: onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*BareReturnRule) Name() string { - return "bare-return" -} - -type lintBareReturnRule struct { - onFailure func(lint.Failure) -} - -func (w lintBareReturnRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - w.checkFunc(n.Type.Results, n.Body) - case *ast.FuncLit: // to cope with deferred functions and go-routines - w.checkFunc(n.Type.Results, n.Body) - } - - return w -} - -// checkFunc will verify if the given function has named result and bare returns -func (w lintBareReturnRule) checkFunc(results *ast.FieldList, body *ast.BlockStmt) { - hasNamedResults := results != nil && len(results.List) > 0 && results.List[0].Names != nil - if !hasNamedResults || body == nil { - return // nothing to do - } - - brf := bareReturnFinder{w.onFailure} - ast.Walk(brf, body) -} - -type bareReturnFinder struct { - onFailure func(lint.Failure) -} - -func (w bareReturnFinder) Visit(node ast.Node) ast.Visitor { - _, ok := node.(*ast.FuncLit) - if ok { - // skip analysing function literals - // they will be analysed by the lintBareReturnRule.Visit method - return nil - } - - rs, ok := node.(*ast.ReturnStmt) - if !ok { - return w - } - - if len(rs.Results) > 0 { - return w - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: rs, - Failure: "avoid using bare returns, please add return expressions", - }) - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/bare_return.go b/vendor/github.com/mgechev/revive/rule/bare_return.go new file mode 100644 index 000000000..147fa84db --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/bare_return.go @@ -0,0 +1,84 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// BareReturnRule lints given else constructs. +type BareReturnRule struct{} + +// Apply applies the rule to given file. +func (*BareReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintBareReturnRule{onFailure: onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*BareReturnRule) Name() string { + return "bare-return" +} + +type lintBareReturnRule struct { + onFailure func(lint.Failure) +} + +func (w lintBareReturnRule) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + w.checkFunc(n.Type.Results, n.Body) + case *ast.FuncLit: // to cope with deferred functions and go-routines + w.checkFunc(n.Type.Results, n.Body) + } + + return w +} + +// checkFunc will verify if the given function has named result and bare returns +func (w lintBareReturnRule) checkFunc(results *ast.FieldList, body *ast.BlockStmt) { + hasNamedResults := results != nil && len(results.List) > 0 && results.List[0].Names != nil + if !hasNamedResults || body == nil { + return // nothing to do + } + + brf := bareReturnFinder{w.onFailure} + ast.Walk(brf, body) +} + +type bareReturnFinder struct { + onFailure func(lint.Failure) +} + +func (w bareReturnFinder) Visit(node ast.Node) ast.Visitor { + _, ok := node.(*ast.FuncLit) + if ok { + // skip analysing function literals + // they will be analysed by the lintBareReturnRule.Visit method + return nil + } + + rs, ok := node.(*ast.ReturnStmt) + if !ok { + return w + } + + if len(rs.Results) > 0 { + return w + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: rs, + Failure: "avoid using bare returns, please add return expressions", + }) + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/blank-imports.go b/vendor/github.com/mgechev/revive/rule/blank-imports.go deleted file mode 100644 index 0ddb4aad2..000000000 --- a/vendor/github.com/mgechev/revive/rule/blank-imports.go +++ /dev/null @@ -1,75 +0,0 @@ -package rule - -import ( - "go/ast" - "strings" - - "github.com/mgechev/revive/lint" -) - -// BlankImportsRule lints given else constructs. -type BlankImportsRule struct{} - -// Name returns the rule name. -func (*BlankImportsRule) Name() string { - return "blank-imports" -} - -// Apply applies the rule to given file. -func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - if file.Pkg.IsMain() || file.IsTest() { - return nil - } - - const ( - message = "a blank import should be only in a main or test package, or have a comment justifying it" - category = "imports" - embedImportPath = `"embed"` - ) - - var failures []lint.Failure - - // The first element of each contiguous group of blank imports should have - // an explanatory comment of some kind. - for i, imp := range file.AST.Imports { - pos := file.ToPosition(imp.Pos()) - - if !isBlank(imp.Name) { - continue // Ignore non-blank imports. - } - - isNotFirstElement := i > 0 - if isNotFirstElement { - prev := file.AST.Imports[i-1] - prevPos := file.ToPosition(prev.Pos()) - - isSubsequentBlancInAGroup := prevPos.Line+1 == pos.Line && prev.Path.Value != embedImportPath && isBlank(prev.Name) - if isSubsequentBlancInAGroup { - continue - } - } - - if imp.Path.Value == embedImportPath && r.fileHasValidEmbedComment(file.AST) { - continue - } - - // This is the first blank import of a group. - if imp.Doc == nil && imp.Comment == nil { - failures = append(failures, lint.Failure{Failure: message, Category: category, Node: imp, Confidence: 1}) - } - } - - return failures -} - -func (*BlankImportsRule) fileHasValidEmbedComment(fileAst *ast.File) bool { - for _, commentGroup := range fileAst.Comments { - for _, comment := range commentGroup.List { - if strings.HasPrefix(comment.Text, "//go:embed ") { - return true - } - } - } - - return false -} diff --git a/vendor/github.com/mgechev/revive/rule/blank_imports.go b/vendor/github.com/mgechev/revive/rule/blank_imports.go new file mode 100644 index 000000000..0ddb4aad2 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/blank_imports.go @@ -0,0 +1,75 @@ +package rule + +import ( + "go/ast" + "strings" + + "github.com/mgechev/revive/lint" +) + +// BlankImportsRule lints given else constructs. +type BlankImportsRule struct{} + +// Name returns the rule name. +func (*BlankImportsRule) Name() string { + return "blank-imports" +} + +// Apply applies the rule to given file. +func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + if file.Pkg.IsMain() || file.IsTest() { + return nil + } + + const ( + message = "a blank import should be only in a main or test package, or have a comment justifying it" + category = "imports" + embedImportPath = `"embed"` + ) + + var failures []lint.Failure + + // The first element of each contiguous group of blank imports should have + // an explanatory comment of some kind. + for i, imp := range file.AST.Imports { + pos := file.ToPosition(imp.Pos()) + + if !isBlank(imp.Name) { + continue // Ignore non-blank imports. + } + + isNotFirstElement := i > 0 + if isNotFirstElement { + prev := file.AST.Imports[i-1] + prevPos := file.ToPosition(prev.Pos()) + + isSubsequentBlancInAGroup := prevPos.Line+1 == pos.Line && prev.Path.Value != embedImportPath && isBlank(prev.Name) + if isSubsequentBlancInAGroup { + continue + } + } + + if imp.Path.Value == embedImportPath && r.fileHasValidEmbedComment(file.AST) { + continue + } + + // This is the first blank import of a group. + if imp.Doc == nil && imp.Comment == nil { + failures = append(failures, lint.Failure{Failure: message, Category: category, Node: imp, Confidence: 1}) + } + } + + return failures +} + +func (*BlankImportsRule) fileHasValidEmbedComment(fileAst *ast.File) bool { + for _, commentGroup := range fileAst.Comments { + for _, comment := range commentGroup.List { + if strings.HasPrefix(comment.Text, "//go:embed ") { + return true + } + } + } + + return false +} diff --git a/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go b/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go deleted file mode 100644 index 71551e55a..000000000 --- a/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go +++ /dev/null @@ -1,72 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// BoolLiteralRule warns when logic expressions contains Boolean literals. -type BoolLiteralRule struct{} - -// Apply applies the rule to given file. -func (*BoolLiteralRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - w := &lintBoolLiteral{astFile, onFailure} - ast.Walk(w, astFile) - - return failures -} - -// Name returns the rule name. -func (*BoolLiteralRule) Name() string { - return "bool-literal-in-expr" -} - -type lintBoolLiteral struct { - file *ast.File - onFailure func(lint.Failure) -} - -func (w *lintBoolLiteral) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.BinaryExpr: - if !isBoolOp(n.Op) { - return w - } - - lexeme, ok := isExprABooleanLit(n.X) - if !ok { - lexeme, ok = isExprABooleanLit(n.Y) - if !ok { - return w - } - } - - isConstant := (n.Op == token.LAND && lexeme == "false") || (n.Op == token.LOR && lexeme == "true") - - if isConstant { - w.addFailure(n, "Boolean expression seems to always evaluate to "+lexeme, "logic") - } else { - w.addFailure(n, "omit Boolean literal in expression", "style") - } - } - - return w -} - -func (w lintBoolLiteral) addFailure(node ast.Node, msg, cat string) { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: node, - Category: cat, - Failure: msg, - }) -} diff --git a/vendor/github.com/mgechev/revive/rule/bool_literal_in_expr.go b/vendor/github.com/mgechev/revive/rule/bool_literal_in_expr.go new file mode 100644 index 000000000..71551e55a --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/bool_literal_in_expr.go @@ -0,0 +1,72 @@ +package rule + +import ( + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// BoolLiteralRule warns when logic expressions contains Boolean literals. +type BoolLiteralRule struct{} + +// Apply applies the rule to given file. +func (*BoolLiteralRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + w := &lintBoolLiteral{astFile, onFailure} + ast.Walk(w, astFile) + + return failures +} + +// Name returns the rule name. +func (*BoolLiteralRule) Name() string { + return "bool-literal-in-expr" +} + +type lintBoolLiteral struct { + file *ast.File + onFailure func(lint.Failure) +} + +func (w *lintBoolLiteral) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.BinaryExpr: + if !isBoolOp(n.Op) { + return w + } + + lexeme, ok := isExprABooleanLit(n.X) + if !ok { + lexeme, ok = isExprABooleanLit(n.Y) + if !ok { + return w + } + } + + isConstant := (n.Op == token.LAND && lexeme == "false") || (n.Op == token.LOR && lexeme == "true") + + if isConstant { + w.addFailure(n, "Boolean expression seems to always evaluate to "+lexeme, "logic") + } else { + w.addFailure(n, "omit Boolean literal in expression", "style") + } + } + + return w +} + +func (w lintBoolLiteral) addFailure(node ast.Node, msg, cat string) { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: node, + Category: cat, + Failure: msg, + }) +} diff --git a/vendor/github.com/mgechev/revive/rule/call-to-gc.go b/vendor/github.com/mgechev/revive/rule/call-to-gc.go deleted file mode 100644 index 9c68380a4..000000000 --- a/vendor/github.com/mgechev/revive/rule/call-to-gc.go +++ /dev/null @@ -1,70 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// CallToGCRule lints calls to the garbage collector. -type CallToGCRule struct{} - -// Apply applies the rule to given file. -func (*CallToGCRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - gcTriggeringFunctions := map[string]map[string]bool{ - "runtime": {"GC": true}, - } - - w := lintCallToGC{onFailure, gcTriggeringFunctions} - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*CallToGCRule) Name() string { - return "call-to-gc" -} - -type lintCallToGC struct { - onFailure func(lint.Failure) - gcTriggeringFunctions map[string]map[string]bool -} - -func (w lintCallToGC) Visit(node ast.Node) ast.Visitor { - ce, ok := node.(*ast.CallExpr) - if !ok { - return w // nothing to do, the node is not a call - } - - fc, ok := ce.Fun.(*ast.SelectorExpr) - if !ok { - return nil // nothing to do, the call is not of the form pkg.func(...) - } - - id, ok := fc.X.(*ast.Ident) - - if !ok { - return nil // in case X is not an id (it should be!) - } - - fn := fc.Sel.Name - pkg := id.Name - if !w.gcTriggeringFunctions[pkg][fn] { - return nil // it isn't a call to a GC triggering function - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: node, - Category: "bad practice", - Failure: "explicit call to the garbage collector", - }) - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/call_to_gc.go b/vendor/github.com/mgechev/revive/rule/call_to_gc.go new file mode 100644 index 000000000..9c68380a4 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/call_to_gc.go @@ -0,0 +1,70 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// CallToGCRule lints calls to the garbage collector. +type CallToGCRule struct{} + +// Apply applies the rule to given file. +func (*CallToGCRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + gcTriggeringFunctions := map[string]map[string]bool{ + "runtime": {"GC": true}, + } + + w := lintCallToGC{onFailure, gcTriggeringFunctions} + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*CallToGCRule) Name() string { + return "call-to-gc" +} + +type lintCallToGC struct { + onFailure func(lint.Failure) + gcTriggeringFunctions map[string]map[string]bool +} + +func (w lintCallToGC) Visit(node ast.Node) ast.Visitor { + ce, ok := node.(*ast.CallExpr) + if !ok { + return w // nothing to do, the node is not a call + } + + fc, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return nil // nothing to do, the call is not of the form pkg.func(...) + } + + id, ok := fc.X.(*ast.Ident) + + if !ok { + return nil // in case X is not an id (it should be!) + } + + fn := fc.Sel.Name + pkg := id.Name + if !w.gcTriggeringFunctions[pkg][fn] { + return nil // it isn't a call to a GC triggering function + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: node, + Category: "bad practice", + Failure: "explicit call to the garbage collector", + }) + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go b/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go deleted file mode 100644 index 83640fd3d..000000000 --- a/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go +++ /dev/null @@ -1,214 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "sync" - - "github.com/mgechev/revive/lint" - "golang.org/x/tools/go/ast/astutil" -) - -// CognitiveComplexityRule lints given else constructs. -type CognitiveComplexityRule struct { - maxComplexity int - sync.Mutex -} - -const defaultMaxCognitiveComplexity = 7 - -func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.maxComplexity != 0 { - return // already configured - } - - if len(arguments) < 1 { - r.maxComplexity = defaultMaxCognitiveComplexity - return - } - - complexity, ok := arguments[0].(int64) - if !ok { - panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])) - } - - r.maxComplexity = int(complexity) -} - -// Apply applies the rule to given file. -func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - linter := cognitiveComplexityLinter{ - file: file, - maxComplexity: r.maxComplexity, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - linter.lintCognitiveComplexity() - - return failures -} - -// Name returns the rule name. -func (*CognitiveComplexityRule) Name() string { - return "cognitive-complexity" -} - -type cognitiveComplexityLinter struct { - file *lint.File - maxComplexity int - onFailure func(lint.Failure) -} - -func (w cognitiveComplexityLinter) lintCognitiveComplexity() { - f := w.file - for _, decl := range f.AST.Decls { - if fn, ok := decl.(*ast.FuncDecl); ok && fn.Body != nil { - v := cognitiveComplexityVisitor{} - c := v.subTreeComplexity(fn.Body) - if c > w.maxComplexity { - w.onFailure(lint.Failure{ - Confidence: 1, - Category: "maintenance", - Failure: fmt.Sprintf("function %s has cognitive complexity %d (> max enabled %d)", funcName(fn), c, w.maxComplexity), - Node: fn, - }) - } - } - } -} - -type cognitiveComplexityVisitor struct { - complexity int - nestingLevel int -} - -// subTreeComplexity calculates the cognitive complexity of an AST-subtree. -func (v cognitiveComplexityVisitor) subTreeComplexity(n ast.Node) int { - ast.Walk(&v, n) - return v.complexity -} - -// Visit implements the ast.Visitor interface. -func (v *cognitiveComplexityVisitor) Visit(n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.IfStmt: - targets := []ast.Node{n.Cond, n.Body, n.Else} - v.walk(1, targets...) - return nil - case *ast.ForStmt: - targets := []ast.Node{n.Cond, n.Body} - v.walk(1, targets...) - return nil - case *ast.RangeStmt: - v.walk(1, n.Body) - return nil - case *ast.SelectStmt: - v.walk(1, n.Body) - return nil - case *ast.SwitchStmt: - v.walk(1, n.Body) - return nil - case *ast.TypeSwitchStmt: - v.walk(1, n.Body) - return nil - case *ast.FuncLit: - v.walk(0, n.Body) // do not increment the complexity, just do the nesting - return nil - case *ast.BinaryExpr: - v.complexity += v.binExpComplexity(n) - return nil // skip visiting binexp sub-tree (already visited by binExpComplexity) - case *ast.BranchStmt: - if n.Label != nil { - v.complexity++ - } - } - // TODO handle (at least) direct recursion - - return v -} - -func (v *cognitiveComplexityVisitor) walk(complexityIncrement int, targets ...ast.Node) { - v.complexity += complexityIncrement + v.nestingLevel - nesting := v.nestingLevel - v.nestingLevel++ - - for _, t := range targets { - if t == nil { - continue - } - - ast.Walk(v, t) - } - - v.nestingLevel = nesting -} - -func (cognitiveComplexityVisitor) binExpComplexity(n *ast.BinaryExpr) int { - calculator := binExprComplexityCalculator{opsStack: []token.Token{}} - - astutil.Apply(n, calculator.pre, calculator.post) - - return calculator.complexity -} - -type binExprComplexityCalculator struct { - complexity int - opsStack []token.Token // stack of bool operators - subexpStarted bool -} - -func (becc *binExprComplexityCalculator) pre(c *astutil.Cursor) bool { - switch n := c.Node().(type) { - case *ast.BinaryExpr: - isBoolOp := n.Op == token.LAND || n.Op == token.LOR - if !isBoolOp { - break - } - - ops := len(becc.opsStack) - // if - // is the first boolop in the expression OR - // is the first boolop inside a subexpression (...) OR - // is not the same to the previous one - // then - // increment complexity - if ops == 0 || becc.subexpStarted || n.Op != becc.opsStack[ops-1] { - becc.complexity++ - becc.subexpStarted = false - } - - becc.opsStack = append(becc.opsStack, n.Op) - case *ast.ParenExpr: - becc.subexpStarted = true - } - - return true -} - -func (becc *binExprComplexityCalculator) post(c *astutil.Cursor) bool { - switch n := c.Node().(type) { - case *ast.BinaryExpr: - isBoolOp := n.Op == token.LAND || n.Op == token.LOR - if !isBoolOp { - break - } - - ops := len(becc.opsStack) - if ops > 0 { - becc.opsStack = becc.opsStack[:ops-1] - } - case *ast.ParenExpr: - becc.subexpStarted = false - } - - return true -} diff --git a/vendor/github.com/mgechev/revive/rule/cognitive_complexity.go b/vendor/github.com/mgechev/revive/rule/cognitive_complexity.go new file mode 100644 index 000000000..ecde3882e --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/cognitive_complexity.go @@ -0,0 +1,209 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "sync" + + "github.com/mgechev/revive/lint" + "golang.org/x/tools/go/ast/astutil" +) + +// CognitiveComplexityRule lints given else constructs. +type CognitiveComplexityRule struct { + maxComplexity int + + configureOnce sync.Once +} + +const defaultMaxCognitiveComplexity = 7 + +func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.maxComplexity = defaultMaxCognitiveComplexity + return + } + + complexity, ok := arguments[0].(int64) + if !ok { + panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])) + } + + r.maxComplexity = int(complexity) +} + +// Apply applies the rule to given file. +func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + linter := cognitiveComplexityLinter{ + file: file, + maxComplexity: r.maxComplexity, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + linter.lintCognitiveComplexity() + + return failures +} + +// Name returns the rule name. +func (*CognitiveComplexityRule) Name() string { + return "cognitive-complexity" +} + +type cognitiveComplexityLinter struct { + file *lint.File + maxComplexity int + onFailure func(lint.Failure) +} + +func (w cognitiveComplexityLinter) lintCognitiveComplexity() { + f := w.file + for _, decl := range f.AST.Decls { + if fn, ok := decl.(*ast.FuncDecl); ok && fn.Body != nil { + v := cognitiveComplexityVisitor{} + c := v.subTreeComplexity(fn.Body) + if c > w.maxComplexity { + w.onFailure(lint.Failure{ + Confidence: 1, + Category: "maintenance", + Failure: fmt.Sprintf("function %s has cognitive complexity %d (> max enabled %d)", funcName(fn), c, w.maxComplexity), + Node: fn, + }) + } + } + } +} + +type cognitiveComplexityVisitor struct { + complexity int + nestingLevel int +} + +// subTreeComplexity calculates the cognitive complexity of an AST-subtree. +func (v cognitiveComplexityVisitor) subTreeComplexity(n ast.Node) int { + ast.Walk(&v, n) + return v.complexity +} + +// Visit implements the ast.Visitor interface. +func (v *cognitiveComplexityVisitor) Visit(n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.IfStmt: + targets := []ast.Node{n.Cond, n.Body, n.Else} + v.walk(1, targets...) + return nil + case *ast.ForStmt: + targets := []ast.Node{n.Cond, n.Body} + v.walk(1, targets...) + return nil + case *ast.RangeStmt: + v.walk(1, n.Body) + return nil + case *ast.SelectStmt: + v.walk(1, n.Body) + return nil + case *ast.SwitchStmt: + v.walk(1, n.Body) + return nil + case *ast.TypeSwitchStmt: + v.walk(1, n.Body) + return nil + case *ast.FuncLit: + v.walk(0, n.Body) // do not increment the complexity, just do the nesting + return nil + case *ast.BinaryExpr: + v.complexity += v.binExpComplexity(n) + return nil // skip visiting binexp sub-tree (already visited by binExpComplexity) + case *ast.BranchStmt: + if n.Label != nil { + v.complexity++ + } + } + // TODO handle (at least) direct recursion + + return v +} + +func (v *cognitiveComplexityVisitor) walk(complexityIncrement int, targets ...ast.Node) { + v.complexity += complexityIncrement + v.nestingLevel + nesting := v.nestingLevel + v.nestingLevel++ + + for _, t := range targets { + if t == nil { + continue + } + + ast.Walk(v, t) + } + + v.nestingLevel = nesting +} + +func (cognitiveComplexityVisitor) binExpComplexity(n *ast.BinaryExpr) int { + calculator := binExprComplexityCalculator{opsStack: []token.Token{}} + + astutil.Apply(n, calculator.pre, calculator.post) + + return calculator.complexity +} + +type binExprComplexityCalculator struct { + complexity int + opsStack []token.Token // stack of bool operators + subexpStarted bool +} + +func (becc *binExprComplexityCalculator) pre(c *astutil.Cursor) bool { + switch n := c.Node().(type) { + case *ast.BinaryExpr: + isBoolOp := n.Op == token.LAND || n.Op == token.LOR + if !isBoolOp { + break + } + + ops := len(becc.opsStack) + // if + // is the first boolop in the expression OR + // is the first boolop inside a subexpression (...) OR + // is not the same to the previous one + // then + // increment complexity + if ops == 0 || becc.subexpStarted || n.Op != becc.opsStack[ops-1] { + becc.complexity++ + becc.subexpStarted = false + } + + becc.opsStack = append(becc.opsStack, n.Op) + case *ast.ParenExpr: + becc.subexpStarted = true + } + + return true +} + +func (becc *binExprComplexityCalculator) post(c *astutil.Cursor) bool { + switch n := c.Node().(type) { + case *ast.BinaryExpr: + isBoolOp := n.Op == token.LAND || n.Op == token.LOR + if !isBoolOp { + break + } + + ops := len(becc.opsStack) + if ops > 0 { + becc.opsStack = becc.opsStack[:ops-1] + } + case *ast.ParenExpr: + becc.subexpStarted = false + } + + return true +} diff --git a/vendor/github.com/mgechev/revive/rule/comment-spacings.go b/vendor/github.com/mgechev/revive/rule/comment-spacings.go deleted file mode 100644 index f72151301..000000000 --- a/vendor/github.com/mgechev/revive/rule/comment-spacings.go +++ /dev/null @@ -1,87 +0,0 @@ -package rule - -import ( - "fmt" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// CommentSpacingsRule check the whether there is a space between -// the comment symbol( // ) and the start of the comment text -type CommentSpacingsRule struct { - allowList []string - sync.Mutex -} - -func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.allowList != nil { - return // already configured - } - - r.allowList = []string{} - for _, arg := range arguments { - allow, ok := arg.(string) // Alt. non panicking version - if !ok { - panic(fmt.Sprintf("invalid argument %v for %s; expected string but got %T", arg, r.Name(), arg)) - } - r.allowList = append(r.allowList, `//`+allow) - } -} - -// Apply the rule. -func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) - - var failures []lint.Failure - - for _, cg := range file.AST.Comments { - for _, comment := range cg.List { - commentLine := comment.Text - if len(commentLine) < 3 { - continue // nothing to do - } - - isMultiLineComment := commentLine[1] == '*' - isOK := commentLine[2] == '\n' - if isMultiLineComment && isOK { - continue - } - - isOK = (commentLine[2] == ' ') || (commentLine[2] == '\t') - if isOK { - continue - } - - if r.isAllowed(commentLine) { - continue - } - - failures = append(failures, lint.Failure{ - Node: comment, - Confidence: 1, - Category: "style", - Failure: "no space between comment delimiter and comment text", - }) - } - } - return failures -} - -// Name yields this rule name. -func (*CommentSpacingsRule) Name() string { - return "comment-spacings" -} - -func (r *CommentSpacingsRule) isAllowed(line string) bool { - for _, allow := range r.allowList { - if strings.HasPrefix(line, allow) { - return true - } - } - - return isDirectiveComment(line) -} diff --git a/vendor/github.com/mgechev/revive/rule/comment_spacings.go b/vendor/github.com/mgechev/revive/rule/comment_spacings.go new file mode 100644 index 000000000..7bdc0e71d --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/comment_spacings.go @@ -0,0 +1,82 @@ +package rule + +import ( + "fmt" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// CommentSpacingsRule check the whether there is a space between +// the comment symbol( // ) and the start of the comment text +type CommentSpacingsRule struct { + allowList []string + + configureOnce sync.Once +} + +func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { + r.allowList = []string{} + for _, arg := range arguments { + allow, ok := arg.(string) // Alt. non panicking version + if !ok { + panic(fmt.Sprintf("invalid argument %v for %s; expected string but got %T", arg, r.Name(), arg)) + } + r.allowList = append(r.allowList, `//`+allow) + } +} + +// Apply the rule. +func (r *CommentSpacingsRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + + var failures []lint.Failure + + for _, cg := range file.AST.Comments { + for _, comment := range cg.List { + commentLine := comment.Text + if len(commentLine) < 3 { + continue // nothing to do + } + + isMultiLineComment := commentLine[1] == '*' + isOK := commentLine[2] == '\n' + if isMultiLineComment && isOK { + continue + } + + isOK = (commentLine[2] == ' ') || (commentLine[2] == '\t') + if isOK { + continue + } + + if r.isAllowed(commentLine) { + continue + } + + failures = append(failures, lint.Failure{ + Node: comment, + Confidence: 1, + Category: "style", + Failure: "no space between comment delimiter and comment text", + }) + } + } + return failures +} + +// Name yields this rule name. +func (*CommentSpacingsRule) Name() string { + return "comment-spacings" +} + +func (r *CommentSpacingsRule) isAllowed(line string) bool { + for _, allow := range r.allowList { + if strings.HasPrefix(line, allow) { + return true + } + } + + return isDirectiveComment(line) +} diff --git a/vendor/github.com/mgechev/revive/rule/comments-density.go b/vendor/github.com/mgechev/revive/rule/comments-density.go deleted file mode 100644 index c5298ea07..000000000 --- a/vendor/github.com/mgechev/revive/rule/comments-density.go +++ /dev/null @@ -1,96 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// CommentsDensityRule lints given else constructs. -type CommentsDensityRule struct { - minimumCommentsDensity int64 - configured bool - sync.Mutex -} - -const defaultMinimumCommentsPercentage = 0 - -func (r *CommentsDensityRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.configured { - return - } - - r.configured = true - - if len(arguments) < 1 { - r.minimumCommentsDensity = defaultMinimumCommentsPercentage - return - } - - var ok bool - r.minimumCommentsDensity, ok = arguments[0].(int64) - if !ok { - panic(fmt.Sprintf("invalid argument for %q rule: argument should be an int, got %T", r.Name(), arguments[0])) - } -} - -// Apply applies the rule to given file. -func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - commentsLines := countDocLines(file.AST.Comments) - statementsCount := countStatements(file.AST) - density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100 - - if density < float32(r.minimumCommentsDensity) { - return []lint.Failure{ - { - Node: file.AST, - Confidence: 1, - Failure: fmt.Sprintf("the file has a comment density of %2.f%% (%d comment lines for %d code lines) but expected a minimum of %d%%", - density, commentsLines, statementsCount, r.minimumCommentsDensity), - }, - } - } - - return nil -} - -// Name returns the rule name. -func (*CommentsDensityRule) Name() string { - return "comments-density" -} - -// countStatements counts the number of program statements in the given AST. -func countStatements(node ast.Node) int { - counter := 0 - - ast.Inspect(node, func(n ast.Node) bool { - switch n.(type) { - case *ast.ExprStmt, *ast.AssignStmt, *ast.ReturnStmt, *ast.GoStmt, *ast.DeferStmt, - *ast.BranchStmt, *ast.IfStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, - *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt, *ast.CaseClause, *ast.CommClause, - *ast.DeclStmt, *ast.FuncDecl: - counter++ - } - return true - }) - - return counter -} - -func countDocLines(comments []*ast.CommentGroup) int { - acc := 0 - for _, c := range comments { - lines := strings.Split(c.Text(), "\n") - acc += len(lines) - 1 - } - - return acc -} diff --git a/vendor/github.com/mgechev/revive/rule/comments_density.go b/vendor/github.com/mgechev/revive/rule/comments_density.go new file mode 100644 index 000000000..f2382b1f0 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/comments_density.go @@ -0,0 +1,87 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// CommentsDensityRule lints given else constructs. +type CommentsDensityRule struct { + minimumCommentsDensity int64 + + configureOnce sync.Once +} + +const defaultMinimumCommentsPercentage = 0 + +func (r *CommentsDensityRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.minimumCommentsDensity = defaultMinimumCommentsPercentage + return + } + + var ok bool + r.minimumCommentsDensity, ok = arguments[0].(int64) + if !ok { + panic(fmt.Sprintf("invalid argument for %q rule: argument should be an int, got %T", r.Name(), arguments[0])) + } +} + +// Apply applies the rule to given file. +func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + commentsLines := countDocLines(file.AST.Comments) + statementsCount := countStatements(file.AST) + density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100 + + if density < float32(r.minimumCommentsDensity) { + return []lint.Failure{ + { + Node: file.AST, + Confidence: 1, + Failure: fmt.Sprintf("the file has a comment density of %2.f%% (%d comment lines for %d code lines) but expected a minimum of %d%%", + density, commentsLines, statementsCount, r.minimumCommentsDensity), + }, + } + } + + return nil +} + +// Name returns the rule name. +func (*CommentsDensityRule) Name() string { + return "comments-density" +} + +// countStatements counts the number of program statements in the given AST. +func countStatements(node ast.Node) int { + counter := 0 + + ast.Inspect(node, func(n ast.Node) bool { + switch n.(type) { + case *ast.ExprStmt, *ast.AssignStmt, *ast.ReturnStmt, *ast.GoStmt, *ast.DeferStmt, + *ast.BranchStmt, *ast.IfStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, + *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt, *ast.CaseClause, *ast.CommClause, + *ast.DeclStmt, *ast.FuncDecl: + counter++ + } + return true + }) + + return counter +} + +func countDocLines(comments []*ast.CommentGroup) int { + acc := 0 + for _, c := range comments { + lines := strings.Split(c.Text(), "\n") + acc += len(lines) - 1 + } + + return acc +} diff --git a/vendor/github.com/mgechev/revive/rule/confusing-naming.go b/vendor/github.com/mgechev/revive/rule/confusing-naming.go deleted file mode 100644 index 32f6dd803..000000000 --- a/vendor/github.com/mgechev/revive/rule/confusing-naming.go +++ /dev/null @@ -1,207 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -type referenceMethod struct { - fileName string - id *ast.Ident -} - -type pkgMethods struct { - pkg *lint.Package - methods map[string]map[string]*referenceMethod - mu *sync.Mutex -} - -type packages struct { - pkgs []pkgMethods - mu sync.Mutex -} - -func (ps *packages) methodNames(lp *lint.Package) pkgMethods { - ps.mu.Lock() - defer ps.mu.Unlock() - - for _, pkg := range ps.pkgs { - if pkg.pkg == lp { - return pkg - } - } - - pkgm := pkgMethods{pkg: lp, methods: make(map[string]map[string]*referenceMethod), mu: &sync.Mutex{}} - ps.pkgs = append(ps.pkgs, pkgm) - - return pkgm -} - -var allPkgs = packages{pkgs: make([]pkgMethods, 1)} - -// ConfusingNamingRule lints method names that differ only by capitalization -type ConfusingNamingRule struct{} - -// Apply applies the rule to given file. -func (*ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - fileAst := file.AST - pkgm := allPkgs.methodNames(file.Pkg) - walker := lintConfusingNames{ - fileName: file.Name, - pkgm: pkgm, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(&walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ConfusingNamingRule) Name() string { - return "confusing-naming" -} - -// checkMethodName checks if a given method/function name is similar (just case differences) to other method/function of the same struct/file. -func checkMethodName(holder string, id *ast.Ident, w *lintConfusingNames) { - if id.Name == "init" && holder == defaultStructName { - // ignore init functions - return - } - - pkgm := w.pkgm - name := strings.ToUpper(id.Name) - - pkgm.mu.Lock() - defer pkgm.mu.Unlock() - - if pkgm.methods[holder] != nil { - if pkgm.methods[holder][name] != nil { - refMethod := pkgm.methods[holder][name] - // confusing names - var kind string - if holder == defaultStructName { - kind = "function" - } else { - kind = "method" - } - var fileName string - if w.fileName == refMethod.fileName { - fileName = "the same source file" - } else { - fileName = refMethod.fileName - } - w.onFailure(lint.Failure{ - Failure: fmt.Sprintf("Method '%s' differs only by capitalization to %s '%s' in %s", id.Name, kind, refMethod.id.Name, fileName), - Confidence: 1, - Node: id, - Category: "naming", - }) - - return - } - } else { - pkgm.methods[holder] = make(map[string]*referenceMethod, 1) - } - - // update the block list - if pkgm.methods[holder] == nil { - println("no entry for '", holder, "'") - } - pkgm.methods[holder][name] = &referenceMethod{fileName: w.fileName, id: id} -} - -type lintConfusingNames struct { - fileName string - pkgm pkgMethods - onFailure func(lint.Failure) -} - -const defaultStructName = "_" // used to map functions - -// getStructName of a function receiver. Defaults to defaultStructName -func getStructName(r *ast.FieldList) string { - result := defaultStructName - - if r == nil || len(r.List) < 1 { - return result - } - - t := r.List[0].Type - - switch v := t.(type) { - case *ast.StarExpr: - return extractFromStarExpr(v) - case *ast.IndexExpr: - return extractFromIndexExpr(v) - case *ast.Ident: - return v.Name - } - - return defaultStructName -} - -func extractFromStarExpr(expr *ast.StarExpr) string { - switch v := expr.X.(type) { - case *ast.IndexExpr: - return extractFromIndexExpr(v) - case *ast.Ident: - return v.Name - } - return defaultStructName -} - -func extractFromIndexExpr(expr *ast.IndexExpr) string { - switch v := expr.X.(type) { - case *ast.Ident: - return v.Name - } - return defaultStructName -} - -func checkStructFields(fields *ast.FieldList, structName string, w *lintConfusingNames) { - bl := make(map[string]bool, len(fields.List)) - for _, f := range fields.List { - for _, id := range f.Names { - normName := strings.ToUpper(id.Name) - if bl[normName] { - w.onFailure(lint.Failure{ - Failure: fmt.Sprintf("Field '%s' differs only by capitalization to other field in the struct type %s", id.Name, structName), - Confidence: 1, - Node: id, - Category: "naming", - }) - } else { - bl[normName] = true - } - } - } -} - -func (w *lintConfusingNames) Visit(n ast.Node) ast.Visitor { - switch v := n.(type) { - case *ast.FuncDecl: - // Exclude naming warnings for functions that are exported to C but - // not exported in the Go API. - // See https://github.com/golang/lint/issues/144. - if ast.IsExported(v.Name.Name) || !isCgoExported(v) { - checkMethodName(getStructName(v.Recv), v.Name, w) - } - case *ast.TypeSpec: - if s, ok := v.Type.(*ast.StructType); ok { - checkStructFields(s.Fields, v.Name.Name, w) - } - - default: - // will add other checks like field names, struct names, etc. - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/confusing-results.go b/vendor/github.com/mgechev/revive/rule/confusing-results.go deleted file mode 100644 index 1b79ada9c..000000000 --- a/vendor/github.com/mgechev/revive/rule/confusing-results.go +++ /dev/null @@ -1,66 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// ConfusingResultsRule lints given function declarations -type ConfusingResultsRule struct{} - -// Apply applies the rule to given file. -func (*ConfusingResultsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintConfusingResults{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ConfusingResultsRule) Name() string { - return "confusing-results" -} - -type lintConfusingResults struct { - onFailure func(lint.Failure) -} - -func (w lintConfusingResults) Visit(n ast.Node) ast.Visitor { - fn, ok := n.(*ast.FuncDecl) - if !ok || fn.Type.Results == nil || len(fn.Type.Results.List) < 2 { - return w - } - lastType := "" - for _, result := range fn.Type.Results.List { - if len(result.Names) > 0 { - return w - } - - t, ok := result.Type.(*ast.Ident) - if !ok { - return w - } - - if t.Name == lastType { - w.onFailure(lint.Failure{ - Node: n, - Confidence: 1, - Category: "naming", - Failure: "unnamed results of the same type may be confusing, consider using named results", - }) - break - } - lastType = t.Name - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/confusing_naming.go b/vendor/github.com/mgechev/revive/rule/confusing_naming.go new file mode 100644 index 000000000..32f6dd803 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/confusing_naming.go @@ -0,0 +1,207 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +type referenceMethod struct { + fileName string + id *ast.Ident +} + +type pkgMethods struct { + pkg *lint.Package + methods map[string]map[string]*referenceMethod + mu *sync.Mutex +} + +type packages struct { + pkgs []pkgMethods + mu sync.Mutex +} + +func (ps *packages) methodNames(lp *lint.Package) pkgMethods { + ps.mu.Lock() + defer ps.mu.Unlock() + + for _, pkg := range ps.pkgs { + if pkg.pkg == lp { + return pkg + } + } + + pkgm := pkgMethods{pkg: lp, methods: make(map[string]map[string]*referenceMethod), mu: &sync.Mutex{}} + ps.pkgs = append(ps.pkgs, pkgm) + + return pkgm +} + +var allPkgs = packages{pkgs: make([]pkgMethods, 1)} + +// ConfusingNamingRule lints method names that differ only by capitalization +type ConfusingNamingRule struct{} + +// Apply applies the rule to given file. +func (*ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + fileAst := file.AST + pkgm := allPkgs.methodNames(file.Pkg) + walker := lintConfusingNames{ + fileName: file.Name, + pkgm: pkgm, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(&walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ConfusingNamingRule) Name() string { + return "confusing-naming" +} + +// checkMethodName checks if a given method/function name is similar (just case differences) to other method/function of the same struct/file. +func checkMethodName(holder string, id *ast.Ident, w *lintConfusingNames) { + if id.Name == "init" && holder == defaultStructName { + // ignore init functions + return + } + + pkgm := w.pkgm + name := strings.ToUpper(id.Name) + + pkgm.mu.Lock() + defer pkgm.mu.Unlock() + + if pkgm.methods[holder] != nil { + if pkgm.methods[holder][name] != nil { + refMethod := pkgm.methods[holder][name] + // confusing names + var kind string + if holder == defaultStructName { + kind = "function" + } else { + kind = "method" + } + var fileName string + if w.fileName == refMethod.fileName { + fileName = "the same source file" + } else { + fileName = refMethod.fileName + } + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("Method '%s' differs only by capitalization to %s '%s' in %s", id.Name, kind, refMethod.id.Name, fileName), + Confidence: 1, + Node: id, + Category: "naming", + }) + + return + } + } else { + pkgm.methods[holder] = make(map[string]*referenceMethod, 1) + } + + // update the block list + if pkgm.methods[holder] == nil { + println("no entry for '", holder, "'") + } + pkgm.methods[holder][name] = &referenceMethod{fileName: w.fileName, id: id} +} + +type lintConfusingNames struct { + fileName string + pkgm pkgMethods + onFailure func(lint.Failure) +} + +const defaultStructName = "_" // used to map functions + +// getStructName of a function receiver. Defaults to defaultStructName +func getStructName(r *ast.FieldList) string { + result := defaultStructName + + if r == nil || len(r.List) < 1 { + return result + } + + t := r.List[0].Type + + switch v := t.(type) { + case *ast.StarExpr: + return extractFromStarExpr(v) + case *ast.IndexExpr: + return extractFromIndexExpr(v) + case *ast.Ident: + return v.Name + } + + return defaultStructName +} + +func extractFromStarExpr(expr *ast.StarExpr) string { + switch v := expr.X.(type) { + case *ast.IndexExpr: + return extractFromIndexExpr(v) + case *ast.Ident: + return v.Name + } + return defaultStructName +} + +func extractFromIndexExpr(expr *ast.IndexExpr) string { + switch v := expr.X.(type) { + case *ast.Ident: + return v.Name + } + return defaultStructName +} + +func checkStructFields(fields *ast.FieldList, structName string, w *lintConfusingNames) { + bl := make(map[string]bool, len(fields.List)) + for _, f := range fields.List { + for _, id := range f.Names { + normName := strings.ToUpper(id.Name) + if bl[normName] { + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("Field '%s' differs only by capitalization to other field in the struct type %s", id.Name, structName), + Confidence: 1, + Node: id, + Category: "naming", + }) + } else { + bl[normName] = true + } + } + } +} + +func (w *lintConfusingNames) Visit(n ast.Node) ast.Visitor { + switch v := n.(type) { + case *ast.FuncDecl: + // Exclude naming warnings for functions that are exported to C but + // not exported in the Go API. + // See https://github.com/golang/lint/issues/144. + if ast.IsExported(v.Name.Name) || !isCgoExported(v) { + checkMethodName(getStructName(v.Recv), v.Name, w) + } + case *ast.TypeSpec: + if s, ok := v.Type.(*ast.StructType); ok { + checkStructFields(s.Fields, v.Name.Name, w) + } + + default: + // will add other checks like field names, struct names, etc. + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/confusing_results.go b/vendor/github.com/mgechev/revive/rule/confusing_results.go new file mode 100644 index 000000000..1b79ada9c --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/confusing_results.go @@ -0,0 +1,66 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// ConfusingResultsRule lints given function declarations +type ConfusingResultsRule struct{} + +// Apply applies the rule to given file. +func (*ConfusingResultsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := lintConfusingResults{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ConfusingResultsRule) Name() string { + return "confusing-results" +} + +type lintConfusingResults struct { + onFailure func(lint.Failure) +} + +func (w lintConfusingResults) Visit(n ast.Node) ast.Visitor { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Type.Results == nil || len(fn.Type.Results.List) < 2 { + return w + } + lastType := "" + for _, result := range fn.Type.Results.List { + if len(result.Names) > 0 { + return w + } + + t, ok := result.Type.(*ast.Ident) + if !ok { + return w + } + + if t.Name == lastType { + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: "unnamed results of the same type may be confusing, consider using named results", + }) + break + } + lastType = t.Name + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go b/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go deleted file mode 100644 index 9e34d3d16..000000000 --- a/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go +++ /dev/null @@ -1,101 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// ConstantLogicalExprRule warns on constant logical expressions. -type ConstantLogicalExprRule struct{} - -// Apply applies the rule to given file. -func (*ConstantLogicalExprRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - w := &lintConstantLogicalExpr{astFile, onFailure} - ast.Walk(w, astFile) - return failures -} - -// Name returns the rule name. -func (*ConstantLogicalExprRule) Name() string { - return "constant-logical-expr" -} - -type lintConstantLogicalExpr struct { - file *ast.File - onFailure func(lint.Failure) -} - -func (w *lintConstantLogicalExpr) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.BinaryExpr: - if !w.isOperatorWithLogicalResult(n.Op) { - return w - } - - subExpressionsAreNotEqual := gofmt(n.X) != gofmt(n.Y) - if subExpressionsAreNotEqual { - return w // nothing to say - } - - // Handles cases like: a <= a, a == a, a >= a - if w.isEqualityOperator(n.Op) { - w.newFailure(n, "expression always evaluates to true") - return w - } - - // Handles cases like: a < a, a > a, a != a - if w.isInequalityOperator(n.Op) { - w.newFailure(n, "expression always evaluates to false") - return w - } - - w.newFailure(n, "left and right hand-side sub-expressions are the same") - } - - return w -} - -func (*lintConstantLogicalExpr) isOperatorWithLogicalResult(t token.Token) bool { - switch t { - case token.LAND, token.LOR, token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ: - return true - } - - return false -} - -func (*lintConstantLogicalExpr) isEqualityOperator(t token.Token) bool { - switch t { - case token.EQL, token.LEQ, token.GEQ: - return true - } - - return false -} - -func (*lintConstantLogicalExpr) isInequalityOperator(t token.Token) bool { - switch t { - case token.LSS, token.GTR, token.NEQ: - return true - } - - return false -} - -func (w lintConstantLogicalExpr) newFailure(node ast.Node, msg string) { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: node, - Category: "logic", - Failure: msg, - }) -} diff --git a/vendor/github.com/mgechev/revive/rule/constant_logical_expr.go b/vendor/github.com/mgechev/revive/rule/constant_logical_expr.go new file mode 100644 index 000000000..9e34d3d16 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/constant_logical_expr.go @@ -0,0 +1,101 @@ +package rule + +import ( + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// ConstantLogicalExprRule warns on constant logical expressions. +type ConstantLogicalExprRule struct{} + +// Apply applies the rule to given file. +func (*ConstantLogicalExprRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + w := &lintConstantLogicalExpr{astFile, onFailure} + ast.Walk(w, astFile) + return failures +} + +// Name returns the rule name. +func (*ConstantLogicalExprRule) Name() string { + return "constant-logical-expr" +} + +type lintConstantLogicalExpr struct { + file *ast.File + onFailure func(lint.Failure) +} + +func (w *lintConstantLogicalExpr) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.BinaryExpr: + if !w.isOperatorWithLogicalResult(n.Op) { + return w + } + + subExpressionsAreNotEqual := gofmt(n.X) != gofmt(n.Y) + if subExpressionsAreNotEqual { + return w // nothing to say + } + + // Handles cases like: a <= a, a == a, a >= a + if w.isEqualityOperator(n.Op) { + w.newFailure(n, "expression always evaluates to true") + return w + } + + // Handles cases like: a < a, a > a, a != a + if w.isInequalityOperator(n.Op) { + w.newFailure(n, "expression always evaluates to false") + return w + } + + w.newFailure(n, "left and right hand-side sub-expressions are the same") + } + + return w +} + +func (*lintConstantLogicalExpr) isOperatorWithLogicalResult(t token.Token) bool { + switch t { + case token.LAND, token.LOR, token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ: + return true + } + + return false +} + +func (*lintConstantLogicalExpr) isEqualityOperator(t token.Token) bool { + switch t { + case token.EQL, token.LEQ, token.GEQ: + return true + } + + return false +} + +func (*lintConstantLogicalExpr) isInequalityOperator(t token.Token) bool { + switch t { + case token.LSS, token.GTR, token.NEQ: + return true + } + + return false +} + +func (w lintConstantLogicalExpr) newFailure(node ast.Node, msg string) { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: node, + Category: "logic", + Failure: msg, + }) +} diff --git a/vendor/github.com/mgechev/revive/rule/context-as-argument.go b/vendor/github.com/mgechev/revive/rule/context-as-argument.go deleted file mode 100644 index e0c8cfa5e..000000000 --- a/vendor/github.com/mgechev/revive/rule/context-as-argument.go +++ /dev/null @@ -1,110 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// ContextAsArgumentRule lints given else constructs. -type ContextAsArgumentRule struct { - allowTypesLUT map[string]struct{} - sync.Mutex -} - -// Apply applies the rule to given file. -func (r *ContextAsArgumentRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.Lock() - if r.allowTypesLUT == nil { - r.allowTypesLUT = getAllowTypesFromArguments(args) - } - r.Unlock() - - var failures []lint.Failure - r.Lock() - walker := lintContextArguments{ - allowTypesLUT: r.allowTypesLUT, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - r.Unlock() - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*ContextAsArgumentRule) Name() string { - return "context-as-argument" -} - -type lintContextArguments struct { - allowTypesLUT map[string]struct{} - onFailure func(lint.Failure) -} - -func (w lintContextArguments) Visit(n ast.Node) ast.Visitor { - fn, ok := n.(*ast.FuncDecl) - if !ok || len(fn.Type.Params.List) <= 1 { - return w - } - - fnArgs := fn.Type.Params.List - - // A context.Context should be the first parameter of a function. - // Flag any that show up after the first. - isCtxStillAllowed := true - for _, arg := range fnArgs { - argIsCtx := isPkgDot(arg.Type, "context", "Context") - if argIsCtx && !isCtxStillAllowed { - w.onFailure(lint.Failure{ - Node: arg, - Category: "arg-order", - Failure: "context.Context should be the first parameter of a function", - Confidence: 0.9, - }) - break // only flag one - } - - typeName := gofmt(arg.Type) - // a parameter of type context.Context is still allowed if the current arg type is in the LUT - _, isCtxStillAllowed = w.allowTypesLUT[typeName] - } - - return nil // avoid visiting the function body -} - -func getAllowTypesFromArguments(args lint.Arguments) map[string]struct{} { - allowTypesBefore := []string{} - if len(args) >= 1 { - argKV, ok := args[0].(map[string]any) - if !ok { - panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Expecting a k,v map, got %T", args[0])) - } - for k, v := range argKV { - switch k { - case "allowTypesBefore": - typesBefore, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)) - } - allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...) - default: - panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Unrecognized key %s", k)) - } - } - } - - result := make(map[string]struct{}, len(allowTypesBefore)) - for _, v := range allowTypesBefore { - result[v] = struct{}{} - } - - result["context.Context"] = struct{}{} // context.Context is always allowed before another context.Context - return result -} diff --git a/vendor/github.com/mgechev/revive/rule/context-keys-type.go b/vendor/github.com/mgechev/revive/rule/context-keys-type.go deleted file mode 100644 index 60ccec560..000000000 --- a/vendor/github.com/mgechev/revive/rule/context-keys-type.go +++ /dev/null @@ -1,81 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/types" - - "github.com/mgechev/revive/lint" -) - -// ContextKeysType lints given else constructs. -type ContextKeysType struct{} - -// Apply applies the rule to given file. -func (*ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintContextKeyTypes{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - file.Pkg.TypeCheck() - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ContextKeysType) Name() string { - return "context-keys-type" -} - -type lintContextKeyTypes struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) -} - -func (w lintContextKeyTypes) Visit(n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.CallExpr: - checkContextKeyType(w, n) - } - - return w -} - -func checkContextKeyType(w lintContextKeyTypes, x *ast.CallExpr) { - f := w.file - sel, ok := x.Fun.(*ast.SelectorExpr) - if !ok { - return - } - pkg, ok := sel.X.(*ast.Ident) - if !ok || pkg.Name != "context" { - return - } - if sel.Sel.Name != "WithValue" { - return - } - - // key is second argument to context.WithValue - if len(x.Args) != 3 { - return - } - key := f.Pkg.TypesInfo().Types[x.Args[1]] - - if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: x, - Category: "content", - Failure: fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type), - }) - } -} diff --git a/vendor/github.com/mgechev/revive/rule/context_as_argument.go b/vendor/github.com/mgechev/revive/rule/context_as_argument.go new file mode 100644 index 000000000..8bc5f8b61 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/context_as_argument.go @@ -0,0 +1,109 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// ContextAsArgumentRule lints given else constructs. +type ContextAsArgumentRule struct { + allowTypesLUT map[string]struct{} + + configureOnce sync.Once +} + +// Apply applies the rule to given file. +func (r *ContextAsArgumentRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + + var failures []lint.Failure + walker := lintContextArguments{ + allowTypesLUT: r.allowTypesLUT, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +func (r *ContextAsArgumentRule) configure(arguments lint.Arguments) { + r.allowTypesLUT = getAllowTypesFromArguments(arguments) +} + +// Name returns the rule name. +func (*ContextAsArgumentRule) Name() string { + return "context-as-argument" +} + +type lintContextArguments struct { + allowTypesLUT map[string]struct{} + onFailure func(lint.Failure) +} + +func (w lintContextArguments) Visit(n ast.Node) ast.Visitor { + fn, ok := n.(*ast.FuncDecl) + if !ok || len(fn.Type.Params.List) <= 1 { + return w + } + + fnArgs := fn.Type.Params.List + + // A context.Context should be the first parameter of a function. + // Flag any that show up after the first. + isCtxStillAllowed := true + for _, arg := range fnArgs { + argIsCtx := isPkgDot(arg.Type, "context", "Context") + if argIsCtx && !isCtxStillAllowed { + w.onFailure(lint.Failure{ + Node: arg, + Category: "arg-order", + Failure: "context.Context should be the first parameter of a function", + Confidence: 0.9, + }) + break // only flag one + } + + typeName := gofmt(arg.Type) + // a parameter of type context.Context is still allowed if the current arg type is in the LUT + _, isCtxStillAllowed = w.allowTypesLUT[typeName] + } + + return nil // avoid visiting the function body +} + +func getAllowTypesFromArguments(args lint.Arguments) map[string]struct{} { + allowTypesBefore := []string{} + if len(args) >= 1 { + argKV, ok := args[0].(map[string]any) + if !ok { + panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Expecting a k,v map, got %T", args[0])) + } + for k, v := range argKV { + switch k { + case "allowTypesBefore": + typesBefore, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)) + } + allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...) + default: + panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Unrecognized key %s", k)) + } + } + } + + result := make(map[string]struct{}, len(allowTypesBefore)) + for _, v := range allowTypesBefore { + result[v] = struct{}{} + } + + result["context.Context"] = struct{}{} // context.Context is always allowed before another context.Context + return result +} diff --git a/vendor/github.com/mgechev/revive/rule/context_keys_type.go b/vendor/github.com/mgechev/revive/rule/context_keys_type.go new file mode 100644 index 000000000..60ccec560 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/context_keys_type.go @@ -0,0 +1,81 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/types" + + "github.com/mgechev/revive/lint" +) + +// ContextKeysType lints given else constructs. +type ContextKeysType struct{} + +// Apply applies the rule to given file. +func (*ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := lintContextKeyTypes{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + file.Pkg.TypeCheck() + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ContextKeysType) Name() string { + return "context-keys-type" +} + +type lintContextKeyTypes struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) +} + +func (w lintContextKeyTypes) Visit(n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.CallExpr: + checkContextKeyType(w, n) + } + + return w +} + +func checkContextKeyType(w lintContextKeyTypes, x *ast.CallExpr) { + f := w.file + sel, ok := x.Fun.(*ast.SelectorExpr) + if !ok { + return + } + pkg, ok := sel.X.(*ast.Ident) + if !ok || pkg.Name != "context" { + return + } + if sel.Sel.Name != "WithValue" { + return + } + + // key is second argument to context.WithValue + if len(x.Args) != 3 { + return + } + key := f.Pkg.TypesInfo().Types[x.Args[1]] + + if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: x, + Category: "content", + Failure: fmt.Sprintf("should not use basic type %s as key in context.WithValue", key.Type), + }) + } +} diff --git a/vendor/github.com/mgechev/revive/rule/cyclomatic.go b/vendor/github.com/mgechev/revive/rule/cyclomatic.go index 10413de24..c1a2de97a 100644 --- a/vendor/github.com/mgechev/revive/rule/cyclomatic.go +++ b/vendor/github.com/mgechev/revive/rule/cyclomatic.go @@ -14,18 +14,13 @@ import ( // CyclomaticRule lints given else constructs. type CyclomaticRule struct { maxComplexity int - sync.Mutex + + configureOnce sync.Once } const defaultMaxCyclomaticComplexity = 10 func (r *CyclomaticRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.maxComplexity != 0 { - return // already configured - } - if len(arguments) < 1 { r.maxComplexity = defaultMaxCyclomaticComplexity return @@ -40,7 +35,7 @@ func (r *CyclomaticRule) configure(arguments lint.Arguments) { // Apply applies the rule to given file. func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) + r.configureOnce.Do(func() { r.configure(arguments) }) var failures []lint.Failure fileAst := file.AST diff --git a/vendor/github.com/mgechev/revive/rule/deep-exit.go b/vendor/github.com/mgechev/revive/rule/deep-exit.go deleted file mode 100644 index 7b3dd0f82..000000000 --- a/vendor/github.com/mgechev/revive/rule/deep-exit.go +++ /dev/null @@ -1,95 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// DeepExitRule lints program exit at functions other than main or init. -type DeepExitRule struct{} - -// Apply applies the rule to given file. -func (*DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - exitFunctions := map[string]map[string]bool{ - "os": {"Exit": true}, - "syscall": {"Exit": true}, - "log": { - "Fatal": true, - "Fatalf": true, - "Fatalln": true, - "Panic": true, - "Panicf": true, - "Panicln": true, - }, - } - - w := lintDeepExit{onFailure, exitFunctions, file.IsTest()} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*DeepExitRule) Name() string { - return "deep-exit" -} - -type lintDeepExit struct { - onFailure func(lint.Failure) - exitFunctions map[string]map[string]bool - isTestFile bool -} - -func (w lintDeepExit) Visit(node ast.Node) ast.Visitor { - if fd, ok := node.(*ast.FuncDecl); ok { - if w.mustIgnore(fd) { - return nil // skip analysis of this function - } - - return w - } - - se, ok := node.(*ast.ExprStmt) - if !ok { - return w - } - ce, ok := se.X.(*ast.CallExpr) - if !ok { - return w - } - - fc, ok := ce.Fun.(*ast.SelectorExpr) - if !ok { - return w - } - id, ok := fc.X.(*ast.Ident) - if !ok { - return w - } - - pkg := id.Name - fn := fc.Sel.Name - isACallToExitFunction := w.exitFunctions[pkg] != nil && w.exitFunctions[pkg][fn] - if isACallToExitFunction { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: ce, - Category: "bad practice", - Failure: fmt.Sprintf("calls to %s.%s only in main() or init() functions", pkg, fn), - }) - } - - return w -} - -func (w *lintDeepExit) mustIgnore(fd *ast.FuncDecl) bool { - fn := fd.Name.Name - - return fn == "init" || fn == "main" || (w.isTestFile && fn == "TestMain") -} diff --git a/vendor/github.com/mgechev/revive/rule/deep_exit.go b/vendor/github.com/mgechev/revive/rule/deep_exit.go new file mode 100644 index 000000000..7b3dd0f82 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/deep_exit.go @@ -0,0 +1,95 @@ +package rule + +import ( + "fmt" + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// DeepExitRule lints program exit at functions other than main or init. +type DeepExitRule struct{} + +// Apply applies the rule to given file. +func (*DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + exitFunctions := map[string]map[string]bool{ + "os": {"Exit": true}, + "syscall": {"Exit": true}, + "log": { + "Fatal": true, + "Fatalf": true, + "Fatalln": true, + "Panic": true, + "Panicf": true, + "Panicln": true, + }, + } + + w := lintDeepExit{onFailure, exitFunctions, file.IsTest()} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*DeepExitRule) Name() string { + return "deep-exit" +} + +type lintDeepExit struct { + onFailure func(lint.Failure) + exitFunctions map[string]map[string]bool + isTestFile bool +} + +func (w lintDeepExit) Visit(node ast.Node) ast.Visitor { + if fd, ok := node.(*ast.FuncDecl); ok { + if w.mustIgnore(fd) { + return nil // skip analysis of this function + } + + return w + } + + se, ok := node.(*ast.ExprStmt) + if !ok { + return w + } + ce, ok := se.X.(*ast.CallExpr) + if !ok { + return w + } + + fc, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return w + } + id, ok := fc.X.(*ast.Ident) + if !ok { + return w + } + + pkg := id.Name + fn := fc.Sel.Name + isACallToExitFunction := w.exitFunctions[pkg] != nil && w.exitFunctions[pkg][fn] + if isACallToExitFunction { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: ce, + Category: "bad practice", + Failure: fmt.Sprintf("calls to %s.%s only in main() or init() functions", pkg, fn), + }) + } + + return w +} + +func (w *lintDeepExit) mustIgnore(fd *ast.FuncDecl) bool { + fn := fd.Name.Name + + return fn == "init" || fn == "main" || (w.isTestFile && fn == "TestMain") +} diff --git a/vendor/github.com/mgechev/revive/rule/defer.go b/vendor/github.com/mgechev/revive/rule/defer.go index 3c31d507b..f7c716eb6 100644 --- a/vendor/github.com/mgechev/revive/rule/defer.go +++ b/vendor/github.com/mgechev/revive/rule/defer.go @@ -11,22 +11,17 @@ import ( // DeferRule lints unused params in functions. type DeferRule struct { allow map[string]bool - sync.Mutex + + configureOnce sync.Once } func (r *DeferRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.allow != nil { - return // already configured - } - r.allow = r.allowFromArgs(arguments) } // Apply applies the rule to given file. func (r *DeferRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) + r.configureOnce.Do(func() { r.configure(arguments) }) var failures []lint.Failure onFailure := func(failure lint.Failure) { diff --git a/vendor/github.com/mgechev/revive/rule/dot-imports.go b/vendor/github.com/mgechev/revive/rule/dot-imports.go deleted file mode 100644 index df0b2a7f4..000000000 --- a/vendor/github.com/mgechev/revive/rule/dot-imports.go +++ /dev/null @@ -1,107 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -// DotImportsRule lints given else constructs. -type DotImportsRule struct { - sync.Mutex - allowedPackages allowPackages -} - -// Apply applies the rule to given file. -func (r *DotImportsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - fileAst := file.AST - walker := lintImports{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - allowPackages: r.allowedPackages, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*DotImportsRule) Name() string { - return "dot-imports" -} - -func (r *DotImportsRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.allowedPackages != nil { - return - } - - r.allowedPackages = make(allowPackages) - if len(arguments) == 0 { - return - } - - args, ok := arguments[0].(map[string]any) - if !ok { - panic(fmt.Sprintf("Invalid argument to the dot-imports rule. Expecting a k,v map, got %T", arguments[0])) - } - - if allowedPkgArg, ok := args["allowedPackages"]; ok { - pkgs, ok := allowedPkgArg.([]any) - if !ok { - panic(fmt.Sprintf("Invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", allowedPkgArg, allowedPkgArg)) - } - for _, p := range pkgs { - pkg, ok := p.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the dot-imports rule, string expected. Got '%v' (%T)", p, p)) - } - r.allowedPackages.add(pkg) - } - } -} - -type lintImports struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) - allowPackages allowPackages -} - -func (w lintImports) Visit(_ ast.Node) ast.Visitor { - for _, importSpec := range w.fileAst.Imports { - isDotImport := importSpec.Name != nil && importSpec.Name.Name == "." - if isDotImport && !w.allowPackages.isAllowedPackage(importSpec.Path.Value) { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: "should not use dot imports", - Node: importSpec, - Category: "imports", - }) - } - } - return nil -} - -type allowPackages map[string]struct{} - -func (ap allowPackages) add(pkg string) { - ap[fmt.Sprintf(`"%s"`, pkg)] = struct{}{} // import path strings are with double quotes -} - -func (ap allowPackages) isAllowedPackage(pkg string) bool { - _, allowed := ap[pkg] - return allowed -} diff --git a/vendor/github.com/mgechev/revive/rule/dot_imports.go b/vendor/github.com/mgechev/revive/rule/dot_imports.go new file mode 100644 index 000000000..f6c7fbcfb --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/dot_imports.go @@ -0,0 +1,101 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +// DotImportsRule lints given else constructs. +type DotImportsRule struct { + allowedPackages allowPackages + + configureOnce sync.Once +} + +// Apply applies the rule to given file. +func (r *DotImportsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + fileAst := file.AST + walker := lintImports{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + allowPackages: r.allowedPackages, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*DotImportsRule) Name() string { + return "dot-imports" +} + +func (r *DotImportsRule) configure(arguments lint.Arguments) { + r.allowedPackages = make(allowPackages) + if len(arguments) == 0 { + return + } + + args, ok := arguments[0].(map[string]any) + if !ok { + panic(fmt.Sprintf("Invalid argument to the dot-imports rule. Expecting a k,v map, got %T", arguments[0])) + } + + if allowedPkgArg, ok := args["allowedPackages"]; ok { + pkgs, ok := allowedPkgArg.([]any) + if !ok { + panic(fmt.Sprintf("Invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", allowedPkgArg, allowedPkgArg)) + } + for _, p := range pkgs { + pkg, ok := p.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the dot-imports rule, string expected. Got '%v' (%T)", p, p)) + } + r.allowedPackages.add(pkg) + } + } +} + +type lintImports struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) + allowPackages allowPackages +} + +func (w lintImports) Visit(_ ast.Node) ast.Visitor { + for _, importSpec := range w.fileAst.Imports { + isDotImport := importSpec.Name != nil && importSpec.Name.Name == "." + if isDotImport && !w.allowPackages.isAllowedPackage(importSpec.Path.Value) { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: "should not use dot imports", + Node: importSpec, + Category: "imports", + }) + } + } + return nil +} + +type allowPackages map[string]struct{} + +func (ap allowPackages) add(pkg string) { + ap[fmt.Sprintf(`"%s"`, pkg)] = struct{}{} // import path strings are with double quotes +} + +func (ap allowPackages) isAllowedPackage(pkg string) bool { + _, allowed := ap[pkg] + return allowed +} diff --git a/vendor/github.com/mgechev/revive/rule/duplicated-imports.go b/vendor/github.com/mgechev/revive/rule/duplicated-imports.go deleted file mode 100644 index 2b177fac6..000000000 --- a/vendor/github.com/mgechev/revive/rule/duplicated-imports.go +++ /dev/null @@ -1,39 +0,0 @@ -package rule - -import ( - "fmt" - - "github.com/mgechev/revive/lint" -) - -// DuplicatedImportsRule lints given else constructs. -type DuplicatedImportsRule struct{} - -// Apply applies the rule to given file. -func (*DuplicatedImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - impPaths := map[string]struct{}{} - for _, imp := range file.AST.Imports { - path := imp.Path.Value - _, ok := impPaths[path] - if ok { - failures = append(failures, lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("Package %s already imported", path), - Node: imp, - Category: "imports", - }) - continue - } - - impPaths[path] = struct{}{} - } - - return failures -} - -// Name returns the rule name. -func (*DuplicatedImportsRule) Name() string { - return "duplicated-imports" -} diff --git a/vendor/github.com/mgechev/revive/rule/duplicated_imports.go b/vendor/github.com/mgechev/revive/rule/duplicated_imports.go new file mode 100644 index 000000000..2b177fac6 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/duplicated_imports.go @@ -0,0 +1,39 @@ +package rule + +import ( + "fmt" + + "github.com/mgechev/revive/lint" +) + +// DuplicatedImportsRule lints given else constructs. +type DuplicatedImportsRule struct{} + +// Apply applies the rule to given file. +func (*DuplicatedImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + impPaths := map[string]struct{}{} + for _, imp := range file.AST.Imports { + path := imp.Path.Value + _, ok := impPaths[path] + if ok { + failures = append(failures, lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("Package %s already imported", path), + Node: imp, + Category: "imports", + }) + continue + } + + impPaths[path] = struct{}{} + } + + return failures +} + +// Name returns the rule name. +func (*DuplicatedImportsRule) Name() string { + return "duplicated-imports" +} diff --git a/vendor/github.com/mgechev/revive/rule/early-return.go b/vendor/github.com/mgechev/revive/rule/early-return.go deleted file mode 100644 index 62d491f27..000000000 --- a/vendor/github.com/mgechev/revive/rule/early-return.go +++ /dev/null @@ -1,51 +0,0 @@ -package rule - -import ( - "fmt" - - "github.com/mgechev/revive/internal/ifelse" - "github.com/mgechev/revive/lint" -) - -// EarlyReturnRule finds opportunities to reduce nesting by inverting -// the condition of an "if" block. -type EarlyReturnRule struct{} - -// Apply applies the rule to given file. -func (e *EarlyReturnRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - return ifelse.Apply(e, file.AST, ifelse.TargetIf, args) -} - -// Name returns the rule name. -func (*EarlyReturnRule) Name() string { - return "early-return" -} - -// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable. -func (*EarlyReturnRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string { - if !chain.Else.Deviates() { - // this rule only applies if the else-block deviates control flow - return "" - } - - if chain.HasPriorNonDeviating && !chain.If.IsEmpty() { - // if we de-indent this block then a previous branch - // might flow into it, affecting program behaviour - return "" - } - - if chain.If.Deviates() { - // avoid overlapping with superfluous-else - return "" - } - - if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.If.HasDecls) { - // avoid increasing variable scope - return "" - } - - if chain.If.IsEmpty() { - return fmt.Sprintf("if c { } else { %[1]v } can be simplified to if !c { %[1]v }", chain.Else) - } - return fmt.Sprintf("if c { ... } else { %[1]v } can be simplified to if !c { %[1]v } ...", chain.Else) -} diff --git a/vendor/github.com/mgechev/revive/rule/early_return.go b/vendor/github.com/mgechev/revive/rule/early_return.go new file mode 100644 index 000000000..62d491f27 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/early_return.go @@ -0,0 +1,51 @@ +package rule + +import ( + "fmt" + + "github.com/mgechev/revive/internal/ifelse" + "github.com/mgechev/revive/lint" +) + +// EarlyReturnRule finds opportunities to reduce nesting by inverting +// the condition of an "if" block. +type EarlyReturnRule struct{} + +// Apply applies the rule to given file. +func (e *EarlyReturnRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + return ifelse.Apply(e, file.AST, ifelse.TargetIf, args) +} + +// Name returns the rule name. +func (*EarlyReturnRule) Name() string { + return "early-return" +} + +// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable. +func (*EarlyReturnRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string { + if !chain.Else.Deviates() { + // this rule only applies if the else-block deviates control flow + return "" + } + + if chain.HasPriorNonDeviating && !chain.If.IsEmpty() { + // if we de-indent this block then a previous branch + // might flow into it, affecting program behaviour + return "" + } + + if chain.If.Deviates() { + // avoid overlapping with superfluous-else + return "" + } + + if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.If.HasDecls) { + // avoid increasing variable scope + return "" + } + + if chain.If.IsEmpty() { + return fmt.Sprintf("if c { } else { %[1]v } can be simplified to if !c { %[1]v }", chain.Else) + } + return fmt.Sprintf("if c { ... } else { %[1]v } can be simplified to if !c { %[1]v } ...", chain.Else) +} diff --git a/vendor/github.com/mgechev/revive/rule/empty-block.go b/vendor/github.com/mgechev/revive/rule/empty-block.go deleted file mode 100644 index 25a052a0e..000000000 --- a/vendor/github.com/mgechev/revive/rule/empty-block.go +++ /dev/null @@ -1,75 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// EmptyBlockRule lints given else constructs. -type EmptyBlockRule struct{} - -// Apply applies the rule to given file. -func (*EmptyBlockRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintEmptyBlock{make(map[*ast.BlockStmt]bool), onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*EmptyBlockRule) Name() string { - return "empty-block" -} - -type lintEmptyBlock struct { - ignore map[*ast.BlockStmt]bool - onFailure func(lint.Failure) -} - -func (w lintEmptyBlock) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - w.ignore[n.Body] = true - return w - case *ast.FuncLit: - w.ignore[n.Body] = true - return w - case *ast.SelectStmt: - w.ignore[n.Body] = true - return w - case *ast.ForStmt: - if len(n.Body.List) == 0 && n.Init == nil && n.Post == nil && n.Cond != nil { - if _, isCall := n.Cond.(*ast.CallExpr); isCall { - w.ignore[n.Body] = true - return w - } - } - case *ast.RangeStmt: - if len(n.Body.List) == 0 { - w.onFailure(lint.Failure{ - Confidence: 0.9, - Node: n, - Category: "logic", - Failure: "this block is empty, you can remove it", - }) - return nil // skip visiting the range subtree (it will produce a duplicated failure) - } - case *ast.BlockStmt: - if !w.ignore[n] && len(n.List) == 0 { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: n, - Category: "logic", - Failure: "this block is empty, you can remove it", - }) - } - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/empty-lines.go b/vendor/github.com/mgechev/revive/rule/empty-lines.go deleted file mode 100644 index 2710a8979..000000000 --- a/vendor/github.com/mgechev/revive/rule/empty-lines.go +++ /dev/null @@ -1,105 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// EmptyLinesRule lints empty lines in blocks. -type EmptyLinesRule struct{} - -// Apply applies the rule to given file. -func (r *EmptyLinesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintEmptyLines{file, r.commentLines(file.CommentMap(), file), onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*EmptyLinesRule) Name() string { - return "empty-lines" -} - -type lintEmptyLines struct { - file *lint.File - cmap map[int]struct{} - onFailure func(lint.Failure) -} - -func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor { - block, ok := node.(*ast.BlockStmt) - if !ok || len(block.List) == 0 { - return w - } - - w.checkStart(block) - w.checkEnd(block) - - return w -} - -func (w lintEmptyLines) checkStart(block *ast.BlockStmt) { - blockStart := w.position(block.Lbrace) - firstNode := block.List[0] - firstStmt := w.position(firstNode.Pos()) - - firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) <= 0 - _, firstBlockLineIsComment := w.cmap[blockStart.Line+1] - if firstBlockLineIsStmt || firstBlockLineIsComment { - return - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: block, - Category: "style", - Failure: "extra empty line at the start of a block", - }) -} - -func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) { - blockEnd := w.position(block.Rbrace) - lastNode := block.List[len(block.List)-1] - lastStmt := w.position(lastNode.End()) - - lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line <= 0 - _, lastBlockLineIsComment := w.cmap[blockEnd.Line-1] - if lastBlockLineIsStmt || lastBlockLineIsComment { - return - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: block, - Category: "style", - Failure: "extra empty line at the end of a block", - }) -} - -func (w lintEmptyLines) position(pos token.Pos) token.Position { - return w.file.ToPosition(pos) -} - -func (*EmptyLinesRule) commentLines(cmap ast.CommentMap, file *lint.File) map[int]struct{} { - result := map[int]struct{}{} - - for _, comments := range cmap { - for _, comment := range comments { - start := file.ToPosition(comment.Pos()) - end := file.ToPosition(comment.End()) - for i := start.Line; i <= end.Line; i++ { - result[i] = struct{}{} - } - } - } - - return result -} diff --git a/vendor/github.com/mgechev/revive/rule/empty_block.go b/vendor/github.com/mgechev/revive/rule/empty_block.go new file mode 100644 index 000000000..25a052a0e --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/empty_block.go @@ -0,0 +1,75 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// EmptyBlockRule lints given else constructs. +type EmptyBlockRule struct{} + +// Apply applies the rule to given file. +func (*EmptyBlockRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintEmptyBlock{make(map[*ast.BlockStmt]bool), onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*EmptyBlockRule) Name() string { + return "empty-block" +} + +type lintEmptyBlock struct { + ignore map[*ast.BlockStmt]bool + onFailure func(lint.Failure) +} + +func (w lintEmptyBlock) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + w.ignore[n.Body] = true + return w + case *ast.FuncLit: + w.ignore[n.Body] = true + return w + case *ast.SelectStmt: + w.ignore[n.Body] = true + return w + case *ast.ForStmt: + if len(n.Body.List) == 0 && n.Init == nil && n.Post == nil && n.Cond != nil { + if _, isCall := n.Cond.(*ast.CallExpr); isCall { + w.ignore[n.Body] = true + return w + } + } + case *ast.RangeStmt: + if len(n.Body.List) == 0 { + w.onFailure(lint.Failure{ + Confidence: 0.9, + Node: n, + Category: "logic", + Failure: "this block is empty, you can remove it", + }) + return nil // skip visiting the range subtree (it will produce a duplicated failure) + } + case *ast.BlockStmt: + if !w.ignore[n] && len(n.List) == 0 { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: n, + Category: "logic", + Failure: "this block is empty, you can remove it", + }) + } + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/empty_lines.go b/vendor/github.com/mgechev/revive/rule/empty_lines.go new file mode 100644 index 000000000..2710a8979 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/empty_lines.go @@ -0,0 +1,105 @@ +package rule + +import ( + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// EmptyLinesRule lints empty lines in blocks. +type EmptyLinesRule struct{} + +// Apply applies the rule to given file. +func (r *EmptyLinesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintEmptyLines{file, r.commentLines(file.CommentMap(), file), onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*EmptyLinesRule) Name() string { + return "empty-lines" +} + +type lintEmptyLines struct { + file *lint.File + cmap map[int]struct{} + onFailure func(lint.Failure) +} + +func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor { + block, ok := node.(*ast.BlockStmt) + if !ok || len(block.List) == 0 { + return w + } + + w.checkStart(block) + w.checkEnd(block) + + return w +} + +func (w lintEmptyLines) checkStart(block *ast.BlockStmt) { + blockStart := w.position(block.Lbrace) + firstNode := block.List[0] + firstStmt := w.position(firstNode.Pos()) + + firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) <= 0 + _, firstBlockLineIsComment := w.cmap[blockStart.Line+1] + if firstBlockLineIsStmt || firstBlockLineIsComment { + return + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: block, + Category: "style", + Failure: "extra empty line at the start of a block", + }) +} + +func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) { + blockEnd := w.position(block.Rbrace) + lastNode := block.List[len(block.List)-1] + lastStmt := w.position(lastNode.End()) + + lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line <= 0 + _, lastBlockLineIsComment := w.cmap[blockEnd.Line-1] + if lastBlockLineIsStmt || lastBlockLineIsComment { + return + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: block, + Category: "style", + Failure: "extra empty line at the end of a block", + }) +} + +func (w lintEmptyLines) position(pos token.Pos) token.Position { + return w.file.ToPosition(pos) +} + +func (*EmptyLinesRule) commentLines(cmap ast.CommentMap, file *lint.File) map[int]struct{} { + result := map[int]struct{}{} + + for _, comments := range cmap { + for _, comment := range comments { + start := file.ToPosition(comment.Pos()) + end := file.ToPosition(comment.End()) + for i := start.Line; i <= end.Line; i++ { + result[i] = struct{}{} + } + } + } + + return result +} diff --git a/vendor/github.com/mgechev/revive/rule/enforce-map-style.go b/vendor/github.com/mgechev/revive/rule/enforce-map-style.go deleted file mode 100644 index c698c40ed..000000000 --- a/vendor/github.com/mgechev/revive/rule/enforce-map-style.go +++ /dev/null @@ -1,163 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -type enforceMapStyleType string - -const ( - enforceMapStyleTypeAny enforceMapStyleType = "any" - enforceMapStyleTypeMake enforceMapStyleType = "make" - enforceMapStyleTypeLiteral enforceMapStyleType = "literal" -) - -func mapStyleFromString(s string) (enforceMapStyleType, error) { - switch s { - case string(enforceMapStyleTypeAny), "": - return enforceMapStyleTypeAny, nil - case string(enforceMapStyleTypeMake): - return enforceMapStyleTypeMake, nil - case string(enforceMapStyleTypeLiteral): - return enforceMapStyleTypeLiteral, nil - default: - return enforceMapStyleTypeAny, fmt.Errorf( - "invalid map style: %s (expecting one of %v)", - s, - []enforceMapStyleType{ - enforceMapStyleTypeAny, - enforceMapStyleTypeMake, - enforceMapStyleTypeLiteral, - }, - ) - } -} - -// EnforceMapStyleRule implements a rule to enforce `make(map[type]type)` over `map[type]type{}`. -type EnforceMapStyleRule struct { - configured bool - enforceMapStyle enforceMapStyleType - sync.Mutex -} - -func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.configured { - return - } - r.configured = true - - if len(arguments) < 1 { - r.enforceMapStyle = enforceMapStyleTypeAny - return - } - - enforceMapStyle, ok := arguments[0].(string) - if !ok { - panic(fmt.Sprintf("Invalid argument '%v' for 'enforce-map-style' rule. Expecting string, got %T", arguments[0], arguments[0])) - } - - var err error - r.enforceMapStyle, err = mapStyleFromString(enforceMapStyle) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the enforce-map-style rule: %v", err)) - } -} - -// Apply applies the rule to given file. -func (r *EnforceMapStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - if r.enforceMapStyle == enforceMapStyleTypeAny { - // this linter is not configured - return nil - } - - var failures []lint.Failure - - astFile := file.AST - ast.Inspect(astFile, func(n ast.Node) bool { - switch v := n.(type) { - case *ast.CompositeLit: - if r.enforceMapStyle != enforceMapStyleTypeMake { - return true - } - - if !r.isMapType(v.Type) { - return true - } - - isEmptyMap := len(v.Elts) > 0 - if isEmptyMap { - return true - } - - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: v, - Category: "style", - Failure: "use make(map[type]type) instead of map[type]type{}", - }) - case *ast.CallExpr: - if r.enforceMapStyle != enforceMapStyleTypeLiteral { - // skip any function calls, even if it's make(map[type]type) - // we don't want to report it if literals are not enforced - return true - } - - ident, ok := v.Fun.(*ast.Ident) - if !ok || ident.Name != "make" { - return true - } - - if len(v.Args) != 1 { - // skip make(map[type]type, size) and invalid empty declarations - return true - } - - if !r.isMapType(v.Args[0]) { - // not a map type - return true - } - - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: v.Args[0], - Category: "style", - Failure: "use map[type]type{} instead of make(map[type]type)", - }) - } - return true - }) - - return failures -} - -// Name returns the rule name. -func (*EnforceMapStyleRule) Name() string { - return "enforce-map-style" -} - -func (r *EnforceMapStyleRule) isMapType(v ast.Expr) bool { - switch t := v.(type) { - case *ast.MapType: - return true - case *ast.Ident: - if t.Obj == nil { - return false - } - typeSpec, ok := t.Obj.Decl.(*ast.TypeSpec) - if !ok { - return false - } - return r.isMapType(typeSpec.Type) - default: - return false - } -} diff --git a/vendor/github.com/mgechev/revive/rule/enforce-repeated-arg-type-style.go b/vendor/github.com/mgechev/revive/rule/enforce-repeated-arg-type-style.go deleted file mode 100644 index a435ee186..000000000 --- a/vendor/github.com/mgechev/revive/rule/enforce-repeated-arg-type-style.go +++ /dev/null @@ -1,187 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -type enforceRepeatedArgTypeStyleType string - -const ( - enforceRepeatedArgTypeStyleTypeAny enforceRepeatedArgTypeStyleType = "any" - enforceRepeatedArgTypeStyleTypeShort enforceRepeatedArgTypeStyleType = "short" - enforceRepeatedArgTypeStyleTypeFull enforceRepeatedArgTypeStyleType = "full" -) - -func repeatedArgTypeStyleFromString(s string) enforceRepeatedArgTypeStyleType { - switch s { - case string(enforceRepeatedArgTypeStyleTypeAny), "": - return enforceRepeatedArgTypeStyleTypeAny - case string(enforceRepeatedArgTypeStyleTypeShort): - return enforceRepeatedArgTypeStyleTypeShort - case string(enforceRepeatedArgTypeStyleTypeFull): - return enforceRepeatedArgTypeStyleTypeFull - default: - err := fmt.Errorf( - "invalid repeated arg type style: %s (expecting one of %v)", - s, - []enforceRepeatedArgTypeStyleType{ - enforceRepeatedArgTypeStyleTypeAny, - enforceRepeatedArgTypeStyleTypeShort, - enforceRepeatedArgTypeStyleTypeFull, - }, - ) - - panic(fmt.Sprintf("Invalid argument to the enforce-repeated-arg-type-style rule: %v", err)) - } -} - -// EnforceRepeatedArgTypeStyleRule implements a rule to enforce repeated argument type style. -type EnforceRepeatedArgTypeStyleRule struct { - configured bool - funcArgStyle enforceRepeatedArgTypeStyleType - funcRetValStyle enforceRepeatedArgTypeStyleType - - sync.Mutex -} - -func (r *EnforceRepeatedArgTypeStyleRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.configured { - return - } - r.configured = true - - r.funcArgStyle = enforceRepeatedArgTypeStyleTypeAny - r.funcRetValStyle = enforceRepeatedArgTypeStyleTypeAny - - if len(arguments) == 0 { - return - } - - switch funcArgStyle := arguments[0].(type) { - case string: - r.funcArgStyle = repeatedArgTypeStyleFromString(funcArgStyle) - r.funcRetValStyle = repeatedArgTypeStyleFromString(funcArgStyle) - case map[string]any: // expecting map[string]string - for k, v := range funcArgStyle { - switch k { - case "funcArgStyle": - val, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid map value type for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v)) - } - r.funcArgStyle = repeatedArgTypeStyleFromString(val) - case "funcRetValStyle": - val, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid map value '%v' for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v, v)) - } - r.funcRetValStyle = repeatedArgTypeStyleFromString(val) - default: - panic(fmt.Sprintf("Invalid map key for 'enforce-repeated-arg-type-style' rule. Expecting 'funcArgStyle' or 'funcRetValStyle', got %v", k)) - } - } - default: - panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])) - } -} - -// Apply applies the rule to a given file. -func (r *EnforceRepeatedArgTypeStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeAny && r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeAny { - // This linter is not configured, return no failures. - return nil - } - - var failures []lint.Failure - - astFile := file.AST - ast.Inspect(astFile, func(n ast.Node) bool { - switch fn := n.(type) { - case *ast.FuncDecl: - if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeFull { - if fn.Type.Params != nil { - for _, field := range fn.Type.Params.List { - if len(field.Names) > 1 { - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: field, - Category: "style", - Failure: "argument types should not be omitted", - }) - } - } - } - } - - if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeShort { - var prevType ast.Expr - if fn.Type.Params != nil { - for _, field := range fn.Type.Params.List { - prevTypeStr := gofmt(prevType) - currentTypeStr := gofmt(field.Type) - if currentTypeStr == prevTypeStr { - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: prevType, - Category: "style", - Failure: fmt.Sprintf("repeated argument type %q can be omitted", prevTypeStr), - }) - } - prevType = field.Type - } - } - } - - if r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeFull { - if fn.Type.Results != nil { - for _, field := range fn.Type.Results.List { - if len(field.Names) > 1 { - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: field, - Category: "style", - Failure: "return types should not be omitted", - }) - } - } - } - } - - if r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeShort { - var prevType ast.Expr - if fn.Type.Results != nil { - for _, field := range fn.Type.Results.List { - prevTypeStr := gofmt(prevType) - currentTypeStr := gofmt(field.Type) - if field.Names != nil && currentTypeStr == prevTypeStr { - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: prevType, - Category: "style", - Failure: fmt.Sprintf("repeated return type %q can be omitted", prevTypeStr), - }) - } - prevType = field.Type - } - } - } - } - return true - }) - - return failures -} - -// Name returns the name of the linter rule. -func (*EnforceRepeatedArgTypeStyleRule) Name() string { - return "enforce-repeated-arg-type-style" -} diff --git a/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go b/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go deleted file mode 100644 index 14be25893..000000000 --- a/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go +++ /dev/null @@ -1,213 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -type enforceSliceStyleType string - -const ( - enforceSliceStyleTypeAny enforceSliceStyleType = "any" - enforceSliceStyleTypeMake enforceSliceStyleType = "make" - enforceSliceStyleTypeLiteral enforceSliceStyleType = "literal" - enforceSliceStyleTypeNil enforceSliceStyleType = "nil" -) - -func sliceStyleFromString(s string) (enforceSliceStyleType, error) { - switch s { - case string(enforceSliceStyleTypeAny), "": - return enforceSliceStyleTypeAny, nil - case string(enforceSliceStyleTypeMake): - return enforceSliceStyleTypeMake, nil - case string(enforceSliceStyleTypeLiteral): - return enforceSliceStyleTypeLiteral, nil - case string(enforceSliceStyleTypeNil): - return enforceSliceStyleTypeNil, nil - default: - return enforceSliceStyleTypeAny, fmt.Errorf( - "invalid slice style: %s (expecting one of %v)", - s, - []enforceSliceStyleType{ - enforceSliceStyleTypeAny, - enforceSliceStyleTypeMake, - enforceSliceStyleTypeLiteral, - enforceSliceStyleTypeNil, - }, - ) - } -} - -// EnforceSliceStyleRule implements a rule to enforce `make([]type)` over `[]type{}`. -type EnforceSliceStyleRule struct { - configured bool - enforceSliceStyle enforceSliceStyleType - sync.Mutex -} - -func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.configured { - return - } - r.configured = true - - if len(arguments) < 1 { - r.enforceSliceStyle = enforceSliceStyleTypeAny - return - } - - enforceSliceStyle, ok := arguments[0].(string) - if !ok { - panic(fmt.Sprintf("Invalid argument '%v' for 'enforce-slice-style' rule. Expecting string, got %T", arguments[0], arguments[0])) - } - - var err error - r.enforceSliceStyle, err = sliceStyleFromString(enforceSliceStyle) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the enforce-slice-style rule: %v", err)) - } -} - -// Apply applies the rule to given file. -func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - if r.enforceSliceStyle == enforceSliceStyleTypeAny { - // this linter is not configured - return nil - } - - var failures []lint.Failure - - astFile := file.AST - ast.Inspect(astFile, func(n ast.Node) bool { - switch v := n.(type) { - case *ast.CompositeLit: - switch r.enforceSliceStyle { - case enforceSliceStyleTypeMake, enforceSliceStyleTypeNil: - // continue - default: - return true - } - - if !r.isSliceType(v.Type) { - return true - } - - isNotEmptySlice := len(v.Elts) > 0 - if isNotEmptySlice { - return true - } - - var failureMessage string - if r.enforceSliceStyle == enforceSliceStyleTypeNil { - failureMessage = "use nil slice declaration (e.g. var args []type) instead of []type{}" - } else { - failureMessage = "use make([]type) instead of []type{} (or declare nil slice)" - } - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: v, - Category: "style", - Failure: failureMessage, - }) - case *ast.CallExpr: - switch r.enforceSliceStyle { - case enforceSliceStyleTypeLiteral, enforceSliceStyleTypeNil: - default: - // skip any function calls, even if it's make([]type) - // we don't want to report it if literals are not enforced - return true - } - - ident, ok := v.Fun.(*ast.Ident) - if !ok || ident.Name != "make" { - return true - } - - isInvalidMakeDeclaration := len(v.Args) < 2 - if isInvalidMakeDeclaration { - return true - } - - if !r.isSliceType(v.Args[0]) { - // not a slice type - return true - } - - arg, ok := v.Args[1].(*ast.BasicLit) - if !ok { - // skip invalid make declarations - return true - } - - isSliceSizeNotZero := arg.Value != "0" - if isSliceSizeNotZero { - return true - } - - if len(v.Args) > 2 { - arg, ok := v.Args[2].(*ast.BasicLit) - if !ok { - // skip invalid make declarations - return true - } - - isNonZeroCapacitySlice := arg.Value != "0" - if isNonZeroCapacitySlice { - return true - } - } - - var failureMessage string - if r.enforceSliceStyle == enforceSliceStyleTypeNil { - failureMessage = "use nil slice declaration (e.g. var args []type) instead of make([]type, 0)" - } else { - failureMessage = "use []type{} instead of make([]type, 0) (or declare nil slice)" - } - failures = append(failures, lint.Failure{ - Confidence: 1, - Node: v.Args[0], - Category: "style", - Failure: failureMessage, - }) - } - return true - }) - - return failures -} - -// Name returns the rule name. -func (*EnforceSliceStyleRule) Name() string { - return "enforce-slice-style" -} - -func (r *EnforceSliceStyleRule) isSliceType(v ast.Expr) bool { - switch t := v.(type) { - case *ast.ArrayType: - if t.Len != nil { - // array - return false - } - // slice - return true - case *ast.Ident: - if t.Obj == nil { - return false - } - typeSpec, ok := t.Obj.Decl.(*ast.TypeSpec) - if !ok { - return false - } - return r.isSliceType(typeSpec.Type) - default: - return false - } -} diff --git a/vendor/github.com/mgechev/revive/rule/enforce_map_style.go b/vendor/github.com/mgechev/revive/rule/enforce_map_style.go new file mode 100644 index 000000000..7ddf31e35 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/enforce_map_style.go @@ -0,0 +1,155 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +type enforceMapStyleType string + +const ( + enforceMapStyleTypeAny enforceMapStyleType = "any" + enforceMapStyleTypeMake enforceMapStyleType = "make" + enforceMapStyleTypeLiteral enforceMapStyleType = "literal" +) + +func mapStyleFromString(s string) (enforceMapStyleType, error) { + switch s { + case string(enforceMapStyleTypeAny), "": + return enforceMapStyleTypeAny, nil + case string(enforceMapStyleTypeMake): + return enforceMapStyleTypeMake, nil + case string(enforceMapStyleTypeLiteral): + return enforceMapStyleTypeLiteral, nil + default: + return enforceMapStyleTypeAny, fmt.Errorf( + "invalid map style: %s (expecting one of %v)", + s, + []enforceMapStyleType{ + enforceMapStyleTypeAny, + enforceMapStyleTypeMake, + enforceMapStyleTypeLiteral, + }, + ) + } +} + +// EnforceMapStyleRule implements a rule to enforce `make(map[type]type)` over `map[type]type{}`. +type EnforceMapStyleRule struct { + enforceMapStyle enforceMapStyleType + + configureOnce sync.Once +} + +func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.enforceMapStyle = enforceMapStyleTypeAny + return + } + + enforceMapStyle, ok := arguments[0].(string) + if !ok { + panic(fmt.Sprintf("Invalid argument '%v' for 'enforce-map-style' rule. Expecting string, got %T", arguments[0], arguments[0])) + } + + var err error + r.enforceMapStyle, err = mapStyleFromString(enforceMapStyle) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the enforce-map-style rule: %v", err)) + } +} + +// Apply applies the rule to given file. +func (r *EnforceMapStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + if r.enforceMapStyle == enforceMapStyleTypeAny { + // this linter is not configured + return nil + } + + var failures []lint.Failure + + astFile := file.AST + ast.Inspect(astFile, func(n ast.Node) bool { + switch v := n.(type) { + case *ast.CompositeLit: + if r.enforceMapStyle != enforceMapStyleTypeMake { + return true + } + + if !r.isMapType(v.Type) { + return true + } + + isEmptyMap := len(v.Elts) > 0 + if isEmptyMap { + return true + } + + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: v, + Category: "style", + Failure: "use make(map[type]type) instead of map[type]type{}", + }) + case *ast.CallExpr: + if r.enforceMapStyle != enforceMapStyleTypeLiteral { + // skip any function calls, even if it's make(map[type]type) + // we don't want to report it if literals are not enforced + return true + } + + ident, ok := v.Fun.(*ast.Ident) + if !ok || ident.Name != "make" { + return true + } + + if len(v.Args) != 1 { + // skip make(map[type]type, size) and invalid empty declarations + return true + } + + if !r.isMapType(v.Args[0]) { + // not a map type + return true + } + + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: v.Args[0], + Category: "style", + Failure: "use map[type]type{} instead of make(map[type]type)", + }) + } + return true + }) + + return failures +} + +// Name returns the rule name. +func (*EnforceMapStyleRule) Name() string { + return "enforce-map-style" +} + +func (r *EnforceMapStyleRule) isMapType(v ast.Expr) bool { + switch t := v.(type) { + case *ast.MapType: + return true + case *ast.Ident: + if t.Obj == nil { + return false + } + typeSpec, ok := t.Obj.Decl.(*ast.TypeSpec) + if !ok { + return false + } + return r.isMapType(typeSpec.Type) + default: + return false + } +} diff --git a/vendor/github.com/mgechev/revive/rule/enforce_repeated_arg_type_style.go b/vendor/github.com/mgechev/revive/rule/enforce_repeated_arg_type_style.go new file mode 100644 index 000000000..3f9712aef --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/enforce_repeated_arg_type_style.go @@ -0,0 +1,178 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +type enforceRepeatedArgTypeStyleType string + +const ( + enforceRepeatedArgTypeStyleTypeAny enforceRepeatedArgTypeStyleType = "any" + enforceRepeatedArgTypeStyleTypeShort enforceRepeatedArgTypeStyleType = "short" + enforceRepeatedArgTypeStyleTypeFull enforceRepeatedArgTypeStyleType = "full" +) + +func repeatedArgTypeStyleFromString(s string) enforceRepeatedArgTypeStyleType { + switch s { + case string(enforceRepeatedArgTypeStyleTypeAny), "": + return enforceRepeatedArgTypeStyleTypeAny + case string(enforceRepeatedArgTypeStyleTypeShort): + return enforceRepeatedArgTypeStyleTypeShort + case string(enforceRepeatedArgTypeStyleTypeFull): + return enforceRepeatedArgTypeStyleTypeFull + default: + err := fmt.Errorf( + "invalid repeated arg type style: %s (expecting one of %v)", + s, + []enforceRepeatedArgTypeStyleType{ + enforceRepeatedArgTypeStyleTypeAny, + enforceRepeatedArgTypeStyleTypeShort, + enforceRepeatedArgTypeStyleTypeFull, + }, + ) + + panic(fmt.Sprintf("Invalid argument to the enforce-repeated-arg-type-style rule: %v", err)) + } +} + +// EnforceRepeatedArgTypeStyleRule implements a rule to enforce repeated argument type style. +type EnforceRepeatedArgTypeStyleRule struct { + funcArgStyle enforceRepeatedArgTypeStyleType + funcRetValStyle enforceRepeatedArgTypeStyleType + + configureOnce sync.Once +} + +func (r *EnforceRepeatedArgTypeStyleRule) configure(arguments lint.Arguments) { + r.funcArgStyle = enforceRepeatedArgTypeStyleTypeAny + r.funcRetValStyle = enforceRepeatedArgTypeStyleTypeAny + + if len(arguments) == 0 { + return + } + + switch funcArgStyle := arguments[0].(type) { + case string: + r.funcArgStyle = repeatedArgTypeStyleFromString(funcArgStyle) + r.funcRetValStyle = repeatedArgTypeStyleFromString(funcArgStyle) + case map[string]any: // expecting map[string]string + for k, v := range funcArgStyle { + switch k { + case "funcArgStyle": + val, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid map value type for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v)) + } + r.funcArgStyle = repeatedArgTypeStyleFromString(val) + case "funcRetValStyle": + val, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid map value '%v' for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v, v)) + } + r.funcRetValStyle = repeatedArgTypeStyleFromString(val) + default: + panic(fmt.Sprintf("Invalid map key for 'enforce-repeated-arg-type-style' rule. Expecting 'funcArgStyle' or 'funcRetValStyle', got %v", k)) + } + } + default: + panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])) + } +} + +// Apply applies the rule to a given file. +func (r *EnforceRepeatedArgTypeStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeAny && r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeAny { + // This linter is not configured, return no failures. + return nil + } + + var failures []lint.Failure + + astFile := file.AST + ast.Inspect(astFile, func(n ast.Node) bool { + switch fn := n.(type) { + case *ast.FuncDecl: + if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeFull { + if fn.Type.Params != nil { + for _, field := range fn.Type.Params.List { + if len(field.Names) > 1 { + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: field, + Category: "style", + Failure: "argument types should not be omitted", + }) + } + } + } + } + + if r.funcArgStyle == enforceRepeatedArgTypeStyleTypeShort { + var prevType ast.Expr + if fn.Type.Params != nil { + for _, field := range fn.Type.Params.List { + prevTypeStr := gofmt(prevType) + currentTypeStr := gofmt(field.Type) + if currentTypeStr == prevTypeStr { + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: prevType, + Category: "style", + Failure: fmt.Sprintf("repeated argument type %q can be omitted", prevTypeStr), + }) + } + prevType = field.Type + } + } + } + + if r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeFull { + if fn.Type.Results != nil { + for _, field := range fn.Type.Results.List { + if len(field.Names) > 1 { + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: field, + Category: "style", + Failure: "return types should not be omitted", + }) + } + } + } + } + + if r.funcRetValStyle == enforceRepeatedArgTypeStyleTypeShort { + var prevType ast.Expr + if fn.Type.Results != nil { + for _, field := range fn.Type.Results.List { + prevTypeStr := gofmt(prevType) + currentTypeStr := gofmt(field.Type) + if field.Names != nil && currentTypeStr == prevTypeStr { + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: prevType, + Category: "style", + Failure: fmt.Sprintf("repeated return type %q can be omitted", prevTypeStr), + }) + } + prevType = field.Type + } + } + } + } + return true + }) + + return failures +} + +// Name returns the name of the linter rule. +func (*EnforceRepeatedArgTypeStyleRule) Name() string { + return "enforce-repeated-arg-type-style" +} diff --git a/vendor/github.com/mgechev/revive/rule/enforce_slice_style.go b/vendor/github.com/mgechev/revive/rule/enforce_slice_style.go new file mode 100644 index 000000000..7170379d9 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/enforce_slice_style.go @@ -0,0 +1,205 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +type enforceSliceStyleType string + +const ( + enforceSliceStyleTypeAny enforceSliceStyleType = "any" + enforceSliceStyleTypeMake enforceSliceStyleType = "make" + enforceSliceStyleTypeLiteral enforceSliceStyleType = "literal" + enforceSliceStyleTypeNil enforceSliceStyleType = "nil" +) + +func sliceStyleFromString(s string) (enforceSliceStyleType, error) { + switch s { + case string(enforceSliceStyleTypeAny), "": + return enforceSliceStyleTypeAny, nil + case string(enforceSliceStyleTypeMake): + return enforceSliceStyleTypeMake, nil + case string(enforceSliceStyleTypeLiteral): + return enforceSliceStyleTypeLiteral, nil + case string(enforceSliceStyleTypeNil): + return enforceSliceStyleTypeNil, nil + default: + return enforceSliceStyleTypeAny, fmt.Errorf( + "invalid slice style: %s (expecting one of %v)", + s, + []enforceSliceStyleType{ + enforceSliceStyleTypeAny, + enforceSliceStyleTypeMake, + enforceSliceStyleTypeLiteral, + enforceSliceStyleTypeNil, + }, + ) + } +} + +// EnforceSliceStyleRule implements a rule to enforce `make([]type)` over `[]type{}`. +type EnforceSliceStyleRule struct { + enforceSliceStyle enforceSliceStyleType + + configureOnce sync.Once +} + +func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.enforceSliceStyle = enforceSliceStyleTypeAny + return + } + + enforceSliceStyle, ok := arguments[0].(string) + if !ok { + panic(fmt.Sprintf("Invalid argument '%v' for 'enforce-slice-style' rule. Expecting string, got %T", arguments[0], arguments[0])) + } + + var err error + r.enforceSliceStyle, err = sliceStyleFromString(enforceSliceStyle) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the enforce-slice-style rule: %v", err)) + } +} + +// Apply applies the rule to given file. +func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + if r.enforceSliceStyle == enforceSliceStyleTypeAny { + // this linter is not configured + return nil + } + + var failures []lint.Failure + + astFile := file.AST + ast.Inspect(astFile, func(n ast.Node) bool { + switch v := n.(type) { + case *ast.CompositeLit: + switch r.enforceSliceStyle { + case enforceSliceStyleTypeMake, enforceSliceStyleTypeNil: + // continue + default: + return true + } + + if !r.isSliceType(v.Type) { + return true + } + + isNotEmptySlice := len(v.Elts) > 0 + if isNotEmptySlice { + return true + } + + var failureMessage string + if r.enforceSliceStyle == enforceSliceStyleTypeNil { + failureMessage = "use nil slice declaration (e.g. var args []type) instead of []type{}" + } else { + failureMessage = "use make([]type) instead of []type{} (or declare nil slice)" + } + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: v, + Category: "style", + Failure: failureMessage, + }) + case *ast.CallExpr: + switch r.enforceSliceStyle { + case enforceSliceStyleTypeLiteral, enforceSliceStyleTypeNil: + default: + // skip any function calls, even if it's make([]type) + // we don't want to report it if literals are not enforced + return true + } + + ident, ok := v.Fun.(*ast.Ident) + if !ok || ident.Name != "make" { + return true + } + + isInvalidMakeDeclaration := len(v.Args) < 2 + if isInvalidMakeDeclaration { + return true + } + + if !r.isSliceType(v.Args[0]) { + // not a slice type + return true + } + + arg, ok := v.Args[1].(*ast.BasicLit) + if !ok { + // skip invalid make declarations + return true + } + + isSliceSizeNotZero := arg.Value != "0" + if isSliceSizeNotZero { + return true + } + + if len(v.Args) > 2 { + arg, ok := v.Args[2].(*ast.BasicLit) + if !ok { + // skip invalid make declarations + return true + } + + isNonZeroCapacitySlice := arg.Value != "0" + if isNonZeroCapacitySlice { + return true + } + } + + var failureMessage string + if r.enforceSliceStyle == enforceSliceStyleTypeNil { + failureMessage = "use nil slice declaration (e.g. var args []type) instead of make([]type, 0)" + } else { + failureMessage = "use []type{} instead of make([]type, 0) (or declare nil slice)" + } + failures = append(failures, lint.Failure{ + Confidence: 1, + Node: v.Args[0], + Category: "style", + Failure: failureMessage, + }) + } + return true + }) + + return failures +} + +// Name returns the rule name. +func (*EnforceSliceStyleRule) Name() string { + return "enforce-slice-style" +} + +func (r *EnforceSliceStyleRule) isSliceType(v ast.Expr) bool { + switch t := v.(type) { + case *ast.ArrayType: + if t.Len != nil { + // array + return false + } + // slice + return true + case *ast.Ident: + if t.Obj == nil { + return false + } + typeSpec, ok := t.Obj.Decl.(*ast.TypeSpec) + if !ok { + return false + } + return r.isSliceType(typeSpec.Type) + default: + return false + } +} diff --git a/vendor/github.com/mgechev/revive/rule/error-naming.go b/vendor/github.com/mgechev/revive/rule/error-naming.go deleted file mode 100644 index a4f24f3f0..000000000 --- a/vendor/github.com/mgechev/revive/rule/error-naming.go +++ /dev/null @@ -1,79 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "strings" - - "github.com/mgechev/revive/lint" -) - -// ErrorNamingRule lints given else constructs. -type ErrorNamingRule struct{} - -// Apply applies the rule to given file. -func (*ErrorNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintErrors{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ErrorNamingRule) Name() string { - return "error-naming" -} - -type lintErrors struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) -} - -func (w lintErrors) Visit(_ ast.Node) ast.Visitor { - for _, decl := range w.fileAst.Decls { - gd, ok := decl.(*ast.GenDecl) - if !ok || gd.Tok != token.VAR { - continue - } - for _, spec := range gd.Specs { - spec := spec.(*ast.ValueSpec) - if len(spec.Names) != 1 || len(spec.Values) != 1 { - continue - } - ce, ok := spec.Values[0].(*ast.CallExpr) - if !ok { - continue - } - if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") { - continue - } - - id := spec.Names[0] - prefix := "err" - if id.IsExported() { - prefix = "Err" - } - if !strings.HasPrefix(id.Name, prefix) { - w.onFailure(lint.Failure{ - Node: id, - Confidence: 0.9, - Category: "naming", - Failure: fmt.Sprintf("error var %s should have name of the form %sFoo", id.Name, prefix), - }) - } - } - } - return nil -} diff --git a/vendor/github.com/mgechev/revive/rule/error-return.go b/vendor/github.com/mgechev/revive/rule/error-return.go deleted file mode 100644 index a724e001c..000000000 --- a/vendor/github.com/mgechev/revive/rule/error-return.go +++ /dev/null @@ -1,67 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// ErrorReturnRule lints given else constructs. -type ErrorReturnRule struct{} - -// Apply applies the rule to given file. -func (*ErrorReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintErrorReturn{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ErrorReturnRule) Name() string { - return "error-return" -} - -type lintErrorReturn struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) -} - -func (w lintErrorReturn) Visit(n ast.Node) ast.Visitor { - fn, ok := n.(*ast.FuncDecl) - if !ok || fn.Type.Results == nil { - return w - } - ret := fn.Type.Results.List - if len(ret) <= 1 { - return w - } - if isIdent(ret[len(ret)-1].Type, "error") { - return nil - } - // An error return parameter should be the last parameter. - // Flag any error parameters found before the last. - for _, r := range ret[:len(ret)-1] { - if isIdent(r.Type, "error") { - w.onFailure(lint.Failure{ - Category: "arg-order", - Confidence: 0.9, - Node: fn, - Failure: "error should be the last type when returning multiple items", - }) - break // only flag one - } - } - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/error-strings.go b/vendor/github.com/mgechev/revive/rule/error-strings.go deleted file mode 100644 index 81ebda540..000000000 --- a/vendor/github.com/mgechev/revive/rule/error-strings.go +++ /dev/null @@ -1,199 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - "strconv" - "strings" - "sync" - "unicode" - "unicode/utf8" - - "github.com/mgechev/revive/lint" -) - -// ErrorStringsRule lints given else constructs. -type ErrorStringsRule struct { - errorFunctions map[string]map[string]struct{} - sync.Mutex -} - -func (r *ErrorStringsRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.errorFunctions != nil { - return - } - - r.errorFunctions = map[string]map[string]struct{}{ - "fmt": { - "Errorf": {}, - }, - "errors": { - "Errorf": {}, - "WithMessage": {}, - "Wrap": {}, - "New": {}, - "WithMessagef": {}, - "Wrapf": {}, - }, - } - - var invalidCustomFunctions []string - for _, argument := range arguments { - if functionName, ok := argument.(string); ok { - fields := strings.Split(strings.TrimSpace(functionName), ".") - if len(fields) != 2 || len(fields[0]) == 0 || len(fields[1]) == 0 { - invalidCustomFunctions = append(invalidCustomFunctions, functionName) - continue - } - r.errorFunctions[fields[0]] = map[string]struct{}{fields[1]: {}} - } - } - if len(invalidCustomFunctions) != 0 { - panic("found invalid custom function: " + strings.Join(invalidCustomFunctions, ",")) - } -} - -// Apply applies the rule to given file. -func (r *ErrorStringsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - var failures []lint.Failure - - r.configure(arguments) - - fileAst := file.AST - walker := lintErrorStrings{ - file: file, - fileAst: fileAst, - errorFunctions: r.errorFunctions, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ErrorStringsRule) Name() string { - return "error-strings" -} - -type lintErrorStrings struct { - file *lint.File - fileAst *ast.File - errorFunctions map[string]map[string]struct{} - onFailure func(lint.Failure) -} - -// Visit browses the AST -func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor { - ce, ok := n.(*ast.CallExpr) - if !ok { - return w - } - - if len(ce.Args) < 1 { - return w - } - - // expression matches the known pkg.function - ok = w.match(ce) - if !ok { - return w - } - - str, ok := w.getMessage(ce) - if !ok { - return w - } - s, _ := strconv.Unquote(str.Value) // can assume well-formed Go - if s == "" { - return w - } - clean, conf := lintErrorString(s) - if clean { - return w - } - w.onFailure(lint.Failure{ - Node: str, - Confidence: conf, - Category: "errors", - Failure: "error strings should not be capitalized or end with punctuation or a newline", - }) - return w -} - -// match returns true if the expression corresponds to the known pkg.function -// i.e.: errors.Wrap -func (w lintErrorStrings) match(expr *ast.CallExpr) bool { - sel, ok := expr.Fun.(*ast.SelectorExpr) - if !ok { - return false - } - // retrieve the package - id, ok := sel.X.(*ast.Ident) - if !ok { - return false - } - functions, ok := w.errorFunctions[id.Name] - if !ok { - return false - } - // retrieve the function - _, ok = functions[sel.Sel.Name] - return ok -} - -// getMessage returns the message depending on its position -// returns false if the cast is unsuccessful -func (w lintErrorStrings) getMessage(expr *ast.CallExpr) (s *ast.BasicLit, success bool) { - str, ok := w.checkArg(expr, 0) - if ok { - return str, true - } - if len(expr.Args) < 2 { - return s, false - } - str, ok = w.checkArg(expr, 1) - if !ok { - return s, false - } - return str, true -} - -func (lintErrorStrings) checkArg(expr *ast.CallExpr, arg int) (s *ast.BasicLit, success bool) { - str, ok := expr.Args[arg].(*ast.BasicLit) - if !ok { - return s, false - } - if str.Kind != token.STRING { - return s, false - } - return str, true -} - -func lintErrorString(s string) (isClean bool, conf float64) { - const basicConfidence = 0.8 - const capConfidence = basicConfidence - 0.2 - first, firstN := utf8.DecodeRuneInString(s) - last, _ := utf8.DecodeLastRuneInString(s) - if last == '.' || last == ':' || last == '!' || last == '\n' { - return false, basicConfidence - } - if unicode.IsUpper(first) { - // People use proper nouns and exported Go identifiers in error strings, - // so decrease the confidence of warnings for capitalization. - if len(s) <= firstN { - return false, capConfidence - } - // Flag strings starting with something that doesn't look like an initialism. - if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) { - return false, capConfidence - } - } - return true, 0 -} diff --git a/vendor/github.com/mgechev/revive/rule/error_naming.go b/vendor/github.com/mgechev/revive/rule/error_naming.go new file mode 100644 index 000000000..a4f24f3f0 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/error_naming.go @@ -0,0 +1,79 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "github.com/mgechev/revive/lint" +) + +// ErrorNamingRule lints given else constructs. +type ErrorNamingRule struct{} + +// Apply applies the rule to given file. +func (*ErrorNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := lintErrors{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ErrorNamingRule) Name() string { + return "error-naming" +} + +type lintErrors struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) +} + +func (w lintErrors) Visit(_ ast.Node) ast.Visitor { + for _, decl := range w.fileAst.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.VAR { + continue + } + for _, spec := range gd.Specs { + spec := spec.(*ast.ValueSpec) + if len(spec.Names) != 1 || len(spec.Values) != 1 { + continue + } + ce, ok := spec.Values[0].(*ast.CallExpr) + if !ok { + continue + } + if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") { + continue + } + + id := spec.Names[0] + prefix := "err" + if id.IsExported() { + prefix = "Err" + } + if !strings.HasPrefix(id.Name, prefix) { + w.onFailure(lint.Failure{ + Node: id, + Confidence: 0.9, + Category: "naming", + Failure: fmt.Sprintf("error var %s should have name of the form %sFoo", id.Name, prefix), + }) + } + } + } + return nil +} diff --git a/vendor/github.com/mgechev/revive/rule/error_return.go b/vendor/github.com/mgechev/revive/rule/error_return.go new file mode 100644 index 000000000..a724e001c --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/error_return.go @@ -0,0 +1,67 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// ErrorReturnRule lints given else constructs. +type ErrorReturnRule struct{} + +// Apply applies the rule to given file. +func (*ErrorReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := lintErrorReturn{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ErrorReturnRule) Name() string { + return "error-return" +} + +type lintErrorReturn struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) +} + +func (w lintErrorReturn) Visit(n ast.Node) ast.Visitor { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Type.Results == nil { + return w + } + ret := fn.Type.Results.List + if len(ret) <= 1 { + return w + } + if isIdent(ret[len(ret)-1].Type, "error") { + return nil + } + // An error return parameter should be the last parameter. + // Flag any error parameters found before the last. + for _, r := range ret[:len(ret)-1] { + if isIdent(r.Type, "error") { + w.onFailure(lint.Failure{ + Category: "arg-order", + Confidence: 0.9, + Node: fn, + Failure: "error should be the last type when returning multiple items", + }) + break // only flag one + } + } + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/error_strings.go b/vendor/github.com/mgechev/revive/rule/error_strings.go new file mode 100644 index 000000000..97a0f4d06 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/error_strings.go @@ -0,0 +1,193 @@ +package rule + +import ( + "go/ast" + "go/token" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" + + "github.com/mgechev/revive/lint" +) + +// ErrorStringsRule lints given else constructs. +type ErrorStringsRule struct { + errorFunctions map[string]map[string]struct{} + + configureOnce sync.Once +} + +func (r *ErrorStringsRule) configure(arguments lint.Arguments) { + r.errorFunctions = map[string]map[string]struct{}{ + "fmt": { + "Errorf": {}, + }, + "errors": { + "Errorf": {}, + "WithMessage": {}, + "Wrap": {}, + "New": {}, + "WithMessagef": {}, + "Wrapf": {}, + }, + } + + var invalidCustomFunctions []string + for _, argument := range arguments { + if functionName, ok := argument.(string); ok { + fields := strings.Split(strings.TrimSpace(functionName), ".") + if len(fields) != 2 || len(fields[0]) == 0 || len(fields[1]) == 0 { + invalidCustomFunctions = append(invalidCustomFunctions, functionName) + continue + } + r.errorFunctions[fields[0]] = map[string]struct{}{fields[1]: {}} + } + } + if len(invalidCustomFunctions) != 0 { + panic("found invalid custom function: " + strings.Join(invalidCustomFunctions, ",")) + } +} + +// Apply applies the rule to given file. +func (r *ErrorStringsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + var failures []lint.Failure + + r.configureOnce.Do(func() { r.configure(arguments) }) + + fileAst := file.AST + walker := lintErrorStrings{ + file: file, + fileAst: fileAst, + errorFunctions: r.errorFunctions, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ErrorStringsRule) Name() string { + return "error-strings" +} + +type lintErrorStrings struct { + file *lint.File + fileAst *ast.File + errorFunctions map[string]map[string]struct{} + onFailure func(lint.Failure) +} + +// Visit browses the AST +func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor { + ce, ok := n.(*ast.CallExpr) + if !ok { + return w + } + + if len(ce.Args) < 1 { + return w + } + + // expression matches the known pkg.function + ok = w.match(ce) + if !ok { + return w + } + + str, ok := w.getMessage(ce) + if !ok { + return w + } + s, _ := strconv.Unquote(str.Value) // can assume well-formed Go + if s == "" { + return w + } + clean, conf := lintErrorString(s) + if clean { + return w + } + w.onFailure(lint.Failure{ + Node: str, + Confidence: conf, + Category: "errors", + Failure: "error strings should not be capitalized or end with punctuation or a newline", + }) + return w +} + +// match returns true if the expression corresponds to the known pkg.function +// i.e.: errors.Wrap +func (w lintErrorStrings) match(expr *ast.CallExpr) bool { + sel, ok := expr.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + // retrieve the package + id, ok := sel.X.(*ast.Ident) + if !ok { + return false + } + functions, ok := w.errorFunctions[id.Name] + if !ok { + return false + } + // retrieve the function + _, ok = functions[sel.Sel.Name] + return ok +} + +// getMessage returns the message depending on its position +// returns false if the cast is unsuccessful +func (w lintErrorStrings) getMessage(expr *ast.CallExpr) (s *ast.BasicLit, success bool) { + str, ok := w.checkArg(expr, 0) + if ok { + return str, true + } + if len(expr.Args) < 2 { + return s, false + } + str, ok = w.checkArg(expr, 1) + if !ok { + return s, false + } + return str, true +} + +func (lintErrorStrings) checkArg(expr *ast.CallExpr, arg int) (s *ast.BasicLit, success bool) { + str, ok := expr.Args[arg].(*ast.BasicLit) + if !ok { + return s, false + } + if str.Kind != token.STRING { + return s, false + } + return str, true +} + +func lintErrorString(s string) (isClean bool, conf float64) { + const basicConfidence = 0.8 + const capConfidence = basicConfidence - 0.2 + first, firstN := utf8.DecodeRuneInString(s) + last, _ := utf8.DecodeLastRuneInString(s) + if last == '.' || last == ':' || last == '!' || last == '\n' { + return false, basicConfidence + } + if unicode.IsUpper(first) { + // People use proper nouns and exported Go identifiers in error strings, + // so decrease the confidence of warnings for capitalization. + if len(s) <= firstN { + return false, capConfidence + } + // Flag strings starting with something that doesn't look like an initialism. + if second, _ := utf8.DecodeRuneInString(s[firstN:]); !unicode.IsUpper(second) { + return false, capConfidence + } + } + return true, 0 +} diff --git a/vendor/github.com/mgechev/revive/rule/exported.go b/vendor/github.com/mgechev/revive/rule/exported.go index e3972d40e..7ee27b309 100644 --- a/vendor/github.com/mgechev/revive/rule/exported.go +++ b/vendor/github.com/mgechev/revive/rule/exported.go @@ -55,20 +55,13 @@ func (dc *disabledChecks) isDisabled(checkName string) bool { // ExportedRule lints given else constructs. type ExportedRule struct { - configured bool stuttersMsg string disabledChecks disabledChecks - sync.Mutex + + configureOnce sync.Once } func (r *ExportedRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.configured { - return - } - r.configured = true - r.disabledChecks = disabledChecks{PrivateReceivers: true, PublicInterfaces: true} r.stuttersMsg = "stutters" for _, flag := range arguments { @@ -104,7 +97,7 @@ func (r *ExportedRule) configure(arguments lint.Arguments) { // Apply applies the rule to given file. func (r *ExportedRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) + r.configureOnce.Do(func() { r.configure(args) }) var failures []lint.Failure if file.IsTest() { diff --git a/vendor/github.com/mgechev/revive/rule/file-header.go b/vendor/github.com/mgechev/revive/rule/file-header.go deleted file mode 100644 index 0dcb57746..000000000 --- a/vendor/github.com/mgechev/revive/rule/file-header.go +++ /dev/null @@ -1,89 +0,0 @@ -package rule - -import ( - "fmt" - "regexp" - "sync" - - "github.com/mgechev/revive/lint" -) - -// FileHeaderRule lints given else constructs. -type FileHeaderRule struct { - header string - sync.Mutex -} - -var ( - multiRegexp = regexp.MustCompile(`^/\*`) - singleRegexp = regexp.MustCompile("^//") -) - -func (r *FileHeaderRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.header != "" { - return // already configured - } - - if len(arguments) < 1 { - return - } - - var ok bool - r.header, ok = arguments[0].(string) - if !ok { - panic(fmt.Sprintf("invalid argument for \"file-header\" rule: argument should be a string, got %T", arguments[0])) - } -} - -// Apply applies the rule to given file. -func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - if r.header == "" { - return nil - } - - failure := []lint.Failure{ - { - Node: file.AST, - Confidence: 1, - Failure: "the file doesn't have an appropriate header", - }, - } - - if len(file.AST.Comments) == 0 { - return failure - } - - g := file.AST.Comments[0] - if g == nil { - return failure - } - comment := "" - for _, c := range g.List { - text := c.Text - if multiRegexp.MatchString(text) { - text = text[2 : len(text)-2] - } else if singleRegexp.MatchString(text) { - text = text[2:] - } - comment += text - } - - regex, err := regexp.Compile(r.header) - if err != nil { - panic(err.Error()) - } - - if !regex.MatchString(comment) { - return failure - } - return nil -} - -// Name returns the rule name. -func (*FileHeaderRule) Name() string { - return "file-header" -} diff --git a/vendor/github.com/mgechev/revive/rule/file-length-limit.go b/vendor/github.com/mgechev/revive/rule/file-length-limit.go deleted file mode 100644 index c5a5641f4..000000000 --- a/vendor/github.com/mgechev/revive/rule/file-length-limit.go +++ /dev/null @@ -1,138 +0,0 @@ -package rule - -import ( - "bufio" - "bytes" - "fmt" - "go/ast" - "go/token" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// FileLengthLimitRule lints the number of lines in a file. -type FileLengthLimitRule struct { - // max is the maximum number of lines allowed in a file. 0 means the rule is disabled. - max int - // skipComments indicates whether to skip comment lines when counting lines. - skipComments bool - // skipBlankLines indicates whether to skip blank lines when counting lines. - skipBlankLines bool - sync.Mutex -} - -// Apply applies the rule to given file. -func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - if r.max <= 0 { - // when max is negative or 0 the rule is disabled - return nil - } - - all := 0 - blank := 0 - scanner := bufio.NewScanner(bytes.NewReader(file.Content())) - for scanner.Scan() { - all++ - if len(bytes.TrimSpace(scanner.Bytes())) == 0 { - blank++ - } - } - - if err := scanner.Err(); err != nil { - panic(err.Error()) - } - - lines := all - if r.skipComments { - lines -= countCommentLines(file.AST.Comments) - } - - if r.skipBlankLines { - lines -= blank - } - - if lines <= r.max { - return nil - } - - return []lint.Failure{ - { - Category: "code-style", - Confidence: 1, - Position: lint.FailurePosition{ - Start: token.Position{ - Filename: file.Name, - Line: all, - }, - }, - Failure: fmt.Sprintf("file length is %d lines, which exceeds the limit of %d", lines, r.max), - }, - } -} - -func (r *FileLengthLimitRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.max != 0 { - return // already configured - } - - if len(arguments) < 1 { - return // use default - } - - argKV, ok := arguments[0].(map[string]any) - if !ok { - panic(fmt.Sprintf(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got %T`, arguments[0])) - } - for k, v := range argKV { - switch k { - case "max": - maxLines, ok := v.(int64) - if !ok || maxLines < 0 { - panic(fmt.Sprintf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, arguments[0])) - } - r.max = int(maxLines) - case "skipComments": - skipComments, ok := v.(bool) - if !ok { - panic(fmt.Sprintf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, arguments[1])) - } - r.skipComments = skipComments - case "skipBlankLines": - skipBlankLines, ok := v.(bool) - if !ok { - panic(fmt.Sprintf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, arguments[2])) - } - r.skipBlankLines = skipBlankLines - } - } -} - -// Name returns the rule name. -func (*FileLengthLimitRule) Name() string { - return "file-length-limit" -} - -func countCommentLines(comments []*ast.CommentGroup) int { - count := 0 - for _, cg := range comments { - for _, comment := range cg.List { - if len(comment.Text) < 2 { - continue - } - switch comment.Text[1] { - case '/': // single-line comment - count++ - case '*': // multi-line comment - count += strings.Count(comment.Text, "\n") + 1 - } - } - } - return count -} diff --git a/vendor/github.com/mgechev/revive/rule/file_header.go b/vendor/github.com/mgechev/revive/rule/file_header.go new file mode 100644 index 000000000..52513d8e8 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/file_header.go @@ -0,0 +1,84 @@ +package rule + +import ( + "fmt" + "regexp" + "sync" + + "github.com/mgechev/revive/lint" +) + +// FileHeaderRule lints given else constructs. +type FileHeaderRule struct { + header string + + configureOnce sync.Once +} + +var ( + multiRegexp = regexp.MustCompile(`^/\*`) + singleRegexp = regexp.MustCompile("^//") +) + +func (r *FileHeaderRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + return + } + + var ok bool + r.header, ok = arguments[0].(string) + if !ok { + panic(fmt.Sprintf("invalid argument for \"file-header\" rule: argument should be a string, got %T", arguments[0])) + } +} + +// Apply applies the rule to given file. +func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + if r.header == "" { + return nil + } + + failure := []lint.Failure{ + { + Node: file.AST, + Confidence: 1, + Failure: "the file doesn't have an appropriate header", + }, + } + + if len(file.AST.Comments) == 0 { + return failure + } + + g := file.AST.Comments[0] + if g == nil { + return failure + } + comment := "" + for _, c := range g.List { + text := c.Text + if multiRegexp.MatchString(text) { + text = text[2 : len(text)-2] + } else if singleRegexp.MatchString(text) { + text = text[2:] + } + comment += text + } + + regex, err := regexp.Compile(r.header) + if err != nil { + panic(err.Error()) + } + + if !regex.MatchString(comment) { + return failure + } + return nil +} + +// Name returns the rule name. +func (*FileHeaderRule) Name() string { + return "file-header" +} diff --git a/vendor/github.com/mgechev/revive/rule/file_length_limit.go b/vendor/github.com/mgechev/revive/rule/file_length_limit.go new file mode 100644 index 000000000..0fe075c56 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/file_length_limit.go @@ -0,0 +1,132 @@ +package rule + +import ( + "bufio" + "bytes" + "fmt" + "go/ast" + "go/token" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// FileLengthLimitRule lints the number of lines in a file. +type FileLengthLimitRule struct { + // max is the maximum number of lines allowed in a file. 0 means the rule is disabled. + max int + // skipComments indicates whether to skip comment lines when counting lines. + skipComments bool + // skipBlankLines indicates whether to skip blank lines when counting lines. + skipBlankLines bool + + configureOnce sync.Once +} + +// Apply applies the rule to given file. +func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + if r.max <= 0 { + // when max is negative or 0 the rule is disabled + return nil + } + + all := 0 + blank := 0 + scanner := bufio.NewScanner(bytes.NewReader(file.Content())) + for scanner.Scan() { + all++ + if len(bytes.TrimSpace(scanner.Bytes())) == 0 { + blank++ + } + } + + if err := scanner.Err(); err != nil { + panic(err.Error()) + } + + lines := all + if r.skipComments { + lines -= countCommentLines(file.AST.Comments) + } + + if r.skipBlankLines { + lines -= blank + } + + if lines <= r.max { + return nil + } + + return []lint.Failure{ + { + Category: "code-style", + Confidence: 1, + Position: lint.FailurePosition{ + Start: token.Position{ + Filename: file.Name, + Line: all, + }, + }, + Failure: fmt.Sprintf("file length is %d lines, which exceeds the limit of %d", lines, r.max), + }, + } +} + +func (r *FileLengthLimitRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + return // use default + } + + argKV, ok := arguments[0].(map[string]any) + if !ok { + panic(fmt.Sprintf(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got %T`, arguments[0])) + } + for k, v := range argKV { + switch k { + case "max": + maxLines, ok := v.(int64) + if !ok || maxLines < 0 { + panic(fmt.Sprintf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, arguments[0])) + } + r.max = int(maxLines) + case "skipComments": + skipComments, ok := v.(bool) + if !ok { + panic(fmt.Sprintf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, arguments[1])) + } + r.skipComments = skipComments + case "skipBlankLines": + skipBlankLines, ok := v.(bool) + if !ok { + panic(fmt.Sprintf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, arguments[2])) + } + r.skipBlankLines = skipBlankLines + } + } +} + +// Name returns the rule name. +func (*FileLengthLimitRule) Name() string { + return "file-length-limit" +} + +func countCommentLines(comments []*ast.CommentGroup) int { + count := 0 + for _, cg := range comments { + for _, comment := range cg.List { + if len(comment.Text) < 2 { + continue + } + switch comment.Text[1] { + case '/': // single-line comment + count++ + case '*': // multi-line comment + count += strings.Count(comment.Text, "\n") + 1 + } + } + } + return count +} diff --git a/vendor/github.com/mgechev/revive/rule/filename-format.go b/vendor/github.com/mgechev/revive/rule/filename-format.go deleted file mode 100644 index 49fdf9c3e..000000000 --- a/vendor/github.com/mgechev/revive/rule/filename-format.go +++ /dev/null @@ -1,87 +0,0 @@ -package rule - -import ( - "fmt" - "path/filepath" - "regexp" - "sync" - "unicode" - - "github.com/mgechev/revive/lint" -) - -// FilenameFormatRule lints source filenames according to a set of regular expressions given as arguments -type FilenameFormatRule struct { - format *regexp.Regexp - sync.Mutex -} - -// Apply applies the rule to the given file. -func (r *FilenameFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - filename := filepath.Base(file.Name) - if r.format.MatchString(filename) { - return nil - } - - failureMsg := fmt.Sprintf("Filename %s is not of the format %s.%s", filename, r.format.String(), r.getMsgForNonASCIIChars(filename)) - return []lint.Failure{{ - Confidence: 1, - Failure: failureMsg, - RuleName: r.Name(), - Node: file.AST.Name, - }} -} - -func (r *FilenameFormatRule) getMsgForNonASCIIChars(str string) string { - result := "" - for _, c := range str { - if c <= unicode.MaxASCII { - continue - } - - result += fmt.Sprintf(" Non ASCII character %c (%U) found.", c, c) - } - - return result -} - -// Name returns the rule name. -func (*FilenameFormatRule) Name() string { - return "filename-format" -} - -var defaultFormat = regexp.MustCompile("^[_A-Za-z0-9][_A-Za-z0-9-]*.go$") - -func (r *FilenameFormatRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.format != nil { - return - } - - argsCount := len(arguments) - if argsCount == 0 { - r.format = defaultFormat - return - } - - if argsCount > 1 { - panic(fmt.Sprintf("rule %q expects only one argument, got %d %v", r.Name(), argsCount, arguments)) - } - - arg := arguments[0] - str, ok := arg.(string) - if !ok { - panic(fmt.Sprintf("rule %q expects a string argument, got %v of type %T", r.Name(), arg, arg)) - } - - format, err := regexp.Compile(str) - if err != nil { - panic(fmt.Sprintf("rule %q expects a valid regexp argument, got %v for %s", r.Name(), err, arg)) - } - - r.format = format -} diff --git a/vendor/github.com/mgechev/revive/rule/filename_format.go b/vendor/github.com/mgechev/revive/rule/filename_format.go new file mode 100644 index 000000000..9d8047829 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/filename_format.go @@ -0,0 +1,81 @@ +package rule + +import ( + "fmt" + "path/filepath" + "regexp" + "sync" + "unicode" + + "github.com/mgechev/revive/lint" +) + +// FilenameFormatRule lints source filenames according to a set of regular expressions given as arguments +type FilenameFormatRule struct { + format *regexp.Regexp + + configureOnce sync.Once +} + +// Apply applies the rule to the given file. +func (r *FilenameFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + filename := filepath.Base(file.Name) + if r.format.MatchString(filename) { + return nil + } + + failureMsg := fmt.Sprintf("Filename %s is not of the format %s.%s", filename, r.format.String(), r.getMsgForNonASCIIChars(filename)) + return []lint.Failure{{ + Confidence: 1, + Failure: failureMsg, + RuleName: r.Name(), + Node: file.AST.Name, + }} +} + +func (r *FilenameFormatRule) getMsgForNonASCIIChars(str string) string { + result := "" + for _, c := range str { + if c <= unicode.MaxASCII { + continue + } + + result += fmt.Sprintf(" Non ASCII character %c (%U) found.", c, c) + } + + return result +} + +// Name returns the rule name. +func (*FilenameFormatRule) Name() string { + return "filename-format" +} + +var defaultFormat = regexp.MustCompile("^[_A-Za-z0-9][_A-Za-z0-9-]*.go$") + +func (r *FilenameFormatRule) configure(arguments lint.Arguments) { + argsCount := len(arguments) + if argsCount == 0 { + r.format = defaultFormat + return + } + + if argsCount > 1 { + panic(fmt.Sprintf("rule %q expects only one argument, got %d %v", r.Name(), argsCount, arguments)) + } + + arg := arguments[0] + str, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("rule %q expects a string argument, got %v of type %T", r.Name(), arg, arg)) + } + + format, err := regexp.Compile(str) + if err != nil { + panic(fmt.Sprintf("rule %q expects a valid regexp argument, got %v for %s", r.Name(), err, arg)) + } + + r.format = format +} diff --git a/vendor/github.com/mgechev/revive/rule/flag-param.go b/vendor/github.com/mgechev/revive/rule/flag-param.go deleted file mode 100644 index f9bfb712c..000000000 --- a/vendor/github.com/mgechev/revive/rule/flag-param.go +++ /dev/null @@ -1,105 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// FlagParamRule lints given else constructs. -type FlagParamRule struct{} - -// Apply applies the rule to given file. -func (*FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintFlagParamRule{onFailure: onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*FlagParamRule) Name() string { - return "flag-parameter" -} - -type lintFlagParamRule struct { - onFailure func(lint.Failure) -} - -func (w lintFlagParamRule) Visit(node ast.Node) ast.Visitor { - fd, ok := node.(*ast.FuncDecl) - if !ok { - return w - } - - if fd.Body == nil { - return nil // skip whole function declaration - } - - for _, p := range fd.Type.Params.List { - t := p.Type - - id, ok := t.(*ast.Ident) - if !ok { - continue - } - - if id.Name != "bool" { - continue - } - - cv := conditionVisitor{p.Names, fd, w} - ast.Walk(cv, fd.Body) - } - - return w -} - -type conditionVisitor struct { - ids []*ast.Ident - fd *ast.FuncDecl - linter lintFlagParamRule -} - -func (w conditionVisitor) Visit(node ast.Node) ast.Visitor { - ifStmt, ok := node.(*ast.IfStmt) - if !ok { - return w - } - - fselect := func(n ast.Node) bool { - ident, ok := n.(*ast.Ident) - if !ok { - return false - } - - for _, id := range w.ids { - if ident.Name == id.Name { - return true - } - } - - return false - } - - uses := pick(ifStmt.Cond, fselect) - - if len(uses) < 1 { - return w - } - - w.linter.onFailure(lint.Failure{ - Confidence: 1, - Node: w.fd.Type.Params, - Category: "bad practice", - Failure: fmt.Sprintf("parameter '%s' seems to be a control flag, avoid control coupling", uses[0]), - }) - - return nil -} diff --git a/vendor/github.com/mgechev/revive/rule/flag_param.go b/vendor/github.com/mgechev/revive/rule/flag_param.go new file mode 100644 index 000000000..f9bfb712c --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/flag_param.go @@ -0,0 +1,105 @@ +package rule + +import ( + "fmt" + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// FlagParamRule lints given else constructs. +type FlagParamRule struct{} + +// Apply applies the rule to given file. +func (*FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintFlagParamRule{onFailure: onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*FlagParamRule) Name() string { + return "flag-parameter" +} + +type lintFlagParamRule struct { + onFailure func(lint.Failure) +} + +func (w lintFlagParamRule) Visit(node ast.Node) ast.Visitor { + fd, ok := node.(*ast.FuncDecl) + if !ok { + return w + } + + if fd.Body == nil { + return nil // skip whole function declaration + } + + for _, p := range fd.Type.Params.List { + t := p.Type + + id, ok := t.(*ast.Ident) + if !ok { + continue + } + + if id.Name != "bool" { + continue + } + + cv := conditionVisitor{p.Names, fd, w} + ast.Walk(cv, fd.Body) + } + + return w +} + +type conditionVisitor struct { + ids []*ast.Ident + fd *ast.FuncDecl + linter lintFlagParamRule +} + +func (w conditionVisitor) Visit(node ast.Node) ast.Visitor { + ifStmt, ok := node.(*ast.IfStmt) + if !ok { + return w + } + + fselect := func(n ast.Node) bool { + ident, ok := n.(*ast.Ident) + if !ok { + return false + } + + for _, id := range w.ids { + if ident.Name == id.Name { + return true + } + } + + return false + } + + uses := pick(ifStmt.Cond, fselect) + + if len(uses) < 1 { + return w + } + + w.linter.onFailure(lint.Failure{ + Confidence: 1, + Node: w.fd.Type.Params, + Category: "bad practice", + Failure: fmt.Sprintf("parameter '%s' seems to be a control flag, avoid control coupling", uses[0]), + }) + + return nil +} diff --git a/vendor/github.com/mgechev/revive/rule/function-length.go b/vendor/github.com/mgechev/revive/rule/function-length.go deleted file mode 100644 index 30402313d..000000000 --- a/vendor/github.com/mgechev/revive/rule/function-length.go +++ /dev/null @@ -1,181 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "reflect" - "sync" - - "github.com/mgechev/revive/lint" -) - -// FunctionLength lint. -type FunctionLength struct { - maxStmt int - maxLines int - configured bool - sync.Mutex -} - -func (r *FunctionLength) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.configured { - return - } - - r.configured = true - maxStmt, maxLines := r.parseArguments(arguments) - r.maxStmt = int(maxStmt) - r.maxLines = int(maxLines) -} - -// Apply applies the rule to given file. -func (r *FunctionLength) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - walker := lintFuncLength{ - file: file, - maxStmt: r.maxStmt, - maxLines: r.maxLines, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*FunctionLength) Name() string { - return "function-length" -} - -const defaultFuncStmtsLimit = 50 -const defaultFuncLinesLimit = 75 - -func (*FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt, maxLines int64) { - if len(arguments) == 0 { - return defaultFuncStmtsLimit, defaultFuncLinesLimit - } - - const minArguments = 2 - if len(arguments) != minArguments { - panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected %d arguments but got %d`, minArguments, len(arguments))) - } - - maxStmt, maxStmtOk := arguments[0].(int64) - if !maxStmtOk { - panic(fmt.Sprintf(`invalid configuration value for max statements in "function-length" rule; need int64 but got %T`, arguments[0])) - } - if maxStmt < 0 { - panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxStmt)) - } - - maxLines, maxLinesOk := arguments[1].(int64) - if !maxLinesOk { - panic(fmt.Sprintf(`invalid configuration value for max lines in "function-length" rule; need int64 but got %T`, arguments[1])) - } - if maxLines < 0 { - panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxLines)) - } - - return maxStmt, maxLines -} - -type lintFuncLength struct { - file *lint.File - maxStmt int - maxLines int - onFailure func(lint.Failure) -} - -func (w lintFuncLength) Visit(n ast.Node) ast.Visitor { - node, ok := n.(*ast.FuncDecl) - if !ok { - return w - } - - body := node.Body - emptyBody := body == nil || len(node.Body.List) == 0 - if emptyBody { - return nil - } - - if w.maxStmt > 0 { - stmtCount := w.countStmts(node.Body.List) - if stmtCount > w.maxStmt { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("maximum number of statements per function exceeded; max %d but got %d", w.maxStmt, stmtCount), - Node: node, - }) - } - } - - if w.maxLines > 0 { - lineCount := w.countLines(node.Body) - if lineCount > w.maxLines { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("maximum number of lines per function exceeded; max %d but got %d", w.maxLines, lineCount), - Node: node, - }) - } - } - - return nil -} - -func (w lintFuncLength) countLines(b *ast.BlockStmt) int { - return w.file.ToPosition(b.End()).Line - w.file.ToPosition(b.Pos()).Line - 1 -} - -func (w lintFuncLength) countStmts(b []ast.Stmt) int { - count := 0 - for _, s := range b { - switch stmt := s.(type) { - case *ast.BlockStmt: - count += w.countStmts(stmt.List) - case *ast.IfStmt: - count += 1 + w.countBodyListStmts(stmt) - if stmt.Else != nil { - elseBody, ok := stmt.Else.(*ast.BlockStmt) - if ok { - count += w.countStmts(elseBody.List) - } - } - case *ast.ForStmt, *ast.RangeStmt, - *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt: - count += 1 + w.countBodyListStmts(stmt) - case *ast.CaseClause: - count += w.countStmts(stmt.Body) - case *ast.AssignStmt: - count += 1 + w.countFuncLitStmts(stmt.Rhs[0]) - case *ast.GoStmt: - count += 1 + w.countFuncLitStmts(stmt.Call.Fun) - case *ast.DeferStmt: - count += 1 + w.countFuncLitStmts(stmt.Call.Fun) - default: - count++ - } - } - - return count -} - -func (w lintFuncLength) countFuncLitStmts(stmt ast.Expr) int { - if block, ok := stmt.(*ast.FuncLit); ok { - return w.countStmts(block.Body.List) - } - return 0 -} - -func (w lintFuncLength) countBodyListStmts(t any) int { - i := reflect.ValueOf(t).Elem().FieldByName(`Body`).Elem().FieldByName(`List`).Interface() - return w.countStmts(i.([]ast.Stmt)) -} diff --git a/vendor/github.com/mgechev/revive/rule/function-result-limit.go b/vendor/github.com/mgechev/revive/rule/function-result-limit.go deleted file mode 100644 index 23474b5ee..000000000 --- a/vendor/github.com/mgechev/revive/rule/function-result-limit.go +++ /dev/null @@ -1,90 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -// FunctionResultsLimitRule lints given else constructs. -type FunctionResultsLimitRule struct { - max int - sync.Mutex -} - -const defaultResultsLimit = 3 - -func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.max != 0 { - return // already configured - } - - if len(arguments) < 1 { - r.max = defaultResultsLimit - return - } - - maxResults, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])) - } - if maxResults < 0 { - panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`) - } - - r.max = int(maxResults) -} - -// Apply applies the rule to given file. -func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - walker := lintFunctionResultsNum{ - max: r.max, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*FunctionResultsLimitRule) Name() string { - return "function-result-limit" -} - -type lintFunctionResultsNum struct { - max int - onFailure func(lint.Failure) -} - -func (w lintFunctionResultsNum) Visit(n ast.Node) ast.Visitor { - node, ok := n.(*ast.FuncDecl) - if ok { - num := 0 - hasResults := node.Type.Results != nil - if hasResults { - num = node.Type.Results.NumFields() - } - if num > w.max { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("maximum number of return results per function exceeded; max %d but got %d", w.max, num), - Node: node.Type, - }) - } - - return nil // skip visiting function's body - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/function_length.go b/vendor/github.com/mgechev/revive/rule/function_length.go new file mode 100644 index 000000000..c58cd4c0f --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/function_length.go @@ -0,0 +1,174 @@ +package rule + +import ( + "fmt" + "go/ast" + "reflect" + "sync" + + "github.com/mgechev/revive/lint" +) + +// FunctionLength lint. +type FunctionLength struct { + maxStmt int + maxLines int + + configureOnce sync.Once +} + +func (r *FunctionLength) configure(arguments lint.Arguments) { + maxStmt, maxLines := r.parseArguments(arguments) + r.maxStmt = int(maxStmt) + r.maxLines = int(maxLines) +} + +// Apply applies the rule to given file. +func (r *FunctionLength) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + walker := lintFuncLength{ + file: file, + maxStmt: r.maxStmt, + maxLines: r.maxLines, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*FunctionLength) Name() string { + return "function-length" +} + +const defaultFuncStmtsLimit = 50 +const defaultFuncLinesLimit = 75 + +func (*FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt, maxLines int64) { + if len(arguments) == 0 { + return defaultFuncStmtsLimit, defaultFuncLinesLimit + } + + const minArguments = 2 + if len(arguments) != minArguments { + panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected %d arguments but got %d`, minArguments, len(arguments))) + } + + maxStmt, maxStmtOk := arguments[0].(int64) + if !maxStmtOk { + panic(fmt.Sprintf(`invalid configuration value for max statements in "function-length" rule; need int64 but got %T`, arguments[0])) + } + if maxStmt < 0 { + panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxStmt)) + } + + maxLines, maxLinesOk := arguments[1].(int64) + if !maxLinesOk { + panic(fmt.Sprintf(`invalid configuration value for max lines in "function-length" rule; need int64 but got %T`, arguments[1])) + } + if maxLines < 0 { + panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxLines)) + } + + return maxStmt, maxLines +} + +type lintFuncLength struct { + file *lint.File + maxStmt int + maxLines int + onFailure func(lint.Failure) +} + +func (w lintFuncLength) Visit(n ast.Node) ast.Visitor { + node, ok := n.(*ast.FuncDecl) + if !ok { + return w + } + + body := node.Body + emptyBody := body == nil || len(node.Body.List) == 0 + if emptyBody { + return nil + } + + if w.maxStmt > 0 { + stmtCount := w.countStmts(node.Body.List) + if stmtCount > w.maxStmt { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("maximum number of statements per function exceeded; max %d but got %d", w.maxStmt, stmtCount), + Node: node, + }) + } + } + + if w.maxLines > 0 { + lineCount := w.countLines(node.Body) + if lineCount > w.maxLines { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("maximum number of lines per function exceeded; max %d but got %d", w.maxLines, lineCount), + Node: node, + }) + } + } + + return nil +} + +func (w lintFuncLength) countLines(b *ast.BlockStmt) int { + return w.file.ToPosition(b.End()).Line - w.file.ToPosition(b.Pos()).Line - 1 +} + +func (w lintFuncLength) countStmts(b []ast.Stmt) int { + count := 0 + for _, s := range b { + switch stmt := s.(type) { + case *ast.BlockStmt: + count += w.countStmts(stmt.List) + case *ast.IfStmt: + count += 1 + w.countBodyListStmts(stmt) + if stmt.Else != nil { + elseBody, ok := stmt.Else.(*ast.BlockStmt) + if ok { + count += w.countStmts(elseBody.List) + } + } + case *ast.ForStmt, *ast.RangeStmt, + *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt: + count += 1 + w.countBodyListStmts(stmt) + case *ast.CaseClause: + count += w.countStmts(stmt.Body) + case *ast.AssignStmt: + count += 1 + w.countFuncLitStmts(stmt.Rhs[0]) + case *ast.GoStmt: + count += 1 + w.countFuncLitStmts(stmt.Call.Fun) + case *ast.DeferStmt: + count += 1 + w.countFuncLitStmts(stmt.Call.Fun) + default: + count++ + } + } + + return count +} + +func (w lintFuncLength) countFuncLitStmts(stmt ast.Expr) int { + if block, ok := stmt.(*ast.FuncLit); ok { + return w.countStmts(block.Body.List) + } + return 0 +} + +func (w lintFuncLength) countBodyListStmts(t any) int { + i := reflect.ValueOf(t).Elem().FieldByName(`Body`).Elem().FieldByName(`List`).Interface() + return w.countStmts(i.([]ast.Stmt)) +} diff --git a/vendor/github.com/mgechev/revive/rule/function_result_limit.go b/vendor/github.com/mgechev/revive/rule/function_result_limit.go new file mode 100644 index 000000000..5b72f01ab --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/function_result_limit.go @@ -0,0 +1,85 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +// FunctionResultsLimitRule lints given else constructs. +type FunctionResultsLimitRule struct { + max int + + configureOnce sync.Once +} + +const defaultResultsLimit = 3 + +func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.max = defaultResultsLimit + return + } + + maxResults, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])) + } + if maxResults < 0 { + panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`) + } + + r.max = int(maxResults) +} + +// Apply applies the rule to given file. +func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + walker := lintFunctionResultsNum{ + max: r.max, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*FunctionResultsLimitRule) Name() string { + return "function-result-limit" +} + +type lintFunctionResultsNum struct { + max int + onFailure func(lint.Failure) +} + +func (w lintFunctionResultsNum) Visit(n ast.Node) ast.Visitor { + node, ok := n.(*ast.FuncDecl) + if ok { + num := 0 + hasResults := node.Type.Results != nil + if hasResults { + num = node.Type.Results.NumFields() + } + if num > w.max { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("maximum number of return results per function exceeded; max %d but got %d", w.max, num), + Node: node.Type, + }) + } + + return nil // skip visiting function's body + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/get-return.go b/vendor/github.com/mgechev/revive/rule/get-return.go deleted file mode 100644 index 06323a087..000000000 --- a/vendor/github.com/mgechev/revive/rule/get-return.go +++ /dev/null @@ -1,80 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strings" - - "github.com/mgechev/revive/lint" -) - -// GetReturnRule lints given else constructs. -type GetReturnRule struct{} - -// Apply applies the rule to given file. -func (*GetReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintReturnRule{onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*GetReturnRule) Name() string { - return "get-return" -} - -type lintReturnRule struct { - onFailure func(lint.Failure) -} - -const getterPrefix = "GET" - -var lenGetterPrefix = len(getterPrefix) - -func isGetter(name string) bool { - nameHasGetterPrefix := strings.HasPrefix(strings.ToUpper(name), getterPrefix) - if !nameHasGetterPrefix { - return false - } - - isJustGet := len(name) == lenGetterPrefix - if isJustGet { - return false - } - - c := name[lenGetterPrefix] - lowerCaseAfterGetterPrefix := c >= 'a' && c <= 'z' - - return !lowerCaseAfterGetterPrefix -} - -func hasResults(rs *ast.FieldList) bool { - return rs != nil && len(rs.List) > 0 -} - -func (w lintReturnRule) Visit(node ast.Node) ast.Visitor { - fd, ok := node.(*ast.FuncDecl) - if !ok { - return w - } - - if !isGetter(fd.Name.Name) { - return w - } - if !hasResults(fd.Type.Results) { - w.onFailure(lint.Failure{ - Confidence: 0.8, - Node: fd, - Category: "logic", - Failure: fmt.Sprintf("function '%s' seems to be a getter but it does not return any result", fd.Name.Name), - }) - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/get_return.go b/vendor/github.com/mgechev/revive/rule/get_return.go new file mode 100644 index 000000000..06323a087 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/get_return.go @@ -0,0 +1,80 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + + "github.com/mgechev/revive/lint" +) + +// GetReturnRule lints given else constructs. +type GetReturnRule struct{} + +// Apply applies the rule to given file. +func (*GetReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintReturnRule{onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*GetReturnRule) Name() string { + return "get-return" +} + +type lintReturnRule struct { + onFailure func(lint.Failure) +} + +const getterPrefix = "GET" + +var lenGetterPrefix = len(getterPrefix) + +func isGetter(name string) bool { + nameHasGetterPrefix := strings.HasPrefix(strings.ToUpper(name), getterPrefix) + if !nameHasGetterPrefix { + return false + } + + isJustGet := len(name) == lenGetterPrefix + if isJustGet { + return false + } + + c := name[lenGetterPrefix] + lowerCaseAfterGetterPrefix := c >= 'a' && c <= 'z' + + return !lowerCaseAfterGetterPrefix +} + +func hasResults(rs *ast.FieldList) bool { + return rs != nil && len(rs.List) > 0 +} + +func (w lintReturnRule) Visit(node ast.Node) ast.Visitor { + fd, ok := node.(*ast.FuncDecl) + if !ok { + return w + } + + if !isGetter(fd.Name.Name) { + return w + } + if !hasResults(fd.Type.Results) { + w.onFailure(lint.Failure{ + Confidence: 0.8, + Node: fd, + Category: "logic", + Failure: fmt.Sprintf("function '%s' seems to be a getter but it does not return any result", fd.Name.Name), + }) + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/identical-branches.go b/vendor/github.com/mgechev/revive/rule/identical-branches.go deleted file mode 100644 index c6008925f..000000000 --- a/vendor/github.com/mgechev/revive/rule/identical-branches.go +++ /dev/null @@ -1,87 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// IdenticalBranchesRule warns on constant logical expressions. -type IdenticalBranchesRule struct{} - -// Apply applies the rule to given file. -func (*IdenticalBranchesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - w := &lintIdenticalBranches{astFile, onFailure} - ast.Walk(w, astFile) - return failures -} - -// Name returns the rule name. -func (*IdenticalBranchesRule) Name() string { - return "identical-branches" -} - -type lintIdenticalBranches struct { - file *ast.File - onFailure func(lint.Failure) -} - -func (w *lintIdenticalBranches) Visit(node ast.Node) ast.Visitor { - n, ok := node.(*ast.IfStmt) - if !ok { - return w - } - - noElseBranch := n.Else == nil - if noElseBranch { - return w - } - - branches := []*ast.BlockStmt{n.Body} - - elseBranch, ok := n.Else.(*ast.BlockStmt) - if !ok { // if-else-if construction - return w - } - branches = append(branches, elseBranch) - - if w.identicalBranches(branches) { - w.newFailure(n, "both branches of the if are identical") - } - - return w -} - -func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { - if len(branches) < 2 { - return false // only one branch to compare thus we return - } - - referenceBranch := gofmt(branches[0]) - referenceBranchSize := len(branches[0].List) - for i := 1; i < len(branches); i++ { - currentBranch := branches[i] - currentBranchSize := len(currentBranch.List) - if currentBranchSize != referenceBranchSize || gofmt(currentBranch) != referenceBranch { - return false - } - } - - return true -} - -func (w lintIdenticalBranches) newFailure(node ast.Node, msg string) { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: node, - Category: "logic", - Failure: msg, - }) -} diff --git a/vendor/github.com/mgechev/revive/rule/identical_branches.go b/vendor/github.com/mgechev/revive/rule/identical_branches.go new file mode 100644 index 000000000..c6008925f --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/identical_branches.go @@ -0,0 +1,87 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// IdenticalBranchesRule warns on constant logical expressions. +type IdenticalBranchesRule struct{} + +// Apply applies the rule to given file. +func (*IdenticalBranchesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + w := &lintIdenticalBranches{astFile, onFailure} + ast.Walk(w, astFile) + return failures +} + +// Name returns the rule name. +func (*IdenticalBranchesRule) Name() string { + return "identical-branches" +} + +type lintIdenticalBranches struct { + file *ast.File + onFailure func(lint.Failure) +} + +func (w *lintIdenticalBranches) Visit(node ast.Node) ast.Visitor { + n, ok := node.(*ast.IfStmt) + if !ok { + return w + } + + noElseBranch := n.Else == nil + if noElseBranch { + return w + } + + branches := []*ast.BlockStmt{n.Body} + + elseBranch, ok := n.Else.(*ast.BlockStmt) + if !ok { // if-else-if construction + return w + } + branches = append(branches, elseBranch) + + if w.identicalBranches(branches) { + w.newFailure(n, "both branches of the if are identical") + } + + return w +} + +func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { + if len(branches) < 2 { + return false // only one branch to compare thus we return + } + + referenceBranch := gofmt(branches[0]) + referenceBranchSize := len(branches[0].List) + for i := 1; i < len(branches); i++ { + currentBranch := branches[i] + currentBranchSize := len(currentBranch.List) + if currentBranchSize != referenceBranchSize || gofmt(currentBranch) != referenceBranch { + return false + } + } + + return true +} + +func (w lintIdenticalBranches) newFailure(node ast.Node, msg string) { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: node, + Category: "logic", + Failure: msg, + }) +} diff --git a/vendor/github.com/mgechev/revive/rule/if-return.go b/vendor/github.com/mgechev/revive/rule/if-return.go deleted file mode 100644 index a6a3113ad..000000000 --- a/vendor/github.com/mgechev/revive/rule/if-return.go +++ /dev/null @@ -1,115 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - "strings" - - "github.com/mgechev/revive/lint" -) - -// IfReturnRule lints given else constructs. -type IfReturnRule struct{} - -// Apply applies the rule to given file. -func (*IfReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - w := &lintElseError{astFile, onFailure} - ast.Walk(w, astFile) - return failures -} - -// Name returns the rule name. -func (*IfReturnRule) Name() string { - return "if-return" -} - -type lintElseError struct { - file *ast.File - onFailure func(lint.Failure) -} - -func (w *lintElseError) Visit(node ast.Node) ast.Visitor { - switch v := node.(type) { - case *ast.BlockStmt: - for i := 0; i < len(v.List)-1; i++ { - // if var := whatever; var != nil { return var } - s, ok := v.List[i].(*ast.IfStmt) - if !ok || s.Body == nil || len(s.Body.List) != 1 || s.Else != nil { - continue - } - assign, ok := s.Init.(*ast.AssignStmt) - if !ok || len(assign.Lhs) != 1 || !(assign.Tok == token.DEFINE || assign.Tok == token.ASSIGN) { - continue - } - id, ok := assign.Lhs[0].(*ast.Ident) - if !ok { - continue - } - expr, ok := s.Cond.(*ast.BinaryExpr) - if !ok || expr.Op != token.NEQ { - continue - } - if lhs, ok := expr.X.(*ast.Ident); !ok || lhs.Name != id.Name { - continue - } - if rhs, ok := expr.Y.(*ast.Ident); !ok || rhs.Name != "nil" { - continue - } - r, ok := s.Body.List[0].(*ast.ReturnStmt) - if !ok || len(r.Results) != 1 { - continue - } - if r, ok := r.Results[0].(*ast.Ident); !ok || r.Name != id.Name { - continue - } - - // return nil - r, ok = v.List[i+1].(*ast.ReturnStmt) - if !ok || len(r.Results) != 1 { - continue - } - if r, ok := r.Results[0].(*ast.Ident); !ok || r.Name != "nil" { - continue - } - - // check if there are any comments explaining the construct, don't emit an error if there are some. - if containsComments(s.Pos(), r.Pos(), w.file) { - continue - } - - w.onFailure(lint.Failure{ - Confidence: .9, - Node: v.List[i], - Failure: "redundant if ...; err != nil check, just return error instead.", - }) - } - } - return w -} - -func containsComments(start, end token.Pos, f *ast.File) bool { - for _, cgroup := range f.Comments { - comments := cgroup.List - if comments[0].Slash >= end { - // All comments starting with this group are after end pos. - return false - } - if comments[len(comments)-1].Slash < start { - // Comments group ends before start pos. - continue - } - for _, c := range comments { - if start <= c.Slash && c.Slash < end && !strings.HasPrefix(c.Text, "// MATCH ") { - return true - } - } - } - return false -} diff --git a/vendor/github.com/mgechev/revive/rule/if_return.go b/vendor/github.com/mgechev/revive/rule/if_return.go new file mode 100644 index 000000000..a6a3113ad --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/if_return.go @@ -0,0 +1,115 @@ +package rule + +import ( + "go/ast" + "go/token" + "strings" + + "github.com/mgechev/revive/lint" +) + +// IfReturnRule lints given else constructs. +type IfReturnRule struct{} + +// Apply applies the rule to given file. +func (*IfReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + w := &lintElseError{astFile, onFailure} + ast.Walk(w, astFile) + return failures +} + +// Name returns the rule name. +func (*IfReturnRule) Name() string { + return "if-return" +} + +type lintElseError struct { + file *ast.File + onFailure func(lint.Failure) +} + +func (w *lintElseError) Visit(node ast.Node) ast.Visitor { + switch v := node.(type) { + case *ast.BlockStmt: + for i := 0; i < len(v.List)-1; i++ { + // if var := whatever; var != nil { return var } + s, ok := v.List[i].(*ast.IfStmt) + if !ok || s.Body == nil || len(s.Body.List) != 1 || s.Else != nil { + continue + } + assign, ok := s.Init.(*ast.AssignStmt) + if !ok || len(assign.Lhs) != 1 || !(assign.Tok == token.DEFINE || assign.Tok == token.ASSIGN) { + continue + } + id, ok := assign.Lhs[0].(*ast.Ident) + if !ok { + continue + } + expr, ok := s.Cond.(*ast.BinaryExpr) + if !ok || expr.Op != token.NEQ { + continue + } + if lhs, ok := expr.X.(*ast.Ident); !ok || lhs.Name != id.Name { + continue + } + if rhs, ok := expr.Y.(*ast.Ident); !ok || rhs.Name != "nil" { + continue + } + r, ok := s.Body.List[0].(*ast.ReturnStmt) + if !ok || len(r.Results) != 1 { + continue + } + if r, ok := r.Results[0].(*ast.Ident); !ok || r.Name != id.Name { + continue + } + + // return nil + r, ok = v.List[i+1].(*ast.ReturnStmt) + if !ok || len(r.Results) != 1 { + continue + } + if r, ok := r.Results[0].(*ast.Ident); !ok || r.Name != "nil" { + continue + } + + // check if there are any comments explaining the construct, don't emit an error if there are some. + if containsComments(s.Pos(), r.Pos(), w.file) { + continue + } + + w.onFailure(lint.Failure{ + Confidence: .9, + Node: v.List[i], + Failure: "redundant if ...; err != nil check, just return error instead.", + }) + } + } + return w +} + +func containsComments(start, end token.Pos, f *ast.File) bool { + for _, cgroup := range f.Comments { + comments := cgroup.List + if comments[0].Slash >= end { + // All comments starting with this group are after end pos. + return false + } + if comments[len(comments)-1].Slash < start { + // Comments group ends before start pos. + continue + } + for _, c := range comments { + if start <= c.Slash && c.Slash < end && !strings.HasPrefix(c.Text, "// MATCH ") { + return true + } + } + } + return false +} diff --git a/vendor/github.com/mgechev/revive/rule/import-alias-naming.go b/vendor/github.com/mgechev/revive/rule/import-alias-naming.go deleted file mode 100644 index 48d22566a..000000000 --- a/vendor/github.com/mgechev/revive/rule/import-alias-naming.go +++ /dev/null @@ -1,126 +0,0 @@ -package rule - -import ( - "fmt" - "regexp" - "sync" - - "github.com/mgechev/revive/lint" -) - -// ImportAliasNamingRule lints import alias naming. -type ImportAliasNamingRule struct { - configured bool - allowRegexp *regexp.Regexp - denyRegexp *regexp.Regexp - sync.Mutex -} - -const defaultImportAliasNamingAllowRule = "^[a-z][a-z0-9]{0,}$" - -var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule) - -func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.configured { - return - } - - if len(arguments) == 0 { - r.allowRegexp = defaultImportAliasNamingAllowRegexp - return - } - - switch namingRule := arguments[0].(type) { - case string: - r.setAllowRule(namingRule) - case map[string]any: // expecting map[string]string - for k, v := range namingRule { - switch k { - case "allowRegex": - r.setAllowRule(v) - case "denyRegex": - r.setDenyRule(v) - default: - panic(fmt.Sprintf("Invalid map key for 'import-alias-naming' rule. Expecting 'allowRegex' or 'denyRegex', got %v", k)) - } - } - default: - panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])) - } - - if r.allowRegexp == nil && r.denyRegexp == nil { - r.allowRegexp = defaultImportAliasNamingAllowRegexp - } -} - -// Apply applies the rule to given file. -func (r *ImportAliasNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - for _, is := range file.AST.Imports { - path := is.Path - if path == nil { - continue - } - - alias := is.Name - if alias == nil || alias.Name == "_" || alias.Name == "." { // "_" and "." are special types of import aliases and should be processed by another linter rule - continue - } - - if r.allowRegexp != nil && !r.allowRegexp.MatchString(alias.Name) { - failures = append(failures, lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("import name (%s) must match the regular expression: %s", alias.Name, r.allowRegexp.String()), - Node: alias, - Category: "imports", - }) - } - - if r.denyRegexp != nil && r.denyRegexp.MatchString(alias.Name) { - failures = append(failures, lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("import name (%s) must NOT match the regular expression: %s", alias.Name, r.denyRegexp.String()), - Node: alias, - Category: "imports", - }) - } - } - - return failures -} - -// Name returns the rule name. -func (*ImportAliasNamingRule) Name() string { - return "import-alias-naming" -} - -func (r *ImportAliasNamingRule) setAllowRule(value any) { - namingRule, ok := value.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument '%v' for import-alias-naming allowRegexp rule. Expecting string, got %T", value, value)) - } - - namingRuleRegexp, err := regexp.Compile(namingRule) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the import-alias-naming allowRegexp rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err)) - } - r.allowRegexp = namingRuleRegexp -} - -func (r *ImportAliasNamingRule) setDenyRule(value any) { - namingRule, ok := value.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument '%v' for import-alias-naming denyRegexp rule. Expecting string, got %T", value, value)) - } - - namingRuleRegexp, err := regexp.Compile(namingRule) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the import-alias-naming denyRegexp rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err)) - } - r.denyRegexp = namingRuleRegexp -} diff --git a/vendor/github.com/mgechev/revive/rule/import-shadowing.go b/vendor/github.com/mgechev/revive/rule/import-shadowing.go deleted file mode 100644 index 046aeb688..000000000 --- a/vendor/github.com/mgechev/revive/rule/import-shadowing.go +++ /dev/null @@ -1,115 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "strings" - - "github.com/mgechev/revive/lint" -) - -// ImportShadowingRule lints given else constructs. -type ImportShadowingRule struct{} - -// Apply applies the rule to given file. -func (*ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - importNames := map[string]struct{}{} - for _, imp := range file.AST.Imports { - importNames[getName(imp)] = struct{}{} - } - - fileAst := file.AST - walker := importShadowing{ - packageNameIdent: fileAst.Name, - importNames: importNames, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - alreadySeen: map[*ast.Object]struct{}{}, - skipIdents: map[*ast.Ident]struct{}{}, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ImportShadowingRule) Name() string { - return "import-shadowing" -} - -func getName(imp *ast.ImportSpec) string { - const pathSep = "/" - const strDelim = `"` - if imp.Name != nil { - return imp.Name.Name - } - - path := imp.Path.Value - i := strings.LastIndex(path, pathSep) - if i == -1 { - return strings.Trim(path, strDelim) - } - - return strings.Trim(path[i+1:], strDelim) -} - -type importShadowing struct { - packageNameIdent *ast.Ident - importNames map[string]struct{} - onFailure func(lint.Failure) - alreadySeen map[*ast.Object]struct{} - skipIdents map[*ast.Ident]struct{} -} - -// Visit visits AST nodes and checks if id nodes (ast.Ident) shadow an import name -func (w importShadowing) Visit(n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.AssignStmt: - if n.Tok == token.DEFINE { - return w // analyze variable declarations of the form id := expr - } - - return nil // skip assigns of the form id = expr (not an id declaration) - case *ast.CallExpr, // skip call expressions (not an id declaration) - *ast.ImportSpec, // skip import section subtree because we already have the list of imports - *ast.KeyValueExpr, // skip analysis of key-val expressions ({key:value}): ids of such expressions, even the same of an import name, do not shadow the import name - *ast.ReturnStmt, // skip skipping analysis of returns, ids in expression were already analyzed - *ast.SelectorExpr, // skip analysis of selector expressions (anId.otherId): because if anId shadows an import name, it was already detected, and otherId does not shadows the import name - *ast.StructType: // skip analysis of struct type because struct fields can not shadow an import name - return nil - case *ast.FuncDecl: - if n.Recv != nil { - w.skipIdents[n.Name] = struct{}{} - } - case *ast.Ident: - if n == w.packageNameIdent { - return nil // skip the ident corresponding to the package name of this file - } - - id := n.Name - if id == "_" { - return w // skip _ id - } - - _, isImportName := w.importNames[id] - _, alreadySeen := w.alreadySeen[n.Obj] - _, skipIdent := w.skipIdents[n] - if isImportName && !alreadySeen && !skipIdent { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: n, - Category: "naming", - Failure: fmt.Sprintf("The name '%s' shadows an import name", id), - }) - - w.alreadySeen[n.Obj] = struct{}{} - } - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/import_alias_naming.go b/vendor/github.com/mgechev/revive/rule/import_alias_naming.go new file mode 100644 index 000000000..043bf0d76 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/import_alias_naming.go @@ -0,0 +1,120 @@ +package rule + +import ( + "fmt" + "regexp" + "sync" + + "github.com/mgechev/revive/lint" +) + +// ImportAliasNamingRule lints import alias naming. +type ImportAliasNamingRule struct { + allowRegexp *regexp.Regexp + denyRegexp *regexp.Regexp + + configureOnce sync.Once +} + +const defaultImportAliasNamingAllowRule = "^[a-z][a-z0-9]{0,}$" + +var defaultImportAliasNamingAllowRegexp = regexp.MustCompile(defaultImportAliasNamingAllowRule) + +func (r *ImportAliasNamingRule) configure(arguments lint.Arguments) { + if len(arguments) == 0 { + r.allowRegexp = defaultImportAliasNamingAllowRegexp + return + } + + switch namingRule := arguments[0].(type) { + case string: + r.setAllowRule(namingRule) + case map[string]any: // expecting map[string]string + for k, v := range namingRule { + switch k { + case "allowRegex": + r.setAllowRule(v) + case "denyRegex": + r.setDenyRule(v) + default: + panic(fmt.Sprintf("Invalid map key for 'import-alias-naming' rule. Expecting 'allowRegex' or 'denyRegex', got %v", k)) + } + } + default: + panic(fmt.Sprintf("Invalid argument '%v' for 'import-alias-naming' rule. Expecting string or map[string]string, got %T", arguments[0], arguments[0])) + } + + if r.allowRegexp == nil && r.denyRegexp == nil { + r.allowRegexp = defaultImportAliasNamingAllowRegexp + } +} + +// Apply applies the rule to given file. +func (r *ImportAliasNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + for _, is := range file.AST.Imports { + path := is.Path + if path == nil { + continue + } + + alias := is.Name + if alias == nil || alias.Name == "_" || alias.Name == "." { // "_" and "." are special types of import aliases and should be processed by another linter rule + continue + } + + if r.allowRegexp != nil && !r.allowRegexp.MatchString(alias.Name) { + failures = append(failures, lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("import name (%s) must match the regular expression: %s", alias.Name, r.allowRegexp.String()), + Node: alias, + Category: "imports", + }) + } + + if r.denyRegexp != nil && r.denyRegexp.MatchString(alias.Name) { + failures = append(failures, lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("import name (%s) must NOT match the regular expression: %s", alias.Name, r.denyRegexp.String()), + Node: alias, + Category: "imports", + }) + } + } + + return failures +} + +// Name returns the rule name. +func (*ImportAliasNamingRule) Name() string { + return "import-alias-naming" +} + +func (r *ImportAliasNamingRule) setAllowRule(value any) { + namingRule, ok := value.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument '%v' for import-alias-naming allowRegexp rule. Expecting string, got %T", value, value)) + } + + namingRuleRegexp, err := regexp.Compile(namingRule) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the import-alias-naming allowRegexp rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err)) + } + r.allowRegexp = namingRuleRegexp +} + +func (r *ImportAliasNamingRule) setDenyRule(value any) { + namingRule, ok := value.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument '%v' for import-alias-naming denyRegexp rule. Expecting string, got %T", value, value)) + } + + namingRuleRegexp, err := regexp.Compile(namingRule) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the import-alias-naming denyRegexp rule. Expecting %q to be a valid regular expression, got: %v", namingRule, err)) + } + r.denyRegexp = namingRuleRegexp +} diff --git a/vendor/github.com/mgechev/revive/rule/import_shadowing.go b/vendor/github.com/mgechev/revive/rule/import_shadowing.go new file mode 100644 index 000000000..046aeb688 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/import_shadowing.go @@ -0,0 +1,115 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "github.com/mgechev/revive/lint" +) + +// ImportShadowingRule lints given else constructs. +type ImportShadowingRule struct{} + +// Apply applies the rule to given file. +func (*ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + importNames := map[string]struct{}{} + for _, imp := range file.AST.Imports { + importNames[getName(imp)] = struct{}{} + } + + fileAst := file.AST + walker := importShadowing{ + packageNameIdent: fileAst.Name, + importNames: importNames, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + alreadySeen: map[*ast.Object]struct{}{}, + skipIdents: map[*ast.Ident]struct{}{}, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ImportShadowingRule) Name() string { + return "import-shadowing" +} + +func getName(imp *ast.ImportSpec) string { + const pathSep = "/" + const strDelim = `"` + if imp.Name != nil { + return imp.Name.Name + } + + path := imp.Path.Value + i := strings.LastIndex(path, pathSep) + if i == -1 { + return strings.Trim(path, strDelim) + } + + return strings.Trim(path[i+1:], strDelim) +} + +type importShadowing struct { + packageNameIdent *ast.Ident + importNames map[string]struct{} + onFailure func(lint.Failure) + alreadySeen map[*ast.Object]struct{} + skipIdents map[*ast.Ident]struct{} +} + +// Visit visits AST nodes and checks if id nodes (ast.Ident) shadow an import name +func (w importShadowing) Visit(n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.AssignStmt: + if n.Tok == token.DEFINE { + return w // analyze variable declarations of the form id := expr + } + + return nil // skip assigns of the form id = expr (not an id declaration) + case *ast.CallExpr, // skip call expressions (not an id declaration) + *ast.ImportSpec, // skip import section subtree because we already have the list of imports + *ast.KeyValueExpr, // skip analysis of key-val expressions ({key:value}): ids of such expressions, even the same of an import name, do not shadow the import name + *ast.ReturnStmt, // skip skipping analysis of returns, ids in expression were already analyzed + *ast.SelectorExpr, // skip analysis of selector expressions (anId.otherId): because if anId shadows an import name, it was already detected, and otherId does not shadows the import name + *ast.StructType: // skip analysis of struct type because struct fields can not shadow an import name + return nil + case *ast.FuncDecl: + if n.Recv != nil { + w.skipIdents[n.Name] = struct{}{} + } + case *ast.Ident: + if n == w.packageNameIdent { + return nil // skip the ident corresponding to the package name of this file + } + + id := n.Name + if id == "_" { + return w // skip _ id + } + + _, isImportName := w.importNames[id] + _, alreadySeen := w.alreadySeen[n.Obj] + _, skipIdent := w.skipIdents[n] + if isImportName && !alreadySeen && !skipIdent { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: n, + Category: "naming", + Failure: fmt.Sprintf("The name '%s' shadows an import name", id), + }) + + w.alreadySeen[n.Obj] = struct{}{} + } + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/imports-blocklist.go b/vendor/github.com/mgechev/revive/rule/imports-blocklist.go deleted file mode 100644 index 431066403..000000000 --- a/vendor/github.com/mgechev/revive/rule/imports-blocklist.go +++ /dev/null @@ -1,73 +0,0 @@ -package rule - -import ( - "fmt" - "regexp" - "sync" - - "github.com/mgechev/revive/lint" -) - -// ImportsBlocklistRule lints given else constructs. -type ImportsBlocklistRule struct { - blocklist []*regexp.Regexp - sync.Mutex -} - -var replaceImportRegexp = regexp.MustCompile(`/?\*\*/?`) - -func (r *ImportsBlocklistRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.blocklist == nil { - r.blocklist = make([]*regexp.Regexp, 0) - - for _, arg := range arguments { - argStr, ok := arg.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the imports-blocklist rule. Expecting a string, got %T", arg)) - } - regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceImportRegexp.ReplaceAllString(argStr, `(\W|\w)*`))) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the imports-blocklist rule. Expecting %q to be a valid regular expression, got: %v", argStr, err)) - } - r.blocklist = append(r.blocklist, regStr) - } - } -} - -func (r *ImportsBlocklistRule) isBlocklisted(path string) bool { - for _, regex := range r.blocklist { - if regex.MatchString(path) { - return true - } - } - return false -} - -// Apply applies the rule to given file. -func (r *ImportsBlocklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - for _, is := range file.AST.Imports { - path := is.Path - if path != nil && r.isBlocklisted(path.Value) { - failures = append(failures, lint.Failure{ - Confidence: 1, - Failure: "should not use the following blocklisted import: " + path.Value, - Node: is, - Category: "imports", - }) - } - } - - return failures -} - -// Name returns the rule name. -func (*ImportsBlocklistRule) Name() string { - return "imports-blocklist" -} diff --git a/vendor/github.com/mgechev/revive/rule/imports_blocklist.go b/vendor/github.com/mgechev/revive/rule/imports_blocklist.go new file mode 100644 index 000000000..18d77ca1c --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/imports_blocklist.go @@ -0,0 +1,68 @@ +package rule + +import ( + "fmt" + "regexp" + "sync" + + "github.com/mgechev/revive/lint" +) + +// ImportsBlocklistRule lints given else constructs. +type ImportsBlocklistRule struct { + blocklist []*regexp.Regexp + + configureOnce sync.Once +} + +var replaceImportRegexp = regexp.MustCompile(`/?\*\*/?`) + +func (r *ImportsBlocklistRule) configure(arguments lint.Arguments) { + r.blocklist = []*regexp.Regexp{} + for _, arg := range arguments { + argStr, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the imports-blocklist rule. Expecting a string, got %T", arg)) + } + regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceImportRegexp.ReplaceAllString(argStr, `(\W|\w)*`))) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the imports-blocklist rule. Expecting %q to be a valid regular expression, got: %v", argStr, err)) + } + r.blocklist = append(r.blocklist, regStr) + } +} + +func (r *ImportsBlocklistRule) isBlocklisted(path string) bool { + for _, regex := range r.blocklist { + if regex.MatchString(path) { + return true + } + } + return false +} + +// Apply applies the rule to given file. +func (r *ImportsBlocklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + for _, is := range file.AST.Imports { + path := is.Path + if path != nil && r.isBlocklisted(path.Value) { + failures = append(failures, lint.Failure{ + Confidence: 1, + Failure: "should not use the following blocklisted import: " + path.Value, + Node: is, + Category: "imports", + }) + } + } + + return failures +} + +// Name returns the rule name. +func (*ImportsBlocklistRule) Name() string { + return "imports-blocklist" +} diff --git a/vendor/github.com/mgechev/revive/rule/increment-decrement.go b/vendor/github.com/mgechev/revive/rule/increment-decrement.go deleted file mode 100644 index 34a8e1ec5..000000000 --- a/vendor/github.com/mgechev/revive/rule/increment-decrement.go +++ /dev/null @@ -1,73 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// IncrementDecrementRule lints given else constructs. -type IncrementDecrementRule struct{} - -// Apply applies the rule to given file. -func (*IncrementDecrementRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintIncrementDecrement{ - file: file, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*IncrementDecrementRule) Name() string { - return "increment-decrement" -} - -type lintIncrementDecrement struct { - file *lint.File - onFailure func(lint.Failure) -} - -func (w lintIncrementDecrement) Visit(n ast.Node) ast.Visitor { - as, ok := n.(*ast.AssignStmt) - if !ok { - return w - } - if len(as.Lhs) != 1 { - return w - } - if !isOne(as.Rhs[0]) { - return w - } - var suffix string - switch as.Tok { - case token.ADD_ASSIGN: - suffix = "++" - case token.SUB_ASSIGN: - suffix = "--" - default: - return w - } - w.onFailure(lint.Failure{ - Confidence: 0.8, - Node: as, - Category: "unary-op", - Failure: fmt.Sprintf("should replace %s with %s%s", w.file.Render(as), w.file.Render(as.Lhs[0]), suffix), - }) - return w -} - -func isOne(expr ast.Expr) bool { - lit, ok := expr.(*ast.BasicLit) - return ok && lit.Kind == token.INT && lit.Value == "1" -} diff --git a/vendor/github.com/mgechev/revive/rule/increment_decrement.go b/vendor/github.com/mgechev/revive/rule/increment_decrement.go new file mode 100644 index 000000000..34a8e1ec5 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/increment_decrement.go @@ -0,0 +1,73 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// IncrementDecrementRule lints given else constructs. +type IncrementDecrementRule struct{} + +// Apply applies the rule to given file. +func (*IncrementDecrementRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := lintIncrementDecrement{ + file: file, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*IncrementDecrementRule) Name() string { + return "increment-decrement" +} + +type lintIncrementDecrement struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (w lintIncrementDecrement) Visit(n ast.Node) ast.Visitor { + as, ok := n.(*ast.AssignStmt) + if !ok { + return w + } + if len(as.Lhs) != 1 { + return w + } + if !isOne(as.Rhs[0]) { + return w + } + var suffix string + switch as.Tok { + case token.ADD_ASSIGN: + suffix = "++" + case token.SUB_ASSIGN: + suffix = "--" + default: + return w + } + w.onFailure(lint.Failure{ + Confidence: 0.8, + Node: as, + Category: "unary-op", + Failure: fmt.Sprintf("should replace %s with %s%s", w.file.Render(as), w.file.Render(as.Lhs[0]), suffix), + }) + return w +} + +func isOne(expr ast.Expr) bool { + lit, ok := expr.(*ast.BasicLit) + return ok && lit.Kind == token.INT && lit.Value == "1" +} diff --git a/vendor/github.com/mgechev/revive/rule/indent-error-flow.go b/vendor/github.com/mgechev/revive/rule/indent-error-flow.go deleted file mode 100644 index ebc1e793a..000000000 --- a/vendor/github.com/mgechev/revive/rule/indent-error-flow.go +++ /dev/null @@ -1,45 +0,0 @@ -package rule - -import ( - "github.com/mgechev/revive/internal/ifelse" - "github.com/mgechev/revive/lint" -) - -// IndentErrorFlowRule lints given else constructs. -type IndentErrorFlowRule struct{} - -// Apply applies the rule to given file. -func (e *IndentErrorFlowRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - return ifelse.Apply(e, file.AST, ifelse.TargetElse, args) -} - -// Name returns the rule name. -func (*IndentErrorFlowRule) Name() string { - return "indent-error-flow" -} - -// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable. -func (*IndentErrorFlowRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string { - if !chain.If.Deviates() { - // this rule only applies if the if-block deviates control flow - return "" - } - - if chain.HasPriorNonDeviating { - // if we de-indent the "else" block then a previous branch - // might flow into it, affecting program behaviour - return "" - } - - if !chain.If.Returns() { - // avoid overlapping with superfluous-else - return "" - } - - if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls) { - // avoid increasing variable scope - return "" - } - - return "if block ends with a return statement, so drop this else and outdent its block" -} diff --git a/vendor/github.com/mgechev/revive/rule/indent_error_flow.go b/vendor/github.com/mgechev/revive/rule/indent_error_flow.go new file mode 100644 index 000000000..ebc1e793a --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/indent_error_flow.go @@ -0,0 +1,45 @@ +package rule + +import ( + "github.com/mgechev/revive/internal/ifelse" + "github.com/mgechev/revive/lint" +) + +// IndentErrorFlowRule lints given else constructs. +type IndentErrorFlowRule struct{} + +// Apply applies the rule to given file. +func (e *IndentErrorFlowRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + return ifelse.Apply(e, file.AST, ifelse.TargetElse, args) +} + +// Name returns the rule name. +func (*IndentErrorFlowRule) Name() string { + return "indent-error-flow" +} + +// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable. +func (*IndentErrorFlowRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string { + if !chain.If.Deviates() { + // this rule only applies if the if-block deviates control flow + return "" + } + + if chain.HasPriorNonDeviating { + // if we de-indent the "else" block then a previous branch + // might flow into it, affecting program behaviour + return "" + } + + if !chain.If.Returns() { + // avoid overlapping with superfluous-else + return "" + } + + if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls) { + // avoid increasing variable scope + return "" + } + + return "if block ends with a return statement, so drop this else and outdent its block" +} diff --git a/vendor/github.com/mgechev/revive/rule/line-length-limit.go b/vendor/github.com/mgechev/revive/rule/line-length-limit.go deleted file mode 100644 index a154b7aec..000000000 --- a/vendor/github.com/mgechev/revive/rule/line-length-limit.go +++ /dev/null @@ -1,104 +0,0 @@ -package rule - -import ( - "bufio" - "bytes" - "fmt" - "go/token" - "strings" - "sync" - "unicode/utf8" - - "github.com/mgechev/revive/lint" -) - -// LineLengthLimitRule lints given else constructs. -type LineLengthLimitRule struct { - max int - sync.Mutex -} - -const defaultLineLengthLimit = 80 - -func (r *LineLengthLimitRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.max != 0 { - return // already configured - } - - if len(arguments) < 1 { - r.max = defaultLineLengthLimit - return - } - - maxLength, ok := arguments[0].(int64) // Alt. non panicking version - if !ok || maxLength < 0 { - panic(`invalid value passed as argument number to the "line-length-limit" rule`) - } - - r.max = int(maxLength) -} - -// Apply applies the rule to given file. -func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - checker := lintLineLengthNum{ - max: r.max, - file: file, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - checker.check() - - return failures -} - -// Name returns the rule name. -func (*LineLengthLimitRule) Name() string { - return "line-length-limit" -} - -type lintLineLengthNum struct { - max int - file *lint.File - onFailure func(lint.Failure) -} - -func (r lintLineLengthNum) check() { - f := bytes.NewReader(r.file.Content()) - spaces := strings.Repeat(" ", 4) // tab width = 4 - l := 1 - s := bufio.NewScanner(f) - for s.Scan() { - t := s.Text() - t = strings.ReplaceAll(t, "\t", spaces) - c := utf8.RuneCountInString(t) - if c > r.max { - r.onFailure(lint.Failure{ - Category: "code-style", - Position: lint.FailurePosition{ - // Offset not set; it is non-trivial, and doesn't appear to be needed. - Start: token.Position{ - Filename: r.file.Name, - Line: l, - Column: 0, - }, - End: token.Position{ - Filename: r.file.Name, - Line: l, - Column: c, - }, - }, - Confidence: 1, - Failure: fmt.Sprintf("line is %d characters, out of limit %d", c, r.max), - }) - } - l++ - } -} diff --git a/vendor/github.com/mgechev/revive/rule/line_length_limit.go b/vendor/github.com/mgechev/revive/rule/line_length_limit.go new file mode 100644 index 000000000..415761e1e --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/line_length_limit.go @@ -0,0 +1,99 @@ +package rule + +import ( + "bufio" + "bytes" + "fmt" + "go/token" + "strings" + "sync" + "unicode/utf8" + + "github.com/mgechev/revive/lint" +) + +// LineLengthLimitRule lints given else constructs. +type LineLengthLimitRule struct { + max int + + configureOnce sync.Once +} + +const defaultLineLengthLimit = 80 + +func (r *LineLengthLimitRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.max = defaultLineLengthLimit + return + } + + maxLength, ok := arguments[0].(int64) // Alt. non panicking version + if !ok || maxLength < 0 { + panic(`invalid value passed as argument number to the "line-length-limit" rule`) + } + + r.max = int(maxLength) +} + +// Apply applies the rule to given file. +func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + checker := lintLineLengthNum{ + max: r.max, + file: file, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + checker.check() + + return failures +} + +// Name returns the rule name. +func (*LineLengthLimitRule) Name() string { + return "line-length-limit" +} + +type lintLineLengthNum struct { + max int + file *lint.File + onFailure func(lint.Failure) +} + +func (r lintLineLengthNum) check() { + f := bytes.NewReader(r.file.Content()) + spaces := strings.Repeat(" ", 4) // tab width = 4 + l := 1 + s := bufio.NewScanner(f) + for s.Scan() { + t := s.Text() + t = strings.ReplaceAll(t, "\t", spaces) + c := utf8.RuneCountInString(t) + if c > r.max { + r.onFailure(lint.Failure{ + Category: "code-style", + Position: lint.FailurePosition{ + // Offset not set; it is non-trivial, and doesn't appear to be needed. + Start: token.Position{ + Filename: r.file.Name, + Line: l, + Column: 0, + }, + End: token.Position{ + Filename: r.file.Name, + Line: l, + Column: c, + }, + }, + Confidence: 1, + Failure: fmt.Sprintf("line is %d characters, out of limit %d", c, r.max), + }) + } + l++ + } +} diff --git a/vendor/github.com/mgechev/revive/rule/max-control-nesting.go b/vendor/github.com/mgechev/revive/rule/max-control-nesting.go deleted file mode 100644 index 5dbb1eefa..000000000 --- a/vendor/github.com/mgechev/revive/rule/max-control-nesting.go +++ /dev/null @@ -1,128 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -// MaxControlNestingRule lints given else constructs. -type MaxControlNestingRule struct { - max int64 - sync.Mutex -} - -const defaultMaxControlNesting = 5 - -// Apply applies the rule to given file. -func (r *MaxControlNestingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - fileAst := file.AST - - walker := &lintMaxControlNesting{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - max: int(r.max), - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*MaxControlNestingRule) Name() string { - return "max-control-nesting" -} - -type lintMaxControlNesting struct { - max int - onFailure func(lint.Failure) - nestingLevelAcc int - lastCtrlStmt ast.Node -} - -func (w *lintMaxControlNesting) Visit(n ast.Node) ast.Visitor { - if w.nestingLevelAcc > w.max { // we are visiting a node beyond the max nesting level - w.onFailure(lint.Failure{ - Failure: fmt.Sprintf("control flow nesting exceeds %d", w.max), - Confidence: 1, - Node: w.lastCtrlStmt, - Category: "complexity", - }) - return nil // stop visiting deeper - } - - switch v := n.(type) { - case *ast.IfStmt: - w.lastCtrlStmt = v - w.walkControlledBlock(v.Body) // "then" branch block - if v.Else != nil { - w.walkControlledBlock(v.Else) // "else" branch block - } - return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) - - case *ast.ForStmt: - w.lastCtrlStmt = v - w.walkControlledBlock(v.Body) - return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) - - case *ast.CaseClause: // switch case - w.lastCtrlStmt = v - for _, s := range v.Body { // visit each statement in the case clause - w.walkControlledBlock(s) - } - return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) - - case *ast.CommClause: // select case - w.lastCtrlStmt = v - for _, s := range v.Body { // visit each statement in the select case clause - w.walkControlledBlock(s) - } - return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) - - case *ast.FuncLit: - walker := &lintMaxControlNesting{ - onFailure: w.onFailure, - max: w.max, - } - ast.Walk(walker, v.Body) - return nil - } - - return w -} - -func (w *lintMaxControlNesting) walkControlledBlock(b ast.Node) { - oldNestingLevel := w.nestingLevelAcc - w.nestingLevelAcc++ - ast.Walk(w, b) - w.nestingLevelAcc = oldNestingLevel -} - -func (r *MaxControlNestingRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if !(r.max < 1) { - return // max already configured - } - - if len(arguments) < 1 { - r.max = defaultMaxControlNesting - return - } - - checkNumberOfArguments(1, arguments, r.Name()) - - maxNesting, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(`invalid value passed as argument number to the "max-control-nesting" rule`) - } - r.max = maxNesting -} diff --git a/vendor/github.com/mgechev/revive/rule/max-public-structs.go b/vendor/github.com/mgechev/revive/rule/max-public-structs.go deleted file mode 100644 index 70840e734..000000000 --- a/vendor/github.com/mgechev/revive/rule/max-public-structs.go +++ /dev/null @@ -1,90 +0,0 @@ -package rule - -import ( - "go/ast" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// MaxPublicStructsRule lints given else constructs. -type MaxPublicStructsRule struct { - max int64 - sync.Mutex -} - -const defaultMaxPublicStructs = 5 - -func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.max == 0 { - return // already configured - } - - if len(arguments) < 1 { - r.max = defaultMaxPublicStructs - return - } - - checkNumberOfArguments(1, arguments, r.Name()) - - maxStructs, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(`invalid value passed as argument number to the "max-public-structs" rule`) - } - r.max = maxStructs -} - -// Apply applies the rule to given file. -func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - fileAst := file.AST - - walker := &lintMaxPublicStructs{ - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - if walker.current > r.max { - walker.onFailure(lint.Failure{ - Failure: "you have exceeded the maximum number of public struct declarations", - Confidence: 1, - Node: fileAst, - Category: "style", - }) - } - - return failures -} - -// Name returns the rule name. -func (*MaxPublicStructsRule) Name() string { - return "max-public-structs" -} - -type lintMaxPublicStructs struct { - current int64 - fileAst *ast.File - onFailure func(lint.Failure) -} - -func (w *lintMaxPublicStructs) Visit(n ast.Node) ast.Visitor { - switch v := n.(type) { - case *ast.TypeSpec: - name := v.Name.Name - first := string(name[0]) - if strings.ToUpper(first) == first { - w.current++ - } - } - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/max_control_nesting.go b/vendor/github.com/mgechev/revive/rule/max_control_nesting.go new file mode 100644 index 000000000..b2c5af70e --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/max_control_nesting.go @@ -0,0 +1,123 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +// MaxControlNestingRule lints given else constructs. +type MaxControlNestingRule struct { + max int64 + + configureOnce sync.Once +} + +const defaultMaxControlNesting = 5 + +// Apply applies the rule to given file. +func (r *MaxControlNestingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + fileAst := file.AST + + walker := &lintMaxControlNesting{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + max: int(r.max), + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*MaxControlNestingRule) Name() string { + return "max-control-nesting" +} + +type lintMaxControlNesting struct { + max int + onFailure func(lint.Failure) + nestingLevelAcc int + lastCtrlStmt ast.Node +} + +func (w *lintMaxControlNesting) Visit(n ast.Node) ast.Visitor { + if w.nestingLevelAcc > w.max { // we are visiting a node beyond the max nesting level + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("control flow nesting exceeds %d", w.max), + Confidence: 1, + Node: w.lastCtrlStmt, + Category: "complexity", + }) + return nil // stop visiting deeper + } + + switch v := n.(type) { + case *ast.IfStmt: + w.lastCtrlStmt = v + w.walkControlledBlock(v.Body) // "then" branch block + if v.Else != nil { + w.walkControlledBlock(v.Else) // "else" branch block + } + return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) + + case *ast.ForStmt: + w.lastCtrlStmt = v + w.walkControlledBlock(v.Body) + return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) + + case *ast.CaseClause: // switch case + w.lastCtrlStmt = v + for _, s := range v.Body { // visit each statement in the case clause + w.walkControlledBlock(s) + } + return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) + + case *ast.CommClause: // select case + w.lastCtrlStmt = v + for _, s := range v.Body { // visit each statement in the select case clause + w.walkControlledBlock(s) + } + return nil // stop re-visiting nesting blocks (already visited by w.walkControlledBlock) + + case *ast.FuncLit: + walker := &lintMaxControlNesting{ + onFailure: w.onFailure, + max: w.max, + } + ast.Walk(walker, v.Body) + return nil + } + + return w +} + +func (w *lintMaxControlNesting) walkControlledBlock(b ast.Node) { + oldNestingLevel := w.nestingLevelAcc + w.nestingLevelAcc++ + ast.Walk(w, b) + w.nestingLevelAcc = oldNestingLevel +} + +func (r *MaxControlNestingRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.max = defaultMaxControlNesting + return + } + + checkNumberOfArguments(1, arguments, r.Name()) + + maxNesting, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(`invalid value passed as argument number to the "max-control-nesting" rule`) + } + r.max = maxNesting +} diff --git a/vendor/github.com/mgechev/revive/rule/max_public_structs.go b/vendor/github.com/mgechev/revive/rule/max_public_structs.go new file mode 100644 index 000000000..d6f91e375 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/max_public_structs.go @@ -0,0 +1,90 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// MaxPublicStructsRule lints given else constructs. +type MaxPublicStructsRule struct { + max int64 + + configureOnce sync.Once +} + +const defaultMaxPublicStructs = 5 + +func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) { + if len(arguments) < 1 { + r.max = defaultMaxPublicStructs + return + } + + checkNumberOfArguments(1, arguments, r.Name()) + + maxStructs, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(`invalid value passed as argument number to the "max-public-structs" rule`) + } + r.max = maxStructs +} + +// Apply applies the rule to given file. +func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + if r.max < 1 { + return failures + } + + fileAst := file.AST + + walker := &lintMaxPublicStructs{ + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, fileAst) + + if walker.current > r.max { + walker.onFailure(lint.Failure{ + Failure: fmt.Sprintf("you have exceeded the maximum number (%d) of public struct declarations", r.max), + Confidence: 1, + Node: fileAst, + Category: "style", + }) + } + + return failures +} + +// Name returns the rule name. +func (*MaxPublicStructsRule) Name() string { + return "max-public-structs" +} + +type lintMaxPublicStructs struct { + current int64 + fileAst *ast.File + onFailure func(lint.Failure) +} + +func (w *lintMaxPublicStructs) Visit(n ast.Node) ast.Visitor { + switch v := n.(type) { + case *ast.TypeSpec: + name := v.Name.Name + first := string(name[0]) + if strings.ToUpper(first) == first { + w.current++ + } + } + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/modifies-param.go b/vendor/github.com/mgechev/revive/rule/modifies-param.go deleted file mode 100644 index a68ae2501..000000000 --- a/vendor/github.com/mgechev/revive/rule/modifies-param.go +++ /dev/null @@ -1,80 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// ModifiesParamRule lints given else constructs. -type ModifiesParamRule struct{} - -// Apply applies the rule to given file. -func (*ModifiesParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintModifiesParamRule{onFailure: onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*ModifiesParamRule) Name() string { - return "modifies-parameter" -} - -type lintModifiesParamRule struct { - params map[string]bool - onFailure func(lint.Failure) -} - -func retrieveParamNames(pl []*ast.Field) map[string]bool { - result := make(map[string]bool, len(pl)) - for _, p := range pl { - for _, n := range p.Names { - if n.Name == "_" { - continue - } - - result[n.Name] = true - } - } - return result -} - -func (w lintModifiesParamRule) Visit(node ast.Node) ast.Visitor { - switch v := node.(type) { - case *ast.FuncDecl: - w.params = retrieveParamNames(v.Type.Params.List) - case *ast.IncDecStmt: - if id, ok := v.X.(*ast.Ident); ok { - checkParam(id, &w) - } - case *ast.AssignStmt: - lhs := v.Lhs - for _, e := range lhs { - id, ok := e.(*ast.Ident) - if ok { - checkParam(id, &w) - } - } - } - - return w -} - -func checkParam(id *ast.Ident, w *lintModifiesParamRule) { - if w.params[id.Name] { - w.onFailure(lint.Failure{ - Confidence: 0.5, // confidence is low because of shadow variables - Node: id, - Category: "bad practice", - Failure: fmt.Sprintf("parameter '%s' seems to be modified", id), - }) - } -} diff --git a/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go b/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go deleted file mode 100644 index e9e64b9a6..000000000 --- a/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go +++ /dev/null @@ -1,129 +0,0 @@ -package rule - -import ( - "go/ast" - "strings" - - "github.com/mgechev/revive/lint" -) - -// ModifiesValRecRule lints assignments to value method-receivers. -type ModifiesValRecRule struct{} - -// Apply applies the rule to given file. -func (*ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintModifiesValRecRule{file: file, onFailure: onFailure} - file.Pkg.TypeCheck() - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*ModifiesValRecRule) Name() string { - return "modifies-value-receiver" -} - -type lintModifiesValRecRule struct { - file *lint.File - onFailure func(lint.Failure) -} - -func (w lintModifiesValRecRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - if n.Recv == nil { - return nil // skip, not a method - } - - receiver := n.Recv.List[0] - if _, ok := receiver.Type.(*ast.StarExpr); ok { - return nil // skip, method with pointer receiver - } - - if w.skipType(receiver.Type) { - return nil // skip, receiver is a map or array - } - - if len(receiver.Names) < 1 { - return nil // skip, anonymous receiver - } - - receiverName := receiver.Names[0].Name - if receiverName == "_" { - return nil // skip, anonymous receiver - } - - fselect := func(n ast.Node) bool { - // look for assignments with the receiver in the right hand - asgmt, ok := n.(*ast.AssignStmt) - if !ok { - return false - } - - for _, exp := range asgmt.Lhs { - switch e := exp.(type) { - case *ast.IndexExpr: // receiver...[] = ... - continue - case *ast.StarExpr: // *receiver = ... - continue - case *ast.SelectorExpr: // receiver.field = ... - name := w.getNameFromExpr(e.X) - if name == "" || name != receiverName { - continue - } - case *ast.Ident: // receiver := ... - if e.Name != receiverName { - continue - } - default: - continue - } - - return true - } - - return false - } - - assignmentsToReceiver := pick(n.Body, fselect) - - for _, assignment := range assignmentsToReceiver { - w.onFailure(lint.Failure{ - Node: assignment, - Confidence: 1, - Failure: "suspicious assignment to a by-value method receiver", - }) - } - } - - return w -} - -func (w lintModifiesValRecRule) skipType(t ast.Expr) bool { - rt := w.file.Pkg.TypeOf(t) - if rt == nil { - return false - } - - rt = rt.Underlying() - rtName := rt.String() - - // skip when receiver is a map or array - return strings.HasPrefix(rtName, "[]") || strings.HasPrefix(rtName, "map[") -} - -func (lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string { - ident, ok := ie.(*ast.Ident) - if !ok { - return "" - } - - return ident.Name -} diff --git a/vendor/github.com/mgechev/revive/rule/modifies_param.go b/vendor/github.com/mgechev/revive/rule/modifies_param.go new file mode 100644 index 000000000..a68ae2501 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/modifies_param.go @@ -0,0 +1,80 @@ +package rule + +import ( + "fmt" + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// ModifiesParamRule lints given else constructs. +type ModifiesParamRule struct{} + +// Apply applies the rule to given file. +func (*ModifiesParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintModifiesParamRule{onFailure: onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*ModifiesParamRule) Name() string { + return "modifies-parameter" +} + +type lintModifiesParamRule struct { + params map[string]bool + onFailure func(lint.Failure) +} + +func retrieveParamNames(pl []*ast.Field) map[string]bool { + result := make(map[string]bool, len(pl)) + for _, p := range pl { + for _, n := range p.Names { + if n.Name == "_" { + continue + } + + result[n.Name] = true + } + } + return result +} + +func (w lintModifiesParamRule) Visit(node ast.Node) ast.Visitor { + switch v := node.(type) { + case *ast.FuncDecl: + w.params = retrieveParamNames(v.Type.Params.List) + case *ast.IncDecStmt: + if id, ok := v.X.(*ast.Ident); ok { + checkParam(id, &w) + } + case *ast.AssignStmt: + lhs := v.Lhs + for _, e := range lhs { + id, ok := e.(*ast.Ident) + if ok { + checkParam(id, &w) + } + } + } + + return w +} + +func checkParam(id *ast.Ident, w *lintModifiesParamRule) { + if w.params[id.Name] { + w.onFailure(lint.Failure{ + Confidence: 0.5, // confidence is low because of shadow variables + Node: id, + Category: "bad practice", + Failure: fmt.Sprintf("parameter '%s' seems to be modified", id), + }) + } +} diff --git a/vendor/github.com/mgechev/revive/rule/modifies_value_receiver.go b/vendor/github.com/mgechev/revive/rule/modifies_value_receiver.go new file mode 100644 index 000000000..2f92991f5 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/modifies_value_receiver.go @@ -0,0 +1,179 @@ +package rule + +import ( + "go/ast" + "go/token" + "strings" + + "github.com/mgechev/revive/lint" +) + +// ModifiesValRecRule lints assignments to value method-receivers. +type ModifiesValRecRule struct{} + +// Apply applies the rule to given file. +func (*ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintModifiesValRecRule{file: file, onFailure: onFailure} + file.Pkg.TypeCheck() + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*ModifiesValRecRule) Name() string { + return "modifies-value-receiver" +} + +type lintModifiesValRecRule struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (w lintModifiesValRecRule) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + if n.Recv == nil { + return nil // skip, not a method + } + + receiver := n.Recv.List[0] + if _, ok := receiver.Type.(*ast.StarExpr); ok { + return nil // skip, method with pointer receiver + } + + if w.skipType(receiver.Type) { + return nil // skip, receiver is a map or array + } + + if len(receiver.Names) < 1 { + return nil // skip, anonymous receiver + } + + receiverName := receiver.Names[0].Name + if receiverName == "_" { + return nil // skip, anonymous receiver + } + + receiverAssignmentFinder := func(n ast.Node) bool { + // look for assignments with the receiver in the right hand + assignment, ok := n.(*ast.AssignStmt) + if !ok { + return false + } + + for _, exp := range assignment.Lhs { + switch e := exp.(type) { + case *ast.IndexExpr: // receiver...[] = ... + continue + case *ast.StarExpr: // *receiver = ... + continue + case *ast.SelectorExpr: // receiver.field = ... + name := w.getNameFromExpr(e.X) + if name == "" || name != receiverName { + continue + } + case *ast.Ident: // receiver := ... + if e.Name != receiverName { + continue + } + default: + continue + } + + return true + } + + return false + } + + assignmentsToReceiver := pick(n.Body, receiverAssignmentFinder) + if len(assignmentsToReceiver) == 0 { + return nil // receiver is not modified + } + + methodReturnsReceiver := len(w.findReturnReceiverStatements(receiverName, n.Body)) > 0 + if methodReturnsReceiver { + return nil // modification seems legit (see issue #1066) + } + + for _, assignment := range assignmentsToReceiver { + w.onFailure(lint.Failure{ + Node: assignment, + Confidence: 1, + Failure: "suspicious assignment to a by-value method receiver", + }) + } + } + + return w +} + +func (w lintModifiesValRecRule) skipType(t ast.Expr) bool { + rt := w.file.Pkg.TypeOf(t) + if rt == nil { + return false + } + + rt = rt.Underlying() + rtName := rt.String() + + // skip when receiver is a map or array + return strings.HasPrefix(rtName, "[]") || strings.HasPrefix(rtName, "map[") +} + +func (lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string { + ident, ok := ie.(*ast.Ident) + if !ok { + return "" + } + + return ident.Name +} + +func (w lintModifiesValRecRule) findReturnReceiverStatements(receiverName string, target ast.Node) []ast.Node { + finder := func(n ast.Node) bool { + // look for returns with the receiver as value + returnStatement, ok := n.(*ast.ReturnStmt) + if !ok { + return false + } + + for _, exp := range returnStatement.Results { + switch e := exp.(type) { + case *ast.SelectorExpr: // receiver.field = ... + name := w.getNameFromExpr(e.X) + if name == "" || name != receiverName { + continue + } + case *ast.Ident: // receiver := ... + if e.Name != receiverName { + continue + } + case *ast.UnaryExpr: + if e.Op != token.AND { + continue + } + name := w.getNameFromExpr(e.X) + if name == "" || name != receiverName { + continue + } + + default: + continue + } + + return true + } + + return false + } + + return pick(target, finder) +} diff --git a/vendor/github.com/mgechev/revive/rule/nested-structs.go b/vendor/github.com/mgechev/revive/rule/nested-structs.go deleted file mode 100644 index 147bd482b..000000000 --- a/vendor/github.com/mgechev/revive/rule/nested-structs.go +++ /dev/null @@ -1,75 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// NestedStructs lints nested structs. -type NestedStructs struct{} - -// Apply applies the rule to given file. -func (*NestedStructs) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - walker := &lintNestedStructs{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*NestedStructs) Name() string { - return "nested-structs" -} - -type lintNestedStructs struct { - onFailure func(lint.Failure) -} - -func (l *lintNestedStructs) Visit(n ast.Node) ast.Visitor { - if v, ok := n.(*ast.StructType); ok { - ls := &lintStruct{l.onFailure} - ast.Walk(ls, v.Fields) - } - - return l -} - -type lintStruct struct { - onFailure func(lint.Failure) -} - -func (l *lintStruct) Visit(n ast.Node) ast.Visitor { - switch s := n.(type) { - case *ast.StructType: - l.fail(s) - return nil - case *ast.ArrayType: - if _, ok := s.Elt.(*ast.StructType); ok { - l.fail(s) - } - return nil - case *ast.ChanType: - return nil - case *ast.MapType: - return nil - default: - return l - } -} - -func (l *lintStruct) fail(n ast.Node) { - l.onFailure(lint.Failure{ - Failure: "no nested structs are allowed", - Category: "style", - Node: n, - Confidence: 1, - }) -} diff --git a/vendor/github.com/mgechev/revive/rule/nested_structs.go b/vendor/github.com/mgechev/revive/rule/nested_structs.go new file mode 100644 index 000000000..147bd482b --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/nested_structs.go @@ -0,0 +1,75 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// NestedStructs lints nested structs. +type NestedStructs struct{} + +// Apply applies the rule to given file. +func (*NestedStructs) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + walker := &lintNestedStructs{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*NestedStructs) Name() string { + return "nested-structs" +} + +type lintNestedStructs struct { + onFailure func(lint.Failure) +} + +func (l *lintNestedStructs) Visit(n ast.Node) ast.Visitor { + if v, ok := n.(*ast.StructType); ok { + ls := &lintStruct{l.onFailure} + ast.Walk(ls, v.Fields) + } + + return l +} + +type lintStruct struct { + onFailure func(lint.Failure) +} + +func (l *lintStruct) Visit(n ast.Node) ast.Visitor { + switch s := n.(type) { + case *ast.StructType: + l.fail(s) + return nil + case *ast.ArrayType: + if _, ok := s.Elt.(*ast.StructType); ok { + l.fail(s) + } + return nil + case *ast.ChanType: + return nil + case *ast.MapType: + return nil + default: + return l + } +} + +func (l *lintStruct) fail(n ast.Node) { + l.onFailure(lint.Failure{ + Failure: "no nested structs are allowed", + Category: "style", + Node: n, + Confidence: 1, + }) +} diff --git a/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go b/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go deleted file mode 100644 index 43d982d6b..000000000 --- a/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go +++ /dev/null @@ -1,86 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// OptimizeOperandsOrderRule lints given else constructs. -type OptimizeOperandsOrderRule struct{} - -// Apply applies the rule to given file. -func (*OptimizeOperandsOrderRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - w := lintOptimizeOperandsOrderlExpr{ - onFailure: onFailure, - } - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*OptimizeOperandsOrderRule) Name() string { - return "optimize-operands-order" -} - -type lintOptimizeOperandsOrderlExpr struct { - onFailure func(failure lint.Failure) -} - -// Visit checks boolean AND and OR expressions to determine -// if swapping their operands may result in an execution speedup. -func (w lintOptimizeOperandsOrderlExpr) Visit(node ast.Node) ast.Visitor { - binExpr, ok := node.(*ast.BinaryExpr) - if !ok { - return w - } - - switch binExpr.Op { - case token.LAND, token.LOR: - default: - return w - } - - isCaller := func(n ast.Node) bool { - ce, ok := n.(*ast.CallExpr) - if !ok { - return false - } - - ident, isIdent := ce.Fun.(*ast.Ident) - if !isIdent { - return true - } - - return ident.Name != "len" || ident.Obj != nil - } - - // check if the left sub-expression contains a function call - nodes := pick(binExpr.X, isCaller) - if len(nodes) < 1 { - return w - } - - // check if the right sub-expression does not contain a function call - nodes = pick(binExpr.Y, isCaller) - if len(nodes) > 0 { - return w - } - - newExpr := ast.BinaryExpr{X: binExpr.Y, Y: binExpr.X, Op: binExpr.Op} - w.onFailure(lint.Failure{ - Failure: fmt.Sprintf("for better performance '%v' might be rewritten as '%v'", gofmt(binExpr), gofmt(&newExpr)), - Node: node, - Category: "optimization", - Confidence: 0.3, - }) - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/optimize_operands_order.go b/vendor/github.com/mgechev/revive/rule/optimize_operands_order.go new file mode 100644 index 000000000..43d982d6b --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/optimize_operands_order.go @@ -0,0 +1,86 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// OptimizeOperandsOrderRule lints given else constructs. +type OptimizeOperandsOrderRule struct{} + +// Apply applies the rule to given file. +func (*OptimizeOperandsOrderRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + w := lintOptimizeOperandsOrderlExpr{ + onFailure: onFailure, + } + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*OptimizeOperandsOrderRule) Name() string { + return "optimize-operands-order" +} + +type lintOptimizeOperandsOrderlExpr struct { + onFailure func(failure lint.Failure) +} + +// Visit checks boolean AND and OR expressions to determine +// if swapping their operands may result in an execution speedup. +func (w lintOptimizeOperandsOrderlExpr) Visit(node ast.Node) ast.Visitor { + binExpr, ok := node.(*ast.BinaryExpr) + if !ok { + return w + } + + switch binExpr.Op { + case token.LAND, token.LOR: + default: + return w + } + + isCaller := func(n ast.Node) bool { + ce, ok := n.(*ast.CallExpr) + if !ok { + return false + } + + ident, isIdent := ce.Fun.(*ast.Ident) + if !isIdent { + return true + } + + return ident.Name != "len" || ident.Obj != nil + } + + // check if the left sub-expression contains a function call + nodes := pick(binExpr.X, isCaller) + if len(nodes) < 1 { + return w + } + + // check if the right sub-expression does not contain a function call + nodes = pick(binExpr.Y, isCaller) + if len(nodes) > 0 { + return w + } + + newExpr := ast.BinaryExpr{X: binExpr.Y, Y: binExpr.X, Op: binExpr.Op} + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("for better performance '%v' might be rewritten as '%v'", gofmt(binExpr), gofmt(&newExpr)), + Node: node, + Category: "optimization", + Confidence: 0.3, + }) + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/package-comments.go b/vendor/github.com/mgechev/revive/rule/package-comments.go deleted file mode 100644 index f1e5462fe..000000000 --- a/vendor/github.com/mgechev/revive/rule/package-comments.go +++ /dev/null @@ -1,164 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// PackageCommentsRule lints the package comments. It complains if -// there is no package comment, or if it is not of the right form. -// This has a notable false positive in that a package comment -// could rightfully appear in a different file of the same package, -// but that's not easy to fix since this linter is file-oriented. -type PackageCommentsRule struct { - checkPackageCommentCache sync.Map -} - -// Apply applies the rule to given file. -func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - if file.IsTest() { - return failures - } - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - fileAst := file.AST - w := &lintPackageComments{fileAst, file, onFailure, r} - ast.Walk(w, fileAst) - return failures -} - -// Name returns the rule name. -func (*PackageCommentsRule) Name() string { - return "package-comments" -} - -type lintPackageComments struct { - fileAst *ast.File - file *lint.File - onFailure func(lint.Failure) - rule *PackageCommentsRule -} - -func (l *lintPackageComments) checkPackageComment() []lint.Failure { - // deduplicate warnings in package - if _, exists := l.rule.checkPackageCommentCache.LoadOrStore(l.file.Pkg, struct{}{}); exists { - return nil - } - var docFile *ast.File // which name is doc.go - var packageFile *ast.File // which name is $package.go - var firstFile *ast.File - var firstFileName string - var fileSource string - for name, file := range l.file.Pkg.Files() { - if file.AST.Doc != nil { - return nil - } - if name == "doc.go" { - docFile = file.AST - fileSource = "doc.go" - } - if name == file.AST.Name.String()+".go" { - packageFile = file.AST - } - if firstFileName == "" || firstFileName > name { - firstFile = file.AST - firstFileName = name - } - } - // prefer warning on doc.go, $package.go over first file - if docFile == nil { - docFile = packageFile - fileSource = l.fileAst.Name.String() + ".go" - } - if docFile == nil { - docFile = firstFile - fileSource = firstFileName - } - - if docFile != nil { - pkgFile := l.file.Pkg.Files()[fileSource] - return []lint.Failure{{ - Category: "comments", - Position: lint.FailurePosition{ - Start: pkgFile.ToPosition(docFile.Pos()), - End: pkgFile.ToPosition(docFile.Name.End()), - }, - Confidence: 1, - Failure: "should have a package comment", - }} - } - return nil -} - -func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { - if l.file.IsTest() { - return nil - } - - prefix := "Package " + l.fileAst.Name.Name + " " - - // Look for a detached package comment. - // First, scan for the last comment that occurs before the "package" keyword. - var lastCG *ast.CommentGroup - for _, cg := range l.fileAst.Comments { - if cg.Pos() > l.fileAst.Package { - // Gone past "package" keyword. - break - } - lastCG = cg - } - if lastCG != nil && strings.HasPrefix(lastCG.Text(), prefix) { - endPos := l.file.ToPosition(lastCG.End()) - pkgPos := l.file.ToPosition(l.fileAst.Package) - if endPos.Line+1 < pkgPos.Line { - // There isn't a great place to anchor this error; - // the start of the blank lines between the doc and the package statement - // is at least pointing at the location of the problem. - pos := token.Position{ - Filename: endPos.Filename, - // Offset not set; it is non-trivial, and doesn't appear to be needed. - Line: endPos.Line + 1, - Column: 1, - } - l.onFailure(lint.Failure{ - Category: "comments", - Position: lint.FailurePosition{ - Start: pos, - End: pos, - }, - Confidence: 0.9, - Failure: "package comment is detached; there should be no blank lines between it and the package statement", - }) - return nil - } - } - - if l.fileAst.Doc == nil { - for _, failure := range l.checkPackageComment() { - l.onFailure(failure) - } - return nil - } - s := l.fileAst.Doc.Text() - - // Only non-main packages need to keep to this form. - if !l.file.Pkg.IsMain() && !strings.HasPrefix(s, prefix) && !isDirectiveComment(s) { - l.onFailure(lint.Failure{ - Category: "comments", - Node: l.fileAst.Doc, - Confidence: 1, - Failure: fmt.Sprintf(`package comment should be of the form "%s..."`, prefix), - }) - } - return nil -} diff --git a/vendor/github.com/mgechev/revive/rule/package_comments.go b/vendor/github.com/mgechev/revive/rule/package_comments.go new file mode 100644 index 000000000..f1e5462fe --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/package_comments.go @@ -0,0 +1,164 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// PackageCommentsRule lints the package comments. It complains if +// there is no package comment, or if it is not of the right form. +// This has a notable false positive in that a package comment +// could rightfully appear in a different file of the same package, +// but that's not easy to fix since this linter is file-oriented. +type PackageCommentsRule struct { + checkPackageCommentCache sync.Map +} + +// Apply applies the rule to given file. +func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + if file.IsTest() { + return failures + } + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + fileAst := file.AST + w := &lintPackageComments{fileAst, file, onFailure, r} + ast.Walk(w, fileAst) + return failures +} + +// Name returns the rule name. +func (*PackageCommentsRule) Name() string { + return "package-comments" +} + +type lintPackageComments struct { + fileAst *ast.File + file *lint.File + onFailure func(lint.Failure) + rule *PackageCommentsRule +} + +func (l *lintPackageComments) checkPackageComment() []lint.Failure { + // deduplicate warnings in package + if _, exists := l.rule.checkPackageCommentCache.LoadOrStore(l.file.Pkg, struct{}{}); exists { + return nil + } + var docFile *ast.File // which name is doc.go + var packageFile *ast.File // which name is $package.go + var firstFile *ast.File + var firstFileName string + var fileSource string + for name, file := range l.file.Pkg.Files() { + if file.AST.Doc != nil { + return nil + } + if name == "doc.go" { + docFile = file.AST + fileSource = "doc.go" + } + if name == file.AST.Name.String()+".go" { + packageFile = file.AST + } + if firstFileName == "" || firstFileName > name { + firstFile = file.AST + firstFileName = name + } + } + // prefer warning on doc.go, $package.go over first file + if docFile == nil { + docFile = packageFile + fileSource = l.fileAst.Name.String() + ".go" + } + if docFile == nil { + docFile = firstFile + fileSource = firstFileName + } + + if docFile != nil { + pkgFile := l.file.Pkg.Files()[fileSource] + return []lint.Failure{{ + Category: "comments", + Position: lint.FailurePosition{ + Start: pkgFile.ToPosition(docFile.Pos()), + End: pkgFile.ToPosition(docFile.Name.End()), + }, + Confidence: 1, + Failure: "should have a package comment", + }} + } + return nil +} + +func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { + if l.file.IsTest() { + return nil + } + + prefix := "Package " + l.fileAst.Name.Name + " " + + // Look for a detached package comment. + // First, scan for the last comment that occurs before the "package" keyword. + var lastCG *ast.CommentGroup + for _, cg := range l.fileAst.Comments { + if cg.Pos() > l.fileAst.Package { + // Gone past "package" keyword. + break + } + lastCG = cg + } + if lastCG != nil && strings.HasPrefix(lastCG.Text(), prefix) { + endPos := l.file.ToPosition(lastCG.End()) + pkgPos := l.file.ToPosition(l.fileAst.Package) + if endPos.Line+1 < pkgPos.Line { + // There isn't a great place to anchor this error; + // the start of the blank lines between the doc and the package statement + // is at least pointing at the location of the problem. + pos := token.Position{ + Filename: endPos.Filename, + // Offset not set; it is non-trivial, and doesn't appear to be needed. + Line: endPos.Line + 1, + Column: 1, + } + l.onFailure(lint.Failure{ + Category: "comments", + Position: lint.FailurePosition{ + Start: pos, + End: pos, + }, + Confidence: 0.9, + Failure: "package comment is detached; there should be no blank lines between it and the package statement", + }) + return nil + } + } + + if l.fileAst.Doc == nil { + for _, failure := range l.checkPackageComment() { + l.onFailure(failure) + } + return nil + } + s := l.fileAst.Doc.Text() + + // Only non-main packages need to keep to this form. + if !l.file.Pkg.IsMain() && !strings.HasPrefix(s, prefix) && !isDirectiveComment(s) { + l.onFailure(lint.Failure{ + Category: "comments", + Node: l.fileAst.Doc, + Confidence: 1, + Failure: fmt.Sprintf(`package comment should be of the form "%s..."`, prefix), + }) + } + return nil +} diff --git a/vendor/github.com/mgechev/revive/rule/range-val-address.go b/vendor/github.com/mgechev/revive/rule/range-val-address.go deleted file mode 100644 index d2ab0392a..000000000 --- a/vendor/github.com/mgechev/revive/rule/range-val-address.go +++ /dev/null @@ -1,166 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "strings" - - "github.com/mgechev/revive/lint" -) - -// RangeValAddress lints -type RangeValAddress struct{} - -// Apply applies the rule to given file. -func (*RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - if file.Pkg.IsAtLeastGo122() { - return failures - } - - walker := rangeValAddress{ - file: file, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - file.Pkg.TypeCheck() - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*RangeValAddress) Name() string { - return "range-val-address" -} - -type rangeValAddress struct { - file *lint.File - onFailure func(lint.Failure) -} - -func (w rangeValAddress) Visit(node ast.Node) ast.Visitor { - n, ok := node.(*ast.RangeStmt) - if !ok { - return w - } - - value, ok := n.Value.(*ast.Ident) - if !ok { - return w - } - - valueIsStarExpr := false - if t := w.file.Pkg.TypeOf(value); t != nil { - valueIsStarExpr = strings.HasPrefix(t.String(), "*") - } - - ast.Walk(rangeBodyVisitor{ - valueIsStarExpr: valueIsStarExpr, - valueID: value.Obj, - onFailure: w.onFailure, - }, n.Body) - - return w -} - -type rangeBodyVisitor struct { - valueIsStarExpr bool - valueID *ast.Object - onFailure func(lint.Failure) -} - -func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor { - asgmt, ok := node.(*ast.AssignStmt) - if !ok { - return bw - } - - for _, exp := range asgmt.Lhs { - e, ok := exp.(*ast.IndexExpr) - if !ok { - continue - } - if bw.isAccessingRangeValueAddress(e.Index) { // e.g. a[&value]... - bw.onFailure(bw.newFailure(e.Index)) - } - } - - for _, exp := range asgmt.Rhs { - switch e := exp.(type) { - case *ast.UnaryExpr: // e.g. ...&value, ...&value.id - if bw.isAccessingRangeValueAddress(e) { - bw.onFailure(bw.newFailure(e)) - } - case *ast.CallExpr: - if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value) - for _, v := range e.Args { - if lit, ok := v.(*ast.CompositeLit); ok { // e.g. ...append(arr, v{id:&value}) - bw.checkCompositeLit(lit) - continue - } - if bw.isAccessingRangeValueAddress(v) { // e.g. ...append(arr, &value) - bw.onFailure(bw.newFailure(v)) - } - } - } - case *ast.CompositeLit: // e.g. ...v{id:&value} - bw.checkCompositeLit(e) - } - } - return bw -} - -func (bw rangeBodyVisitor) checkCompositeLit(comp *ast.CompositeLit) { - for _, exp := range comp.Elts { - e, ok := exp.(*ast.KeyValueExpr) - if !ok { - continue - } - if bw.isAccessingRangeValueAddress(e.Value) { - bw.onFailure(bw.newFailure(e.Value)) - } - } -} - -func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool { - u, ok := exp.(*ast.UnaryExpr) - if !ok { - return false - } - - if u.Op != token.AND { - return false - } - - v, ok := u.X.(*ast.Ident) - if !ok { - var s *ast.SelectorExpr - s, ok = u.X.(*ast.SelectorExpr) - if !ok { - return false - } - v, ok = s.X.(*ast.Ident) - if !ok { - return false - } - - if bw.valueIsStarExpr { // check type of value - return false - } - } - - return ok && v.Obj == bw.valueID -} - -func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure { - return lint.Failure{ - Node: node, - Confidence: 1, - Failure: fmt.Sprintf("suspicious assignment of '%s'. range-loop variables always have the same address", bw.valueID.Name), - } -} diff --git a/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go b/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go deleted file mode 100644 index 6f9255a74..000000000 --- a/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go +++ /dev/null @@ -1,125 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// RangeValInClosureRule lints given else constructs. -type RangeValInClosureRule struct{} - -// Apply applies the rule to given file. -func (*RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - if file.Pkg.IsAtLeastGo122() { - return failures - } - - walker := rangeValInClosure{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*RangeValInClosureRule) Name() string { - return "range-val-in-closure" -} - -type rangeValInClosure struct { - onFailure func(lint.Failure) -} - -func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor { - // Find the variables updated by the loop statement. - var vars []*ast.Ident - addVar := func(expr ast.Expr) { - if id, ok := expr.(*ast.Ident); ok { - vars = append(vars, id) - } - } - var body *ast.BlockStmt - switch n := node.(type) { - case *ast.RangeStmt: - body = n.Body - addVar(n.Key) - addVar(n.Value) - case *ast.ForStmt: - body = n.Body - switch post := n.Post.(type) { - case *ast.AssignStmt: - // e.g. for p = head; p != nil; p = p.next - for _, lhs := range post.Lhs { - addVar(lhs) - } - case *ast.IncDecStmt: - // e.g. for i := 0; i < n; i++ - addVar(post.X) - } - } - if vars == nil { - return w - } - - // Inspect a go or defer statement - // if it's the last one in the loop body. - // (We give up if there are following statements, - // because it's hard to prove go isn't followed by wait, - // or defer by return.) - if len(body.List) == 0 { - return w - } - var last *ast.CallExpr - switch s := body.List[len(body.List)-1].(type) { - case *ast.GoStmt: - last = s.Call - case *ast.DeferStmt: - last = s.Call - default: - return w - } - lit, ok := last.Fun.(*ast.FuncLit) - if !ok { - return w - } - - if lit.Type == nil { - // Not referring to a variable (e.g. struct field name) - return w - } - - var inspector func(n ast.Node) bool - inspector = func(n ast.Node) bool { - kv, ok := n.(*ast.KeyValueExpr) - if ok { - // do not check identifiers acting as key in key-value expressions (see issue #637) - ast.Inspect(kv.Value, inspector) - return false - } - id, ok := n.(*ast.Ident) - if !ok || id.Obj == nil { - return true - } - - for _, v := range vars { - if v.Obj == id.Obj { - w.onFailure(lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("loop variable %v captured by func literal", id.Name), - Node: n, - }) - } - } - return true - } - ast.Inspect(lit.Body, inspector) - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/range_val_address.go b/vendor/github.com/mgechev/revive/rule/range_val_address.go new file mode 100644 index 000000000..d2ab0392a --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/range_val_address.go @@ -0,0 +1,166 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "github.com/mgechev/revive/lint" +) + +// RangeValAddress lints +type RangeValAddress struct{} + +// Apply applies the rule to given file. +func (*RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + if file.Pkg.IsAtLeastGo122() { + return failures + } + + walker := rangeValAddress{ + file: file, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + file.Pkg.TypeCheck() + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*RangeValAddress) Name() string { + return "range-val-address" +} + +type rangeValAddress struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (w rangeValAddress) Visit(node ast.Node) ast.Visitor { + n, ok := node.(*ast.RangeStmt) + if !ok { + return w + } + + value, ok := n.Value.(*ast.Ident) + if !ok { + return w + } + + valueIsStarExpr := false + if t := w.file.Pkg.TypeOf(value); t != nil { + valueIsStarExpr = strings.HasPrefix(t.String(), "*") + } + + ast.Walk(rangeBodyVisitor{ + valueIsStarExpr: valueIsStarExpr, + valueID: value.Obj, + onFailure: w.onFailure, + }, n.Body) + + return w +} + +type rangeBodyVisitor struct { + valueIsStarExpr bool + valueID *ast.Object + onFailure func(lint.Failure) +} + +func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor { + asgmt, ok := node.(*ast.AssignStmt) + if !ok { + return bw + } + + for _, exp := range asgmt.Lhs { + e, ok := exp.(*ast.IndexExpr) + if !ok { + continue + } + if bw.isAccessingRangeValueAddress(e.Index) { // e.g. a[&value]... + bw.onFailure(bw.newFailure(e.Index)) + } + } + + for _, exp := range asgmt.Rhs { + switch e := exp.(type) { + case *ast.UnaryExpr: // e.g. ...&value, ...&value.id + if bw.isAccessingRangeValueAddress(e) { + bw.onFailure(bw.newFailure(e)) + } + case *ast.CallExpr: + if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value) + for _, v := range e.Args { + if lit, ok := v.(*ast.CompositeLit); ok { // e.g. ...append(arr, v{id:&value}) + bw.checkCompositeLit(lit) + continue + } + if bw.isAccessingRangeValueAddress(v) { // e.g. ...append(arr, &value) + bw.onFailure(bw.newFailure(v)) + } + } + } + case *ast.CompositeLit: // e.g. ...v{id:&value} + bw.checkCompositeLit(e) + } + } + return bw +} + +func (bw rangeBodyVisitor) checkCompositeLit(comp *ast.CompositeLit) { + for _, exp := range comp.Elts { + e, ok := exp.(*ast.KeyValueExpr) + if !ok { + continue + } + if bw.isAccessingRangeValueAddress(e.Value) { + bw.onFailure(bw.newFailure(e.Value)) + } + } +} + +func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool { + u, ok := exp.(*ast.UnaryExpr) + if !ok { + return false + } + + if u.Op != token.AND { + return false + } + + v, ok := u.X.(*ast.Ident) + if !ok { + var s *ast.SelectorExpr + s, ok = u.X.(*ast.SelectorExpr) + if !ok { + return false + } + v, ok = s.X.(*ast.Ident) + if !ok { + return false + } + + if bw.valueIsStarExpr { // check type of value + return false + } + } + + return ok && v.Obj == bw.valueID +} + +func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure { + return lint.Failure{ + Node: node, + Confidence: 1, + Failure: fmt.Sprintf("suspicious assignment of '%s'. range-loop variables always have the same address", bw.valueID.Name), + } +} diff --git a/vendor/github.com/mgechev/revive/rule/range_val_in_closure.go b/vendor/github.com/mgechev/revive/rule/range_val_in_closure.go new file mode 100644 index 000000000..6f9255a74 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/range_val_in_closure.go @@ -0,0 +1,125 @@ +package rule + +import ( + "fmt" + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// RangeValInClosureRule lints given else constructs. +type RangeValInClosureRule struct{} + +// Apply applies the rule to given file. +func (*RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + if file.Pkg.IsAtLeastGo122() { + return failures + } + + walker := rangeValInClosure{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*RangeValInClosureRule) Name() string { + return "range-val-in-closure" +} + +type rangeValInClosure struct { + onFailure func(lint.Failure) +} + +func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor { + // Find the variables updated by the loop statement. + var vars []*ast.Ident + addVar := func(expr ast.Expr) { + if id, ok := expr.(*ast.Ident); ok { + vars = append(vars, id) + } + } + var body *ast.BlockStmt + switch n := node.(type) { + case *ast.RangeStmt: + body = n.Body + addVar(n.Key) + addVar(n.Value) + case *ast.ForStmt: + body = n.Body + switch post := n.Post.(type) { + case *ast.AssignStmt: + // e.g. for p = head; p != nil; p = p.next + for _, lhs := range post.Lhs { + addVar(lhs) + } + case *ast.IncDecStmt: + // e.g. for i := 0; i < n; i++ + addVar(post.X) + } + } + if vars == nil { + return w + } + + // Inspect a go or defer statement + // if it's the last one in the loop body. + // (We give up if there are following statements, + // because it's hard to prove go isn't followed by wait, + // or defer by return.) + if len(body.List) == 0 { + return w + } + var last *ast.CallExpr + switch s := body.List[len(body.List)-1].(type) { + case *ast.GoStmt: + last = s.Call + case *ast.DeferStmt: + last = s.Call + default: + return w + } + lit, ok := last.Fun.(*ast.FuncLit) + if !ok { + return w + } + + if lit.Type == nil { + // Not referring to a variable (e.g. struct field name) + return w + } + + var inspector func(n ast.Node) bool + inspector = func(n ast.Node) bool { + kv, ok := n.(*ast.KeyValueExpr) + if ok { + // do not check identifiers acting as key in key-value expressions (see issue #637) + ast.Inspect(kv.Value, inspector) + return false + } + id, ok := n.(*ast.Ident) + if !ok || id.Obj == nil { + return true + } + + for _, v := range vars { + if v.Obj == id.Obj { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("loop variable %v captured by func literal", id.Name), + Node: n, + }) + } + } + return true + } + ast.Inspect(lit.Body, inspector) + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/receiver-naming.go b/vendor/github.com/mgechev/revive/rule/receiver-naming.go deleted file mode 100644 index afcd99b8f..000000000 --- a/vendor/github.com/mgechev/revive/rule/receiver-naming.go +++ /dev/null @@ -1,133 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/internal/typeparams" - "github.com/mgechev/revive/lint" -) - -// ReceiverNamingRule lints given else constructs. -type ReceiverNamingRule struct { - receiverNameMaxLength int - sync.Mutex -} - -const defaultReceiverNameMaxLength = -1 // thus will not check - -func (r *ReceiverNamingRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.receiverNameMaxLength != 0 { - return - } - - r.receiverNameMaxLength = defaultReceiverNameMaxLength - if len(arguments) < 1 { - return - } - - args, ok := arguments[0].(map[string]any) - if !ok { - panic(fmt.Sprintf("Unable to get arguments for rule %s. Expected object of key-value-pairs.", r.Name())) - } - - for k, v := range args { - switch k { - case "maxLength": - value, ok := v.(int64) - if !ok { - panic(fmt.Sprintf("Invalid value %v for argument %s of rule %s, expected integer value got %T", v, k, r.Name(), v)) - } - r.receiverNameMaxLength = int(value) - default: - panic(fmt.Sprintf("Unknown argument %s for %s rule.", k, r.Name())) - } - } -} - -// Apply applies the rule to given file. -func (r *ReceiverNamingRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) - - var failures []lint.Failure - - fileAst := file.AST - walker := lintReceiverName{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - typeReceiver: map[string]string{}, - receiverNameMaxLength: r.receiverNameMaxLength, - } - - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*ReceiverNamingRule) Name() string { - return "receiver-naming" -} - -type lintReceiverName struct { - onFailure func(lint.Failure) - typeReceiver map[string]string - receiverNameMaxLength int -} - -func (w lintReceiverName) Visit(n ast.Node) ast.Visitor { - fn, ok := n.(*ast.FuncDecl) - if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 { - return w - } - names := fn.Recv.List[0].Names - if len(names) < 1 { - return w - } - name := names[0].Name - if name == "_" { - w.onFailure(lint.Failure{ - Node: n, - Confidence: 1, - Category: "naming", - Failure: "receiver name should not be an underscore, omit the name if it is unused", - }) - return w - } - if name == "this" || name == "self" { - w.onFailure(lint.Failure{ - Node: n, - Confidence: 1, - Category: "naming", - Failure: `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`, - }) - return w - } - - if w.receiverNameMaxLength > 0 && len([]rune(name)) > w.receiverNameMaxLength { - w.onFailure(lint.Failure{ - Node: n, - Confidence: 1, - Category: "naming", - Failure: fmt.Sprintf("receiver name %s is longer than %d characters", name, w.receiverNameMaxLength), - }) - return w - } - - recv := typeparams.ReceiverType(fn) - if prev, ok := w.typeReceiver[recv]; ok && prev != name { - w.onFailure(lint.Failure{ - Node: n, - Confidence: 1, - Category: "naming", - Failure: fmt.Sprintf("receiver name %s should be consistent with previous receiver name %s for %s", name, prev, recv), - }) - return w - } - w.typeReceiver[recv] = name - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/receiver_naming.go b/vendor/github.com/mgechev/revive/rule/receiver_naming.go new file mode 100644 index 000000000..c83bacc2f --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/receiver_naming.go @@ -0,0 +1,128 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/internal/typeparams" + "github.com/mgechev/revive/lint" +) + +// ReceiverNamingRule lints given else constructs. +type ReceiverNamingRule struct { + receiverNameMaxLength int + + configureOnce sync.Once +} + +const defaultReceiverNameMaxLength = -1 // thus will not check + +func (r *ReceiverNamingRule) configure(arguments lint.Arguments) { + r.receiverNameMaxLength = defaultReceiverNameMaxLength + if len(arguments) < 1 { + return + } + + args, ok := arguments[0].(map[string]any) + if !ok { + panic(fmt.Sprintf("Unable to get arguments for rule %s. Expected object of key-value-pairs.", r.Name())) + } + + for k, v := range args { + switch k { + case "maxLength": + value, ok := v.(int64) + if !ok { + panic(fmt.Sprintf("Invalid value %v for argument %s of rule %s, expected integer value got %T", v, k, r.Name(), v)) + } + r.receiverNameMaxLength = int(value) + default: + panic(fmt.Sprintf("Unknown argument %s for %s rule.", k, r.Name())) + } + } +} + +// Apply applies the rule to given file. +func (r *ReceiverNamingRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + + var failures []lint.Failure + + fileAst := file.AST + walker := lintReceiverName{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + typeReceiver: map[string]string{}, + receiverNameMaxLength: r.receiverNameMaxLength, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*ReceiverNamingRule) Name() string { + return "receiver-naming" +} + +type lintReceiverName struct { + onFailure func(lint.Failure) + typeReceiver map[string]string + receiverNameMaxLength int +} + +func (w lintReceiverName) Visit(n ast.Node) ast.Visitor { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Recv == nil || len(fn.Recv.List) == 0 { + return w + } + names := fn.Recv.List[0].Names + if len(names) < 1 { + return w + } + name := names[0].Name + if name == "_" { + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: "receiver name should not be an underscore, omit the name if it is unused", + }) + return w + } + if name == "this" || name == "self" { + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`, + }) + return w + } + + if w.receiverNameMaxLength > 0 && len([]rune(name)) > w.receiverNameMaxLength { + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: fmt.Sprintf("receiver name %s is longer than %d characters", name, w.receiverNameMaxLength), + }) + return w + } + + recv := typeparams.ReceiverType(fn) + if prev, ok := w.typeReceiver[recv]; ok && prev != name { + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: fmt.Sprintf("receiver name %s should be consistent with previous receiver name %s for %s", name, prev, recv), + }) + return w + } + w.typeReceiver[recv] = name + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go b/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go deleted file mode 100644 index 10ea16ae1..000000000 --- a/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go +++ /dev/null @@ -1,224 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "maps" - - "github.com/mgechev/revive/lint" -) - -var builtInConstAndVars = map[string]bool{ - "true": true, - "false": true, - "iota": true, - "nil": true, -} - -var builtFunctions = map[string]bool{ - "append": true, - "cap": true, - "close": true, - "complex": true, - "copy": true, - "delete": true, - "imag": true, - "len": true, - "make": true, - "new": true, - "panic": true, - "print": true, - "println": true, - "real": true, - "recover": true, -} - -var builtFunctionsAfterGo121 = map[string]bool{ - "clear": true, - "max": true, - "min": true, -} - -var builtInTypes = map[string]bool{ - "bool": true, - "byte": true, - "complex128": true, - "complex64": true, - "error": true, - "float32": true, - "float64": true, - "int": true, - "int16": true, - "int32": true, - "int64": true, - "int8": true, - "rune": true, - "string": true, - "uint": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uint8": true, - "uintptr": true, - "any": true, -} - -// RedefinesBuiltinIDRule warns when a builtin identifier is shadowed. -type RedefinesBuiltinIDRule struct{} - -// Apply applies the rule to given file. -func (*RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - - builtFuncs := maps.Clone(builtFunctions) - if file.Pkg.IsAtLeastGo121() { - maps.Copy(builtFuncs, builtFunctionsAfterGo121) - } - w := &lintRedefinesBuiltinID{ - onFailure: onFailure, - builtInConstAndVars: builtInConstAndVars, - builtFunctions: builtFuncs, - builtInTypes: builtInTypes, - } - ast.Walk(w, astFile) - - return failures -} - -// Name returns the rule name. -func (*RedefinesBuiltinIDRule) Name() string { - return "redefines-builtin-id" -} - -type lintRedefinesBuiltinID struct { - onFailure func(lint.Failure) - builtInConstAndVars map[string]bool - builtFunctions map[string]bool - builtInTypes map[string]bool -} - -func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.GenDecl: - switch n.Tok { - case token.TYPE: - if len(n.Specs) < 1 { - return nil - } - typeSpec, ok := n.Specs[0].(*ast.TypeSpec) - if !ok { - return nil - } - id := typeSpec.Name.Name - if ok, bt := w.isBuiltIn(id); ok { - w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) - } - case token.VAR, token.CONST: - for _, vs := range n.Specs { - valSpec, ok := vs.(*ast.ValueSpec) - if !ok { - continue - } - for _, name := range valSpec.Names { - if ok, bt := w.isBuiltIn(name.Name); ok { - w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, name)) - } - } - } - default: - return nil // skip if not type/var/const declaration - } - - case *ast.FuncDecl: - if n.Recv != nil { - return w // skip methods - } - - id := n.Name.Name - if ok, bt := w.isBuiltIn(id); ok { - w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) - } - case *ast.FuncType: - var fields []*ast.Field - if n.TypeParams != nil { - fields = append(fields, n.TypeParams.List...) - } - if n.Params != nil { - fields = append(fields, n.Params.List...) - } - if n.Results != nil { - fields = append(fields, n.Results.List...) - } - for _, field := range fields { - for _, name := range field.Names { - obj := name.Obj - isTypeOrName := obj != nil && (obj.Kind == ast.Var || obj.Kind == ast.Typ) - if !isTypeOrName { - continue - } - - id := obj.Name - if ok, bt := w.isBuiltIn(id); ok { - w.addFailure(name, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) - } - } - } - case *ast.AssignStmt: - for _, e := range n.Lhs { - id, ok := e.(*ast.Ident) - if !ok { - continue - } - - if ok, bt := w.isBuiltIn(id.Name); ok { - var msg string - switch bt { - case "constant or variable": - if n.Tok == token.DEFINE { - msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name) - } else { - msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name) - } - default: - msg = fmt.Sprintf("redefinition of the built-in %s %s", bt, id) - } - - w.addFailure(n, msg) - } - } - } - - return w -} - -func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: node, - Category: "logic", - Failure: msg, - }) -} - -func (w *lintRedefinesBuiltinID) isBuiltIn(id string) (r bool, builtInKind string) { - if w.builtFunctions[id] { - return true, "function" - } - - if w.builtInConstAndVars[id] { - return true, "constant or variable" - } - - if w.builtInTypes[id] { - return true, "type" - } - - return false, "" -} diff --git a/vendor/github.com/mgechev/revive/rule/redefines_builtin_id.go b/vendor/github.com/mgechev/revive/rule/redefines_builtin_id.go new file mode 100644 index 000000000..10ea16ae1 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/redefines_builtin_id.go @@ -0,0 +1,224 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "maps" + + "github.com/mgechev/revive/lint" +) + +var builtInConstAndVars = map[string]bool{ + "true": true, + "false": true, + "iota": true, + "nil": true, +} + +var builtFunctions = map[string]bool{ + "append": true, + "cap": true, + "close": true, + "complex": true, + "copy": true, + "delete": true, + "imag": true, + "len": true, + "make": true, + "new": true, + "panic": true, + "print": true, + "println": true, + "real": true, + "recover": true, +} + +var builtFunctionsAfterGo121 = map[string]bool{ + "clear": true, + "max": true, + "min": true, +} + +var builtInTypes = map[string]bool{ + "bool": true, + "byte": true, + "complex128": true, + "complex64": true, + "error": true, + "float32": true, + "float64": true, + "int": true, + "int16": true, + "int32": true, + "int64": true, + "int8": true, + "rune": true, + "string": true, + "uint": true, + "uint16": true, + "uint32": true, + "uint64": true, + "uint8": true, + "uintptr": true, + "any": true, +} + +// RedefinesBuiltinIDRule warns when a builtin identifier is shadowed. +type RedefinesBuiltinIDRule struct{} + +// Apply applies the rule to given file. +func (*RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + + builtFuncs := maps.Clone(builtFunctions) + if file.Pkg.IsAtLeastGo121() { + maps.Copy(builtFuncs, builtFunctionsAfterGo121) + } + w := &lintRedefinesBuiltinID{ + onFailure: onFailure, + builtInConstAndVars: builtInConstAndVars, + builtFunctions: builtFuncs, + builtInTypes: builtInTypes, + } + ast.Walk(w, astFile) + + return failures +} + +// Name returns the rule name. +func (*RedefinesBuiltinIDRule) Name() string { + return "redefines-builtin-id" +} + +type lintRedefinesBuiltinID struct { + onFailure func(lint.Failure) + builtInConstAndVars map[string]bool + builtFunctions map[string]bool + builtInTypes map[string]bool +} + +func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.GenDecl: + switch n.Tok { + case token.TYPE: + if len(n.Specs) < 1 { + return nil + } + typeSpec, ok := n.Specs[0].(*ast.TypeSpec) + if !ok { + return nil + } + id := typeSpec.Name.Name + if ok, bt := w.isBuiltIn(id); ok { + w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) + } + case token.VAR, token.CONST: + for _, vs := range n.Specs { + valSpec, ok := vs.(*ast.ValueSpec) + if !ok { + continue + } + for _, name := range valSpec.Names { + if ok, bt := w.isBuiltIn(name.Name); ok { + w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, name)) + } + } + } + default: + return nil // skip if not type/var/const declaration + } + + case *ast.FuncDecl: + if n.Recv != nil { + return w // skip methods + } + + id := n.Name.Name + if ok, bt := w.isBuiltIn(id); ok { + w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) + } + case *ast.FuncType: + var fields []*ast.Field + if n.TypeParams != nil { + fields = append(fields, n.TypeParams.List...) + } + if n.Params != nil { + fields = append(fields, n.Params.List...) + } + if n.Results != nil { + fields = append(fields, n.Results.List...) + } + for _, field := range fields { + for _, name := range field.Names { + obj := name.Obj + isTypeOrName := obj != nil && (obj.Kind == ast.Var || obj.Kind == ast.Typ) + if !isTypeOrName { + continue + } + + id := obj.Name + if ok, bt := w.isBuiltIn(id); ok { + w.addFailure(name, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) + } + } + } + case *ast.AssignStmt: + for _, e := range n.Lhs { + id, ok := e.(*ast.Ident) + if !ok { + continue + } + + if ok, bt := w.isBuiltIn(id.Name); ok { + var msg string + switch bt { + case "constant or variable": + if n.Tok == token.DEFINE { + msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name) + } else { + msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name) + } + default: + msg = fmt.Sprintf("redefinition of the built-in %s %s", bt, id) + } + + w.addFailure(n, msg) + } + } + } + + return w +} + +func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: node, + Category: "logic", + Failure: msg, + }) +} + +func (w *lintRedefinesBuiltinID) isBuiltIn(id string) (r bool, builtInKind string) { + if w.builtFunctions[id] { + return true, "function" + } + + if w.builtInConstAndVars[id] { + return true, "constant or variable" + } + + if w.builtInTypes[id] { + return true, "type" + } + + return false, "" +} diff --git a/vendor/github.com/mgechev/revive/rule/redundant-import-alias.go b/vendor/github.com/mgechev/revive/rule/redundant-import-alias.go deleted file mode 100644 index fa5281f24..000000000 --- a/vendor/github.com/mgechev/revive/rule/redundant-import-alias.go +++ /dev/null @@ -1,52 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strings" - - "github.com/mgechev/revive/lint" -) - -// RedundantImportAlias lints given else constructs. -type RedundantImportAlias struct{} - -// Apply applies the rule to given file. -func (*RedundantImportAlias) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - for _, imp := range file.AST.Imports { - if imp.Name == nil { - continue - } - - if getImportPackageName(imp) == imp.Name.Name { - failures = append(failures, lint.Failure{ - Confidence: 1, - Failure: fmt.Sprintf("Import alias \"%s\" is redundant", imp.Name.Name), - Node: imp, - Category: "imports", - }) - } - } - - return failures -} - -// Name returns the rule name. -func (*RedundantImportAlias) Name() string { - return "redundant-import-alias" -} - -func getImportPackageName(imp *ast.ImportSpec) string { - const pathSep = "/" - const strDelim = `"` - - path := imp.Path.Value - i := strings.LastIndex(path, pathSep) - if i == -1 { - return strings.Trim(path, strDelim) - } - - return strings.Trim(path[i+1:], strDelim) -} diff --git a/vendor/github.com/mgechev/revive/rule/redundant_import_alias.go b/vendor/github.com/mgechev/revive/rule/redundant_import_alias.go new file mode 100644 index 000000000..fa5281f24 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/redundant_import_alias.go @@ -0,0 +1,52 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + + "github.com/mgechev/revive/lint" +) + +// RedundantImportAlias lints given else constructs. +type RedundantImportAlias struct{} + +// Apply applies the rule to given file. +func (*RedundantImportAlias) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + for _, imp := range file.AST.Imports { + if imp.Name == nil { + continue + } + + if getImportPackageName(imp) == imp.Name.Name { + failures = append(failures, lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("Import alias \"%s\" is redundant", imp.Name.Name), + Node: imp, + Category: "imports", + }) + } + } + + return failures +} + +// Name returns the rule name. +func (*RedundantImportAlias) Name() string { + return "redundant-import-alias" +} + +func getImportPackageName(imp *ast.ImportSpec) string { + const pathSep = "/" + const strDelim = `"` + + path := imp.Path.Value + i := strings.LastIndex(path, pathSep) + if i == -1 { + return strings.Trim(path, strDelim) + } + + return strings.Trim(path[i+1:], strDelim) +} diff --git a/vendor/github.com/mgechev/revive/rule/string-format.go b/vendor/github.com/mgechev/revive/rule/string-format.go deleted file mode 100644 index ecac3fa7c..000000000 --- a/vendor/github.com/mgechev/revive/rule/string-format.go +++ /dev/null @@ -1,328 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "regexp" - "strconv" - "strings" - - "github.com/mgechev/revive/lint" -) - -// #region Revive API - -// StringFormatRule lints strings and/or comments according to a set of regular expressions given as Arguments -type StringFormatRule struct{} - -// Apply applies the rule to the given file. -func (*StringFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintStringFormatRule{onFailure: onFailure} - w.parseArguments(arguments) - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*StringFormatRule) Name() string { - return "string-format" -} - -// ParseArgumentsTest is a public wrapper around w.parseArguments used for testing. Returns the error message provided to panic, or nil if no error was encountered -func (StringFormatRule) ParseArgumentsTest(arguments lint.Arguments) *string { - w := lintStringFormatRule{} - c := make(chan any) - // Parse the arguments in a goroutine, defer a recover() call, return the error encountered (or nil if there was no error) - go func() { - defer func() { - err := recover() - c <- err - }() - w.parseArguments(arguments) - }() - err := <-c - if err != nil { - e := fmt.Sprintf("%s", err) - return &e - } - return nil -} - -// #endregion - -// #region Internal structure - -type lintStringFormatRule struct { - onFailure func(lint.Failure) - rules []stringFormatSubrule -} - -type stringFormatSubrule struct { - parent *lintStringFormatRule - scopes stringFormatSubruleScopes - regexp *regexp.Regexp - negated bool - errorMessage string -} - -type stringFormatSubruleScopes []*stringFormatSubruleScope - -type stringFormatSubruleScope struct { - funcName string // Function name the rule is scoped to - argument int // (optional) Which argument in calls to the function is checked against the rule (the first argument is checked by default) - field string // (optional) If the argument to be checked is a struct, which member of the struct is checked against the rule (top level members only) -} - -// Regex inserted to match valid function/struct field identifiers -const identRegex = "[_A-Za-z][_A-Za-z0-9]*" - -var parseStringFormatScope = regexp.MustCompile( - fmt.Sprintf("^(%s(?:\\.%s)?)(?:\\[([0-9]+)\\](?:\\.(%s))?)?$", identRegex, identRegex, identRegex)) - -// #endregion - -// #region Argument parsing - -func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) { - for i, argument := range arguments { - scopes, regex, negated, errorMessage := w.parseArgument(argument, i) - w.rules = append(w.rules, stringFormatSubrule{ - parent: w, - scopes: scopes, - regexp: regex, - negated: negated, - errorMessage: errorMessage, - }) - } -} - -func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes stringFormatSubruleScopes, regex *regexp.Regexp, negated bool, errorMessage string) { - g, ok := argument.([]any) // Cast to generic slice first - if !ok { - w.configError("argument is not a slice", ruleNum, 0) - } - if len(g) < 2 { - w.configError("less than two slices found in argument, scope and regex are required", ruleNum, len(g)-1) - } - rule := make([]string, len(g)) - for i, obj := range g { - val, ok := obj.(string) - if !ok { - w.configError("unexpected value, string was expected", ruleNum, i) - } - rule[i] = val - } - - // Validate scope and regex length - if rule[0] == "" { - w.configError("empty scope provided", ruleNum, 0) - } else if len(rule[1]) < 2 { - w.configError("regex is too small (regexes should begin and end with '/')", ruleNum, 1) - } - - // Parse rule scopes - rawScopes := strings.Split(rule[0], ",") - - scopes = make([]*stringFormatSubruleScope, 0, len(rawScopes)) - for scopeNum, rawScope := range rawScopes { - rawScope = strings.TrimSpace(rawScope) - - if len(rawScope) == 0 { - w.parseScopeError("empty scope in rule scopes:", ruleNum, 0, scopeNum) - } - - scope := stringFormatSubruleScope{} - matches := parseStringFormatScope.FindStringSubmatch(rawScope) - if matches == nil { - // The rule's scope didn't match the parsing regex at all, probably a configuration error - w.parseScopeError("unable to parse rule scope", ruleNum, 0, scopeNum) - } else if len(matches) != 4 { - // The rule's scope matched the parsing regex, but an unexpected number of submatches was returned, probably a bug - w.parseScopeError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0, scopeNum) - } - scope.funcName = matches[1] - if len(matches[2]) > 0 { - var err error - scope.argument, err = strconv.Atoi(matches[2]) - if err != nil { - w.parseScopeError("unable to parse argument number in rule scope", ruleNum, 0, scopeNum) - } - } - if len(matches[3]) > 0 { - scope.field = matches[3] - } - - scopes = append(scopes, &scope) - } - - // Strip / characters from the beginning and end of rule[1] before compiling - negated = rule[1][0] == '!' - offset := 1 - if negated { - offset++ - } - regex, err := regexp.Compile(rule[1][offset : len(rule[1])-1]) - if err != nil { - w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1) - } - - // Use custom error message if provided - if len(rule) == 3 { - errorMessage = rule[2] - } - return scopes, regex, negated, errorMessage -} - -// Report an invalid config, this is specifically the user's fault -func (lintStringFormatRule) configError(msg string, ruleNum, option int) { - panic(fmt.Sprintf("invalid configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) -} - -// Report a general config parsing failure, this may be the user's fault, but it isn't known for certain -func (lintStringFormatRule) parseError(msg string, ruleNum, option int) { - panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) -} - -// Report a general scope config parsing failure, this may be the user's fault, but it isn't known for certain -func (lintStringFormatRule) parseScopeError(msg string, ruleNum, option, scopeNum int) { - panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d, scope index %d]", msg, ruleNum, option, scopeNum)) -} - -// #endregion - -// #region Node traversal - -func (w lintStringFormatRule) Visit(node ast.Node) ast.Visitor { - // First, check if node is a call expression - call, ok := node.(*ast.CallExpr) - if !ok { - return w - } - - // Get the name of the call expression to check against rule scope - callName, ok := w.getCallName(call) - if !ok { - return w - } - - for _, rule := range w.rules { - for _, scope := range rule.scopes { - if scope.funcName == callName { - rule.apply(call, scope) - } - } - } - - return w -} - -// Return the name of a call expression in the form of package.Func or Func -func (lintStringFormatRule) getCallName(call *ast.CallExpr) (callName string, ok bool) { - if ident, ok := call.Fun.(*ast.Ident); ok { - // Local function call - return ident.Name, true - } - - if selector, ok := call.Fun.(*ast.SelectorExpr); ok { - // Scoped function call - scope, ok := selector.X.(*ast.Ident) - if ok { - return scope.Name + "." + selector.Sel.Name, true - } - // Scoped function call inside structure - recv, ok := selector.X.(*ast.SelectorExpr) - if ok { - return recv.Sel.Name + "." + selector.Sel.Name, true - } - } - - return "", false -} - -// #endregion - -// #region Linting logic - -// apply a single format rule to a call expression (should be done after verifying the that the call expression matches the rule's scope) -func (r *stringFormatSubrule) apply(call *ast.CallExpr, scope *stringFormatSubruleScope) { - if len(call.Args) <= scope.argument { - return - } - - arg := call.Args[scope.argument] - var lit *ast.BasicLit - if len(scope.field) > 0 { - // Try finding the scope's Field, treating arg as a composite literal - composite, ok := arg.(*ast.CompositeLit) - if !ok { - return - } - for _, el := range composite.Elts { - kv, ok := el.(*ast.KeyValueExpr) - if !ok { - continue - } - key, ok := kv.Key.(*ast.Ident) - if !ok || key.Name != scope.field { - continue - } - - // We're now dealing with the exact field in the rule's scope, so if anything fails, we can safely return instead of continuing the loop - lit, ok = kv.Value.(*ast.BasicLit) - if !ok || lit.Kind != token.STRING { - return - } - } - } else { - var ok bool - // Treat arg as a string literal - lit, ok = arg.(*ast.BasicLit) - if !ok || lit.Kind != token.STRING { - return - } - } - // Unquote the string literal before linting - unquoted := lit.Value[1 : len(lit.Value)-1] - if r.stringIsOK(unquoted) { - return - } - - r.generateFailure(lit) -} - -func (r *stringFormatSubrule) stringIsOK(s string) bool { - matches := r.regexp.MatchString(s) - if r.negated { - return !matches - } - - return matches -} - -func (r *stringFormatSubrule) generateFailure(node ast.Node) { - var failure string - switch { - case len(r.errorMessage) > 0: - failure = r.errorMessage - case r.negated: - failure = fmt.Sprintf("string literal matches user defined regex /%s/", r.regexp.String()) - case !r.negated: - failure = fmt.Sprintf("string literal doesn't match user defined regex /%s/", r.regexp.String()) - } - - r.parent.onFailure(lint.Failure{ - Confidence: 1, - Failure: failure, - Node: node, - }) -} - -// #endregion diff --git a/vendor/github.com/mgechev/revive/rule/string-of-int.go b/vendor/github.com/mgechev/revive/rule/string-of-int.go deleted file mode 100644 index 3bec1d6ac..000000000 --- a/vendor/github.com/mgechev/revive/rule/string-of-int.go +++ /dev/null @@ -1,95 +0,0 @@ -package rule - -import ( - "go/ast" - "go/types" - - "github.com/mgechev/revive/lint" -) - -// StringOfIntRule warns when logic expressions contains Boolean literals. -type StringOfIntRule struct{} - -// Apply applies the rule to given file. -func (*StringOfIntRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - file.Pkg.TypeCheck() - - w := &lintStringInt{file, onFailure} - ast.Walk(w, astFile) - - return failures -} - -// Name returns the rule name. -func (*StringOfIntRule) Name() string { - return "string-of-int" -} - -type lintStringInt struct { - file *lint.File - onFailure func(lint.Failure) -} - -func (w *lintStringInt) Visit(node ast.Node) ast.Visitor { - ce, ok := node.(*ast.CallExpr) - if !ok { - return w - } - - if !w.isCallStringCast(ce.Fun) { - return w - } - - if !w.isIntExpression(ce.Args) { - return w - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: ce, - Failure: "dubious conversion of an integer into a string, use strconv.Itoa", - }) - - return w -} - -func (w *lintStringInt) isCallStringCast(e ast.Expr) bool { - t := w.file.Pkg.TypeOf(e) - if t == nil { - return false - } - - tb, _ := t.Underlying().(*types.Basic) - - return tb != nil && tb.Kind() == types.String -} - -func (w *lintStringInt) isIntExpression(es []ast.Expr) bool { - if len(es) != 1 { - return false - } - - t := w.file.Pkg.TypeOf(es[0]) - if t == nil { - return false - } - - ut, _ := t.Underlying().(*types.Basic) - if ut == nil || ut.Info()&types.IsInteger == 0 { - return false - } - - switch ut.Kind() { - case types.Byte, types.Rune, types.UntypedRune: - return false - } - - return true -} diff --git a/vendor/github.com/mgechev/revive/rule/string_format.go b/vendor/github.com/mgechev/revive/rule/string_format.go new file mode 100644 index 000000000..ecac3fa7c --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/string_format.go @@ -0,0 +1,328 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "regexp" + "strconv" + "strings" + + "github.com/mgechev/revive/lint" +) + +// #region Revive API + +// StringFormatRule lints strings and/or comments according to a set of regular expressions given as Arguments +type StringFormatRule struct{} + +// Apply applies the rule to the given file. +func (*StringFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintStringFormatRule{onFailure: onFailure} + w.parseArguments(arguments) + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*StringFormatRule) Name() string { + return "string-format" +} + +// ParseArgumentsTest is a public wrapper around w.parseArguments used for testing. Returns the error message provided to panic, or nil if no error was encountered +func (StringFormatRule) ParseArgumentsTest(arguments lint.Arguments) *string { + w := lintStringFormatRule{} + c := make(chan any) + // Parse the arguments in a goroutine, defer a recover() call, return the error encountered (or nil if there was no error) + go func() { + defer func() { + err := recover() + c <- err + }() + w.parseArguments(arguments) + }() + err := <-c + if err != nil { + e := fmt.Sprintf("%s", err) + return &e + } + return nil +} + +// #endregion + +// #region Internal structure + +type lintStringFormatRule struct { + onFailure func(lint.Failure) + rules []stringFormatSubrule +} + +type stringFormatSubrule struct { + parent *lintStringFormatRule + scopes stringFormatSubruleScopes + regexp *regexp.Regexp + negated bool + errorMessage string +} + +type stringFormatSubruleScopes []*stringFormatSubruleScope + +type stringFormatSubruleScope struct { + funcName string // Function name the rule is scoped to + argument int // (optional) Which argument in calls to the function is checked against the rule (the first argument is checked by default) + field string // (optional) If the argument to be checked is a struct, which member of the struct is checked against the rule (top level members only) +} + +// Regex inserted to match valid function/struct field identifiers +const identRegex = "[_A-Za-z][_A-Za-z0-9]*" + +var parseStringFormatScope = regexp.MustCompile( + fmt.Sprintf("^(%s(?:\\.%s)?)(?:\\[([0-9]+)\\](?:\\.(%s))?)?$", identRegex, identRegex, identRegex)) + +// #endregion + +// #region Argument parsing + +func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) { + for i, argument := range arguments { + scopes, regex, negated, errorMessage := w.parseArgument(argument, i) + w.rules = append(w.rules, stringFormatSubrule{ + parent: w, + scopes: scopes, + regexp: regex, + negated: negated, + errorMessage: errorMessage, + }) + } +} + +func (w lintStringFormatRule) parseArgument(argument any, ruleNum int) (scopes stringFormatSubruleScopes, regex *regexp.Regexp, negated bool, errorMessage string) { + g, ok := argument.([]any) // Cast to generic slice first + if !ok { + w.configError("argument is not a slice", ruleNum, 0) + } + if len(g) < 2 { + w.configError("less than two slices found in argument, scope and regex are required", ruleNum, len(g)-1) + } + rule := make([]string, len(g)) + for i, obj := range g { + val, ok := obj.(string) + if !ok { + w.configError("unexpected value, string was expected", ruleNum, i) + } + rule[i] = val + } + + // Validate scope and regex length + if rule[0] == "" { + w.configError("empty scope provided", ruleNum, 0) + } else if len(rule[1]) < 2 { + w.configError("regex is too small (regexes should begin and end with '/')", ruleNum, 1) + } + + // Parse rule scopes + rawScopes := strings.Split(rule[0], ",") + + scopes = make([]*stringFormatSubruleScope, 0, len(rawScopes)) + for scopeNum, rawScope := range rawScopes { + rawScope = strings.TrimSpace(rawScope) + + if len(rawScope) == 0 { + w.parseScopeError("empty scope in rule scopes:", ruleNum, 0, scopeNum) + } + + scope := stringFormatSubruleScope{} + matches := parseStringFormatScope.FindStringSubmatch(rawScope) + if matches == nil { + // The rule's scope didn't match the parsing regex at all, probably a configuration error + w.parseScopeError("unable to parse rule scope", ruleNum, 0, scopeNum) + } else if len(matches) != 4 { + // The rule's scope matched the parsing regex, but an unexpected number of submatches was returned, probably a bug + w.parseScopeError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0, scopeNum) + } + scope.funcName = matches[1] + if len(matches[2]) > 0 { + var err error + scope.argument, err = strconv.Atoi(matches[2]) + if err != nil { + w.parseScopeError("unable to parse argument number in rule scope", ruleNum, 0, scopeNum) + } + } + if len(matches[3]) > 0 { + scope.field = matches[3] + } + + scopes = append(scopes, &scope) + } + + // Strip / characters from the beginning and end of rule[1] before compiling + negated = rule[1][0] == '!' + offset := 1 + if negated { + offset++ + } + regex, err := regexp.Compile(rule[1][offset : len(rule[1])-1]) + if err != nil { + w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1) + } + + // Use custom error message if provided + if len(rule) == 3 { + errorMessage = rule[2] + } + return scopes, regex, negated, errorMessage +} + +// Report an invalid config, this is specifically the user's fault +func (lintStringFormatRule) configError(msg string, ruleNum, option int) { + panic(fmt.Sprintf("invalid configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) +} + +// Report a general config parsing failure, this may be the user's fault, but it isn't known for certain +func (lintStringFormatRule) parseError(msg string, ruleNum, option int) { + panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) +} + +// Report a general scope config parsing failure, this may be the user's fault, but it isn't known for certain +func (lintStringFormatRule) parseScopeError(msg string, ruleNum, option, scopeNum int) { + panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d, scope index %d]", msg, ruleNum, option, scopeNum)) +} + +// #endregion + +// #region Node traversal + +func (w lintStringFormatRule) Visit(node ast.Node) ast.Visitor { + // First, check if node is a call expression + call, ok := node.(*ast.CallExpr) + if !ok { + return w + } + + // Get the name of the call expression to check against rule scope + callName, ok := w.getCallName(call) + if !ok { + return w + } + + for _, rule := range w.rules { + for _, scope := range rule.scopes { + if scope.funcName == callName { + rule.apply(call, scope) + } + } + } + + return w +} + +// Return the name of a call expression in the form of package.Func or Func +func (lintStringFormatRule) getCallName(call *ast.CallExpr) (callName string, ok bool) { + if ident, ok := call.Fun.(*ast.Ident); ok { + // Local function call + return ident.Name, true + } + + if selector, ok := call.Fun.(*ast.SelectorExpr); ok { + // Scoped function call + scope, ok := selector.X.(*ast.Ident) + if ok { + return scope.Name + "." + selector.Sel.Name, true + } + // Scoped function call inside structure + recv, ok := selector.X.(*ast.SelectorExpr) + if ok { + return recv.Sel.Name + "." + selector.Sel.Name, true + } + } + + return "", false +} + +// #endregion + +// #region Linting logic + +// apply a single format rule to a call expression (should be done after verifying the that the call expression matches the rule's scope) +func (r *stringFormatSubrule) apply(call *ast.CallExpr, scope *stringFormatSubruleScope) { + if len(call.Args) <= scope.argument { + return + } + + arg := call.Args[scope.argument] + var lit *ast.BasicLit + if len(scope.field) > 0 { + // Try finding the scope's Field, treating arg as a composite literal + composite, ok := arg.(*ast.CompositeLit) + if !ok { + return + } + for _, el := range composite.Elts { + kv, ok := el.(*ast.KeyValueExpr) + if !ok { + continue + } + key, ok := kv.Key.(*ast.Ident) + if !ok || key.Name != scope.field { + continue + } + + // We're now dealing with the exact field in the rule's scope, so if anything fails, we can safely return instead of continuing the loop + lit, ok = kv.Value.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return + } + } + } else { + var ok bool + // Treat arg as a string literal + lit, ok = arg.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return + } + } + // Unquote the string literal before linting + unquoted := lit.Value[1 : len(lit.Value)-1] + if r.stringIsOK(unquoted) { + return + } + + r.generateFailure(lit) +} + +func (r *stringFormatSubrule) stringIsOK(s string) bool { + matches := r.regexp.MatchString(s) + if r.negated { + return !matches + } + + return matches +} + +func (r *stringFormatSubrule) generateFailure(node ast.Node) { + var failure string + switch { + case len(r.errorMessage) > 0: + failure = r.errorMessage + case r.negated: + failure = fmt.Sprintf("string literal matches user defined regex /%s/", r.regexp.String()) + case !r.negated: + failure = fmt.Sprintf("string literal doesn't match user defined regex /%s/", r.regexp.String()) + } + + r.parent.onFailure(lint.Failure{ + Confidence: 1, + Failure: failure, + Node: node, + }) +} + +// #endregion diff --git a/vendor/github.com/mgechev/revive/rule/string_of_int.go b/vendor/github.com/mgechev/revive/rule/string_of_int.go new file mode 100644 index 000000000..3bec1d6ac --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/string_of_int.go @@ -0,0 +1,95 @@ +package rule + +import ( + "go/ast" + "go/types" + + "github.com/mgechev/revive/lint" +) + +// StringOfIntRule warns when logic expressions contains Boolean literals. +type StringOfIntRule struct{} + +// Apply applies the rule to given file. +func (*StringOfIntRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + file.Pkg.TypeCheck() + + w := &lintStringInt{file, onFailure} + ast.Walk(w, astFile) + + return failures +} + +// Name returns the rule name. +func (*StringOfIntRule) Name() string { + return "string-of-int" +} + +type lintStringInt struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (w *lintStringInt) Visit(node ast.Node) ast.Visitor { + ce, ok := node.(*ast.CallExpr) + if !ok { + return w + } + + if !w.isCallStringCast(ce.Fun) { + return w + } + + if !w.isIntExpression(ce.Args) { + return w + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: ce, + Failure: "dubious conversion of an integer into a string, use strconv.Itoa", + }) + + return w +} + +func (w *lintStringInt) isCallStringCast(e ast.Expr) bool { + t := w.file.Pkg.TypeOf(e) + if t == nil { + return false + } + + tb, _ := t.Underlying().(*types.Basic) + + return tb != nil && tb.Kind() == types.String +} + +func (w *lintStringInt) isIntExpression(es []ast.Expr) bool { + if len(es) != 1 { + return false + } + + t := w.file.Pkg.TypeOf(es[0]) + if t == nil { + return false + } + + ut, _ := t.Underlying().(*types.Basic) + if ut == nil || ut.Info()&types.IsInteger == 0 { + return false + } + + switch ut.Kind() { + case types.Byte, types.Rune, types.UntypedRune: + return false + } + + return true +} diff --git a/vendor/github.com/mgechev/revive/rule/struct-tag.go b/vendor/github.com/mgechev/revive/rule/struct-tag.go deleted file mode 100644 index ec3f0c7cf..000000000 --- a/vendor/github.com/mgechev/revive/rule/struct-tag.go +++ /dev/null @@ -1,427 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "strconv" - "strings" - "sync" - - "github.com/fatih/structtag" - "github.com/mgechev/revive/lint" -) - -// StructTagRule lints struct tags. -type StructTagRule struct { - userDefined map[string][]string // map: key -> []option - sync.Mutex -} - -func (r *StructTagRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - mustConfigure := r.userDefined == nil && len(arguments) > 0 - if !mustConfigure { - return - } - - checkNumberOfArguments(1, arguments, r.Name()) - r.userDefined = make(map[string][]string, len(arguments)) - for _, arg := range arguments { - item, ok := arg.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the %s rule. Expecting a string, got %v (of type %T)", r.Name(), arg, arg)) - } - parts := strings.Split(item, ",") - if len(parts) < 2 { - panic(fmt.Sprintf("Invalid argument to the %s rule. Expecting a string of the form key[,option]+, got %s", r.Name(), item)) - } - key := strings.TrimSpace(parts[0]) - for i := 1; i < len(parts); i++ { - option := strings.TrimSpace(parts[i]) - r.userDefined[key] = append(r.userDefined[key], option) - } - } -} - -// Apply applies the rule to given file. -func (r *StructTagRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) - - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintStructTagRule{ - onFailure: onFailure, - userDefined: r.userDefined, - } - - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*StructTagRule) Name() string { - return "struct-tag" -} - -type lintStructTagRule struct { - onFailure func(lint.Failure) - userDefined map[string][]string // map: key -> []option - usedTagNbr map[int]bool // list of used tag numbers - usedTagName map[string]bool // list of used tag keys -} - -func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.StructType: - isEmptyStruct := n.Fields == nil || n.Fields.NumFields() < 1 - if isEmptyStruct { - return nil // skip empty structs - } - - w.usedTagNbr = map[int]bool{} - w.usedTagName = map[string]bool{} - for _, f := range n.Fields.List { - if f.Tag != nil { - w.checkTaggedField(f) - } - } - } - - return w -} - -const keyASN1 = "asn1" -const keyBSON = "bson" -const keyDefault = "default" -const keyJSON = "json" -const keyProtobuf = "protobuf" -const keyRequired = "required" -const keyXML = "xml" -const keyYAML = "yaml" - -func (w lintStructTagRule) checkTagNameIfNeed(tag *structtag.Tag) (string, bool) { - isUnnamedTag := tag.Name == "" || tag.Name == "-" - if isUnnamedTag { - return "", true - } - - needsToCheckTagName := tag.Key == keyBSON || - tag.Key == keyJSON || - tag.Key == keyXML || - tag.Key == keyYAML || - tag.Key == keyProtobuf - - if !needsToCheckTagName { - return "", true - } - - tagName := w.getTagName(tag) - if tagName == "" { - return "", true // No tag name found - } - - // We concat the key and name as the mapping key here - // to allow the same tag name in different tag type. - key := tag.Key + ":" + tagName - if _, ok := w.usedTagName[key]; ok { - return fmt.Sprintf("duplicate tag name: '%s'", tagName), false - } - - w.usedTagName[key] = true - - return "", true -} - -func (lintStructTagRule) getTagName(tag *structtag.Tag) string { - switch tag.Key { - case keyProtobuf: - for _, option := range tag.Options { - if strings.HasPrefix(option, "name=") { - return strings.TrimPrefix(option, "name=") - } - } - return "" // protobuf tag lacks 'name' option - default: - return tag.Name - } -} - -// checkTaggedField checks the tag of the given field. -// precondition: the field has a tag -func (w lintStructTagRule) checkTaggedField(f *ast.Field) { - if len(f.Names) > 0 && !f.Names[0].IsExported() { - w.addFailure(f, "tag on not-exported field "+f.Names[0].Name) - } - - tags, err := structtag.Parse(strings.Trim(f.Tag.Value, "`")) - if err != nil || tags == nil { - w.addFailure(f.Tag, "malformed tag") - return - } - - for _, tag := range tags.Tags() { - if msg, ok := w.checkTagNameIfNeed(tag); !ok { - w.addFailure(f.Tag, msg) - } - - switch key := tag.Key; key { - case keyASN1: - msg, ok := w.checkASN1Tag(f.Type, tag) - if !ok { - w.addFailure(f.Tag, msg) - } - case keyBSON: - msg, ok := w.checkBSONTag(tag.Options) - if !ok { - w.addFailure(f.Tag, msg) - } - case keyDefault: - if !w.typeValueMatch(f.Type, tag.Name) { - w.addFailure(f.Tag, "field's type and default value's type mismatch") - } - case keyJSON: - msg, ok := w.checkJSONTag(tag.Name, tag.Options) - if !ok { - w.addFailure(f.Tag, msg) - } - case keyProtobuf: - msg, ok := w.checkProtobufTag(tag) - if !ok { - w.addFailure(f.Tag, msg) - } - case keyRequired: - if tag.Name != "true" && tag.Name != "false" { - w.addFailure(f.Tag, "required should be 'true' or 'false'") - } - case keyXML: - msg, ok := w.checkXMLTag(tag.Options) - if !ok { - w.addFailure(f.Tag, msg) - } - case keyYAML: - msg, ok := w.checkYAMLTag(tag.Options) - if !ok { - w.addFailure(f.Tag, msg) - } - default: - // unknown key - } - } -} - -func (w lintStructTagRule) checkASN1Tag(t ast.Expr, tag *structtag.Tag) (string, bool) { - checkList := append(tag.Options, tag.Name) - for _, opt := range checkList { - switch opt { - case "application", "explicit", "generalized", "ia5", "omitempty", "optional", "set", "utf8": - - default: - if strings.HasPrefix(opt, "tag:") { - parts := strings.Split(opt, ":") - tagNumber := parts[1] - number, err := strconv.Atoi(tagNumber) - if err != nil { - return fmt.Sprintf("ASN1 tag must be a number, got '%s'", tagNumber), false - } - if w.usedTagNbr[number] { - return fmt.Sprintf("duplicated tag number %v", number), false - } - w.usedTagNbr[number] = true - - continue - } - - if strings.HasPrefix(opt, "default:") { - parts := strings.Split(opt, ":") - if len(parts) < 2 { - return "malformed default for ASN1 tag", false - } - if !w.typeValueMatch(t, parts[1]) { - return "field's type and default value's type mismatch", false - } - - continue - } - - if w.isUserDefined(keyASN1, opt) { - continue - } - - return fmt.Sprintf("unknown option '%s' in ASN1 tag", opt), false - } - } - - return "", true -} - -func (w lintStructTagRule) checkBSONTag(options []string) (string, bool) { - for _, opt := range options { - switch opt { - case "inline", "minsize", "omitempty": - default: - if w.isUserDefined(keyBSON, opt) { - continue - } - return fmt.Sprintf("unknown option '%s' in BSON tag", opt), false - } - } - - return "", true -} - -func (w lintStructTagRule) checkJSONTag(name string, options []string) (string, bool) { - for _, opt := range options { - switch opt { - case "omitempty", "string": - case "": - // special case for JSON key "-" - if name != "-" { - return "option can not be empty in JSON tag", false - } - default: - if w.isUserDefined(keyJSON, opt) { - continue - } - return fmt.Sprintf("unknown option '%s' in JSON tag", opt), false - } - } - - return "", true -} - -func (w lintStructTagRule) checkXMLTag(options []string) (string, bool) { - for _, opt := range options { - switch opt { - case "any", "attr", "cdata", "chardata", "comment", "innerxml", "omitempty", "typeattr": - default: - if w.isUserDefined(keyXML, opt) { - continue - } - return fmt.Sprintf("unknown option '%s' in XML tag", opt), false - } - } - - return "", true -} - -func (w lintStructTagRule) checkYAMLTag(options []string) (string, bool) { - for _, opt := range options { - switch opt { - case "flow", "inline", "omitempty": - default: - if w.isUserDefined(keyYAML, opt) { - continue - } - return fmt.Sprintf("unknown option '%s' in YAML tag", opt), false - } - } - - return "", true -} - -func (lintStructTagRule) typeValueMatch(t ast.Expr, val string) bool { - tID, ok := t.(*ast.Ident) - if !ok { - return true - } - - typeMatches := true - switch tID.Name { - case "bool": - typeMatches = val == "true" || val == "false" - case "float64": - _, err := strconv.ParseFloat(val, 64) - typeMatches = err == nil - case "int": - _, err := strconv.ParseInt(val, 10, 64) - typeMatches = err == nil - case "string": - case "nil": - default: - // unchecked type - } - - return typeMatches -} - -func (w lintStructTagRule) checkProtobufTag(tag *structtag.Tag) (string, bool) { - // check name - switch tag.Name { - case "bytes", "fixed32", "fixed64", "group", "varint", "zigzag32", "zigzag64": - // do nothing - default: - return fmt.Sprintf("invalid protobuf tag name '%s'", tag.Name), false - } - - // check options - seenOptions := map[string]bool{} - for _, opt := range tag.Options { - if number, err := strconv.Atoi(opt); err == nil { - _, alreadySeen := w.usedTagNbr[number] - if alreadySeen { - return fmt.Sprintf("duplicated tag number %v", number), false - } - w.usedTagNbr[number] = true - continue // option is an integer - } - - switch { - case opt == "opt" || opt == "proto3" || opt == "rep" || opt == "req": - // do nothing - case strings.Contains(opt, "="): - o := strings.Split(opt, "=")[0] - _, alreadySeen := seenOptions[o] - if alreadySeen { - return fmt.Sprintf("protobuf tag has duplicated option '%s'", o), false - } - seenOptions[o] = true - continue - } - } - _, hasName := seenOptions["name"] - if !hasName { - return "protobuf tag lacks mandatory option 'name'", false - } - - for k := range seenOptions { - switch k { - case "name", "json": - // do nothing - default: - if w.isUserDefined(keyProtobuf, k) { - continue - } - return fmt.Sprintf("unknown option '%s' in protobuf tag", k), false - } - } - - return "", true -} - -func (w lintStructTagRule) addFailure(n ast.Node, msg string) { - w.onFailure(lint.Failure{ - Node: n, - Failure: msg, - Confidence: 1, - }) -} - -func (w lintStructTagRule) isUserDefined(key, opt string) bool { - if w.userDefined == nil { - return false - } - - options := w.userDefined[key] - for _, o := range options { - if opt == o { - return true - } - } - return false -} diff --git a/vendor/github.com/mgechev/revive/rule/struct_tag.go b/vendor/github.com/mgechev/revive/rule/struct_tag.go new file mode 100644 index 000000000..4dd927827 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/struct_tag.go @@ -0,0 +1,424 @@ +package rule + +import ( + "fmt" + "go/ast" + "strconv" + "strings" + "sync" + + "github.com/fatih/structtag" + "github.com/mgechev/revive/lint" +) + +// StructTagRule lints struct tags. +type StructTagRule struct { + userDefined map[string][]string // map: key -> []option + + configureOnce sync.Once +} + +func (r *StructTagRule) configure(arguments lint.Arguments) { + if len(arguments) == 0 { + return + } + + checkNumberOfArguments(1, arguments, r.Name()) + r.userDefined = make(map[string][]string, len(arguments)) + for _, arg := range arguments { + item, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the %s rule. Expecting a string, got %v (of type %T)", r.Name(), arg, arg)) + } + parts := strings.Split(item, ",") + if len(parts) < 2 { + panic(fmt.Sprintf("Invalid argument to the %s rule. Expecting a string of the form key[,option]+, got %s", r.Name(), item)) + } + key := strings.TrimSpace(parts[0]) + for i := 1; i < len(parts); i++ { + option := strings.TrimSpace(parts[i]) + r.userDefined[key] = append(r.userDefined[key], option) + } + } +} + +// Apply applies the rule to given file. +func (r *StructTagRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintStructTagRule{ + onFailure: onFailure, + userDefined: r.userDefined, + } + + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*StructTagRule) Name() string { + return "struct-tag" +} + +type lintStructTagRule struct { + onFailure func(lint.Failure) + userDefined map[string][]string // map: key -> []option + usedTagNbr map[int]bool // list of used tag numbers + usedTagName map[string]bool // list of used tag keys +} + +func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.StructType: + isEmptyStruct := n.Fields == nil || n.Fields.NumFields() < 1 + if isEmptyStruct { + return nil // skip empty structs + } + + w.usedTagNbr = map[int]bool{} + w.usedTagName = map[string]bool{} + for _, f := range n.Fields.List { + if f.Tag != nil { + w.checkTaggedField(f) + } + } + } + + return w +} + +const keyASN1 = "asn1" +const keyBSON = "bson" +const keyDefault = "default" +const keyJSON = "json" +const keyProtobuf = "protobuf" +const keyRequired = "required" +const keyXML = "xml" +const keyYAML = "yaml" + +func (w lintStructTagRule) checkTagNameIfNeed(tag *structtag.Tag) (string, bool) { + isUnnamedTag := tag.Name == "" || tag.Name == "-" + if isUnnamedTag { + return "", true + } + + needsToCheckTagName := tag.Key == keyBSON || + tag.Key == keyJSON || + tag.Key == keyXML || + tag.Key == keyYAML || + tag.Key == keyProtobuf + + if !needsToCheckTagName { + return "", true + } + + tagName := w.getTagName(tag) + if tagName == "" { + return "", true // No tag name found + } + + // We concat the key and name as the mapping key here + // to allow the same tag name in different tag type. + key := tag.Key + ":" + tagName + if _, ok := w.usedTagName[key]; ok { + return fmt.Sprintf("duplicate tag name: '%s'", tagName), false + } + + w.usedTagName[key] = true + + return "", true +} + +func (lintStructTagRule) getTagName(tag *structtag.Tag) string { + switch tag.Key { + case keyProtobuf: + for _, option := range tag.Options { + if strings.HasPrefix(option, "name=") { + return strings.TrimPrefix(option, "name=") + } + } + return "" // protobuf tag lacks 'name' option + default: + return tag.Name + } +} + +// checkTaggedField checks the tag of the given field. +// precondition: the field has a tag +func (w lintStructTagRule) checkTaggedField(f *ast.Field) { + if len(f.Names) > 0 && !f.Names[0].IsExported() { + w.addFailure(f, "tag on not-exported field "+f.Names[0].Name) + } + + tags, err := structtag.Parse(strings.Trim(f.Tag.Value, "`")) + if err != nil || tags == nil { + w.addFailure(f.Tag, "malformed tag") + return + } + + for _, tag := range tags.Tags() { + if msg, ok := w.checkTagNameIfNeed(tag); !ok { + w.addFailure(f.Tag, msg) + } + + switch key := tag.Key; key { + case keyASN1: + msg, ok := w.checkASN1Tag(f.Type, tag) + if !ok { + w.addFailure(f.Tag, msg) + } + case keyBSON: + msg, ok := w.checkBSONTag(tag.Options) + if !ok { + w.addFailure(f.Tag, msg) + } + case keyDefault: + if !w.typeValueMatch(f.Type, tag.Name) { + w.addFailure(f.Tag, "field's type and default value's type mismatch") + } + case keyJSON: + msg, ok := w.checkJSONTag(tag.Name, tag.Options) + if !ok { + w.addFailure(f.Tag, msg) + } + case keyProtobuf: + msg, ok := w.checkProtobufTag(tag) + if !ok { + w.addFailure(f.Tag, msg) + } + case keyRequired: + if tag.Name != "true" && tag.Name != "false" { + w.addFailure(f.Tag, "required should be 'true' or 'false'") + } + case keyXML: + msg, ok := w.checkXMLTag(tag.Options) + if !ok { + w.addFailure(f.Tag, msg) + } + case keyYAML: + msg, ok := w.checkYAMLTag(tag.Options) + if !ok { + w.addFailure(f.Tag, msg) + } + default: + // unknown key + } + } +} + +func (w lintStructTagRule) checkASN1Tag(t ast.Expr, tag *structtag.Tag) (string, bool) { + checkList := append(tag.Options, tag.Name) + for _, opt := range checkList { + switch opt { + case "application", "explicit", "generalized", "ia5", "omitempty", "optional", "set", "utf8": + + default: + if strings.HasPrefix(opt, "tag:") { + parts := strings.Split(opt, ":") + tagNumber := parts[1] + number, err := strconv.Atoi(tagNumber) + if err != nil { + return fmt.Sprintf("ASN1 tag must be a number, got '%s'", tagNumber), false + } + if w.usedTagNbr[number] { + return fmt.Sprintf("duplicated tag number %v", number), false + } + w.usedTagNbr[number] = true + + continue + } + + if strings.HasPrefix(opt, "default:") { + parts := strings.Split(opt, ":") + if len(parts) < 2 { + return "malformed default for ASN1 tag", false + } + if !w.typeValueMatch(t, parts[1]) { + return "field's type and default value's type mismatch", false + } + + continue + } + + if w.isUserDefined(keyASN1, opt) { + continue + } + + return fmt.Sprintf("unknown option '%s' in ASN1 tag", opt), false + } + } + + return "", true +} + +func (w lintStructTagRule) checkBSONTag(options []string) (string, bool) { + for _, opt := range options { + switch opt { + case "inline", "minsize", "omitempty": + default: + if w.isUserDefined(keyBSON, opt) { + continue + } + return fmt.Sprintf("unknown option '%s' in BSON tag", opt), false + } + } + + return "", true +} + +func (w lintStructTagRule) checkJSONTag(name string, options []string) (string, bool) { + for _, opt := range options { + switch opt { + case "omitempty", "string": + case "": + // special case for JSON key "-" + if name != "-" { + return "option can not be empty in JSON tag", false + } + default: + if w.isUserDefined(keyJSON, opt) { + continue + } + return fmt.Sprintf("unknown option '%s' in JSON tag", opt), false + } + } + + return "", true +} + +func (w lintStructTagRule) checkXMLTag(options []string) (string, bool) { + for _, opt := range options { + switch opt { + case "any", "attr", "cdata", "chardata", "comment", "innerxml", "omitempty", "typeattr": + default: + if w.isUserDefined(keyXML, opt) { + continue + } + return fmt.Sprintf("unknown option '%s' in XML tag", opt), false + } + } + + return "", true +} + +func (w lintStructTagRule) checkYAMLTag(options []string) (string, bool) { + for _, opt := range options { + switch opt { + case "flow", "inline", "omitempty": + default: + if w.isUserDefined(keyYAML, opt) { + continue + } + return fmt.Sprintf("unknown option '%s' in YAML tag", opt), false + } + } + + return "", true +} + +func (lintStructTagRule) typeValueMatch(t ast.Expr, val string) bool { + tID, ok := t.(*ast.Ident) + if !ok { + return true + } + + typeMatches := true + switch tID.Name { + case "bool": + typeMatches = val == "true" || val == "false" + case "float64": + _, err := strconv.ParseFloat(val, 64) + typeMatches = err == nil + case "int": + _, err := strconv.ParseInt(val, 10, 64) + typeMatches = err == nil + case "string": + case "nil": + default: + // unchecked type + } + + return typeMatches +} + +func (w lintStructTagRule) checkProtobufTag(tag *structtag.Tag) (string, bool) { + // check name + switch tag.Name { + case "bytes", "fixed32", "fixed64", "group", "varint", "zigzag32", "zigzag64": + // do nothing + default: + return fmt.Sprintf("invalid protobuf tag name '%s'", tag.Name), false + } + + // check options + seenOptions := map[string]bool{} + for _, opt := range tag.Options { + if number, err := strconv.Atoi(opt); err == nil { + _, alreadySeen := w.usedTagNbr[number] + if alreadySeen { + return fmt.Sprintf("duplicated tag number %v", number), false + } + w.usedTagNbr[number] = true + continue // option is an integer + } + + switch { + case opt == "opt" || opt == "proto3" || opt == "rep" || opt == "req": + // do nothing + case strings.Contains(opt, "="): + o := strings.Split(opt, "=")[0] + _, alreadySeen := seenOptions[o] + if alreadySeen { + return fmt.Sprintf("protobuf tag has duplicated option '%s'", o), false + } + seenOptions[o] = true + continue + } + } + _, hasName := seenOptions["name"] + if !hasName { + return "protobuf tag lacks mandatory option 'name'", false + } + + for k := range seenOptions { + switch k { + case "name", "json": + // do nothing + default: + if w.isUserDefined(keyProtobuf, k) { + continue + } + return fmt.Sprintf("unknown option '%s' in protobuf tag", k), false + } + } + + return "", true +} + +func (w lintStructTagRule) addFailure(n ast.Node, msg string) { + w.onFailure(lint.Failure{ + Node: n, + Failure: msg, + Confidence: 1, + }) +} + +func (w lintStructTagRule) isUserDefined(key, opt string) bool { + if w.userDefined == nil { + return false + } + + options := w.userDefined[key] + for _, o := range options { + if opt == o { + return true + } + } + return false +} diff --git a/vendor/github.com/mgechev/revive/rule/superfluous-else.go b/vendor/github.com/mgechev/revive/rule/superfluous-else.go deleted file mode 100644 index 18e8f3bdd..000000000 --- a/vendor/github.com/mgechev/revive/rule/superfluous-else.go +++ /dev/null @@ -1,47 +0,0 @@ -package rule - -import ( - "fmt" - - "github.com/mgechev/revive/internal/ifelse" - "github.com/mgechev/revive/lint" -) - -// SuperfluousElseRule lints given else constructs. -type SuperfluousElseRule struct{} - -// Apply applies the rule to given file. -func (e *SuperfluousElseRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - return ifelse.Apply(e, file.AST, ifelse.TargetElse, args) -} - -// Name returns the rule name. -func (*SuperfluousElseRule) Name() string { - return "superfluous-else" -} - -// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable. -func (*SuperfluousElseRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string { - if !chain.If.Deviates() { - // this rule only applies if the if-block deviates control flow - return "" - } - - if chain.HasPriorNonDeviating { - // if we de-indent the "else" block then a previous branch - // might flow into it, affecting program behaviour - return "" - } - - if chain.If.Returns() { - // avoid overlapping with indent-error-flow - return "" - } - - if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls) { - // avoid increasing variable scope - return "" - } - - return fmt.Sprintf("if block ends with %v, so drop this else and outdent its block", chain.If.LongString()) -} diff --git a/vendor/github.com/mgechev/revive/rule/superfluous_else.go b/vendor/github.com/mgechev/revive/rule/superfluous_else.go new file mode 100644 index 000000000..18e8f3bdd --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/superfluous_else.go @@ -0,0 +1,47 @@ +package rule + +import ( + "fmt" + + "github.com/mgechev/revive/internal/ifelse" + "github.com/mgechev/revive/lint" +) + +// SuperfluousElseRule lints given else constructs. +type SuperfluousElseRule struct{} + +// Apply applies the rule to given file. +func (e *SuperfluousElseRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + return ifelse.Apply(e, file.AST, ifelse.TargetElse, args) +} + +// Name returns the rule name. +func (*SuperfluousElseRule) Name() string { + return "superfluous-else" +} + +// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable. +func (*SuperfluousElseRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string { + if !chain.If.Deviates() { + // this rule only applies if the if-block deviates control flow + return "" + } + + if chain.HasPriorNonDeviating { + // if we de-indent the "else" block then a previous branch + // might flow into it, affecting program behaviour + return "" + } + + if chain.If.Returns() { + // avoid overlapping with indent-error-flow + return "" + } + + if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls) { + // avoid increasing variable scope + return "" + } + + return fmt.Sprintf("if block ends with %v, so drop this else and outdent its block", chain.If.LongString()) +} diff --git a/vendor/github.com/mgechev/revive/rule/time-equal.go b/vendor/github.com/mgechev/revive/rule/time-equal.go deleted file mode 100644 index a4fab88b3..000000000 --- a/vendor/github.com/mgechev/revive/rule/time-equal.go +++ /dev/null @@ -1,73 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// TimeEqualRule shows where "==" and "!=" used for equality check time.Time -type TimeEqualRule struct{} - -// Apply applies the rule to given file. -func (*TimeEqualRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := &lintTimeEqual{file, onFailure} - if w.file.Pkg.TypeCheck() != nil { - return nil - } - - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*TimeEqualRule) Name() string { - return "time-equal" -} - -type lintTimeEqual struct { - file *lint.File - onFailure func(lint.Failure) -} - -func (l *lintTimeEqual) Visit(node ast.Node) ast.Visitor { - expr, ok := node.(*ast.BinaryExpr) - if !ok { - return l - } - - switch expr.Op { - case token.EQL, token.NEQ: - default: - return l - } - - typeOfX := l.file.Pkg.TypeOf(expr.X) - typeOfY := l.file.Pkg.TypeOf(expr.Y) - bothAreOfTimeType := isNamedType(typeOfX, "time", "Time") && isNamedType(typeOfY, "time", "Time") - if !bothAreOfTimeType { - return l - } - - negateStr := "" - if token.NEQ == expr.Op { - negateStr = "!" - } - - l.onFailure(lint.Failure{ - Category: "time", - Confidence: 1, - Node: node, - Failure: fmt.Sprintf("use %s%s.Equal(%s) instead of %q operator", negateStr, gofmt(expr.X), gofmt(expr.Y), expr.Op), - }) - - return l -} diff --git a/vendor/github.com/mgechev/revive/rule/time-naming.go b/vendor/github.com/mgechev/revive/rule/time-naming.go deleted file mode 100644 index 5bccf8a7a..000000000 --- a/vendor/github.com/mgechev/revive/rule/time-naming.go +++ /dev/null @@ -1,96 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/types" - "strings" - - "github.com/mgechev/revive/lint" -) - -// TimeNamingRule lints given else constructs. -type TimeNamingRule struct{} - -// Apply applies the rule to given file. -func (*TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := &lintTimeNames{file, onFailure} - - file.Pkg.TypeCheck() - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*TimeNamingRule) Name() string { - return "time-naming" -} - -type lintTimeNames struct { - file *lint.File - onFailure func(lint.Failure) -} - -func (w *lintTimeNames) Visit(node ast.Node) ast.Visitor { - v, ok := node.(*ast.ValueSpec) - if !ok { - return w - } - for _, name := range v.Names { - origTyp := w.file.Pkg.TypeOf(name) - // Look for time.Duration or *time.Duration; - // the latter is common when using flag.Duration. - typ := origTyp - if pt, ok := typ.(*types.Pointer); ok { - typ = pt.Elem() - } - if !isNamedType(typ, "time", "Duration") { - continue - } - suffix := "" - for _, suf := range timeSuffixes { - if strings.HasSuffix(name.Name, suf) { - suffix = suf - break - } - } - if suffix == "" { - continue - } - w.onFailure(lint.Failure{ - Category: "time", - Confidence: 0.9, - Node: v, - Failure: fmt.Sprintf("var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix), - }) - } - return w -} - -// timeSuffixes is a list of name suffixes that imply a time unit. -// This is not an exhaustive list. -var timeSuffixes = []string{ - "Hour", "Hours", - "Min", "Mins", "Minutes", "Minute", - "Sec", "Secs", "Seconds", "Second", - "Msec", "Msecs", - "Milli", "Millis", "Milliseconds", "Millisecond", - "Usec", "Usecs", "Microseconds", "Microsecond", - "MS", "Ms", -} - -func isNamedType(typ types.Type, importPath, name string) bool { - n, ok := typ.(*types.Named) - if !ok { - return false - } - - typeName := n.Obj() - return typeName != nil && typeName.Pkg() != nil && typeName.Pkg().Path() == importPath && typeName.Name() == name -} diff --git a/vendor/github.com/mgechev/revive/rule/time_equal.go b/vendor/github.com/mgechev/revive/rule/time_equal.go new file mode 100644 index 000000000..a4fab88b3 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/time_equal.go @@ -0,0 +1,73 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// TimeEqualRule shows where "==" and "!=" used for equality check time.Time +type TimeEqualRule struct{} + +// Apply applies the rule to given file. +func (*TimeEqualRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := &lintTimeEqual{file, onFailure} + if w.file.Pkg.TypeCheck() != nil { + return nil + } + + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*TimeEqualRule) Name() string { + return "time-equal" +} + +type lintTimeEqual struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (l *lintTimeEqual) Visit(node ast.Node) ast.Visitor { + expr, ok := node.(*ast.BinaryExpr) + if !ok { + return l + } + + switch expr.Op { + case token.EQL, token.NEQ: + default: + return l + } + + typeOfX := l.file.Pkg.TypeOf(expr.X) + typeOfY := l.file.Pkg.TypeOf(expr.Y) + bothAreOfTimeType := isNamedType(typeOfX, "time", "Time") && isNamedType(typeOfY, "time", "Time") + if !bothAreOfTimeType { + return l + } + + negateStr := "" + if token.NEQ == expr.Op { + negateStr = "!" + } + + l.onFailure(lint.Failure{ + Category: "time", + Confidence: 1, + Node: node, + Failure: fmt.Sprintf("use %s%s.Equal(%s) instead of %q operator", negateStr, gofmt(expr.X), gofmt(expr.Y), expr.Op), + }) + + return l +} diff --git a/vendor/github.com/mgechev/revive/rule/time_naming.go b/vendor/github.com/mgechev/revive/rule/time_naming.go new file mode 100644 index 000000000..5bccf8a7a --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/time_naming.go @@ -0,0 +1,96 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/types" + "strings" + + "github.com/mgechev/revive/lint" +) + +// TimeNamingRule lints given else constructs. +type TimeNamingRule struct{} + +// Apply applies the rule to given file. +func (*TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := &lintTimeNames{file, onFailure} + + file.Pkg.TypeCheck() + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*TimeNamingRule) Name() string { + return "time-naming" +} + +type lintTimeNames struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (w *lintTimeNames) Visit(node ast.Node) ast.Visitor { + v, ok := node.(*ast.ValueSpec) + if !ok { + return w + } + for _, name := range v.Names { + origTyp := w.file.Pkg.TypeOf(name) + // Look for time.Duration or *time.Duration; + // the latter is common when using flag.Duration. + typ := origTyp + if pt, ok := typ.(*types.Pointer); ok { + typ = pt.Elem() + } + if !isNamedType(typ, "time", "Duration") { + continue + } + suffix := "" + for _, suf := range timeSuffixes { + if strings.HasSuffix(name.Name, suf) { + suffix = suf + break + } + } + if suffix == "" { + continue + } + w.onFailure(lint.Failure{ + Category: "time", + Confidence: 0.9, + Node: v, + Failure: fmt.Sprintf("var %s is of type %v; don't use unit-specific suffix %q", name.Name, origTyp, suffix), + }) + } + return w +} + +// timeSuffixes is a list of name suffixes that imply a time unit. +// This is not an exhaustive list. +var timeSuffixes = []string{ + "Hour", "Hours", + "Min", "Mins", "Minutes", "Minute", + "Sec", "Secs", "Seconds", "Second", + "Msec", "Msecs", + "Milli", "Millis", "Milliseconds", "Millisecond", + "Usec", "Usecs", "Microseconds", "Microsecond", + "MS", "Ms", +} + +func isNamedType(typ types.Type, importPath, name string) bool { + n, ok := typ.(*types.Named) + if !ok { + return false + } + + typeName := n.Obj() + return typeName != nil && typeName.Pkg() != nil && typeName.Pkg().Path() == importPath && typeName.Name() == name +} diff --git a/vendor/github.com/mgechev/revive/rule/unchecked-type-assertion.go b/vendor/github.com/mgechev/revive/rule/unchecked-type-assertion.go deleted file mode 100644 index eea344060..000000000 --- a/vendor/github.com/mgechev/revive/rule/unchecked-type-assertion.go +++ /dev/null @@ -1,194 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "sync" - - "github.com/mgechev/revive/lint" -) - -const ( - ruleUTAMessagePanic = "type assertion will panic if not matched" - ruleUTAMessageIgnored = "type assertion result ignored" -) - -// UncheckedTypeAssertionRule lints missing or ignored `ok`-value in dynamic type casts. -type UncheckedTypeAssertionRule struct { - sync.Mutex - acceptIgnoredAssertionResult bool - configured bool -} - -func (u *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) { - u.Lock() - defer u.Unlock() - - if len(arguments) == 0 || u.configured { - return - } - - u.configured = true - - args, ok := arguments[0].(map[string]any) - if !ok { - panic("Unable to get arguments. Expected object of key-value-pairs.") - } - - for k, v := range args { - switch k { - case "acceptIgnoredAssertionResult": - u.acceptIgnoredAssertionResult, ok = v.(bool) - if !ok { - panic(fmt.Sprintf("Unable to parse argument '%s'. Expected boolean.", k)) - } - default: - panic(fmt.Sprintf("Unknown argument: %s", k)) - } - } -} - -// Apply applies the rule to given file. -func (u *UncheckedTypeAssertionRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - u.configure(args) - - var failures []lint.Failure - - walker := &lintUncheckedTypeAssertion{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - acceptIgnoredTypeAssertionResult: u.acceptIgnoredAssertionResult, - } - - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*UncheckedTypeAssertionRule) Name() string { - return "unchecked-type-assertion" -} - -type lintUncheckedTypeAssertion struct { - onFailure func(lint.Failure) - acceptIgnoredTypeAssertionResult bool -} - -func isIgnored(e ast.Expr) bool { - ident, ok := e.(*ast.Ident) - if !ok { - return false - } - - return ident.Name == "_" -} - -func isTypeSwitch(e *ast.TypeAssertExpr) bool { - return e.Type == nil -} - -func (w *lintUncheckedTypeAssertion) requireNoTypeAssert(expr ast.Expr) { - e, ok := expr.(*ast.TypeAssertExpr) - if ok && !isTypeSwitch(e) { - w.addFailure(e, ruleUTAMessagePanic) - } -} - -func (w *lintUncheckedTypeAssertion) handleIfStmt(n *ast.IfStmt) { - ifCondition, ok := n.Cond.(*ast.BinaryExpr) - if ok { - w.requireNoTypeAssert(ifCondition.X) - w.requireNoTypeAssert(ifCondition.Y) - } -} - -func (w *lintUncheckedTypeAssertion) requireBinaryExpressionWithoutTypeAssertion(expr ast.Expr) { - binaryExpr, ok := expr.(*ast.BinaryExpr) - if ok { - w.requireNoTypeAssert(binaryExpr.X) - w.requireNoTypeAssert(binaryExpr.Y) - } -} - -func (w *lintUncheckedTypeAssertion) handleCaseClause(n *ast.CaseClause) { - for _, expr := range n.List { - w.requireNoTypeAssert(expr) - w.requireBinaryExpressionWithoutTypeAssertion(expr) - } -} - -func (w *lintUncheckedTypeAssertion) handleSwitch(n *ast.SwitchStmt) { - w.requireNoTypeAssert(n.Tag) - w.requireBinaryExpressionWithoutTypeAssertion(n.Tag) -} - -func (w *lintUncheckedTypeAssertion) handleAssignment(n *ast.AssignStmt) { - if len(n.Rhs) == 0 { - return - } - - e, ok := n.Rhs[0].(*ast.TypeAssertExpr) - if !ok || e == nil { - return - } - - if isTypeSwitch(e) { - return - } - - if len(n.Lhs) == 1 { - w.addFailure(e, ruleUTAMessagePanic) - } - - if !w.acceptIgnoredTypeAssertionResult && len(n.Lhs) == 2 && isIgnored(n.Lhs[1]) { - w.addFailure(e, ruleUTAMessageIgnored) - } -} - -// handles "return foo(.*bar)" - one of them is enough to fail as golang does not forward the type cast tuples in return statements -func (w *lintUncheckedTypeAssertion) handleReturn(n *ast.ReturnStmt) { - for _, r := range n.Results { - w.requireNoTypeAssert(r) - } -} - -func (w *lintUncheckedTypeAssertion) handleRange(n *ast.RangeStmt) { - w.requireNoTypeAssert(n.X) -} - -func (w *lintUncheckedTypeAssertion) handleChannelSend(n *ast.SendStmt) { - w.requireNoTypeAssert(n.Value) -} - -func (w *lintUncheckedTypeAssertion) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.RangeStmt: - w.handleRange(n) - case *ast.SwitchStmt: - w.handleSwitch(n) - case *ast.ReturnStmt: - w.handleReturn(n) - case *ast.AssignStmt: - w.handleAssignment(n) - case *ast.IfStmt: - w.handleIfStmt(n) - case *ast.CaseClause: - w.handleCaseClause(n) - case *ast.SendStmt: - w.handleChannelSend(n) - } - - return w -} - -func (w *lintUncheckedTypeAssertion) addFailure(n *ast.TypeAssertExpr, why string) { - s := fmt.Sprintf("type cast result is unchecked in %v - %s", gofmt(n), why) - w.onFailure(lint.Failure{ - Category: "bad practice", - Confidence: 1, - Node: n, - Failure: s, - }) -} diff --git a/vendor/github.com/mgechev/revive/rule/unchecked_type_assertion.go b/vendor/github.com/mgechev/revive/rule/unchecked_type_assertion.go new file mode 100644 index 000000000..34d854e8f --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unchecked_type_assertion.go @@ -0,0 +1,189 @@ +package rule + +import ( + "fmt" + "go/ast" + "sync" + + "github.com/mgechev/revive/lint" +) + +const ( + ruleUTAMessagePanic = "type assertion will panic if not matched" + ruleUTAMessageIgnored = "type assertion result ignored" +) + +// UncheckedTypeAssertionRule lints missing or ignored `ok`-value in dynamic type casts. +type UncheckedTypeAssertionRule struct { + acceptIgnoredAssertionResult bool + + configureOnce sync.Once +} + +func (r *UncheckedTypeAssertionRule) configure(arguments lint.Arguments) { + if len(arguments) == 0 { + return + } + + args, ok := arguments[0].(map[string]any) + if !ok { + panic("Unable to get arguments. Expected object of key-value-pairs.") + } + + for k, v := range args { + switch k { + case "acceptIgnoredAssertionResult": + r.acceptIgnoredAssertionResult, ok = v.(bool) + if !ok { + panic(fmt.Sprintf("Unable to parse argument '%s'. Expected boolean.", k)) + } + default: + panic(fmt.Sprintf("Unknown argument: %s", k)) + } + } +} + +// Apply applies the rule to given file. +func (r *UncheckedTypeAssertionRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + + var failures []lint.Failure + + walker := &lintUncheckedTypeAssertion{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + acceptIgnoredTypeAssertionResult: r.acceptIgnoredAssertionResult, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*UncheckedTypeAssertionRule) Name() string { + return "unchecked-type-assertion" +} + +type lintUncheckedTypeAssertion struct { + onFailure func(lint.Failure) + acceptIgnoredTypeAssertionResult bool +} + +func isIgnored(e ast.Expr) bool { + ident, ok := e.(*ast.Ident) + if !ok { + return false + } + + return ident.Name == "_" +} + +func isTypeSwitch(e *ast.TypeAssertExpr) bool { + return e.Type == nil +} + +func (w *lintUncheckedTypeAssertion) requireNoTypeAssert(expr ast.Expr) { + e, ok := expr.(*ast.TypeAssertExpr) + if ok && !isTypeSwitch(e) { + w.addFailure(e, ruleUTAMessagePanic) + } +} + +func (w *lintUncheckedTypeAssertion) handleIfStmt(n *ast.IfStmt) { + ifCondition, ok := n.Cond.(*ast.BinaryExpr) + if ok { + w.requireNoTypeAssert(ifCondition.X) + w.requireNoTypeAssert(ifCondition.Y) + } +} + +func (w *lintUncheckedTypeAssertion) requireBinaryExpressionWithoutTypeAssertion(expr ast.Expr) { + binaryExpr, ok := expr.(*ast.BinaryExpr) + if ok { + w.requireNoTypeAssert(binaryExpr.X) + w.requireNoTypeAssert(binaryExpr.Y) + } +} + +func (w *lintUncheckedTypeAssertion) handleCaseClause(n *ast.CaseClause) { + for _, expr := range n.List { + w.requireNoTypeAssert(expr) + w.requireBinaryExpressionWithoutTypeAssertion(expr) + } +} + +func (w *lintUncheckedTypeAssertion) handleSwitch(n *ast.SwitchStmt) { + w.requireNoTypeAssert(n.Tag) + w.requireBinaryExpressionWithoutTypeAssertion(n.Tag) +} + +func (w *lintUncheckedTypeAssertion) handleAssignment(n *ast.AssignStmt) { + if len(n.Rhs) == 0 { + return + } + + e, ok := n.Rhs[0].(*ast.TypeAssertExpr) + if !ok || e == nil { + return + } + + if isTypeSwitch(e) { + return + } + + if len(n.Lhs) == 1 { + w.addFailure(e, ruleUTAMessagePanic) + } + + if !w.acceptIgnoredTypeAssertionResult && len(n.Lhs) == 2 && isIgnored(n.Lhs[1]) { + w.addFailure(e, ruleUTAMessageIgnored) + } +} + +// handles "return foo(.*bar)" - one of them is enough to fail as golang does not forward the type cast tuples in return statements +func (w *lintUncheckedTypeAssertion) handleReturn(n *ast.ReturnStmt) { + for _, r := range n.Results { + w.requireNoTypeAssert(r) + } +} + +func (w *lintUncheckedTypeAssertion) handleRange(n *ast.RangeStmt) { + w.requireNoTypeAssert(n.X) +} + +func (w *lintUncheckedTypeAssertion) handleChannelSend(n *ast.SendStmt) { + w.requireNoTypeAssert(n.Value) +} + +func (w *lintUncheckedTypeAssertion) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.RangeStmt: + w.handleRange(n) + case *ast.SwitchStmt: + w.handleSwitch(n) + case *ast.ReturnStmt: + w.handleReturn(n) + case *ast.AssignStmt: + w.handleAssignment(n) + case *ast.IfStmt: + w.handleIfStmt(n) + case *ast.CaseClause: + w.handleCaseClause(n) + case *ast.SendStmt: + w.handleChannelSend(n) + } + + return w +} + +func (w *lintUncheckedTypeAssertion) addFailure(n *ast.TypeAssertExpr, why string) { + s := fmt.Sprintf("type cast result is unchecked in %v - %s", gofmt(n), why) + w.onFailure(lint.Failure{ + Category: "bad practice", + Confidence: 1, + Node: n, + Failure: s, + }) +} diff --git a/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go b/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go deleted file mode 100644 index d806b6757..000000000 --- a/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go +++ /dev/null @@ -1,200 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// UnconditionalRecursionRule lints given else constructs. -type UnconditionalRecursionRule struct{} - -// Apply applies the rule to given file. -func (*UnconditionalRecursionRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintUnconditionalRecursionRule{onFailure: onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*UnconditionalRecursionRule) Name() string { - return "unconditional-recursion" -} - -type funcDesc struct { - receiverID *ast.Ident - id *ast.Ident -} - -func (fd *funcDesc) equal(other *funcDesc) bool { - receiversAreEqual := (fd.receiverID == nil && other.receiverID == nil) || fd.receiverID != nil && other.receiverID != nil && fd.receiverID.Name == other.receiverID.Name - idsAreEqual := (fd.id == nil && other.id == nil) || fd.id.Name == other.id.Name - - return receiversAreEqual && idsAreEqual -} - -type funcStatus struct { - funcDesc *funcDesc - seenConditionalExit bool -} - -type lintUnconditionalRecursionRule struct { - onFailure func(lint.Failure) - currentFunc *funcStatus - inGoStatement bool -} - -// Visit will traverse the file AST. -// The rule is based in the following algorithm: inside each function body we search for calls to the function itself. -// We do not search inside conditional control structures (if, for, switch, ...) because any recursive call inside them is conditioned -// We do search inside conditional control structures are statements that will take the control out of the function (return, exit, panic) -// If we find conditional control exits, it means the function is NOT unconditionally-recursive -// If we find a recursive call before finding any conditional exit, a failure is generated -// In resume: if we found a recursive call control-dependant from the entry point of the function then we raise a failure. -func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - var rec *ast.Ident - switch { - case n.Recv == nil: - rec = nil - case n.Recv.NumFields() < 1 || len(n.Recv.List[0].Names) < 1: - rec = &ast.Ident{Name: "_"} - default: - rec = n.Recv.List[0].Names[0] - } - w.currentFunc = &funcStatus{&funcDesc{rec, n.Name}, false} - case *ast.CallExpr: - // check if call arguments has a recursive call - for _, arg := range n.Args { - ast.Walk(w, arg) - } - - var funcID *ast.Ident - var selector *ast.Ident - switch c := n.Fun.(type) { - case *ast.Ident: - selector = nil - funcID = c - case *ast.SelectorExpr: - var ok bool - selector, ok = c.X.(*ast.Ident) - if !ok { // a.b....Foo() - return nil - } - funcID = c.Sel - case *ast.FuncLit: - ast.Walk(w, c.Body) // analyze the body of the function literal - return nil - default: - return w - } - - if w.currentFunc != nil && // not in a func body - !w.currentFunc.seenConditionalExit && // there is a conditional exit in the function - w.currentFunc.funcDesc.equal(&funcDesc{selector, funcID}) { - w.onFailure(lint.Failure{ - Category: "logic", - Confidence: 0.8, - Node: n, - Failure: "unconditional recursive call", - }) - } - return nil - case *ast.IfStmt: - w.updateFuncStatus(n.Body) - w.updateFuncStatus(n.Else) - return nil - case *ast.SelectStmt: - w.updateFuncStatus(n.Body) - return nil - case *ast.RangeStmt: - w.updateFuncStatus(n.Body) - return nil - case *ast.TypeSwitchStmt: - w.updateFuncStatus(n.Body) - return nil - case *ast.SwitchStmt: - w.updateFuncStatus(n.Body) - return nil - case *ast.GoStmt: - w.inGoStatement = true - ast.Walk(w, n.Call) - w.inGoStatement = false - return nil - case *ast.ForStmt: - if n.Cond != nil { - return nil - } - // unconditional loop - return w - case *ast.FuncLit: - if w.inGoStatement { - return w - } - return nil // literal call (closure) is not necessarily an issue - } - - return w -} - -func (w *lintUnconditionalRecursionRule) updateFuncStatus(node ast.Node) { - if node == nil || w.currentFunc == nil || w.currentFunc.seenConditionalExit { - return - } - - w.currentFunc.seenConditionalExit = w.hasControlExit(node) -} - -var exitFunctions = map[string]map[string]bool{ - "os": {"Exit": true}, - "syscall": {"Exit": true}, - "log": { - "Fatal": true, - "Fatalf": true, - "Fatalln": true, - "Panic": true, - "Panicf": true, - "Panicln": true, - }, -} - -func (lintUnconditionalRecursionRule) hasControlExit(node ast.Node) bool { - // isExit returns true if the given node makes control exit the function - isExit := func(node ast.Node) bool { - switch n := node.(type) { - case *ast.ReturnStmt: - return true - case *ast.CallExpr: - if isIdent(n.Fun, "panic") { - return true - } - se, ok := n.Fun.(*ast.SelectorExpr) - if !ok { - return false - } - - id, ok := se.X.(*ast.Ident) - if !ok { - return false - } - - functionName := se.Sel.Name - pkgName := id.Name - isCallToExitFunction := exitFunctions[pkgName] != nil && exitFunctions[pkgName][functionName] - if isCallToExitFunction { - return true - } - } - - return false - } - - return len(pick(node, isExit)) != 0 -} diff --git a/vendor/github.com/mgechev/revive/rule/unconditional_recursion.go b/vendor/github.com/mgechev/revive/rule/unconditional_recursion.go new file mode 100644 index 000000000..d806b6757 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unconditional_recursion.go @@ -0,0 +1,200 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// UnconditionalRecursionRule lints given else constructs. +type UnconditionalRecursionRule struct{} + +// Apply applies the rule to given file. +func (*UnconditionalRecursionRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintUnconditionalRecursionRule{onFailure: onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*UnconditionalRecursionRule) Name() string { + return "unconditional-recursion" +} + +type funcDesc struct { + receiverID *ast.Ident + id *ast.Ident +} + +func (fd *funcDesc) equal(other *funcDesc) bool { + receiversAreEqual := (fd.receiverID == nil && other.receiverID == nil) || fd.receiverID != nil && other.receiverID != nil && fd.receiverID.Name == other.receiverID.Name + idsAreEqual := (fd.id == nil && other.id == nil) || fd.id.Name == other.id.Name + + return receiversAreEqual && idsAreEqual +} + +type funcStatus struct { + funcDesc *funcDesc + seenConditionalExit bool +} + +type lintUnconditionalRecursionRule struct { + onFailure func(lint.Failure) + currentFunc *funcStatus + inGoStatement bool +} + +// Visit will traverse the file AST. +// The rule is based in the following algorithm: inside each function body we search for calls to the function itself. +// We do not search inside conditional control structures (if, for, switch, ...) because any recursive call inside them is conditioned +// We do search inside conditional control structures are statements that will take the control out of the function (return, exit, panic) +// If we find conditional control exits, it means the function is NOT unconditionally-recursive +// If we find a recursive call before finding any conditional exit, a failure is generated +// In resume: if we found a recursive call control-dependant from the entry point of the function then we raise a failure. +func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + var rec *ast.Ident + switch { + case n.Recv == nil: + rec = nil + case n.Recv.NumFields() < 1 || len(n.Recv.List[0].Names) < 1: + rec = &ast.Ident{Name: "_"} + default: + rec = n.Recv.List[0].Names[0] + } + w.currentFunc = &funcStatus{&funcDesc{rec, n.Name}, false} + case *ast.CallExpr: + // check if call arguments has a recursive call + for _, arg := range n.Args { + ast.Walk(w, arg) + } + + var funcID *ast.Ident + var selector *ast.Ident + switch c := n.Fun.(type) { + case *ast.Ident: + selector = nil + funcID = c + case *ast.SelectorExpr: + var ok bool + selector, ok = c.X.(*ast.Ident) + if !ok { // a.b....Foo() + return nil + } + funcID = c.Sel + case *ast.FuncLit: + ast.Walk(w, c.Body) // analyze the body of the function literal + return nil + default: + return w + } + + if w.currentFunc != nil && // not in a func body + !w.currentFunc.seenConditionalExit && // there is a conditional exit in the function + w.currentFunc.funcDesc.equal(&funcDesc{selector, funcID}) { + w.onFailure(lint.Failure{ + Category: "logic", + Confidence: 0.8, + Node: n, + Failure: "unconditional recursive call", + }) + } + return nil + case *ast.IfStmt: + w.updateFuncStatus(n.Body) + w.updateFuncStatus(n.Else) + return nil + case *ast.SelectStmt: + w.updateFuncStatus(n.Body) + return nil + case *ast.RangeStmt: + w.updateFuncStatus(n.Body) + return nil + case *ast.TypeSwitchStmt: + w.updateFuncStatus(n.Body) + return nil + case *ast.SwitchStmt: + w.updateFuncStatus(n.Body) + return nil + case *ast.GoStmt: + w.inGoStatement = true + ast.Walk(w, n.Call) + w.inGoStatement = false + return nil + case *ast.ForStmt: + if n.Cond != nil { + return nil + } + // unconditional loop + return w + case *ast.FuncLit: + if w.inGoStatement { + return w + } + return nil // literal call (closure) is not necessarily an issue + } + + return w +} + +func (w *lintUnconditionalRecursionRule) updateFuncStatus(node ast.Node) { + if node == nil || w.currentFunc == nil || w.currentFunc.seenConditionalExit { + return + } + + w.currentFunc.seenConditionalExit = w.hasControlExit(node) +} + +var exitFunctions = map[string]map[string]bool{ + "os": {"Exit": true}, + "syscall": {"Exit": true}, + "log": { + "Fatal": true, + "Fatalf": true, + "Fatalln": true, + "Panic": true, + "Panicf": true, + "Panicln": true, + }, +} + +func (lintUnconditionalRecursionRule) hasControlExit(node ast.Node) bool { + // isExit returns true if the given node makes control exit the function + isExit := func(node ast.Node) bool { + switch n := node.(type) { + case *ast.ReturnStmt: + return true + case *ast.CallExpr: + if isIdent(n.Fun, "panic") { + return true + } + se, ok := n.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + + id, ok := se.X.(*ast.Ident) + if !ok { + return false + } + + functionName := se.Sel.Name + pkgName := id.Name + isCallToExitFunction := exitFunctions[pkgName] != nil && exitFunctions[pkgName][functionName] + if isCallToExitFunction { + return true + } + } + + return false + } + + return len(pick(node, isExit)) != 0 +} diff --git a/vendor/github.com/mgechev/revive/rule/unexported-naming.go b/vendor/github.com/mgechev/revive/rule/unexported-naming.go deleted file mode 100644 index 0c2b39d41..000000000 --- a/vendor/github.com/mgechev/revive/rule/unexported-naming.go +++ /dev/null @@ -1,115 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// UnexportedNamingRule lints wrongly named unexported symbols. -type UnexportedNamingRule struct{} - -// Apply applies the rule to given file. -func (*UnexportedNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - ba := &unexportablenamingLinter{onFailure} - ast.Walk(ba, file.AST) - - return failures -} - -// Name returns the rule name. -func (*UnexportedNamingRule) Name() string { - return "unexported-naming" -} - -type unexportablenamingLinter struct { - onFailure func(lint.Failure) -} - -func (unl unexportablenamingLinter) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - unl.lintFunction(n.Type, n.Body) - return nil - case *ast.FuncLit: - unl.lintFunction(n.Type, n.Body) - - return nil - case *ast.AssignStmt: - if n.Tok != token.DEFINE { - return nil - } - - ids := []*ast.Ident{} - for _, e := range n.Lhs { - id, ok := e.(*ast.Ident) - if !ok { - continue - } - ids = append(ids, id) - } - - unl.lintIDs(ids) - - case *ast.DeclStmt: - gd, ok := n.Decl.(*ast.GenDecl) - if !ok { - return nil - } - - if len(gd.Specs) < 1 { - return nil - } - - vs, ok := gd.Specs[0].(*ast.ValueSpec) - if !ok { - return nil - } - - unl.lintIDs(vs.Names) - } - - return unl -} - -func (unl unexportablenamingLinter) lintFunction(ft *ast.FuncType, body *ast.BlockStmt) { - unl.lintFields(ft.Params) - unl.lintFields(ft.Results) - - if body != nil { - ast.Walk(unl, body) - } -} - -func (unl unexportablenamingLinter) lintFields(fields *ast.FieldList) { - if fields == nil { - return - } - - ids := []*ast.Ident{} - for _, field := range fields.List { - ids = append(ids, field.Names...) - } - - unl.lintIDs(ids) -} - -func (unl unexportablenamingLinter) lintIDs(ids []*ast.Ident) { - for _, id := range ids { - if id.IsExported() { - unl.onFailure(lint.Failure{ - Node: id, - Confidence: 1, - Category: "naming", - Failure: fmt.Sprintf("the symbol %s is local, its name should start with a lowercase letter", id.String()), - }) - } - } -} diff --git a/vendor/github.com/mgechev/revive/rule/unexported-return.go b/vendor/github.com/mgechev/revive/rule/unexported-return.go deleted file mode 100644 index 10f8e3fbe..000000000 --- a/vendor/github.com/mgechev/revive/rule/unexported-return.go +++ /dev/null @@ -1,107 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/types" - - "github.com/mgechev/revive/internal/typeparams" - "github.com/mgechev/revive/lint" -) - -// UnexportedReturnRule lints given else constructs. -type UnexportedReturnRule struct{} - -// Apply applies the rule to given file. -func (*UnexportedReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintUnexportedReturn{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - file.Pkg.TypeCheck() - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*UnexportedReturnRule) Name() string { - return "unexported-return" -} - -type lintUnexportedReturn struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) -} - -func (w lintUnexportedReturn) Visit(n ast.Node) ast.Visitor { - fn, ok := n.(*ast.FuncDecl) - if !ok { - return w - } - if fn.Type.Results == nil { - return nil - } - if !fn.Name.IsExported() { - return nil - } - thing := "func" - if fn.Recv != nil && len(fn.Recv.List) > 0 { - thing = "method" - if !ast.IsExported(typeparams.ReceiverType(fn)) { - // Don't report exported methods of unexported types, - // such as private implementations of sort.Interface. - return nil - } - } - for _, ret := range fn.Type.Results.List { - typ := w.file.Pkg.TypeOf(ret.Type) - if exportedType(typ) { - continue - } - w.onFailure(lint.Failure{ - Category: "unexported-type-in-api", - Node: ret.Type, - Confidence: 0.8, - Failure: fmt.Sprintf("exported %s %s returns unexported type %s, which can be annoying to use", - thing, fn.Name.Name, typ), - }) - break // only flag one - } - return nil -} - -// exportedType reports whether typ is an exported type. -// It is imprecise, and will err on the side of returning true, -// such as for composite types. -func exportedType(typ types.Type) bool { - switch t := typ.(type) { - case *types.Named: - obj := t.Obj() - switch { - // Builtin types have no package. - case obj.Pkg() == nil: - case obj.Exported(): - default: - _, ok := t.Underlying().(*types.Interface) - return ok - } - return true - case *types.Map: - return exportedType(t.Key()) && exportedType(t.Elem()) - case interface { - Elem() types.Type - }: // array, slice, pointer, chan - return exportedType(t.Elem()) - } - // Be conservative about other types, such as struct, interface, etc. - return true -} diff --git a/vendor/github.com/mgechev/revive/rule/unexported_naming.go b/vendor/github.com/mgechev/revive/rule/unexported_naming.go new file mode 100644 index 000000000..0c2b39d41 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unexported_naming.go @@ -0,0 +1,115 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// UnexportedNamingRule lints wrongly named unexported symbols. +type UnexportedNamingRule struct{} + +// Apply applies the rule to given file. +func (*UnexportedNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + ba := &unexportablenamingLinter{onFailure} + ast.Walk(ba, file.AST) + + return failures +} + +// Name returns the rule name. +func (*UnexportedNamingRule) Name() string { + return "unexported-naming" +} + +type unexportablenamingLinter struct { + onFailure func(lint.Failure) +} + +func (unl unexportablenamingLinter) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + unl.lintFunction(n.Type, n.Body) + return nil + case *ast.FuncLit: + unl.lintFunction(n.Type, n.Body) + + return nil + case *ast.AssignStmt: + if n.Tok != token.DEFINE { + return nil + } + + ids := []*ast.Ident{} + for _, e := range n.Lhs { + id, ok := e.(*ast.Ident) + if !ok { + continue + } + ids = append(ids, id) + } + + unl.lintIDs(ids) + + case *ast.DeclStmt: + gd, ok := n.Decl.(*ast.GenDecl) + if !ok { + return nil + } + + if len(gd.Specs) < 1 { + return nil + } + + vs, ok := gd.Specs[0].(*ast.ValueSpec) + if !ok { + return nil + } + + unl.lintIDs(vs.Names) + } + + return unl +} + +func (unl unexportablenamingLinter) lintFunction(ft *ast.FuncType, body *ast.BlockStmt) { + unl.lintFields(ft.Params) + unl.lintFields(ft.Results) + + if body != nil { + ast.Walk(unl, body) + } +} + +func (unl unexportablenamingLinter) lintFields(fields *ast.FieldList) { + if fields == nil { + return + } + + ids := []*ast.Ident{} + for _, field := range fields.List { + ids = append(ids, field.Names...) + } + + unl.lintIDs(ids) +} + +func (unl unexportablenamingLinter) lintIDs(ids []*ast.Ident) { + for _, id := range ids { + if id.IsExported() { + unl.onFailure(lint.Failure{ + Node: id, + Confidence: 1, + Category: "naming", + Failure: fmt.Sprintf("the symbol %s is local, its name should start with a lowercase letter", id.String()), + }) + } + } +} diff --git a/vendor/github.com/mgechev/revive/rule/unexported_return.go b/vendor/github.com/mgechev/revive/rule/unexported_return.go new file mode 100644 index 000000000..10f8e3fbe --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unexported_return.go @@ -0,0 +1,107 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/types" + + "github.com/mgechev/revive/internal/typeparams" + "github.com/mgechev/revive/lint" +) + +// UnexportedReturnRule lints given else constructs. +type UnexportedReturnRule struct{} + +// Apply applies the rule to given file. +func (*UnexportedReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := lintUnexportedReturn{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + file.Pkg.TypeCheck() + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*UnexportedReturnRule) Name() string { + return "unexported-return" +} + +type lintUnexportedReturn struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) +} + +func (w lintUnexportedReturn) Visit(n ast.Node) ast.Visitor { + fn, ok := n.(*ast.FuncDecl) + if !ok { + return w + } + if fn.Type.Results == nil { + return nil + } + if !fn.Name.IsExported() { + return nil + } + thing := "func" + if fn.Recv != nil && len(fn.Recv.List) > 0 { + thing = "method" + if !ast.IsExported(typeparams.ReceiverType(fn)) { + // Don't report exported methods of unexported types, + // such as private implementations of sort.Interface. + return nil + } + } + for _, ret := range fn.Type.Results.List { + typ := w.file.Pkg.TypeOf(ret.Type) + if exportedType(typ) { + continue + } + w.onFailure(lint.Failure{ + Category: "unexported-type-in-api", + Node: ret.Type, + Confidence: 0.8, + Failure: fmt.Sprintf("exported %s %s returns unexported type %s, which can be annoying to use", + thing, fn.Name.Name, typ), + }) + break // only flag one + } + return nil +} + +// exportedType reports whether typ is an exported type. +// It is imprecise, and will err on the side of returning true, +// such as for composite types. +func exportedType(typ types.Type) bool { + switch t := typ.(type) { + case *types.Named: + obj := t.Obj() + switch { + // Builtin types have no package. + case obj.Pkg() == nil: + case obj.Exported(): + default: + _, ok := t.Underlying().(*types.Interface) + return ok + } + return true + case *types.Map: + return exportedType(t.Key()) && exportedType(t.Elem()) + case interface { + Elem() types.Type + }: // array, slice, pointer, chan + return exportedType(t.Elem()) + } + // Be conservative about other types, such as struct, interface, etc. + return true +} diff --git a/vendor/github.com/mgechev/revive/rule/unhandled-error.go b/vendor/github.com/mgechev/revive/rule/unhandled-error.go deleted file mode 100644 index 95ba56180..000000000 --- a/vendor/github.com/mgechev/revive/rule/unhandled-error.go +++ /dev/null @@ -1,181 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/types" - "regexp" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -// UnhandledErrorRule lints given else constructs. -type UnhandledErrorRule struct { - ignoreList []*regexp.Regexp - sync.Mutex -} - -func (r *UnhandledErrorRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.ignoreList != nil { - return // already configured - } - - for _, arg := range arguments { - argStr, ok := arg.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) - } - - argStr = strings.Trim(argStr, " ") - if argStr == "" { - panic("Invalid argument to the unhandled-error rule, expected regular expression must not be empty.") - } - - exp, err := regexp.Compile(argStr) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the unhandled-error rule: regexp %q does not compile: %v", argStr, err)) - } - - r.ignoreList = append(r.ignoreList, exp) - } -} - -// Apply applies the rule to given file. -func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) - - var failures []lint.Failure - - walker := &lintUnhandledErrors{ - ignoreList: r.ignoreList, - pkg: file.Pkg, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - file.Pkg.TypeCheck() - ast.Walk(walker, file.AST) - - return failures -} - -// Name returns the rule name. -func (*UnhandledErrorRule) Name() string { - return "unhandled-error" -} - -type lintUnhandledErrors struct { - ignoreList []*regexp.Regexp - pkg *lint.Package - onFailure func(lint.Failure) -} - -// Visit looks for statements that are function calls. -// If the called function returns a value of type error a failure will be created. -func (w *lintUnhandledErrors) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.ExprStmt: - fCall, ok := n.X.(*ast.CallExpr) - if !ok { - return nil // not a function call - } - - funcType := w.pkg.TypeOf(fCall) - if funcType == nil { - return nil // skip, type info not available - } - - switch t := funcType.(type) { - case *types.Named: - if !w.isTypeError(t) { - return nil // func call does not return an error - } - - w.addFailure(fCall) - default: - retTypes, ok := funcType.Underlying().(*types.Tuple) - if !ok { - return nil // skip, unable to retrieve return type of the called function - } - - if w.returnsAnError(retTypes) { - w.addFailure(fCall) - } - } - } - return w -} - -func (w *lintUnhandledErrors) addFailure(n *ast.CallExpr) { - name := w.funcName(n) - if w.isIgnoredFunc(name) { - return - } - - w.onFailure(lint.Failure{ - Category: "bad practice", - Confidence: 1, - Node: n, - Failure: fmt.Sprintf("Unhandled error in call to function %v", name), - }) -} - -func (w *lintUnhandledErrors) funcName(call *ast.CallExpr) string { - fn, ok := w.getFunc(call) - if !ok { - return gofmt(call.Fun) - } - - name := fn.FullName() - name = strings.ReplaceAll(name, "(", "") - name = strings.ReplaceAll(name, ")", "") - name = strings.ReplaceAll(name, "*", "") - - return name -} - -func (w *lintUnhandledErrors) isIgnoredFunc(funcName string) bool { - for _, pattern := range w.ignoreList { - if len(pattern.FindString(funcName)) == len(funcName) { - return true - } - } - - return false -} - -func (*lintUnhandledErrors) isTypeError(t *types.Named) bool { - const errorTypeName = "_.error" - - return t.Obj().Id() == errorTypeName -} - -func (w *lintUnhandledErrors) returnsAnError(tt *types.Tuple) bool { - for i := 0; i < tt.Len(); i++ { - nt, ok := tt.At(i).Type().(*types.Named) - if ok && w.isTypeError(nt) { - return true - } - } - return false -} - -func (w *lintUnhandledErrors) getFunc(call *ast.CallExpr) (*types.Func, bool) { - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return nil, false - } - - fn, ok := w.pkg.TypesInfo().ObjectOf(sel.Sel).(*types.Func) - if !ok { - return nil, false - } - - return fn, true -} diff --git a/vendor/github.com/mgechev/revive/rule/unhandled_error.go b/vendor/github.com/mgechev/revive/rule/unhandled_error.go new file mode 100644 index 000000000..4fad8ccfc --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unhandled_error.go @@ -0,0 +1,175 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/types" + "regexp" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// UnhandledErrorRule lints given else constructs. +type UnhandledErrorRule struct { + ignoreList []*regexp.Regexp + + configureOnce sync.Once +} + +func (r *UnhandledErrorRule) configure(arguments lint.Arguments) { + for _, arg := range arguments { + argStr, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) + } + + argStr = strings.Trim(argStr, " ") + if argStr == "" { + panic("Invalid argument to the unhandled-error rule, expected regular expression must not be empty.") + } + + exp, err := regexp.Compile(argStr) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the unhandled-error rule: regexp %q does not compile: %v", argStr, err)) + } + + r.ignoreList = append(r.ignoreList, exp) + } +} + +// Apply applies the rule to given file. +func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + + var failures []lint.Failure + + walker := &lintUnhandledErrors{ + ignoreList: r.ignoreList, + pkg: file.Pkg, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + file.Pkg.TypeCheck() + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*UnhandledErrorRule) Name() string { + return "unhandled-error" +} + +type lintUnhandledErrors struct { + ignoreList []*regexp.Regexp + pkg *lint.Package + onFailure func(lint.Failure) +} + +// Visit looks for statements that are function calls. +// If the called function returns a value of type error a failure will be created. +func (w *lintUnhandledErrors) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.ExprStmt: + fCall, ok := n.X.(*ast.CallExpr) + if !ok { + return nil // not a function call + } + + funcType := w.pkg.TypeOf(fCall) + if funcType == nil { + return nil // skip, type info not available + } + + switch t := funcType.(type) { + case *types.Named: + if !w.isTypeError(t) { + return nil // func call does not return an error + } + + w.addFailure(fCall) + default: + retTypes, ok := funcType.Underlying().(*types.Tuple) + if !ok { + return nil // skip, unable to retrieve return type of the called function + } + + if w.returnsAnError(retTypes) { + w.addFailure(fCall) + } + } + } + return w +} + +func (w *lintUnhandledErrors) addFailure(n *ast.CallExpr) { + name := w.funcName(n) + if w.isIgnoredFunc(name) { + return + } + + w.onFailure(lint.Failure{ + Category: "bad practice", + Confidence: 1, + Node: n, + Failure: fmt.Sprintf("Unhandled error in call to function %v", name), + }) +} + +func (w *lintUnhandledErrors) funcName(call *ast.CallExpr) string { + fn, ok := w.getFunc(call) + if !ok { + return gofmt(call.Fun) + } + + name := fn.FullName() + name = strings.ReplaceAll(name, "(", "") + name = strings.ReplaceAll(name, ")", "") + name = strings.ReplaceAll(name, "*", "") + + return name +} + +func (w *lintUnhandledErrors) isIgnoredFunc(funcName string) bool { + for _, pattern := range w.ignoreList { + if len(pattern.FindString(funcName)) == len(funcName) { + return true + } + } + + return false +} + +func (*lintUnhandledErrors) isTypeError(t *types.Named) bool { + const errorTypeName = "_.error" + + return t.Obj().Id() == errorTypeName +} + +func (w *lintUnhandledErrors) returnsAnError(tt *types.Tuple) bool { + for i := 0; i < tt.Len(); i++ { + nt, ok := tt.At(i).Type().(*types.Named) + if ok && w.isTypeError(nt) { + return true + } + } + return false +} + +func (w *lintUnhandledErrors) getFunc(call *ast.CallExpr) (*types.Func, bool) { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return nil, false + } + + fn, ok := w.pkg.TypesInfo().ObjectOf(sel.Sel).(*types.Func) + if !ok { + return nil, false + } + + return fn, true +} diff --git a/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go b/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go deleted file mode 100644 index 8e0784ba4..000000000 --- a/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go +++ /dev/null @@ -1,107 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// UnnecessaryStmtRule warns on unnecessary statements. -type UnnecessaryStmtRule struct{} - -// Apply applies the rule to given file. -func (*UnnecessaryStmtRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintUnnecessaryStmtRule{onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*UnnecessaryStmtRule) Name() string { - return "unnecessary-stmt" -} - -type lintUnnecessaryStmtRule struct { - onFailure func(lint.Failure) -} - -func (w lintUnnecessaryStmtRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - if n.Body == nil || n.Type.Results != nil { - return w - } - stmts := n.Body.List - if len(stmts) == 0 { - return w - } - - lastStmt := stmts[len(stmts)-1] - rs, ok := lastStmt.(*ast.ReturnStmt) - if !ok { - return w - } - - if len(rs.Results) == 0 { - w.newFailure(lastStmt, "omit unnecessary return statement") - } - - case *ast.SwitchStmt: - w.checkSwitchBody(n.Body) - case *ast.TypeSwitchStmt: - w.checkSwitchBody(n.Body) - case *ast.CaseClause: - if n.Body == nil { - return w - } - stmts := n.Body - if len(stmts) == 0 { - return w - } - - lastStmt := stmts[len(stmts)-1] - rs, ok := lastStmt.(*ast.BranchStmt) - if !ok { - return w - } - - if rs.Tok == token.BREAK && rs.Label == nil { - w.newFailure(lastStmt, "omit unnecessary break at the end of case clause") - } - } - - return w -} - -func (w lintUnnecessaryStmtRule) checkSwitchBody(b *ast.BlockStmt) { - cases := b.List - if len(cases) != 1 { - return - } - - cc, ok := cases[0].(*ast.CaseClause) - if !ok { - return - } - - if len(cc.List) > 1 { // skip cases with multiple expressions - return - } - - w.newFailure(b, "switch with only one case can be replaced by an if-then") -} - -func (w lintUnnecessaryStmtRule) newFailure(node ast.Node, msg string) { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: node, - Category: "style", - Failure: msg, - }) -} diff --git a/vendor/github.com/mgechev/revive/rule/unnecessary_stmt.go b/vendor/github.com/mgechev/revive/rule/unnecessary_stmt.go new file mode 100644 index 000000000..8e0784ba4 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unnecessary_stmt.go @@ -0,0 +1,107 @@ +package rule + +import ( + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// UnnecessaryStmtRule warns on unnecessary statements. +type UnnecessaryStmtRule struct{} + +// Apply applies the rule to given file. +func (*UnnecessaryStmtRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintUnnecessaryStmtRule{onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*UnnecessaryStmtRule) Name() string { + return "unnecessary-stmt" +} + +type lintUnnecessaryStmtRule struct { + onFailure func(lint.Failure) +} + +func (w lintUnnecessaryStmtRule) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + if n.Body == nil || n.Type.Results != nil { + return w + } + stmts := n.Body.List + if len(stmts) == 0 { + return w + } + + lastStmt := stmts[len(stmts)-1] + rs, ok := lastStmt.(*ast.ReturnStmt) + if !ok { + return w + } + + if len(rs.Results) == 0 { + w.newFailure(lastStmt, "omit unnecessary return statement") + } + + case *ast.SwitchStmt: + w.checkSwitchBody(n.Body) + case *ast.TypeSwitchStmt: + w.checkSwitchBody(n.Body) + case *ast.CaseClause: + if n.Body == nil { + return w + } + stmts := n.Body + if len(stmts) == 0 { + return w + } + + lastStmt := stmts[len(stmts)-1] + rs, ok := lastStmt.(*ast.BranchStmt) + if !ok { + return w + } + + if rs.Tok == token.BREAK && rs.Label == nil { + w.newFailure(lastStmt, "omit unnecessary break at the end of case clause") + } + } + + return w +} + +func (w lintUnnecessaryStmtRule) checkSwitchBody(b *ast.BlockStmt) { + cases := b.List + if len(cases) != 1 { + return + } + + cc, ok := cases[0].(*ast.CaseClause) + if !ok { + return + } + + if len(cc.List) > 1 { // skip cases with multiple expressions + return + } + + w.newFailure(b, "switch with only one case can be replaced by an if-then") +} + +func (w lintUnnecessaryStmtRule) newFailure(node ast.Node, msg string) { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: node, + Category: "style", + Failure: msg, + }) +} diff --git a/vendor/github.com/mgechev/revive/rule/unreachable-code.go b/vendor/github.com/mgechev/revive/rule/unreachable-code.go deleted file mode 100644 index dcc5b7905..000000000 --- a/vendor/github.com/mgechev/revive/rule/unreachable-code.go +++ /dev/null @@ -1,122 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// UnreachableCodeRule lints unreachable code. -type UnreachableCodeRule struct{} - -// Apply applies the rule to given file. -func (*UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - testingFunctions := map[string]bool{ - "Fatal": true, - "Fatalf": true, - "FailNow": true, - } - branchingFunctions := map[string]map[string]bool{ - "os": {"Exit": true}, - "log": { - "Fatal": true, - "Fatalf": true, - "Fatalln": true, - "Panic": true, - "Panicf": true, - "Panicln": true, - }, - "t": testingFunctions, - "b": testingFunctions, - "f": testingFunctions, - } - - w := lintUnreachableCode{onFailure, branchingFunctions} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*UnreachableCodeRule) Name() string { - return "unreachable-code" -} - -type lintUnreachableCode struct { - onFailure func(lint.Failure) - branchingFunctions map[string]map[string]bool -} - -func (w lintUnreachableCode) Visit(node ast.Node) ast.Visitor { - blk, ok := node.(*ast.BlockStmt) - if !ok { - return w - } - - if len(blk.List) < 2 { - return w - } -loop: - for i, stmt := range blk.List[:len(blk.List)-1] { - // println("iterating ", len(blk.List)) - next := blk.List[i+1] - if _, ok := next.(*ast.LabeledStmt); ok { - continue // skip if next statement is labeled - } - - switch s := stmt.(type) { - case *ast.ReturnStmt: - w.onFailure(newUnreachableCodeFailure(s)) - break loop - case *ast.BranchStmt: - token := s.Tok.String() - if token != "fallthrough" { - w.onFailure(newUnreachableCodeFailure(s)) - break loop - } - case *ast.ExprStmt: - ce, ok := s.X.(*ast.CallExpr) - if !ok { - continue - } - // it's a function call - fc, ok := ce.Fun.(*ast.SelectorExpr) - if !ok { - continue - } - - id, ok := fc.X.(*ast.Ident) - - if !ok { - continue - } - fn := fc.Sel.Name - pkg := id.Name - if !w.branchingFunctions[pkg][fn] { // it isn't a call to a branching function - continue - } - - if _, ok := next.(*ast.ReturnStmt); ok { // return statement needed to satisfy function signature - continue - } - - w.onFailure(newUnreachableCodeFailure(s)) - break loop - } - } - - return w -} - -func newUnreachableCodeFailure(node ast.Node) lint.Failure { - return lint.Failure{ - Confidence: 1, - Node: node, - Category: "logic", - Failure: "unreachable code after this statement", - } -} diff --git a/vendor/github.com/mgechev/revive/rule/unreachable_code.go b/vendor/github.com/mgechev/revive/rule/unreachable_code.go new file mode 100644 index 000000000..dcc5b7905 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unreachable_code.go @@ -0,0 +1,122 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// UnreachableCodeRule lints unreachable code. +type UnreachableCodeRule struct{} + +// Apply applies the rule to given file. +func (*UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + testingFunctions := map[string]bool{ + "Fatal": true, + "Fatalf": true, + "FailNow": true, + } + branchingFunctions := map[string]map[string]bool{ + "os": {"Exit": true}, + "log": { + "Fatal": true, + "Fatalf": true, + "Fatalln": true, + "Panic": true, + "Panicf": true, + "Panicln": true, + }, + "t": testingFunctions, + "b": testingFunctions, + "f": testingFunctions, + } + + w := lintUnreachableCode{onFailure, branchingFunctions} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*UnreachableCodeRule) Name() string { + return "unreachable-code" +} + +type lintUnreachableCode struct { + onFailure func(lint.Failure) + branchingFunctions map[string]map[string]bool +} + +func (w lintUnreachableCode) Visit(node ast.Node) ast.Visitor { + blk, ok := node.(*ast.BlockStmt) + if !ok { + return w + } + + if len(blk.List) < 2 { + return w + } +loop: + for i, stmt := range blk.List[:len(blk.List)-1] { + // println("iterating ", len(blk.List)) + next := blk.List[i+1] + if _, ok := next.(*ast.LabeledStmt); ok { + continue // skip if next statement is labeled + } + + switch s := stmt.(type) { + case *ast.ReturnStmt: + w.onFailure(newUnreachableCodeFailure(s)) + break loop + case *ast.BranchStmt: + token := s.Tok.String() + if token != "fallthrough" { + w.onFailure(newUnreachableCodeFailure(s)) + break loop + } + case *ast.ExprStmt: + ce, ok := s.X.(*ast.CallExpr) + if !ok { + continue + } + // it's a function call + fc, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + + id, ok := fc.X.(*ast.Ident) + + if !ok { + continue + } + fn := fc.Sel.Name + pkg := id.Name + if !w.branchingFunctions[pkg][fn] { // it isn't a call to a branching function + continue + } + + if _, ok := next.(*ast.ReturnStmt); ok { // return statement needed to satisfy function signature + continue + } + + w.onFailure(newUnreachableCodeFailure(s)) + break loop + } + } + + return w +} + +func newUnreachableCodeFailure(node ast.Node) lint.Failure { + return lint.Failure{ + Confidence: 1, + Node: node, + Category: "logic", + Failure: "unreachable code after this statement", + } +} diff --git a/vendor/github.com/mgechev/revive/rule/unused-param.go b/vendor/github.com/mgechev/revive/rule/unused-param.go deleted file mode 100644 index 4b04ee916..000000000 --- a/vendor/github.com/mgechev/revive/rule/unused-param.go +++ /dev/null @@ -1,167 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "regexp" - "sync" - - "github.com/mgechev/revive/lint" -) - -// UnusedParamRule lints unused params in functions. -type UnusedParamRule struct { - configured bool - // regex to check if some name is valid for unused parameter, "^_$" by default - allowRegex *regexp.Regexp - failureMsg string - sync.Mutex -} - -func (r *UnusedParamRule) configure(args lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.configured { - return - } - r.configured = true - - // while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives - // it's more compatible to JSON nature of configurations - var allowedRegexStr string - if len(args) == 0 { - allowedRegexStr = "^_$" - r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it as _" - } else { - // Arguments = [{}] - options := args[0].(map[string]any) - // Arguments = [{allowedRegex="^_"}] - - if allowedRegexParam, ok := options["allowRegex"]; ok { - allowedRegexStr, ok = allowedRegexParam.(string) - if !ok { - panic(fmt.Errorf("error configuring %s rule: allowedRegex is not string but [%T]", r.Name(), allowedRegexParam)) - } - } - } - var err error - r.allowRegex, err = regexp.Compile(allowedRegexStr) - if err != nil { - panic(fmt.Errorf("error configuring %s rule: allowedRegex is not valid regex [%s]: %v", r.Name(), allowedRegexStr, err)) - } - - if r.failureMsg == "" { - r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it to match " + r.allowRegex.String() - } -} - -// Apply applies the rule to given file. -func (r *UnusedParamRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - w := lintUnusedParamRule{ - onFailure: onFailure, - allowRegex: r.allowRegex, - failureMsg: r.failureMsg, - } - - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*UnusedParamRule) Name() string { - return "unused-parameter" -} - -type lintUnusedParamRule struct { - onFailure func(lint.Failure) - allowRegex *regexp.Regexp - failureMsg string -} - -func (w lintUnusedParamRule) Visit(node ast.Node) ast.Visitor { - var ( - funcType *ast.FuncType - funcBody *ast.BlockStmt - ) - switch n := node.(type) { - case *ast.FuncLit: - funcType = n.Type - funcBody = n.Body - case *ast.FuncDecl: - if n.Body == nil { - return nil // skip, is a function prototype - } - - funcType = n.Type - funcBody = n.Body - default: - return w // skip, not a function - } - - params := retrieveNamedParams(funcType.Params) - if len(params) < 1 { - return w // skip, func without parameters - } - - // inspect the func body looking for references to parameters - fselect := func(n ast.Node) bool { - ident, isAnID := n.(*ast.Ident) - - if !isAnID { - return false - } - - _, isAParam := params[ident.Obj] - if isAParam { - params[ident.Obj] = false // mark as used - } - - return false - } - _ = pick(funcBody, fselect) - - for _, p := range funcType.Params.List { - for _, n := range p.Names { - if w.allowRegex.FindStringIndex(n.Name) != nil { - continue - } - if params[n.Obj] { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: n, - Category: "bad practice", - Failure: fmt.Sprintf(w.failureMsg, n.Name), - }) - } - } - } - - return w // full method body was inspected -} - -func retrieveNamedParams(params *ast.FieldList) map[*ast.Object]bool { - result := map[*ast.Object]bool{} - if params.List == nil { - return result - } - - for _, p := range params.List { - for _, n := range p.Names { - if n.Name == "_" { - continue - } - - result[n.Obj] = true - } - } - - return result -} diff --git a/vendor/github.com/mgechev/revive/rule/unused-receiver.go b/vendor/github.com/mgechev/revive/rule/unused-receiver.go deleted file mode 100644 index 715dba338..000000000 --- a/vendor/github.com/mgechev/revive/rule/unused-receiver.go +++ /dev/null @@ -1,133 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "regexp" - "sync" - - "github.com/mgechev/revive/lint" -) - -// UnusedReceiverRule lints unused params in functions. -type UnusedReceiverRule struct { - configured bool - // regex to check if some name is valid for unused parameter, "^_$" by default - allowRegex *regexp.Regexp - failureMsg string - sync.Mutex -} - -func (r *UnusedReceiverRule) configure(args lint.Arguments) { - r.Lock() - defer r.Unlock() - - if r.configured { - return - } - r.configured = true - - // while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives - // it's more compatible to JSON nature of configurations - var allowedRegexStr string - if len(args) == 0 { - allowedRegexStr = "^_$" - r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _" - } else { - // Arguments = [{}] - options := args[0].(map[string]any) - // Arguments = [{allowedRegex="^_"}] - - if allowedRegexParam, ok := options["allowRegex"]; ok { - allowedRegexStr, ok = allowedRegexParam.(string) - if !ok { - panic(fmt.Errorf("error configuring [unused-receiver] rule: allowedRegex is not string but [%T]", allowedRegexParam)) - } - } - } - var err error - r.allowRegex, err = regexp.Compile(allowedRegexStr) - if err != nil { - panic(fmt.Errorf("error configuring [unused-receiver] rule: allowedRegex is not valid regex [%s]: %v", allowedRegexStr, err)) - } - if r.failureMsg == "" { - r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match " + r.allowRegex.String() - } -} - -// Apply applies the rule to given file. -func (r *UnusedReceiverRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - r.configure(args) - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintUnusedReceiverRule{ - onFailure: onFailure, - allowRegex: r.allowRegex, - failureMsg: r.failureMsg, - } - - ast.Walk(w, file.AST) - - return failures -} - -// Name returns the rule name. -func (*UnusedReceiverRule) Name() string { - return "unused-receiver" -} - -type lintUnusedReceiverRule struct { - onFailure func(lint.Failure) - allowRegex *regexp.Regexp - failureMsg string -} - -func (w lintUnusedReceiverRule) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - if n.Recv == nil { - return nil // skip this func decl, not a method - } - - rec := n.Recv.List[0] // safe to access only the first (unique) element of the list - if len(rec.Names) < 1 { - return nil // the receiver is anonymous: func (aType) Foo(...) ... - } - - recID := rec.Names[0] - if recID.Name == "_" { - return nil // the receiver is already named _ - } - - if w.allowRegex != nil && w.allowRegex.FindStringIndex(recID.Name) != nil { - return nil - } - - // inspect the func body looking for references to the receiver id - fselect := func(n ast.Node) bool { - ident, isAnID := n.(*ast.Ident) - - return isAnID && ident.Obj == recID.Obj - } - refs2recID := pick(n.Body, fselect) - - if len(refs2recID) > 0 { - return nil // the receiver is referenced in the func body - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: recID, - Category: "bad practice", - Failure: fmt.Sprintf(w.failureMsg, recID.Name), - }) - - return nil // full method body already inspected - } - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/unused_param.go b/vendor/github.com/mgechev/revive/rule/unused_param.go new file mode 100644 index 000000000..a8514ac2d --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unused_param.go @@ -0,0 +1,159 @@ +package rule + +import ( + "fmt" + "go/ast" + "regexp" + "sync" + + "github.com/mgechev/revive/lint" +) + +// UnusedParamRule lints unused params in functions. +type UnusedParamRule struct { + // regex to check if some name is valid for unused parameter, "^_$" by default + allowRegex *regexp.Regexp + failureMsg string + + configureOnce sync.Once +} + +func (r *UnusedParamRule) configure(args lint.Arguments) { + // while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives + // it's more compatible to JSON nature of configurations + var allowedRegexStr string + if len(args) == 0 { + allowedRegexStr = "^_$" + r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it as _" + } else { + // Arguments = [{}] + options := args[0].(map[string]any) + // Arguments = [{allowedRegex="^_"}] + + if allowedRegexParam, ok := options["allowRegex"]; ok { + allowedRegexStr, ok = allowedRegexParam.(string) + if !ok { + panic(fmt.Errorf("error configuring %s rule: allowedRegex is not string but [%T]", r.Name(), allowedRegexParam)) + } + } + } + var err error + r.allowRegex, err = regexp.Compile(allowedRegexStr) + if err != nil { + panic(fmt.Errorf("error configuring %s rule: allowedRegex is not valid regex [%s]: %v", r.Name(), allowedRegexStr, err)) + } + + if r.failureMsg == "" { + r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it to match " + r.allowRegex.String() + } +} + +// Apply applies the rule to given file. +func (r *UnusedParamRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + w := lintUnusedParamRule{ + onFailure: onFailure, + allowRegex: r.allowRegex, + failureMsg: r.failureMsg, + } + + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*UnusedParamRule) Name() string { + return "unused-parameter" +} + +type lintUnusedParamRule struct { + onFailure func(lint.Failure) + allowRegex *regexp.Regexp + failureMsg string +} + +func (w lintUnusedParamRule) Visit(node ast.Node) ast.Visitor { + var ( + funcType *ast.FuncType + funcBody *ast.BlockStmt + ) + switch n := node.(type) { + case *ast.FuncLit: + funcType = n.Type + funcBody = n.Body + case *ast.FuncDecl: + if n.Body == nil { + return nil // skip, is a function prototype + } + + funcType = n.Type + funcBody = n.Body + default: + return w // skip, not a function + } + + params := retrieveNamedParams(funcType.Params) + if len(params) < 1 { + return w // skip, func without parameters + } + + // inspect the func body looking for references to parameters + fselect := func(n ast.Node) bool { + ident, isAnID := n.(*ast.Ident) + + if !isAnID { + return false + } + + _, isAParam := params[ident.Obj] + if isAParam { + params[ident.Obj] = false // mark as used + } + + return false + } + _ = pick(funcBody, fselect) + + for _, p := range funcType.Params.List { + for _, n := range p.Names { + if w.allowRegex.FindStringIndex(n.Name) != nil { + continue + } + if params[n.Obj] { + w.onFailure(lint.Failure{ + Confidence: 1, + Node: n, + Category: "bad practice", + Failure: fmt.Sprintf(w.failureMsg, n.Name), + }) + } + } + } + + return w // full method body was inspected +} + +func retrieveNamedParams(params *ast.FieldList) map[*ast.Object]bool { + result := map[*ast.Object]bool{} + if params.List == nil { + return result + } + + for _, p := range params.List { + for _, n := range p.Names { + if n.Name == "_" { + continue + } + + result[n.Obj] = true + } + } + + return result +} diff --git a/vendor/github.com/mgechev/revive/rule/unused_receiver.go b/vendor/github.com/mgechev/revive/rule/unused_receiver.go new file mode 100644 index 000000000..131aae5fb --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/unused_receiver.go @@ -0,0 +1,125 @@ +package rule + +import ( + "fmt" + "go/ast" + "regexp" + "sync" + + "github.com/mgechev/revive/lint" +) + +// UnusedReceiverRule lints unused params in functions. +type UnusedReceiverRule struct { + // regex to check if some name is valid for unused parameter, "^_$" by default + allowRegex *regexp.Regexp + failureMsg string + + configureOnce sync.Once +} + +func (r *UnusedReceiverRule) configure(args lint.Arguments) { + // while by default args is an array, i think it's good to provide structures inside it by default, not arrays or primitives + // it's more compatible to JSON nature of configurations + var allowedRegexStr string + if len(args) == 0 { + allowedRegexStr = "^_$" + r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _" + } else { + // Arguments = [{}] + options := args[0].(map[string]any) + // Arguments = [{allowedRegex="^_"}] + + if allowedRegexParam, ok := options["allowRegex"]; ok { + allowedRegexStr, ok = allowedRegexParam.(string) + if !ok { + panic(fmt.Errorf("error configuring [unused-receiver] rule: allowedRegex is not string but [%T]", allowedRegexParam)) + } + } + } + var err error + r.allowRegex, err = regexp.Compile(allowedRegexStr) + if err != nil { + panic(fmt.Errorf("error configuring [unused-receiver] rule: allowedRegex is not valid regex [%s]: %v", allowedRegexStr, err)) + } + if r.failureMsg == "" { + r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match " + r.allowRegex.String() + } +} + +// Apply applies the rule to given file. +func (r *UnusedReceiverRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(args) }) + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintUnusedReceiverRule{ + onFailure: onFailure, + allowRegex: r.allowRegex, + failureMsg: r.failureMsg, + } + + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*UnusedReceiverRule) Name() string { + return "unused-receiver" +} + +type lintUnusedReceiverRule struct { + onFailure func(lint.Failure) + allowRegex *regexp.Regexp + failureMsg string +} + +func (w lintUnusedReceiverRule) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + if n.Recv == nil { + return nil // skip this func decl, not a method + } + + rec := n.Recv.List[0] // safe to access only the first (unique) element of the list + if len(rec.Names) < 1 { + return nil // the receiver is anonymous: func (aType) Foo(...) ... + } + + recID := rec.Names[0] + if recID.Name == "_" { + return nil // the receiver is already named _ + } + + if w.allowRegex != nil && w.allowRegex.FindStringIndex(recID.Name) != nil { + return nil + } + + // inspect the func body looking for references to the receiver id + fselect := func(n ast.Node) bool { + ident, isAnID := n.(*ast.Ident) + + return isAnID && ident.Obj == recID.Obj + } + refs2recID := pick(n.Body, fselect) + + if len(refs2recID) > 0 { + return nil // the receiver is referenced in the func body + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: recID, + Category: "bad practice", + Failure: fmt.Sprintf(w.failureMsg, recID.Name), + }) + + return nil // full method body already inspected + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/use-any.go b/vendor/github.com/mgechev/revive/rule/use-any.go deleted file mode 100644 index 88160c2fa..000000000 --- a/vendor/github.com/mgechev/revive/rule/use-any.go +++ /dev/null @@ -1,54 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// UseAnyRule lints given else constructs. -type UseAnyRule struct{} - -// Apply applies the rule to given file. -func (*UseAnyRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - walker := lintUseAny{ - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - fileAst := file.AST - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*UseAnyRule) Name() string { - return "use-any" -} - -type lintUseAny struct { - onFailure func(lint.Failure) -} - -func (w lintUseAny) Visit(n ast.Node) ast.Visitor { - it, ok := n.(*ast.InterfaceType) - if !ok { - return w - } - - if len(it.Methods.List) != 0 { - return w // it is not and empty interface - } - - w.onFailure(lint.Failure{ - Node: n, - Confidence: 1, - Category: "naming", - Failure: "since Go 1.18 'interface{}' can be replaced by 'any'", - }) - - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/use_any.go b/vendor/github.com/mgechev/revive/rule/use_any.go new file mode 100644 index 000000000..88160c2fa --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/use_any.go @@ -0,0 +1,54 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// UseAnyRule lints given else constructs. +type UseAnyRule struct{} + +// Apply applies the rule to given file. +func (*UseAnyRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + walker := lintUseAny{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + fileAst := file.AST + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*UseAnyRule) Name() string { + return "use-any" +} + +type lintUseAny struct { + onFailure func(lint.Failure) +} + +func (w lintUseAny) Visit(n ast.Node) ast.Visitor { + it, ok := n.(*ast.InterfaceType) + if !ok { + return w + } + + if len(it.Methods.List) != 0 { + return w // it is not and empty interface + } + + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: "since Go 1.18 'interface{}' can be replaced by 'any'", + }) + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/useless-break.go b/vendor/github.com/mgechev/revive/rule/useless-break.go deleted file mode 100644 index 8db20c9b8..000000000 --- a/vendor/github.com/mgechev/revive/rule/useless-break.go +++ /dev/null @@ -1,82 +0,0 @@ -package rule - -import ( - "go/ast" - "go/token" - - "github.com/mgechev/revive/lint" -) - -// UselessBreak lint rule. -type UselessBreak struct{} - -// Apply applies the rule to given file. -func (*UselessBreak) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - astFile := file.AST - w := &lintUselessBreak{onFailure, false} - ast.Walk(w, astFile) - return failures -} - -// Name returns the rule name. -func (*UselessBreak) Name() string { - return "useless-break" -} - -type lintUselessBreak struct { - onFailure func(lint.Failure) - inLoopBody bool -} - -func (w *lintUselessBreak) Visit(node ast.Node) ast.Visitor { - switch v := node.(type) { - case *ast.ForStmt: - w.inLoopBody = true - ast.Walk(w, v.Body) - w.inLoopBody = false - return nil - case *ast.RangeStmt: - w.inLoopBody = true - ast.Walk(w, v.Body) - w.inLoopBody = false - return nil - case *ast.CommClause: - for _, n := range v.Body { - w.inspectCaseStatement(n) - } - return nil - case *ast.CaseClause: - for _, n := range v.Body { - w.inspectCaseStatement(n) - } - return nil - } - return w -} - -func (w *lintUselessBreak) inspectCaseStatement(n ast.Stmt) { - switch s := n.(type) { - case *ast.BranchStmt: - if s.Tok != token.BREAK { - return // not a break statement - } - if s.Label != nil { - return // labeled break statement, usually affects a nesting loop - } - msg := "useless break in case clause" - if w.inLoopBody { - msg += " (WARN: this break statement affects this switch or select statement and not the loop enclosing it)" - } - w.onFailure(lint.Failure{ - Confidence: 1, - Node: s, - Failure: msg, - }) - } -} diff --git a/vendor/github.com/mgechev/revive/rule/useless_break.go b/vendor/github.com/mgechev/revive/rule/useless_break.go new file mode 100644 index 000000000..8db20c9b8 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/useless_break.go @@ -0,0 +1,82 @@ +package rule + +import ( + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// UselessBreak lint rule. +type UselessBreak struct{} + +// Apply applies the rule to given file. +func (*UselessBreak) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + w := &lintUselessBreak{onFailure, false} + ast.Walk(w, astFile) + return failures +} + +// Name returns the rule name. +func (*UselessBreak) Name() string { + return "useless-break" +} + +type lintUselessBreak struct { + onFailure func(lint.Failure) + inLoopBody bool +} + +func (w *lintUselessBreak) Visit(node ast.Node) ast.Visitor { + switch v := node.(type) { + case *ast.ForStmt: + w.inLoopBody = true + ast.Walk(w, v.Body) + w.inLoopBody = false + return nil + case *ast.RangeStmt: + w.inLoopBody = true + ast.Walk(w, v.Body) + w.inLoopBody = false + return nil + case *ast.CommClause: + for _, n := range v.Body { + w.inspectCaseStatement(n) + } + return nil + case *ast.CaseClause: + for _, n := range v.Body { + w.inspectCaseStatement(n) + } + return nil + } + return w +} + +func (w *lintUselessBreak) inspectCaseStatement(n ast.Stmt) { + switch s := n.(type) { + case *ast.BranchStmt: + if s.Tok != token.BREAK { + return // not a break statement + } + if s.Label != nil { + return // labeled break statement, usually affects a nesting loop + } + msg := "useless break in case clause" + if w.inLoopBody { + msg += " (WARN: this break statement affects this switch or select statement and not the loop enclosing it)" + } + w.onFailure(lint.Failure{ + Confidence: 1, + Node: s, + Failure: msg, + }) + } +} diff --git a/vendor/github.com/mgechev/revive/rule/var-declarations.go b/vendor/github.com/mgechev/revive/rule/var-declarations.go deleted file mode 100644 index 3f9d7068a..000000000 --- a/vendor/github.com/mgechev/revive/rule/var-declarations.go +++ /dev/null @@ -1,122 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" - - "github.com/mgechev/revive/lint" -) - -// VarDeclarationsRule lints given else constructs. -type VarDeclarationsRule struct{} - -// Apply applies the rule to given file. -func (*VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := &lintVarDeclarations{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - file.Pkg.TypeCheck() - ast.Walk(walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*VarDeclarationsRule) Name() string { - return "var-declaration" -} - -type lintVarDeclarations struct { - fileAst *ast.File - file *lint.File - lastGen *ast.GenDecl - onFailure func(lint.Failure) -} - -func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor { - switch v := node.(type) { - case *ast.GenDecl: - isVarOrConstDeclaration := v.Tok == token.CONST || v.Tok == token.VAR - if !isVarOrConstDeclaration { - return nil - } - w.lastGen = v - return w - case *ast.ValueSpec: - isConstDeclaration := w.lastGen.Tok == token.CONST - if isConstDeclaration { - return nil - } - if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 { - return nil - } - rhs := v.Values[0] - // An underscore var appears in a common idiom for compile-time interface satisfaction, - // as in "var _ Interface = (*Concrete)(nil)". - if isIdent(v.Names[0], "_") { - return nil - } - // If the RHS is a isZero value, suggest dropping it. - isZero := false - if lit, ok := rhs.(*ast.BasicLit); ok { - isZero = zeroLiteral[lit.Value] - } else if isIdent(rhs, "nil") { - isZero = true - } - if isZero { - w.onFailure(lint.Failure{ - Confidence: 0.9, - Node: rhs, - Category: "zero-value", - Failure: fmt.Sprintf("should drop = %s from declaration of var %s; it is the zero value", w.file.Render(rhs), v.Names[0]), - }) - return nil - } - lhsTyp := w.file.Pkg.TypeOf(v.Type) - rhsTyp := w.file.Pkg.TypeOf(rhs) - - if !validType(lhsTyp) || !validType(rhsTyp) { - // Type checking failed (often due to missing imports). - return nil - } - - if !types.Identical(lhsTyp, rhsTyp) { - // Assignment to a different type is not redundant. - return nil - } - - // The next three conditions are for suppressing the warning in situations - // where we were unable to typecheck. - - // If the LHS type is an interface, don't warn, since it is probably a - // concrete type on the RHS. Note that our feeble lexical check here - // will only pick up interface{} and other literal interface types; - // that covers most of the cases we care to exclude right now. - if _, ok := v.Type.(*ast.InterfaceType); ok { - return nil - } - // If the RHS is an untyped const, only warn if the LHS type is its default type. - if defType, ok := w.file.IsUntypedConst(rhs); ok && !isIdent(v.Type, defType) { - return nil - } - - w.onFailure(lint.Failure{ - Category: "type-inference", - Confidence: 0.8, - Node: v.Type, - Failure: fmt.Sprintf("should omit type %s from declaration of var %s; it will be inferred from the right-hand side", w.file.Render(v.Type), v.Names[0]), - }) - return nil - } - return w -} diff --git a/vendor/github.com/mgechev/revive/rule/var-naming.go b/vendor/github.com/mgechev/revive/rule/var-naming.go deleted file mode 100644 index 5a4d0dc24..000000000 --- a/vendor/github.com/mgechev/revive/rule/var-naming.go +++ /dev/null @@ -1,280 +0,0 @@ -package rule - -import ( - "fmt" - "go/ast" - "go/token" - "regexp" - "strings" - "sync" - - "github.com/mgechev/revive/lint" -) - -var anyCapsRE = regexp.MustCompile(`[A-Z]`) - -// regexp for constant names like `SOME_CONST`, `SOME_CONST_2`, `X123_3`, `_SOME_PRIVATE_CONST` (#851, #865) -var upperCaseConstRE = regexp.MustCompile(`^_?[A-Z][A-Z\d]*(_[A-Z\d]+)*$`) - -// VarNamingRule lints given else constructs. -type VarNamingRule struct { - configured bool - allowList []string - blockList []string - allowUpperCaseConst bool // if true - allows to use UPPER_SOME_NAMES for constants - skipPackageNameChecks bool - sync.Mutex -} - -func (r *VarNamingRule) configure(arguments lint.Arguments) { - r.Lock() - defer r.Unlock() - if r.configured { - return - } - - r.configured = true - if len(arguments) >= 1 { - r.allowList = getList(arguments[0], "allowlist") - } - - if len(arguments) >= 2 { - r.blockList = getList(arguments[1], "blocklist") - } - - if len(arguments) >= 3 { - // not pretty code because should keep compatibility with TOML (no mixed array types) and new map parameters - thirdArgument := arguments[2] - asSlice, ok := thirdArgument.([]any) - if !ok { - panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, got %T", "options", arguments[2])) - } - if len(asSlice) != 1 { - panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, but %d", "options", len(asSlice))) - } - args, ok := asSlice[0].(map[string]any) - if !ok { - panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0])) - } - r.allowUpperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true" - r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true" - } -} - -func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) { - // Package names need slightly different handling than other names. - if strings.Contains(walker.fileAst.Name.Name, "_") && !strings.HasSuffix(walker.fileAst.Name.Name, "_test") { - walker.onFailure(lint.Failure{ - Failure: "don't use an underscore in package name", - Confidence: 1, - Node: walker.fileAst.Name, - Category: "naming", - }) - } - if anyCapsRE.MatchString(walker.fileAst.Name.Name) { - walker.onFailure(lint.Failure{ - Failure: fmt.Sprintf("don't use MixedCaps in package name; %s should be %s", walker.fileAst.Name.Name, strings.ToLower(walker.fileAst.Name.Name)), - Confidence: 1, - Node: walker.fileAst.Name, - Category: "naming", - }) - } -} - -// Apply applies the rule to given file. -func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - r.configure(arguments) - - var failures []lint.Failure - - fileAst := file.AST - - walker := lintNames{ - file: file, - fileAst: fileAst, - allowList: r.allowList, - blockList: r.blockList, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - upperCaseConst: r.allowUpperCaseConst, - } - - if !r.skipPackageNameChecks { - r.applyPackageCheckRules(&walker) - } - - ast.Walk(&walker, fileAst) - - return failures -} - -// Name returns the rule name. -func (*VarNamingRule) Name() string { - return "var-naming" -} - -func (w *lintNames) checkList(fl *ast.FieldList, thing string) { - if fl == nil { - return - } - for _, f := range fl.List { - for _, id := range f.Names { - w.check(id, thing) - } - } -} - -func (w *lintNames) check(id *ast.Ident, thing string) { - if id.Name == "_" { - return - } - if knownNameExceptions[id.Name] { - return - } - - // #851 upperCaseConst support - // if it's const - if thing == token.CONST.String() && w.upperCaseConst && upperCaseConstRE.MatchString(id.Name) { - return - } - - // Handle two common styles from other languages that don't belong in Go. - if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") { - w.onFailure(lint.Failure{ - Failure: "don't use ALL_CAPS in Go names; use CamelCase", - Confidence: 0.8, - Node: id, - Category: "naming", - }) - return - } - - should := lint.Name(id.Name, w.allowList, w.blockList) - if id.Name == should { - return - } - - if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") { - w.onFailure(lint.Failure{ - Failure: fmt.Sprintf("don't use underscores in Go names; %s %s should be %s", thing, id.Name, should), - Confidence: 0.9, - Node: id, - Category: "naming", - }) - return - } - w.onFailure(lint.Failure{ - Failure: fmt.Sprintf("%s %s should be %s", thing, id.Name, should), - Confidence: 0.8, - Node: id, - Category: "naming", - }) -} - -type lintNames struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) - allowList []string - blockList []string - upperCaseConst bool -} - -func (w *lintNames) Visit(n ast.Node) ast.Visitor { - switch v := n.(type) { - case *ast.AssignStmt: - if v.Tok == token.ASSIGN { - return w - } - for _, exp := range v.Lhs { - if id, ok := exp.(*ast.Ident); ok { - w.check(id, "var") - } - } - case *ast.FuncDecl: - funcName := v.Name.Name - if w.file.IsTest() && - (strings.HasPrefix(funcName, "Example") || - strings.HasPrefix(funcName, "Test") || - strings.HasPrefix(funcName, "Benchmark") || - strings.HasPrefix(funcName, "Fuzz")) { - return w - } - - thing := "func" - if v.Recv != nil { - thing = "method" - } - - // Exclude naming warnings for functions that are exported to C but - // not exported in the Go API. - // See https://github.com/golang/lint/issues/144. - if ast.IsExported(v.Name.Name) || !isCgoExported(v) { - w.check(v.Name, thing) - } - - w.checkList(v.Type.Params, thing+" parameter") - w.checkList(v.Type.Results, thing+" result") - case *ast.GenDecl: - if v.Tok == token.IMPORT { - return w - } - - thing := v.Tok.String() - for _, spec := range v.Specs { - switch s := spec.(type) { - case *ast.TypeSpec: - w.check(s.Name, thing) - case *ast.ValueSpec: - for _, id := range s.Names { - w.check(id, thing) - } - } - } - case *ast.InterfaceType: - // Do not check interface method names. - // They are often constrained by the method names of concrete types. - for _, x := range v.Methods.List { - ft, ok := x.Type.(*ast.FuncType) - if !ok { // might be an embedded interface name - continue - } - w.checkList(ft.Params, "interface method parameter") - w.checkList(ft.Results, "interface method result") - } - case *ast.RangeStmt: - if v.Tok == token.ASSIGN { - return w - } - if id, ok := v.Key.(*ast.Ident); ok { - w.check(id, "range var") - } - if id, ok := v.Value.(*ast.Ident); ok { - w.check(id, "range var") - } - case *ast.StructType: - for _, f := range v.Fields.List { - for _, id := range f.Names { - w.check(id, "struct field") - } - } - } - return w -} - -func getList(arg any, argName string) []string { - args, ok := arg.([]any) - if !ok { - panic(fmt.Sprintf("Invalid argument to the var-naming rule. Expecting a %s of type slice with initialisms, got %T", argName, arg)) - } - var list []string - for _, v := range args { - val, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg)) - } - list = append(list, val) - } - return list -} diff --git a/vendor/github.com/mgechev/revive/rule/var_declarations.go b/vendor/github.com/mgechev/revive/rule/var_declarations.go new file mode 100644 index 000000000..3f9d7068a --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/var_declarations.go @@ -0,0 +1,122 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + + "github.com/mgechev/revive/lint" +) + +// VarDeclarationsRule lints given else constructs. +type VarDeclarationsRule struct{} + +// Apply applies the rule to given file. +func (*VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + fileAst := file.AST + walker := &lintVarDeclarations{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + file.Pkg.TypeCheck() + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*VarDeclarationsRule) Name() string { + return "var-declaration" +} + +type lintVarDeclarations struct { + fileAst *ast.File + file *lint.File + lastGen *ast.GenDecl + onFailure func(lint.Failure) +} + +func (w *lintVarDeclarations) Visit(node ast.Node) ast.Visitor { + switch v := node.(type) { + case *ast.GenDecl: + isVarOrConstDeclaration := v.Tok == token.CONST || v.Tok == token.VAR + if !isVarOrConstDeclaration { + return nil + } + w.lastGen = v + return w + case *ast.ValueSpec: + isConstDeclaration := w.lastGen.Tok == token.CONST + if isConstDeclaration { + return nil + } + if len(v.Names) > 1 || v.Type == nil || len(v.Values) == 0 { + return nil + } + rhs := v.Values[0] + // An underscore var appears in a common idiom for compile-time interface satisfaction, + // as in "var _ Interface = (*Concrete)(nil)". + if isIdent(v.Names[0], "_") { + return nil + } + // If the RHS is a isZero value, suggest dropping it. + isZero := false + if lit, ok := rhs.(*ast.BasicLit); ok { + isZero = zeroLiteral[lit.Value] + } else if isIdent(rhs, "nil") { + isZero = true + } + if isZero { + w.onFailure(lint.Failure{ + Confidence: 0.9, + Node: rhs, + Category: "zero-value", + Failure: fmt.Sprintf("should drop = %s from declaration of var %s; it is the zero value", w.file.Render(rhs), v.Names[0]), + }) + return nil + } + lhsTyp := w.file.Pkg.TypeOf(v.Type) + rhsTyp := w.file.Pkg.TypeOf(rhs) + + if !validType(lhsTyp) || !validType(rhsTyp) { + // Type checking failed (often due to missing imports). + return nil + } + + if !types.Identical(lhsTyp, rhsTyp) { + // Assignment to a different type is not redundant. + return nil + } + + // The next three conditions are for suppressing the warning in situations + // where we were unable to typecheck. + + // If the LHS type is an interface, don't warn, since it is probably a + // concrete type on the RHS. Note that our feeble lexical check here + // will only pick up interface{} and other literal interface types; + // that covers most of the cases we care to exclude right now. + if _, ok := v.Type.(*ast.InterfaceType); ok { + return nil + } + // If the RHS is an untyped const, only warn if the LHS type is its default type. + if defType, ok := w.file.IsUntypedConst(rhs); ok && !isIdent(v.Type, defType) { + return nil + } + + w.onFailure(lint.Failure{ + Category: "type-inference", + Confidence: 0.8, + Node: v.Type, + Failure: fmt.Sprintf("should omit type %s from declaration of var %s; it will be inferred from the right-hand side", w.file.Render(v.Type), v.Names[0]), + }) + return nil + } + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/var_naming.go b/vendor/github.com/mgechev/revive/rule/var_naming.go new file mode 100644 index 000000000..2c2198dbd --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/var_naming.go @@ -0,0 +1,273 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "regexp" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +var anyCapsRE = regexp.MustCompile(`[A-Z]`) + +// regexp for constant names like `SOME_CONST`, `SOME_CONST_2`, `X123_3`, `_SOME_PRIVATE_CONST` (#851, #865) +var upperCaseConstRE = regexp.MustCompile(`^_?[A-Z][A-Z\d]*(_[A-Z\d]+)*$`) + +// VarNamingRule lints given else constructs. +type VarNamingRule struct { + allowList []string + blockList []string + allowUpperCaseConst bool // if true - allows to use UPPER_SOME_NAMES for constants + skipPackageNameChecks bool + + configureOnce sync.Once +} + +func (r *VarNamingRule) configure(arguments lint.Arguments) { + if len(arguments) >= 1 { + r.allowList = getList(arguments[0], "allowlist") + } + + if len(arguments) >= 2 { + r.blockList = getList(arguments[1], "blocklist") + } + + if len(arguments) >= 3 { + // not pretty code because should keep compatibility with TOML (no mixed array types) and new map parameters + thirdArgument := arguments[2] + asSlice, ok := thirdArgument.([]any) + if !ok { + panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, got %T", "options", arguments[2])) + } + if len(asSlice) != 1 { + panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, but %d", "options", len(asSlice))) + } + args, ok := asSlice[0].(map[string]any) + if !ok { + panic(fmt.Sprintf("Invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0])) + } + r.allowUpperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true" + r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true" + } +} + +func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) { + // Package names need slightly different handling than other names. + if strings.Contains(walker.fileAst.Name.Name, "_") && !strings.HasSuffix(walker.fileAst.Name.Name, "_test") { + walker.onFailure(lint.Failure{ + Failure: "don't use an underscore in package name", + Confidence: 1, + Node: walker.fileAst.Name, + Category: "naming", + }) + } + if anyCapsRE.MatchString(walker.fileAst.Name.Name) { + walker.onFailure(lint.Failure{ + Failure: fmt.Sprintf("don't use MixedCaps in package name; %s should be %s", walker.fileAst.Name.Name, strings.ToLower(walker.fileAst.Name.Name)), + Confidence: 1, + Node: walker.fileAst.Name, + Category: "naming", + }) + } +} + +// Apply applies the rule to given file. +func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configureOnce.Do(func() { r.configure(arguments) }) + + var failures []lint.Failure + + fileAst := file.AST + + walker := lintNames{ + file: file, + fileAst: fileAst, + allowList: r.allowList, + blockList: r.blockList, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + upperCaseConst: r.allowUpperCaseConst, + } + + if !r.skipPackageNameChecks { + r.applyPackageCheckRules(&walker) + } + + ast.Walk(&walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*VarNamingRule) Name() string { + return "var-naming" +} + +func (w *lintNames) checkList(fl *ast.FieldList, thing string) { + if fl == nil { + return + } + for _, f := range fl.List { + for _, id := range f.Names { + w.check(id, thing) + } + } +} + +func (w *lintNames) check(id *ast.Ident, thing string) { + if id.Name == "_" { + return + } + if knownNameExceptions[id.Name] { + return + } + + // #851 upperCaseConst support + // if it's const + if thing == token.CONST.String() && w.upperCaseConst && upperCaseConstRE.MatchString(id.Name) { + return + } + + // Handle two common styles from other languages that don't belong in Go. + if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") { + w.onFailure(lint.Failure{ + Failure: "don't use ALL_CAPS in Go names; use CamelCase", + Confidence: 0.8, + Node: id, + Category: "naming", + }) + return + } + + should := lint.Name(id.Name, w.allowList, w.blockList) + if id.Name == should { + return + } + + if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") { + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("don't use underscores in Go names; %s %s should be %s", thing, id.Name, should), + Confidence: 0.9, + Node: id, + Category: "naming", + }) + return + } + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("%s %s should be %s", thing, id.Name, should), + Confidence: 0.8, + Node: id, + Category: "naming", + }) +} + +type lintNames struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) + allowList []string + blockList []string + upperCaseConst bool +} + +func (w *lintNames) Visit(n ast.Node) ast.Visitor { + switch v := n.(type) { + case *ast.AssignStmt: + if v.Tok == token.ASSIGN { + return w + } + for _, exp := range v.Lhs { + if id, ok := exp.(*ast.Ident); ok { + w.check(id, "var") + } + } + case *ast.FuncDecl: + funcName := v.Name.Name + if w.file.IsTest() && + (strings.HasPrefix(funcName, "Example") || + strings.HasPrefix(funcName, "Test") || + strings.HasPrefix(funcName, "Benchmark") || + strings.HasPrefix(funcName, "Fuzz")) { + return w + } + + thing := "func" + if v.Recv != nil { + thing = "method" + } + + // Exclude naming warnings for functions that are exported to C but + // not exported in the Go API. + // See https://github.com/golang/lint/issues/144. + if ast.IsExported(v.Name.Name) || !isCgoExported(v) { + w.check(v.Name, thing) + } + + w.checkList(v.Type.Params, thing+" parameter") + w.checkList(v.Type.Results, thing+" result") + case *ast.GenDecl: + if v.Tok == token.IMPORT { + return w + } + + thing := v.Tok.String() + for _, spec := range v.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + w.check(s.Name, thing) + case *ast.ValueSpec: + for _, id := range s.Names { + w.check(id, thing) + } + } + } + case *ast.InterfaceType: + // Do not check interface method names. + // They are often constrained by the method names of concrete types. + for _, x := range v.Methods.List { + ft, ok := x.Type.(*ast.FuncType) + if !ok { // might be an embedded interface name + continue + } + w.checkList(ft.Params, "interface method parameter") + w.checkList(ft.Results, "interface method result") + } + case *ast.RangeStmt: + if v.Tok == token.ASSIGN { + return w + } + if id, ok := v.Key.(*ast.Ident); ok { + w.check(id, "range var") + } + if id, ok := v.Value.(*ast.Ident); ok { + w.check(id, "range var") + } + case *ast.StructType: + for _, f := range v.Fields.List { + for _, id := range f.Names { + w.check(id, "struct field") + } + } + } + return w +} + +func getList(arg any, argName string) []string { + args, ok := arg.([]any) + if !ok { + panic(fmt.Sprintf("Invalid argument to the var-naming rule. Expecting a %s of type slice with initialisms, got %T", argName, arg)) + } + var list []string + for _, v := range args { + val, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg)) + } + list = append(list, val) + } + return list +} diff --git a/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go b/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go deleted file mode 100644 index a2d304ae5..000000000 --- a/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go +++ /dev/null @@ -1,66 +0,0 @@ -package rule - -import ( - "go/ast" - - "github.com/mgechev/revive/lint" -) - -// WaitGroupByValueRule lints sync.WaitGroup passed by copy in functions. -type WaitGroupByValueRule struct{} - -// Apply applies the rule to given file. -func (*WaitGroupByValueRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - onFailure := func(failure lint.Failure) { - failures = append(failures, failure) - } - - w := lintWaitGroupByValueRule{onFailure: onFailure} - ast.Walk(w, file.AST) - return failures -} - -// Name returns the rule name. -func (*WaitGroupByValueRule) Name() string { - return "waitgroup-by-value" -} - -type lintWaitGroupByValueRule struct { - onFailure func(lint.Failure) -} - -func (w lintWaitGroupByValueRule) Visit(node ast.Node) ast.Visitor { - // look for function declarations - fd, ok := node.(*ast.FuncDecl) - if !ok { - return w - } - - // Check all function's parameters - for _, field := range fd.Type.Params.List { - if !w.isWaitGroup(field.Type) { - continue - } - - w.onFailure(lint.Failure{ - Confidence: 1, - Node: field, - Failure: "sync.WaitGroup passed by value, the function will get a copy of the original one", - }) - } - - return nil // skip visiting function body -} - -func (lintWaitGroupByValueRule) isWaitGroup(ft ast.Expr) bool { - se, ok := ft.(*ast.SelectorExpr) - if !ok { - return false - } - - x, _ := se.X.(*ast.Ident) - sel := se.Sel.Name - return x.Name == "sync" && sel == "WaitGroup" -} diff --git a/vendor/github.com/mgechev/revive/rule/waitgroup_by_value.go b/vendor/github.com/mgechev/revive/rule/waitgroup_by_value.go new file mode 100644 index 000000000..a2d304ae5 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/waitgroup_by_value.go @@ -0,0 +1,66 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// WaitGroupByValueRule lints sync.WaitGroup passed by copy in functions. +type WaitGroupByValueRule struct{} + +// Apply applies the rule to given file. +func (*WaitGroupByValueRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintWaitGroupByValueRule{onFailure: onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*WaitGroupByValueRule) Name() string { + return "waitgroup-by-value" +} + +type lintWaitGroupByValueRule struct { + onFailure func(lint.Failure) +} + +func (w lintWaitGroupByValueRule) Visit(node ast.Node) ast.Visitor { + // look for function declarations + fd, ok := node.(*ast.FuncDecl) + if !ok { + return w + } + + // Check all function's parameters + for _, field := range fd.Type.Params.List { + if !w.isWaitGroup(field.Type) { + continue + } + + w.onFailure(lint.Failure{ + Confidence: 1, + Node: field, + Failure: "sync.WaitGroup passed by value, the function will get a copy of the original one", + }) + } + + return nil // skip visiting function body +} + +func (lintWaitGroupByValueRule) isWaitGroup(ft ast.Expr) bool { + se, ok := ft.(*ast.SelectorExpr) + if !ok { + return false + } + + x, _ := se.X.(*ast.Ident) + sel := se.Sel.Name + return x.Name == "sync" && sel == "WaitGroup" +} diff --git a/vendor/github.com/nunnatsa/ginkgolinter/README.md b/vendor/github.com/nunnatsa/ginkgolinter/README.md index 536a65e7b..012628ed7 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/README.md +++ b/vendor/github.com/nunnatsa/ginkgolinter/README.md @@ -249,7 +249,7 @@ This will probably happen when using the old format: Eventually(aFunc, 500 * time.Millisecond /*timeout*/, 10 * time.Second /*polling*/).Should(Succeed()) ``` -### Correct usage of the `Succeed()` matcher [Bug] +### Prevent Wrong Actual Values with the Succeed() matcher [Bug] The `Succeed()` matcher only accepts a single error value. this rule validates that. For example: @@ -271,6 +271,8 @@ a Gomega object as their first parameter, and returns nothing, e.g. this is a va }).WithTimeout(10 * time.Millisecond).WithPolling(time.Millisecond).Should(Succeed()) ``` +***Note***: This rule **does not** support auto-fix. + ### Avoid Spec Pollution: Don't Initialize Variables in Container Nodes [BUG/STYLE]: ***Note***: Only applied when the `--forbid-spec-pollution=true` flag is set (disabled by default). @@ -522,6 +524,8 @@ will trigger a warning with a suggestion to replace the mather to ``` ***This rule is disabled by default***. Use the `--force-succeed=true` command line flag to enable it. +***Note***: This rule **does** support auto-fix, when the `--fix` command line parameter is used. + ## Suppress the linter ### Suppress warning from command line * Use the `--suppress-len-assertion=true` flag to suppress the wrong length and cap assertions warning diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actual.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actual.go index 8e3df5d3f..c289b24de 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actual.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actual.go @@ -21,13 +21,13 @@ type Actual struct { actualOffset int } -func New(origExpr, cloneExpr *ast.CallExpr, orig *ast.CallExpr, clone *ast.CallExpr, pass *analysis.Pass, handler gomegahandler.Handler, timePkg string) (*Actual, bool) { +func New(origExpr, cloneExpr *ast.CallExpr, orig *ast.CallExpr, clone *ast.CallExpr, pass *analysis.Pass, handler gomegahandler.Handler, timePkg string, errMethodExists bool) (*Actual, bool) { funcName, ok := handler.GetActualFuncName(orig) if !ok { return nil, false } - arg, actualOffset := getActualArgPayload(orig, clone, pass, funcName) + arg, actualOffset := getActualArgPayload(orig, clone, pass, funcName, errMethodExists) if arg == nil { return nil, false } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go index 9d251c468..541a22330 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go @@ -28,6 +28,7 @@ const ( ErrFuncActualArgType GomegaParamArgType MultiRetsArgType + ErrorMethodArgType ErrorTypeArgType @@ -39,7 +40,7 @@ func (a ArgType) Is(val ArgType) bool { return a&val != 0 } -func getActualArgPayload(origActualExpr, actualExprClone *ast.CallExpr, pass *analysis.Pass, actualMethodName string) (ArgPayload, int) { +func getActualArgPayload(origActualExpr, actualExprClone *ast.CallExpr, pass *analysis.Pass, actualMethodName string, errMethodExists bool) (ArgPayload, int) { origArgExpr, argExprClone, actualOffset, isGomegaExpr := getActualArg(origActualExpr, actualExprClone, actualMethodName, pass) if !isGomegaExpr { return nil, 0 @@ -47,7 +48,9 @@ func getActualArgPayload(origActualExpr, actualExprClone *ast.CallExpr, pass *an var arg ArgPayload - if value.IsExprError(pass, origArgExpr) { + if errMethodExists { + arg = &ErrorMethodPayload{} + } else if value.IsExprError(pass, origArgExpr) { arg = newErrPayload(origArgExpr, argExprClone, pass) } else { switch expr := origArgExpr.(type) { @@ -56,12 +59,6 @@ func getActualArgPayload(origActualExpr, actualExprClone *ast.CallExpr, pass *an case *ast.BinaryExpr: arg = parseBinaryExpr(expr, argExprClone.(*ast.BinaryExpr), pass) - - default: - t := pass.TypesInfo.TypeOf(origArgExpr) - if sig, ok := t.(*gotypes.Signature); ok { - arg = getAsyncFuncArg(sig) - } } } @@ -70,6 +67,14 @@ func getActualArgPayload(origActualExpr, actualExprClone *ast.CallExpr, pass *an return arg, actualOffset } + t := pass.TypesInfo.TypeOf(origArgExpr) + if sig, ok := t.(*gotypes.Signature); ok { + arg = getAsyncFuncArg(sig) + if arg != nil { + return arg, actualOffset + } + } + return newRegularArgPayload(origArgExpr, argExprClone, pass), actualOffset } @@ -181,6 +186,12 @@ func (*ErrPayload) ArgType() ArgType { return ErrActualArgType | ErrorTypeArgType } +type ErrorMethodPayload struct{} + +func (ErrorMethodPayload) ArgType() ArgType { + return ErrorMethodArgType | ErrorTypeArgType +} + func parseBinaryExpr(origActualExpr, argExprClone *ast.BinaryExpr, pass *analysis.Pass) ArgPayload { left, right, op := origActualExpr.X, origActualExpr.Y, origActualExpr.Op replace := false diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/expression.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/expression.go index 976e726fc..c2aa702b1 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/expression.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/expression.go @@ -52,7 +52,9 @@ func New(origExpr *ast.CallExpr, pass *analysis.Pass, handler gomegahandler.Hand exprClone := astcopy.CallExpr(origExpr) selClone := exprClone.Fun.(*ast.SelectorExpr) - origActual := handler.GetActualExpr(origSel) + errMethodExists := false + + origActual := handler.GetActualExpr(origSel, &errMethodExists) if origActual == nil { return nil, false } @@ -62,7 +64,7 @@ func New(origExpr *ast.CallExpr, pass *analysis.Pass, handler gomegahandler.Hand return nil, false } - actl, ok := actual.New(origExpr, exprClone, origActual, actualClone, pass, handler, timePkg) + actl, ok := actual.New(origExpr, exprClone, origActual, actualClone, pass, handler, timePkg, errMethodExists) if !ok { return nil, false } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/dothandler.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/dothandler.go index bd3b93992..c853ca906 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/dothandler.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/dothandler.go @@ -51,7 +51,7 @@ func (dotHandler) GetNewWrapperMatcher(name string, existing *ast.CallExpr) *ast } } -func (h dotHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr { +func (h dotHandler) GetActualExpr(assertionFunc *ast.SelectorExpr, errMethodExists *bool) *ast.CallExpr { actualExpr, ok := assertionFunc.X.(*ast.CallExpr) if !ok { return nil @@ -66,7 +66,11 @@ func (h dotHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr return actualExpr } } else { - return h.GetActualExpr(fun) + if fun.Sel.Name == "Error" { + *errMethodExists = true + } + + return h.GetActualExpr(fun, errMethodExists) } } return nil diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/handler.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/handler.go index 4dba604a4..414438ba6 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/handler.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/handler.go @@ -18,7 +18,7 @@ type Handler interface { // ReplaceFunction replaces the function with another one, for fix suggestions ReplaceFunction(*ast.CallExpr, *ast.Ident) - GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr + GetActualExpr(assertionFunc *ast.SelectorExpr, errMethodExists *bool) *ast.CallExpr GetActualExprClone(origFunc, funcClone *ast.SelectorExpr) *ast.CallExpr diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/namedhandler.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/namedhandler.go index 712442426..66d563311 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/namedhandler.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/gomegahandler/namedhandler.go @@ -51,7 +51,7 @@ func (g nameHandler) isGomegaVar(x ast.Expr) bool { return gomegainfo.IsGomegaVar(x, g.pass) } -func (g nameHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr { +func (g nameHandler) GetActualExpr(assertionFunc *ast.SelectorExpr, errMethodExists *bool) *ast.CallExpr { actualExpr, ok := assertionFunc.X.(*ast.CallExpr) if !ok { return nil @@ -69,7 +69,10 @@ func (g nameHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExp return actualExpr } } else { - return g.GetActualExpr(fun) + if fun.Sel.Name == "Error" { + *errMethodExists = true + } + return g.GetActualExpr(fun, errMethodExists) } } return nil diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go index 7aaf7631b..81932cc2c 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go @@ -12,8 +12,15 @@ import ( type ErrorEqualNilRule struct{} func (ErrorEqualNilRule) isApplied(gexp *expression.GomegaExpression, config types.Config) bool { - return !bool(config.SuppressErr) && - gexp.ActualArgTypeIs(actual.ErrorTypeArgType) && + if config.SuppressErr { + return false + } + + if !gexp.IsAsync() && gexp.ActualArgTypeIs(actual.FuncSigArgType) { + return false + } + + return gexp.ActualArgTypeIs(actual.ErrorTypeArgType) && gexp.MatcherTypeIs(matcher.BeNilMatcherType|matcher.EqualNilMatcherType) } diff --git a/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go b/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go index cf481708a..c639af6f3 100644 --- a/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go +++ b/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go @@ -53,6 +53,7 @@ func setDefaultAllowedErrors() { {Err: "io.EOF", Fun: "(*io.SectionReader).Read"}, {Err: "io.EOF", Fun: "(*io.SectionReader).ReadAt"}, {Err: "io.ErrClosedPipe", Fun: "(*io.PipeWriter).Write"}, + {Err: "io.EOF", Fun: "io.ReadAtLeast"}, {Err: "io.ErrShortBuffer", Fun: "io.ReadAtLeast"}, {Err: "io.ErrUnexpectedEOF", Fun: "io.ReadAtLeast"}, {Err: "io.EOF", Fun: "io.ReadFull"}, @@ -84,6 +85,7 @@ func setDefaultAllowedErrors() { {Err: "context.Canceled", Fun: "(context.Context).Err"}, // pkg/encoding/json {Err: "io.EOF", Fun: "(*encoding/json.Decoder).Decode"}, + {Err: "io.EOF", Fun: "(*encoding/json.Decoder).Token"}, // pkg/encoding/csv {Err: "io.EOF", Fun: "(*encoding/csv.Reader).Read"}, // pkg/mime/multipart diff --git a/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go b/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go index 9ac465c65..ed3dd0dc6 100644 --- a/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go +++ b/vendor/github.com/polyfloyd/go-errorlint/errorlint/lint.go @@ -82,7 +82,7 @@ func LintFmtErrorfCalls(fset *token.FileSet, info types.Info, multipleWraps bool argIndex++ } - if verb.format == "w" { + if verb.format == "w" || verb.format == "T" { continue } if argIndex-1 >= len(args) { diff --git a/vendor/github.com/uudashr/iface/opaque/opaque.go b/vendor/github.com/uudashr/iface/opaque/opaque.go index fda0f001b..f8b7bf4c6 100644 --- a/vendor/github.com/uudashr/iface/opaque/opaque.go +++ b/vendor/github.com/uudashr/iface/opaque/opaque.go @@ -53,6 +53,11 @@ func (r *runner) run(pass *analysis.Pass) (interface{}, error) { return } + if funcDecl.Body == nil { + // skip functions without body + return + } + if funcDecl.Type.Results == nil { // skip functions without return values return @@ -133,7 +138,7 @@ func (r *runner) run(pass *analysis.Pass) (interface{}, error) { typ := pass.TypesInfo.TypeOf(res) switch typ := typ.(type) { case *types.Tuple: - for i := 0; i < typ.Len(); i++ { + for i := range typ.Len() { v := typ.At(i) vTyp := v.Type() retStmtTypes[i][vTyp] = struct{}{} diff --git a/vendor/modules.txt b/vendor/modules.txt index 444df005c..58053fb5a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -116,7 +116,7 @@ github.com/Antonboom/errname/pkg/analyzer # github.com/Antonboom/nilnil v1.0.0 ## explicit; go 1.22.0 github.com/Antonboom/nilnil/pkg/analyzer -# github.com/Antonboom/testifylint v1.5.0 +# github.com/Antonboom/testifylint v1.5.2 ## explicit; go 1.22.1 github.com/Antonboom/testifylint/analyzer github.com/Antonboom/testifylint/internal/analysisutil @@ -128,7 +128,7 @@ github.com/Antonboom/testifylint/internal/testify ## explicit; go 1.18 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal -# github.com/Crocmagnon/fatcontext v0.5.2 +# github.com/Crocmagnon/fatcontext v0.5.3 ## explicit; go 1.22.0 github.com/Crocmagnon/fatcontext/pkg/analyzer # github.com/Djarvur/go-err113 v0.1.0 @@ -448,7 +448,7 @@ github.com/golangci/go-printf-func-name/pkg/analyzer github.com/golangci/gofmt/gofmt github.com/golangci/gofmt/gofmt/internal/diff github.com/golangci/gofmt/goimports -# github.com/golangci/golangci-lint v1.62.0 +# github.com/golangci/golangci-lint v1.62.2 ## explicit; go 1.22.1 github.com/golangci/golangci-lint/cmd/golangci-lint github.com/golangci/golangci-lint/internal/cache @@ -803,7 +803,7 @@ github.com/mattn/go-isatty # github.com/mattn/go-runewidth v0.0.16 ## explicit; go 1.9 github.com/mattn/go-runewidth -# github.com/mgechev/revive v1.5.0 +# github.com/mgechev/revive v1.5.1 ## explicit; go 1.22.1 github.com/mgechev/revive/config github.com/mgechev/revive/formatter @@ -834,7 +834,7 @@ github.com/nishanths/exhaustive # github.com/nishanths/predeclared v0.2.2 ## explicit; go 1.14 github.com/nishanths/predeclared/passes/predeclared -# github.com/nunnatsa/ginkgolinter v0.18.0 +# github.com/nunnatsa/ginkgolinter v0.18.3 ## explicit; go 1.22.0 github.com/nunnatsa/ginkgolinter github.com/nunnatsa/ginkgolinter/internal/expression @@ -882,7 +882,7 @@ github.com/planetscale/vtprotobuf/types/known/wrapperspb # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/polyfloyd/go-errorlint v1.6.0 +# github.com/polyfloyd/go-errorlint v1.7.0 ## explicit; go 1.20 github.com/polyfloyd/go-errorlint/errorlint # github.com/prometheus/client_golang v1.20.5 @@ -1094,8 +1094,8 @@ github.com/ultraware/whitespace # github.com/uudashr/gocognit v1.1.3 ## explicit; go 1.18 github.com/uudashr/gocognit -# github.com/uudashr/iface v1.2.0 -## explicit; go 1.21.0 +# github.com/uudashr/iface v1.2.1 +## explicit; go 1.22.1 github.com/uudashr/iface/identical github.com/uudashr/iface/internal/directive github.com/uudashr/iface/opaque @@ -1219,7 +1219,7 @@ golang.org/x/crypto/internal/poly1305 ## explicit; go 1.22.0 golang.org/x/exp/constraints golang.org/x/exp/maps -# golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 +# golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f ## explicit; go 1.18 golang.org/x/exp/typeparams # golang.org/x/mod v0.22.0 -- cgit mrf-deployment